From de4f456ea2e4ab6290f2446bf2d179a12bc6a989 Mon Sep 17 00:00:00 2001 From: Chris Blackbourn Date: Mon, 22 May 2023 15:31:49 +1200 Subject: [PATCH 1/3] UV: Fix overlap problem during uv packing with scale locked islands If an island overlaps a pinned island, and that pinned island has locked scale, then the pinning information must be copied to the first island so it can be scaled correctly. Reported in #108037 as "2. Use with Merge Overlapped" --- source/blender/geometry/intern/uv_pack.cc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/source/blender/geometry/intern/uv_pack.cc b/source/blender/geometry/intern/uv_pack.cc index 81122e5448a..4363fa66492 100644 --- a/source/blender/geometry/intern/uv_pack.cc +++ b/source/blender/geometry/intern/uv_pack.cc @@ -1777,17 +1777,25 @@ class OverlapMerger { sub_params.merge_overlap = false; const float result = pack_islands(sub_islands, sub_params); - /* Must loop backwards! */ + /* Must loop backwards, or we will miss sub-sub-islands. */ for (int64_t i = merge_trace.size() - 3; i >= 0; i -= 3) { PackIsland *sub_a = merge_trace[i]; PackIsland *sub_b = merge_trace[i + 1]; PackIsland *merge = merge_trace[i + 2]; + + /* Copy `angle`, `pre_translate` and `pre_rotate` from merged island to sub islands. */ sub_a->angle = merge->angle; sub_b->angle = merge->angle; sub_a->pre_translate = merge->pre_translate; sub_b->pre_translate = merge->pre_translate; sub_a->pre_rotate_ = merge->pre_rotate_; sub_b->pre_rotate_ = merge->pre_rotate_; + + /* If the merged island is pinned, the sub-islands are also pinned to correct scaling. */ + if (merge->pinned) { + sub_a->pinned = true; + sub_b->pinned = true; + } delete merge; } From 47cbeabb1182fbd964d8cbefb04d30a2ced6efc4 Mon Sep 17 00:00:00 2001 From: Chris Blackbourn Date: Mon, 22 May 2023 15:40:15 +1200 Subject: [PATCH 2/3] UV: Fix uv packing problem with rotate and scale line-search When rotation is enabled and doing a scale line-search (locked islands or "fraction" margin method), if the `rotate_inside_square` would result in a a tighter packing, the wrong scale value was being used, resulting in UVs outside of the unit square. Reported as #108037 "1. Use locked scale on after scaling UV..." --- source/blender/geometry/intern/uv_pack.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/geometry/intern/uv_pack.cc b/source/blender/geometry/intern/uv_pack.cc index 4363fa66492..1b27b7770cb 100644 --- a/source/blender/geometry/intern/uv_pack.cc +++ b/source/blender/geometry/intern/uv_pack.cc @@ -1150,12 +1150,12 @@ static bool rotate_inside_square(const Span island_indices, return false; /* Nothing to do. */ } - /* Transform phis. */ + /* Transform phis, rotate by best_angle, then translate back to the origin. No scale. */ for (const int64_t j : island_indices.index_range()) { const int64_t i = island_indices[j]->index; const PackIsland *island = islands[i]; - const float island_scale = island->can_scale_(params) ? scale : 1.0f; - island->build_transformation(island_scale, square_finder.best_angle, matrix); + const float identity_scale = 1.0f; /* Don't rescale the placement, just rotate. */ + island->build_transformation(identity_scale, square_finder.best_angle, matrix); r_phis[i].rotation += square_finder.best_angle; mul_m2_v2(matrix, r_phis[i].translation); r_phis[i].translation.x -= square_finder.best_bounds.xmin; From 96101a66fc83dc58c61b86af1d4d021036626b9e Mon Sep 17 00:00:00 2001 From: Chris Blackbourn Date: Mon, 22 May 2023 15:47:18 +1200 Subject: [PATCH 3/3] UV: Use correct uv packing method when all/none islands are pinned. During packing, some combinations of `Fraction` margin method, and various locking options, interact with situations where all or none of the islands are pinned. Previously, the settings were queried to choose the best packing method. Now, the islands themselves are queried if they can translate or scale, and the packing method is chosen based on the input, rather than the parameters. Fixes unreported crash with "Locked Position" when all islands are pinned. Reported as #108037 "3. In some case locked position is not respected" --- source/blender/geometry/intern/uv_pack.cc | 40 +++++++++++++++-------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/source/blender/geometry/intern/uv_pack.cc b/source/blender/geometry/intern/uv_pack.cc index 1b27b7770cb..992f07bcb8a 100644 --- a/source/blender/geometry/intern/uv_pack.cc +++ b/source/blender/geometry/intern/uv_pack.cc @@ -102,7 +102,7 @@ static float get_aspect_scaled_extent(const rctf &extent, const UVPackIsland_Par } /** - * \return true if `b` is a preferred layout over `a`, given the packing parameters supplied. + * \return true iff `b` is a preferred layout over `a`, given the packing parameters supplied. */ static bool is_larger(const rctf &a, const rctf &b, const UVPackIsland_Params ¶ms) { @@ -1831,8 +1831,24 @@ float pack_islands(const Span &islands, const UVPackIsland_Params finalize_geometry(islands, params); + /* Count the number of islands which can scale and which can translate. */ + int64_t can_scale_count = 0; + int64_t can_translate_count = 0; + for (const int64_t i : islands.index_range()) { + if (islands[i]->can_scale_(params)) { + can_scale_count++; + } + if (islands[i]->can_translate_(params)) { + can_translate_count++; + } + } + + if (can_translate_count == 0) { + return 1.0f; /* Nothing to do, all islands are locked. */ + } + if (params.margin_method == ED_UVPACK_MARGIN_FRACTION && params.margin > 0.0f && - params.scale_to_fit) + can_scale_count > 0) { /* Uses a line search on scale. ~10x slower than other method. */ return pack_islands_margin_fraction(islands, params.margin, false, params); @@ -1845,28 +1861,24 @@ float pack_islands(const Span &islands, const UVPackIsland_Params case ED_UVPACK_MARGIN_SCALED: /* Default for Blender 3.3 and later. */ margin = calc_margin_from_aabb_length_sum(islands, params); break; - case ED_UVPACK_MARGIN_FRACTION: /* Added as an option in Blender 3.4. */ - BLI_assert(params.margin == 0.0f); /* Other (slower) cases are handled above. */ + case ED_UVPACK_MARGIN_FRACTION: /* Added as an option in Blender 3.4. */ + /* Most other cases are handled above, unless pinning is involved. */ break; default: BLI_assert_unreachable(); } - /* TODO: Only line-search if *some* islands can scale and *some* are locked. */ - switch (params.pin_method) { - case ED_UVPACK_PIN_LOCK_ALL: - case ED_UVPACK_PIN_LOCK_SCALE: - case ED_UVPACK_PIN_LOCK_ROTATION_SCALE: - return pack_islands_margin_fraction(islands, margin, true, params); - default: - break; + if (can_scale_count > 0 && can_scale_count != islands.size()) { + /* Search for the best scale parameter. (slow) */ + return pack_islands_margin_fraction(islands, margin, true, params); } + /* Either all of the islands can scale, or none of them can. + * In either case, we pack them all tight to the origin. */ blender::Array phis(islands.size()); - const float scale = 1.0f; const float max_uv = pack_islands_scale_margin(islands, scale, margin, params, phis); - const float result = params.scale_to_fit ? 1.0f / max_uv : 1.0f; + const float result = can_scale_count > 0 ? 1.0f / max_uv : 1.0f; for (const int64_t i : islands.index_range()) { BLI_assert(result == 1.0f || islands[i]->can_scale_(params)); islands[i]->place_(scale, phis[i]);