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.
This commit is contained in:
Chris Blackbourn
2023-04-29 09:44:42 +12:00
parent 97222519de
commit bf56df3236
4 changed files with 23 additions and 28 deletions

View File

@@ -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<blender::geometry::PackIsland *> 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,

View File

@@ -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<PackIsland *> &islands,
const UVPackIsland_Params &params,
float r_scale[2]);
float pack_islands(const Span<PackIsland *> &islands, const UVPackIsland_Params &params);
/** 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]);

View File

@@ -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<PackIsland *> &islands,
const UVPackIsland_Params &params,
float r_scale[2])
static float pack_islands_overlap(const Span<PackIsland *> &islands,
const UVPackIsland_Params &params)
{
/* 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<PackIsland *> &islands, const UVPackIsl
BLI_memarena_free(arena);
}
void pack_islands(const Span<PackIsland *> &islands,
const UVPackIsland_Params &params,
float r_scale[2])
float pack_islands(const Span<PackIsland *> &islands, const UVPackIsland_Params &params)
{
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<PackIsland *> &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;
}
/** \} */

View File

@@ -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);
}