Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/editors/include/ED_uvedit.h5
-rw-r--r--source/blender/editors/uvedit/uvedit_islands.c176
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c72
3 files changed, 247 insertions, 6 deletions
diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h
index ea3d921f2c5..516239a7176 100644
--- a/source/blender/editors/include/ED_uvedit.h
+++ b/source/blender/editors/include/ED_uvedit.h
@@ -246,9 +246,14 @@ struct UVPackIsland_Params {
uint use_seams : 1;
uint correct_aspect : 1;
};
+
+bool uv_coords_isect_udim(const struct Image *image, const int udim_grid[2], float coords[2]);
void ED_uvedit_pack_islands_multi(const struct Scene *scene,
+ const struct SpaceImage *sima,
Object **objects,
const uint objects_len,
+ const bool use_target_udim,
+ int target_udim,
const struct UVPackIsland_Params *params);
#ifdef __cplusplus
diff --git a/source/blender/editors/uvedit/uvedit_islands.c b/source/blender/editors/uvedit/uvedit_islands.c
index 56bcbc63de1..2aa09d7e731 100644
--- a/source/blender/editors/uvedit/uvedit_islands.c
+++ b/source/blender/editors/uvedit/uvedit_islands.c
@@ -29,6 +29,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_scene_types.h"
+#include "DNA_space_types.h"
#include "BLI_boxpack_2d.h"
#include "BLI_convexhull_2d.h"
@@ -38,6 +39,7 @@
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
+#include "BKE_image.h"
#include "DEG_depsgraph.h"
@@ -232,6 +234,99 @@ static void bm_face_array_uv_scale_y(BMFace **faces,
/** \} */
/* -------------------------------------------------------------------- */
+/** \name UDIM packing helper functions
+ * \{ */
+
+/* Returns true if UV coordinates lie on a valid tile in UDIM grid or tiled image. */
+bool uv_coords_isect_udim(const Image *image, const int udim_grid[2], float coords[2])
+{
+ const float coords_floor[2] = {floorf(coords[0]), floorf(coords[1])};
+ const bool is_tiled_image = image && (image->source == IMA_SRC_TILED);
+
+ if (coords[0] < udim_grid[0] && coords[0] > 0 && coords[1] < udim_grid[1] && coords[1] > 0) {
+ return true;
+ }
+ /* Check if selection lies on a valid UDIM image tile. */
+ else if (is_tiled_image) {
+ LISTBASE_FOREACH (const ImageTile *, tile, &image->tiles) {
+ const int tile_index = tile->tile_number - 1001;
+ const int target_x = (tile_index % 10);
+ const int target_y = (tile_index / 10);
+ if (coords_floor[0] == target_x && coords_floor[1] == target_y) {
+ return true;
+ }
+ }
+ }
+ /* Probably not required since UDIM grid checks for 1001. */
+ else if (image && !is_tiled_image) {
+ if (is_zero_v2(coords_floor)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Calculates distance to nearest UDIM image tile in UV space and its UDIM tile number.
+ */
+static float uv_nearest_image_tile_distance(const Image *image,
+ float coords[2],
+ float nearest_tile_co[2])
+{
+ int nearest_image_tile_index = BKE_image_find_nearest_tile(image, coords);
+ if (nearest_image_tile_index == -1) {
+ nearest_image_tile_index = 1001;
+ }
+
+ nearest_tile_co[0] = (nearest_image_tile_index - 1001) % 10;
+ nearest_tile_co[1] = (nearest_image_tile_index - 1001) / 10;
+ /* Add 0.5 to get tile center coordinates. */
+ float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]};
+ add_v2_fl(nearest_tile_center_co, 0.5f);
+
+ return len_squared_v2v2(coords, nearest_tile_center_co);
+}
+
+/**
+ * Calculates distance to nearest UDIM grid tile in UV space and its UDIM tile number.
+ */
+static float uv_nearest_grid_tile_distance(const int udim_grid[2],
+ float coords[2],
+ float nearest_tile_co[2])
+{
+ const float coords_floor[2] = {floorf(coords[0]), floorf(coords[1])};
+
+ if (coords[0] > udim_grid[0]) {
+ nearest_tile_co[0] = udim_grid[0] - 1;
+ }
+ else if (coords[0] < 0) {
+ nearest_tile_co[0] = 0;
+ }
+ else {
+ nearest_tile_co[0] = coords_floor[0];
+ }
+
+ if (coords[1] > udim_grid[1]) {
+ nearest_tile_co[1] = udim_grid[1] - 1;
+ }
+ else if (coords[1] < 0) {
+ nearest_tile_co[1] = 0;
+ }
+ else {
+ nearest_tile_co[1] = coords_floor[1];
+ }
+
+ /* Add 0.5 to get tile center coordinates. */
+ float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]};
+ add_v2_fl(nearest_tile_center_co, 0.5f);
+
+ return len_squared_v2v2(coords, nearest_tile_center_co);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Calculate UV Islands
*
* \note Currently this is a private API/type, it could be made public.
@@ -357,8 +452,11 @@ static int bm_mesh_calc_uv_islands(const Scene *scene,
* \{ */
void ED_uvedit_pack_islands_multi(const Scene *scene,
+ const SpaceImage *sima,
Object **objects,
const uint objects_len,
+ const bool use_target_udim,
+ int target_udim,
const struct UVPackIsland_Params *params)
{
/* Align to the Y axis, could make this configurable. */
@@ -407,8 +505,26 @@ void ED_uvedit_pack_islands_multi(const Scene *scene,
BoxPack *boxarray = MEM_mallocN(sizeof(*boxarray) * island_list_len, __func__);
int index;
+ /* Coordinates of bounding box containing all selected UVs. */
+ float selection_min_co[2], selection_max_co[2];
+ INIT_MINMAX2(selection_min_co, selection_max_co);
+
LISTBASE_FOREACH_INDEX (struct FaceIsland *, island, &island_list, index) {
+ /* Skip calculation if using specified UDIM option. */
+ if (!use_target_udim) {
+ float bounds_min[2], bounds_max[2];
+ INIT_MINMAX2(bounds_min, bounds_max);
+ for (int i = 0; i < island->faces_len; i++) {
+ BMFace *f = island->faces[i];
+ BM_face_uv_minmax(f, bounds_min, bounds_max, island->cd_loop_uv_offset);
+ }
+
+ selection_min_co[0] = MIN2(bounds_min[0], selection_min_co[0]);
+ selection_min_co[1] = MIN2(bounds_min[1], selection_min_co[1]);
+ selection_max_co[0] = MAX2(bounds_max[0], selection_max_co[0]);
+ selection_max_co[1] = MAX2(bounds_max[1], selection_max_co[1]);
+ }
if (params->rotate) {
if (island->aspect_y != 1.0f) {
bm_face_array_uv_scale_y(
@@ -441,6 +557,13 @@ void ED_uvedit_pack_islands_multi(const Scene *scene,
}
}
+ /* Center of bounding box containing all selected UVs. */
+ float selection_center[2];
+ if (!use_target_udim) {
+ selection_center[0] = (selection_min_co[0] + selection_max_co[0]) / 2.0f;
+ selection_center[1] = (selection_min_co[1] + selection_max_co[1]) / 2.0f;
+ }
+
if (margin > 0.0f) {
/* Logic matches behavior from #param_pack,
* use area so multiply the margin by the area to give
@@ -464,6 +587,55 @@ void ED_uvedit_pack_islands_multi(const Scene *scene,
const float scale[2] = {1.0f / boxarray_size[0], 1.0f / boxarray_size[1]};
+ /* Tile offset. */
+ float base_offset[2] = {0.0f, 0.0f};
+
+ /* CASE: Active/specified(smart uv project) UDIM. */
+ if (use_target_udim) {
+ /* Calculate offset based on specified_tile_index. */
+ base_offset[0] = (target_udim - 1001) % 10;
+ base_offset[1] = (target_udim - 1001) / 10;
+ }
+
+ /* CASE: Closest UDIM. */
+ else {
+ const Image *image;
+ int udim_grid[2] = {1, 1};
+ /* To handle cases where `sima == NULL` - Smart UV project in 3D viewport. */
+ if (sima != NULL) {
+ image = sima->image;
+ udim_grid[0] = sima->tile_grid_shape[0];
+ udim_grid[1] = sima->tile_grid_shape[1];
+ }
+
+ /* Check if selection lies on a valid UDIM grid tile. */
+ bool is_valid_udim = uv_coords_isect_udim(image, udim_grid, selection_center);
+ if (is_valid_udim) {
+ base_offset[0] = floorf(selection_center[0]);
+ base_offset[1] = floorf(selection_center[1]);
+ }
+ /* If selection doesn't lie on any UDIM then find the closest UDIM grid or image tile. */
+ else {
+ float nearest_image_tile_co[2] = {FLT_MAX, FLT_MAX};
+ float nearest_image_tile_dist = FLT_MAX, nearest_grid_tile_dist = FLT_MAX;
+ if (image) {
+ nearest_image_tile_dist = uv_nearest_image_tile_distance(
+ image, selection_center, nearest_image_tile_co);
+ }
+
+ float nearest_grid_tile_co[2] = {0.0f, 0.0f};
+ nearest_grid_tile_dist = uv_nearest_grid_tile_distance(
+ udim_grid, selection_center, nearest_grid_tile_co);
+
+ base_offset[0] = (nearest_image_tile_dist < nearest_grid_tile_dist) ?
+ nearest_image_tile_co[0] :
+ nearest_grid_tile_co[0];
+ base_offset[1] = (nearest_image_tile_dist < nearest_grid_tile_dist) ?
+ nearest_image_tile_co[1] :
+ nearest_grid_tile_co[1];
+ }
+ }
+
for (int i = 0; i < island_list_len; i++) {
struct FaceIsland *island = island_array[boxarray[i].index];
const float pivot[2] = {
@@ -471,8 +643,8 @@ void ED_uvedit_pack_islands_multi(const Scene *scene,
island->bounds_rect.ymin,
};
const float offset[2] = {
- (boxarray[i].x * scale[0]) - island->bounds_rect.xmin,
- (boxarray[i].y * scale[1]) - island->bounds_rect.ymin,
+ (boxarray[i].x * scale[0]) - island->bounds_rect.xmin + base_offset[0],
+ (boxarray[i].y * scale[1]) - island->bounds_rect.ymin + base_offset[1],
};
for (int j = 0; j < island->faces_len; j++) {
BMFace *efa = island->faces[j];
diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
index 3d5dabda23d..4e183732db9 100644
--- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c
+++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
@@ -37,6 +37,7 @@
#include "BLI_alloca.h"
#include "BLI_array.h"
#include "BLI_linklist.h"
+#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_memarena.h"
#include "BLI_string.h"
@@ -64,6 +65,7 @@
#include "PIL_time.h"
#include "UI_interface.h"
+#include "UI_view2d.h"
#include "ED_image.h"
#include "ED_mesh.h"
@@ -1005,10 +1007,17 @@ static void uvedit_pack_islands_multi(const Scene *scene,
}
}
+/* Packing targets. */
+enum {
+ PACK_UDIM_SRC_CLOSEST = 0,
+ PACK_UDIM_SRC_ACTIVE = 1,
+};
+
static int pack_islands_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
const Scene *scene = CTX_data_scene(C);
+ const SpaceImage *sima = CTX_wm_space_image(C);
const UnwrapOptions options = {
.topology_from_uvs = true,
@@ -1018,17 +1027,20 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
.correct_aspect = true,
};
- bool rotate = RNA_boolean_get(op->ptr, "rotate");
-
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
view_layer, CTX_wm_view3d(C), &objects_len);
+ /* Early exit in case no UVs are selected. */
if (!uvedit_have_selection_multi(scene, objects, objects_len, &options)) {
MEM_freeN(objects);
return OPERATOR_CANCELLED;
}
+ /* RNA props */
+ const bool rotate = RNA_boolean_get(op->ptr, "rotate");
+ const int udim_source = RNA_enum_get(op->ptr, "udim_source");
+ bool use_target_udim = false;
if (RNA_struct_property_is_set(op->ptr, "margin")) {
scene->toolsettings->uvcalc_margin = RNA_float_get(op->ptr, "margin");
}
@@ -1036,9 +1048,46 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
RNA_float_set(op->ptr, "margin", scene->toolsettings->uvcalc_margin);
}
+ int target_udim = 1001;
+ if (udim_source == PACK_UDIM_SRC_CLOSEST) {
+ /* pass */
+ }
+ else if (udim_source == PACK_UDIM_SRC_ACTIVE) {
+ int active_udim = 1001;
+ /* NOTE: Presently, when UDIM grid and tiled image are present together, only active tile for
+ * the tiled imgae is considered. */
+ if (sima && sima->image) {
+ Image *image = sima->image;
+ ImageTile *active_tile = BLI_findlink(&image->tiles, image->active_tile_index);
+ if (active_tile) {
+ active_udim = active_tile->tile_number;
+ }
+ }
+ else if (sima && !sima->image) {
+ /* Use 2D cursor to find the active tile index for the UDIM grid. */
+ float cursor_loc[2] = {sima->cursor[0], sima->cursor[1]};
+ if (uv_coords_isect_udim(sima->image, sima->tile_grid_shape, cursor_loc)) {
+ int tile_number = 1001;
+ tile_number += floorf(cursor_loc[1]) * 10;
+ tile_number += floorf(cursor_loc[0]);
+ active_udim = tile_number;
+ }
+ /* TODO: Support storing an active UDIM when there are no tiles present. */
+ }
+
+ target_udim = active_udim;
+ use_target_udim = true;
+ }
+ else {
+ BLI_assert_unreachable();
+ }
+
ED_uvedit_pack_islands_multi(scene,
+ sima,
objects,
objects_len,
+ use_target_udim,
+ target_udim,
&(struct UVPackIsland_Params){
.rotate = rotate,
.rotate_align_axis = -1,
@@ -1048,16 +1097,25 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
});
MEM_freeN(objects);
-
return OPERATOR_FINISHED;
}
void UV_OT_pack_islands(wmOperatorType *ot)
{
+ static const EnumPropertyItem pack_target[] = {
+ {PACK_UDIM_SRC_CLOSEST, "CLOSEST_UDIM", 0, "Closest UDIM", "Pack islands to closest UDIM"},
+ {PACK_UDIM_SRC_ACTIVE,
+ "ACTIVE_UDIM",
+ 0,
+ "Active UDIM",
+ "Pack islands to active UDIM image tile or UDIM grid tile where 2D cursor is located"},
+ {0, NULL, 0, NULL, NULL},
+ };
/* identifiers */
ot->name = "Pack Islands";
ot->idname = "UV_OT_pack_islands";
- ot->description = "Transform all islands so that they fill up the UV space as much as possible";
+ ot->description =
+ "Transform all islands so that they fill up the UV/UDIM space as much as possible";
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1066,6 +1124,7 @@ void UV_OT_pack_islands(wmOperatorType *ot)
ot->poll = ED_operator_uvedit;
/* properties */
+ RNA_def_enum(ot->srna, "udim_source", pack_target, PACK_UDIM_SRC_CLOSEST, "Pack to", "");
RNA_def_boolean(ot->srna, "rotate", true, "Rotate", "Rotate islands for best fit");
RNA_def_float_factor(
ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f);
@@ -2055,6 +2114,7 @@ static int smart_project_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
+ const SpaceImage *sima = CTX_wm_space_image(C);
/* May be NULL. */
View3D *v3d = CTX_wm_view3d(C);
@@ -2065,6 +2125,7 @@ static int smart_project_exec(bContext *C, wmOperator *op)
const float project_angle_limit_cos = cosf(project_angle_limit);
const float project_angle_limit_half_cos = cosf(project_angle_limit / 2);
+ const int target_udim = 1001; /* 0-1 UV space. */
/* Memory arena for list links (cleared for each object). */
MemArena *arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
@@ -2204,8 +2265,11 @@ static int smart_project_exec(bContext *C, wmOperator *op)
/* Depsgraph refresh functions are called here. */
const bool correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect");
ED_uvedit_pack_islands_multi(scene,
+ sima,
objects_changed,
object_changed_len,
+ true,
+ target_udim,
&(struct UVPackIsland_Params){
.rotate = true,
/* We could make this optional. */