From c81ca891f510f7694c6ea6a43c0b38326cd80f8a Mon Sep 17 00:00:00 2001 From: Chris Blackbourn Date: Fri, 19 May 2023 12:39:38 +1200 Subject: [PATCH] Fix #108037: UV packing problems with pinned islands Merge overlap was not copying scale information from pinned islands. rotate_inside_square was using incorrect scale. Fix edge cases where all islands are pinned etc. --- source/blender/geometry/intern/uv_pack.cc | 54 ++++++++++++++++------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/source/blender/geometry/intern/uv_pack.cc b/source/blender/geometry/intern/uv_pack.cc index 6a491deafea..992f07bcb8a 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; @@ -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; } @@ -1823,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); @@ -1837,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]); -- 2.30.2