UV: simplify uv packing api and simplify uv packing

Fix #invert_m2_m2 with repeated arguments.

Change UV Packing to use {pivot, half_diagonal} representation.
This commit is contained in:
Chris Blackbourn
2023-04-01 10:42:05 +13:00
parent 6778460e53
commit 531f99ffbd
5 changed files with 91 additions and 93 deletions

View File

@@ -1101,8 +1101,8 @@ double determinant_m3_array_db(const double m[3][3])
bool invert_m2_m2(float inverse[2][2], const float mat[2][2])
{
const float det = determinant_m2(mat[0][0], mat[1][0], mat[0][1], mat[1][1]);
adjoint_m2_m2(inverse, mat);
float det = determinant_m2(mat[0][0], mat[1][0], mat[0][1], mat[1][1]);
bool success = (det != 0.0f);
if (success) {
@@ -1957,11 +1957,15 @@ void normalize_m4_m4(float rmat[4][4], const float mat[4][4])
void adjoint_m2_m2(float R[2][2], const float M[2][2])
{
BLI_assert(R != M);
R[0][0] = M[1][1];
R[0][1] = -M[0][1];
R[1][0] = -M[1][0];
R[1][1] = M[0][0];
const float r00 = M[1][1];
const float r01 = -M[0][1];
const float r10 = -M[1][0];
const float r11 = M[0][0];
R[0][0] = r00;
R[0][1] = r01;
R[1][0] = r10;
R[1][1] = r11;
}
void adjoint_m3_m3(float R[3][3], const float M[3][3])
@@ -2026,9 +2030,8 @@ void adjoint_m4_m4(float R[4][4], const float M[4][4]) /* out = ADJ(in) */
R[3][3] = determinant_m3(a1, a2, a3, b1, b2, b3, c1, c2, c3);
}
float determinant_m2(float a, float b, float c, float d)
float determinant_m2(const float a, const float b, const float c, const float d)
{
return a * d - b * c;
}

View File

@@ -1457,12 +1457,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];
const float cos_angle = cosf(pack_island->angle);
const float sin_angle = sinf(pack_island->angle);
matrix[0][0] = cos_angle * scale[0];
matrix[0][1] = -sin_angle * scale[0] * pack_island->aspect_y;
matrix[1][0] = sin_angle * scale[1] / pack_island->aspect_y;
matrix[1][1] = cos_angle * scale[1];
pack_island->build_transformation(scale[0], pack_island->angle, matrix);
invert_m2_m2(matrix_inverse, matrix);
/* Add base_offset, post transform. */

View File

