From bf56df3236cc4d075e6f474f7d18b17d0ed318ed Mon Sep 17 00:00:00 2001 From: Chris Blackbourn Date: Sat, 29 Apr 2023 09:44:42 +1200 Subject: [PATCH] UV: Add new packing option, scale_to_fit When `scale_to_fit` is enabled, the existing behavior is used, UVs will be scaled to fill the unit square. If disabled, UVs will not be rescaled. They will be packed to the bottom-left corner, possibly overflowing the unit square, or not filling space. --- .../editors/uvedit/uvedit_unwrap_ops.cc | 9 +++--- source/blender/geometry/GEO_uv_pack.hh | 6 ++-- source/blender/geometry/intern/uv_pack.cc | 31 ++++++++----------- .../geometry/intern/uv_parametrizer.cc | 5 ++- 4 files changed, 23 insertions(+), 28 deletions(-) diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.cc b/source/blender/editors/uvedit/uvedit_unwrap_ops.cc index f708a56fca0..bc529760ce6 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.cc +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.cc @@ -1233,7 +1233,6 @@ static void uvedit_pack_islands_multi(const Scene *scene, MemArena *arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); Heap *heap = BLI_heap_new(); - float scale[2] = {1.0f, 1.0f}; blender::Vector pack_island_vector; for (int i = 0; i < island_vector.size(); i++) { FaceIsland *face_island = island_vector[i]; @@ -1264,7 +1263,7 @@ static void uvedit_pack_islands_multi(const Scene *scene, BLI_heap_free(heap, nullptr); BLI_memarena_free(arena); - pack_islands(pack_island_vector, *params, scale); + const float scale = pack_islands(pack_island_vector, *params); float base_offset[2] = {0.0f, 0.0f}; copy_v2_v2(base_offset, params->udim_base_offset); @@ -1306,7 +1305,7 @@ static void uvedit_pack_islands_multi(const Scene *scene, for (int64_t i : pack_island_vector.index_range()) { blender::geometry::PackIsland *pack_island = pack_island_vector[i]; FaceIsland *island = island_vector[pack_island->caller_index]; - pack_island->build_transformation(scale[0], pack_island->angle, matrix); + pack_island->build_transformation(scale, pack_island->angle, matrix); invert_m2_m2(matrix_inverse, matrix); /* Add base_offset, post transform. */ @@ -1323,7 +1322,7 @@ static void uvedit_pack_islands_multi(const Scene *scene, const float rescale_x = (selection_max_co[0] - selection_min_co[0]) / params->target_aspect_y; const float rescale_y = (selection_max_co[1] - selection_min_co[1]); - const float rescale = std::min(rescale_x, rescale_y); + const float rescale = params->scale_to_fit ? std::min(rescale_x, rescale_y) : 1.0f; matrix[0][0] = rescale; matrix[0][1] = 0.0f; matrix[1][0] = 0.0f; @@ -1396,6 +1395,7 @@ static int pack_islands_exec(bContext *C, wmOperator *op) blender::geometry::UVPackIsland_Params pack_island_params; pack_island_params.setFromUnwrapOptions(options); pack_island_params.rotate = RNA_boolean_get(op->ptr, "rotate"); + pack_island_params.scale_to_fit = RNA_boolean_get(op->ptr, "scale"); pack_island_params.merge_overlap = RNA_boolean_get(op->ptr, "merge_overlap"); pack_island_params.ignore_pinned = false; pack_island_params.margin_method = eUVPackIsland_MarginMethod( @@ -1474,6 +1474,7 @@ void UV_OT_pack_islands(wmOperatorType *ot) /* 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_boolean(ot->srna, "scale", true, "Scale", "Scale islands to fill unit square"); RNA_def_boolean( ot->srna, "merge_overlap", false, "Merge Overlapped", "Overlapping islands stick together"); RNA_def_enum(ot->srna, diff --git a/source/blender/geometry/GEO_uv_pack.hh b/source/blender/geometry/GEO_uv_pack.hh index 7d945e67423..6227ebe0c04 100644 --- a/source/blender/geometry/GEO_uv_pack.hh +++ b/source/blender/geometry/GEO_uv_pack.hh @@ -48,6 +48,8 @@ class UVPackIsland_Params { /** Islands can be rotated to improve packing. */ bool rotate; + /** Resize islands to fill the unit square. */ + bool scale_to_fit; /** (In UV Editor) only pack islands which have one or more selected UVs. */ bool only_selected_uvs; /** (In 3D Viewport or UV Editor) only pack islands which have selected faces. */ @@ -119,9 +121,7 @@ class PackIsland { friend class OverlapMerger; }; -void pack_islands(const Span &islands, - const UVPackIsland_Params ¶ms, - float r_scale[2]); +float pack_islands(const Span &islands, const UVPackIsland_Params ¶ms); /** Compute `r = mat * (a + b)` with high precision. */ void mul_v2_m2_add_v2v2(float r[2], const float mat[2][2], const float a[2], const float b[2]); diff --git a/source/blender/geometry/intern/uv_pack.cc b/source/blender/geometry/intern/uv_pack.cc index 299d908a01a..df46dc0db04 100644 --- a/source/blender/geometry/intern/uv_pack.cc +++ b/source/blender/geometry/intern/uv_pack.cc @@ -295,6 +295,7 @@ void PackIsland::place_(const float scale, const uv_phi phi) UVPackIsland_Params::UVPackIsland_Params() { rotate = false; + scale_to_fit = true; only_selected_uvs = false; only_selected_faces = false; use_seams = false; @@ -1283,9 +1284,8 @@ class OverlapMerger { return result; } - static void pack_islands_overlap(const Span &islands, - const UVPackIsland_Params ¶ms, - float r_scale[2]) + static float pack_islands_overlap(const Span &islands, + const UVPackIsland_Params ¶ms) { /* Building the binary-tree of merges is complicated to do in a single pass if we proceed in @@ -1316,7 +1316,7 @@ class OverlapMerger { /* Recursively call pack_islands with `merge_overlap = false`. */ UVPackIsland_Params sub_params(params); sub_params.merge_overlap = false; - pack_islands(sub_islands, sub_params, r_scale); + const float result = pack_islands(sub_islands, sub_params); /* Must loop backwards! */ for (int64_t i = merge_trace.size() - 3; i >= 0; i -= 3) { @@ -1331,6 +1331,8 @@ class OverlapMerger { sub_b->pre_rotate_ = merge->pre_rotate_; delete merge; } + + return result; } }; @@ -1347,31 +1349,25 @@ static void finalize_geometry(const Span &islands, const UVPackIsl BLI_memarena_free(arena); } -void pack_islands(const Span &islands, - const UVPackIsland_Params ¶ms, - float r_scale[2]) +float pack_islands(const Span &islands, const UVPackIsland_Params ¶ms) { BLI_assert(0.0f <= params.margin); BLI_assert(0.0f <= params.target_aspect_y); if (islands.size() == 0) { - r_scale[0] = 1.0f; - r_scale[1] = 1.0f; - return; /* Nothing to do, just create a safe default. */ + return 1.0f; /* Nothing to do, just create a safe default. */ } if (params.merge_overlap) { - return OverlapMerger::pack_islands_overlap(islands, params, r_scale); + return OverlapMerger::pack_islands_overlap(islands, params); } finalize_geometry(islands, params); - if (params.margin_method == ED_UVPACK_MARGIN_FRACTION && params.margin > 0.0f) { + if (params.margin_method == ED_UVPACK_MARGIN_FRACTION && params.margin > 0.0f && + params.scale_to_fit) { /* Uses a line search on scale. ~10x slower than other method. */ - const float scale = pack_islands_margin_fraction(islands, params.margin, params); - r_scale[0] = scale; - r_scale[1] = scale; - return; + return pack_islands_margin_fraction(islands, params.margin, params); } float margin = params.margin; @@ -1395,8 +1391,7 @@ void pack_islands(const Span &islands, for (const int64_t i : islands.index_range()) { islands[i]->place_(scale, phis[i]); } - r_scale[0] = 1.0f / max_uv; - r_scale[1] = r_scale[0]; + return params.scale_to_fit ? 1.0f / max_uv : 1.0f; } /** \} */ diff --git a/source/blender/geometry/intern/uv_parametrizer.cc b/source/blender/geometry/intern/uv_parametrizer.cc index 379fcd1d687..278a6209e17 100644 --- a/source/blender/geometry/intern/uv_parametrizer.cc +++ b/source/blender/geometry/intern/uv_parametrizer.cc @@ -4131,15 +4131,14 @@ void uv_parametrizer_pack(ParamHandle *handle, float margin, bool do_rotate, boo pack_island_vector.append(pack_island); } - float scale[2] = {1.0f, 1.0f}; - pack_islands(pack_island_vector, params, scale); + const float scale = pack_islands(pack_island_vector, params); for (const int64_t i : pack_island_vector.index_range()) { PackIsland *pack_island = pack_island_vector[i]; PChart *chart = handle->charts[pack_island->caller_index]; float matrix[2][2]; - pack_island->build_transformation(scale[0], pack_island->angle, matrix); + pack_island->build_transformation(scale, pack_island->angle, matrix); for (PVert *v = chart->verts; v; v = v->nextlink) { blender::geometry::mul_v2_m2_add_v2v2(v->uv, matrix, v->uv, pack_island->pre_translate); }