Fix #108037: UV packing problems with pinned islands #108063

Open
Chris Blackbourn wants to merge 1 commits from Chris_Blackbourn/blender:uv-fix-108037 into blender-v3.6-release

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
1 changed files with 37 additions and 17 deletions

View File

@ -1150,12 +1150,12 @@ static bool rotate_inside_square(const Span<UVAABBIsland *> 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<PackIsland *> &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<PackIsland *> &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<uv_phi> 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]);