@@ -72,8 +72,6 @@ class UVPackIsland_Params {
class PackIsland {
public:
/** Calculated automatically. */
rctf bounds_rect;
/** Aspect ratio, required for rotation. */
float aspect_y;
/** Output pre-translation. */
@@ -87,8 +85,16 @@ class PackIsland {
void add_polygon(const blender::Span<float2> uvs, MemArena *arena, Heap *heap);
void finalize_geometry(const UVPackIsland_Params &params, MemArena *arena, Heap *heap);
void build_transformation(const float scale, const float angle, float r_matrix[2][2]);
void build_inverse_transformation(const float scale, const float angle, float r_matrix[2][2]);
/** Center of AABB and inside-or-touching the convex hull. */
float2 pivot_;
/** Half of the diagonal of the AABB. */
float2 half_diagonal_;
private:
void calculate_pivot(); /* Choose a pivot based on triangles. */
void calculate_pivot(); /* Calculate `pivot_` and `half_diagonal_` based on added triangles. */
blender::Vector<float2> triangle_vertices_;
friend class Occupancy;
};

View File

@@ -163,15 +163,8 @@ void PackIsland::finalize_geometry(const UVPackIsland_Params &params, MemArena *
void PackIsland::calculate_pivot()
{
Bounds<float2> triangle_bounds = *bounds::min_max(triangle_vertices_.as_span());
float2 aabb_min = triangle_bounds.min;
float2 aabb_max = triangle_bounds.max;
float2 pivot = (aabb_min + aabb_max) * 0.5f;
float2 half_diagonal = (aabb_max - aabb_min) * 0.5f;
bounds_rect.xmin = pivot.x - half_diagonal.x;
bounds_rect.ymin = pivot.y - half_diagonal.y;
bounds_rect.xmax = pivot.x + half_diagonal.x;
bounds_rect.ymax = pivot.y + half_diagonal.y;
pivot_ = (triangle_bounds.min + triangle_bounds.max) * 0.5f;
half_diagonal_ = (triangle_bounds.max - triangle_bounds.min) * 0.5f;
}
UVPackIsland_Params::UVPackIsland_Params()
@@ -243,8 +236,8 @@ static void pack_islands_alpaca_turbo(const Span<UVAABBIsland *> islands,
}
/* Place the island. */
island->uv_placement.x = u0;
island->uv_placement.y = v0;
island->uv_placement.x = u0 + dsm_u * 0.5f;
island->uv_placement.y = v0 + dsm_v * 0.5f;
if (zigzag) {
/* Move upwards. */
v0 += dsm_v;
@@ -277,11 +270,11 @@ static void pack_island_box_pack_2d(const Span<UVAABBIsland *> aabbs,
MEM_mallocN(sizeof(*box_array) * islands.size(), __func__));
/* Prepare for box_pack_2d. */
for (const int64_t i : islands.index_range()) {
for (const int64_t i : aabbs.index_range()) {
PackIsland *island = islands[aabbs[i]->index];
BoxPack *box = box_array + i;
box->w = BLI_rctf_size_x(&island->bounds_rect) * scale + 2 * margin;
box->h = BLI_rctf_size_y(&island->bounds_rect) * scale + 2 * margin;
box->w = island->half_diagonal_.x * 2 * scale + 2 * margin;
box->h = island->half_diagonal_.y * 2 * scale + 2 * margin;
}
const bool sort_boxes = false; /* Use existing ordering from `aabbs`. */
@@ -290,12 +283,12 @@ static void pack_island_box_pack_2d(const Span<UVAABBIsland *> aabbs,
BLI_box_pack_2d(box_array, int(islands.size()), sort_boxes, r_max_u, r_max_v);
/* Write back box_pack UVs. */
for (const int64_t i : islands.index_range()) {
for (const int64_t i : aabbs.index_range()) {
PackIsland *island = islands[aabbs[i]->index];
BoxPack *box = box_array + i;
island->angle = 0.0f; /* #BLI_box_pack_2d never rotates. */
island->pre_translate.x = (box->x + margin) / scale - island->bounds_rect.xmin;
island->pre_translate.y = (box->y + margin) / scale - island->bounds_rect.ymin;
island->pre_translate.x = (box->x + box->w * 0.5f) / scale - island->pivot_.x;
island->pre_translate.y = (box->y + box->h * 0.5f) / scale - island->pivot_.y;
}
/* Housekeeping. */
@@ -448,13 +441,13 @@ float Occupancy::trace_island(PackIsland *island,
{
if (!write) {
if (uv.x <= 0.0f || uv.y <= 0.0f) {
if (uv.x < island->half_diagonal_.x * scale + margin ||
uv.y < island->half_diagonal_.y * scale + margin) {
return terminal; /* Occupied. */
}
}
const float2 origin(island->bounds_rect.xmin, island->bounds_rect.ymin);
const float2 delta = uv - origin * scale;
uint vert_count = uint(island->triangle_vertices_.size());
const float2 delta = uv - island->pivot_ * scale;
uint vert_count = uint(island->triangle_vertices_.size()); /* `uint` is faster than `int`. */
for (uint i = 0; i < vert_count; i += 3) {
uint j = (i + triangle_hint_) % vert_count;
float extent = trace_triangle(delta + island->triangle_vertices_[j] * scale,
@@ -474,36 +467,33 @@ float Occupancy::trace_island(PackIsland *island,
static float2 find_best_fit_for_island(
PackIsland *island, int scan_line, Occupancy &occupancy, const float scale, const float margin)
{
const float scan_line_bscaled = scan_line / occupancy.bitmap_scale_reciprocal;
const float size_x_scaled = BLI_rctf_size_x(&island->bounds_rect) * scale;
const float size_y_scaled = BLI_rctf_size_y(&island->bounds_rect) * scale;
const float bitmap_scale = 1.0f / occupancy.bitmap_scale_reciprocal;
const float half_x_scaled = island->half_diagonal_.x * scale;
const float half_y_scaled = island->half_diagonal_.y * scale;
/* Scan using an "Alpaca"-style search, first horizontally using "less-than". */
int t = int(ceilf(size_x_scaled * occupancy.bitmap_scale_reciprocal));
int t = int(ceilf((2 * half_x_scaled + margin) * occupancy.bitmap_scale_reciprocal));
while (t < scan_line) {
const float t_bscaled = t / occupancy.bitmap_scale_reciprocal;
const float2 probe(t_bscaled - size_x_scaled, scan_line_bscaled - size_y_scaled);
const float2 probe(t * bitmap_scale - half_x_scaled, scan_line * bitmap_scale - half_y_scaled);
const float extent = occupancy.trace_island(island, scale, margin, probe, false);
if (extent < 0.0f) {
return probe;
return probe; /* Success. */
}
t = t + std::max(1, int(extent));
}
/* Then scan vertically using "less-than-or-equal" */
t = int(ceilf(size_y_scaled * occupancy.bitmap_scale_reciprocal));
t = int(ceilf((2 * half_y_scaled + margin) * occupancy.bitmap_scale_reciprocal));
while (t <= scan_line) {
const float t_bscaled = t / occupancy.bitmap_scale_reciprocal;
const float2 probe(scan_line_bscaled - size_x_scaled, t_bscaled - size_y_scaled);
const float2 probe(scan_line * bitmap_scale - half_x_scaled, t * bitmap_scale - half_y_scaled);
const float extent = occupancy.trace_island(island, scale, margin, probe, false);
if (extent < 0.0f) {
return probe;
return probe; /* Success. */
}
t = t + std::max(1, int(extent));
}
return float2(-1, -1);
return float2(-1, -1); /* Unable to find a place to fit. */
}
static float guess_initial_scale(const Span<PackIsland *> islands,
@@ -513,8 +503,8 @@ static float guess_initial_scale(const Span<PackIsland *> islands,
float sum = 1e-40f;
for (int64_t i : islands.index_range()) {
PackIsland *island = islands[i];
sum += BLI_rctf_size_x(&island->bounds_rect) * scale + 2 * margin;
sum += BLI_rctf_size_y(&island->bounds_rect) * scale + 2 * margin;
sum += island->half_diagonal_.x * 2 * scale + 2 * margin;
sum += island->half_diagonal_.y * 2 * scale + 2 * margin;
}
return sqrtf(sum) / 6.0f;
}
@@ -583,8 +573,7 @@ static void pack_island_xatlas(const Span<UVAABBIsland *> island_indices,
/* Redraw already placed islands. (Greedy.) */
for (int j = 0; j < i; j++) {
PackIsland *other = islands[island_indices[j]->index];
float2 where(other->bounds_rect.xmin * scale - margin,
other->bounds_rect.ymin * scale - margin);
float2 where = (other->pre_translate + other->pivot_) * scale;
occupancy.trace_island(islands[island_indices[j]->index], scale, margin, where, true);
}
continue;
@@ -592,13 +581,12 @@ static void pack_island_xatlas(const Span<UVAABBIsland *> island_indices,
/* Place island. */
island->angle = 0.0f;
island->pre_translate.x = (best.x + margin) / scale - island->bounds_rect.xmin;
island->pre_translate.y = (best.y + margin) / scale - island->bounds_rect.ymin;
island->pre_translate = best / scale - island->pivot_;
occupancy.trace_island(island, scale, margin, best, true);
i++; /* Next island. */
max_u = std::max(best.x + BLI_rctf_size_x(&island->bounds_rect) * scale + 2 * margin, max_u);
max_v = std::max(best.y + BLI_rctf_size_y(&island->bounds_rect) * scale + 2 * margin, max_v);
max_u = std::max(best.x + island->half_diagonal_.x * scale + margin, max_u);
max_v = std::max(best.y + island->half_diagonal_.y * scale + margin, max_v);
if (i < 128 || (i & 31) == 16) {
scan_line = 0; /* Restart completely. */
@@ -693,9 +681,13 @@ static void pack_islands_alpaca_rotate(const Span<UVAABBIsland *> islands,
island->uv_placement.y = hole[1];
if (hole_rotate == (min_dsm == island->uv_diagonal.x)) {
island->angle = DEG2RADF(90.0f);
island->uv_placement.x += max_dsm * 0.5f;
island->uv_placement.y += min_dsm * 0.5f;
}
else {
island->angle = 0.0f;
island->uv_placement.x += min_dsm * 0.5f;
island->uv_placement.y += max_dsm * 0.5f;
}
/* Update space left in the hole. */
@@ -730,14 +722,18 @@ static void pack_islands_alpaca_rotate(const Span<UVAABBIsland *> islands,
}
/* Place the island. */
island->uv_placement.x = u0;
island->uv_placement.y = v0;
if (zigzag == (min_dsm == island->uv_diagonal.x)) {
island->angle = DEG2RADF(90.0f);
island->uv_placement.x += max_dsm * 0.5f;
island->uv_placement.y += min_dsm * 0.5f;
}
else {
island->angle = 0.0f;
island->uv_placement.x += min_dsm * 0.5f;
island->uv_placement.y += max_dsm * 0.5f;
}
island->uv_placement.x = u0;
island->uv_placement.y = v0;
/* Move according to the "Alpaca rules", with rotation. */
if (zigzag) {
@@ -797,8 +793,8 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
PackIsland *pack_island = islands[i];
UVAABBIsland *aabb = new UVAABBIsland();
aabb->index = i;
aabb->uv_diagonal.x = BLI_rctf_size_x(&pack_island->bounds_rect) * scale + 2 * margin;
aabb->uv_diagonal.y = BLI_rctf_size_y(&pack_island->bounds_rect) * scale + 2 * margin;
aabb->uv_diagonal.x = pack_island->half_diagonal_.x * 2 * scale + 2 * margin;
aabb->uv_diagonal.y = pack_island->half_diagonal_.y * 2 * scale + 2 * margin;
aabbs[i] = aabb;
if (pack_island->aspect_y != 1.0f) {
contains_non_square_islands = true;
@@ -846,6 +842,7 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
alpaca_cutoff = alpaca_cutoff_fast;
}
}
const int64_t max_box_pack = std::min(alpaca_cutoff, islands.size());
/* Call box_pack_2d (slow for large N.) */
@@ -878,20 +875,10 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
UVAABBIsland *aabb = aabbs[i];
PackIsland *pack_island = islands[aabb->index];
pack_island->angle = aabb->angle;
if (aabb->angle) {
pack_island->pre_translate.x = (aabb->uv_placement.y + margin) / scale /
-pack_island->aspect_y -
pack_island->bounds_rect.xmax;
pack_island->pre_translate.y = (aabb->uv_placement.x + margin) / scale *
pack_island->aspect_y -
pack_island->bounds_rect.ymin;
}
else {
pack_island->pre_translate.x = (aabb->uv_placement.x + margin) / scale -
pack_island->bounds_rect.xmin;
pack_island->pre_translate.y = (aabb->uv_placement.y + margin) / scale -
pack_island->bounds_rect.ymin;
}
float matrix_inverse[2][2];
pack_island->build_inverse_transformation(scale, pack_island->angle, matrix_inverse);
mul_v2_m2v2(pack_island->pre_translate, matrix_inverse, aabb->uv_placement);
pack_island->pre_translate -= pack_island->pivot_;
}
/* Memory management. */
@@ -1018,8 +1005,8 @@ static float calc_margin_from_aabb_length_sum(const Span<PackIsland *> &island_v
* `aabb_length_sum` (was "`area`") to multiply the margin by the length (was "area"). */
double aabb_length_sum = 0.0f;
for (PackIsland *island : island_vector) {
float w = BLI_rctf_size_x(&island->bounds_rect);
float h = BLI_rctf_size_y(&island->bounds_rect);
float w = island->half_diagonal_.x * 2.0f;
float h = island->half_diagonal_.y * 2.0f;
aabb_length_sum += sqrtf(w * h);
}
return params.margin * aabb_length_sum * 0.1f;
@@ -1071,4 +1058,23 @@ void pack_islands(const Span<PackIsland *> &islands,
/** \} */
void PackIsland::build_transformation(const float scale, const float angle, float (*r_matrix)[2])
{
const float cos_angle = cosf(angle);
const float sin_angle = sinf(angle);
r_matrix[0][0] = cos_angle * scale;
r_matrix[0][1] = -sin_angle * scale * aspect_y;
r_matrix[1][0] = sin_angle * scale / aspect_y;
r_matrix[1][1] = cos_angle * scale;
}
void PackIsland::build_inverse_transformation(const float scale,
const float angle,
float (*r_matrix)[2])
{
/* TODO: Generate inverse transform directly. */
build_transformation(scale, angle, r_matrix);
invert_m2_m2(r_matrix, r_matrix);
}
} // namespace blender::geometry

View File

@@ -4146,15 +4146,11 @@ void uv_parametrizer_pack(ParamHandle *handle, float margin, bool do_rotate, boo
MemArena *arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
Heap *heap = BLI_heap_new();
int unpacked = 0;
for (int i = 0; i < handle->ncharts; i++) {
PChart *chart = handle->charts[i];
if (ignore_pinned && chart->has_pins) {
unpacked++;
continue;
}
(void)unpacked; /* Quiet set-but-unused warning (may be removed). */
geometry::PackIsland *pack_island = new geometry::PackIsland();
pack_island->caller_index = i;
@@ -4182,17 +4178,9 @@ void uv_parametrizer_pack(ParamHandle *handle, float margin, bool do_rotate, boo
PChart *chart = handle->charts[pack_island->caller_index];
float matrix[2][2];
float b[2];
const float cos_angle = cosf(pack_island->angle);
const float sin_angle = sinf(pack_island->angle);
matrix[0][0] = cos_angle * scale[0];
matrix[0][1] = -sin_angle * scale[0] * pack_island->aspect_y;
matrix[1][0] = sin_angle * scale[1] / pack_island->aspect_y;
matrix[1][1] = cos_angle * scale[1];
b[0] = pack_island->pre_translate.x;
b[1] = pack_island->pre_translate.y;
pack_island->build_transformation(scale[0], 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, b);
blender::geometry::mul_v2_m2_add_v2v2(v->uv, matrix, v->uv, pack_island->pre_translate);
}
pack_island_vector[i] = nullptr;