UV: improve layout efficiency of uv packing when rotation enabled
Add rotation support to the `xatlas` strategy.
This commit is contained in:
@@ -87,8 +87,15 @@ class PackIsland {
|
||||
void add_polygon(const blender::Span<float2> uvs, MemArena *arena, Heap *heap);
|
||||
void finalize_geometry(const UVPackIsland_Params ¶ms, 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]);
|
||||
void build_transformation(const float scale, const float rotation, float r_matrix[2][2]) const;
|
||||
void build_inverse_transformation(const float scale,
|
||||
const float rotation,
|
||||
float r_matrix[2][2]) const;
|
||||
|
||||
float2 get_diagonal_support(const float scale, const float rotation, const float margin) const;
|
||||
float2 get_diagonal_support_d4(const float scale,
|
||||
const float rotation,
|
||||
const float margin) const;
|
||||
|
||||
/** Center of AABB and inside-or-touching the convex hull. */
|
||||
float2 pivot_;
|
||||
|
||||
@@ -25,6 +25,27 @@
|
||||
|
||||
namespace blender::geometry {
|
||||
|
||||
/* Store information about an island's placement such as translation, rotation and reflection. */
|
||||
class uv_phi {
|
||||
public:
|
||||
uv_phi();
|
||||
bool is_valid() const;
|
||||
|
||||
float2 translation;
|
||||
float rotation;
|
||||
/* bool reflect; */
|
||||
};
|
||||
|
||||
uv_phi::uv_phi() : translation(-1.0f, -1.0f), rotation(0.0f)
|
||||
{
|
||||
/* Initialize invalid. */
|
||||
}
|
||||
|
||||
bool uv_phi::is_valid() const
|
||||
{
|
||||
return translation.x != -1.0f;
|
||||
}
|
||||
|
||||
void mul_v2_m2_add_v2v2(float r[2], const float mat[2][2], const float a[2], const float b[2])
|
||||
{
|
||||
/* Compute `r = mat * (a + b)` with high precision.
|
||||
@@ -320,10 +341,10 @@ class Occupancy {
|
||||
const bool write) const;
|
||||
|
||||
/* Write or Query an island on the bitmap. */
|
||||
float trace_island(PackIsland *island,
|
||||
float trace_island(const PackIsland *island,
|
||||
const uv_phi phi,
|
||||
const float scale,
|
||||
const float margin,
|
||||
const float2 &uv,
|
||||
const bool write) const;
|
||||
|
||||
int bitmap_radix; /* Width and Height of `bitmap`. */
|
||||
@@ -439,28 +460,60 @@ float Occupancy::trace_triangle(const float2 &uv0,
|
||||
return -1.0f; /* Available. */
|
||||
}
|
||||
|
||||
float Occupancy::trace_island(PackIsland *island,
|
||||
float2 PackIsland::get_diagonal_support_d4(const float scale,
|
||||
const float rotation,
|
||||
const float margin) const
|
||||
{
|
||||
if (rotation == 0.0f) {
|
||||
return half_diagonal_ * scale + margin; /* Fast path for common case. */
|
||||
}
|
||||
|
||||
/* TODO: BLI_assert rotation is a "Dihedral Group D4" transform. */
|
||||
float matrix[2][2];
|
||||
build_transformation(scale, rotation, matrix);
|
||||
|
||||
float diagonal_rotated[2];
|
||||
mul_v2_m2v2(diagonal_rotated, matrix, half_diagonal_);
|
||||
return float2(fabsf(diagonal_rotated[0]) + margin, fabsf(diagonal_rotated[1]) + margin);
|
||||
}
|
||||
|
||||
float2 PackIsland::get_diagonal_support(const float scale,
|
||||
const float rotation,
|
||||
const float margin) const
|
||||
{
|
||||
/* Only "D4" transforms are currently supported. */
|
||||
return get_diagonal_support_d4(scale, rotation, margin);
|
||||
}
|
||||
|
||||
float Occupancy::trace_island(const PackIsland *island,
|
||||
const uv_phi phi,
|
||||
const float scale,
|
||||
const float margin,
|
||||
const float2 &uv,
|
||||
const bool write) const
|
||||
{
|
||||
float2 diagonal_support = island->get_diagonal_support(scale, phi.rotation, margin);
|
||||
|
||||
if (!write) {
|
||||
if (uv.x < island->half_diagonal_.x * scale + margin ||
|
||||
uv.y < island->half_diagonal_.y * scale + margin) {
|
||||
if (phi.translation.x < diagonal_support.x || phi.translation.y < diagonal_support.y) {
|
||||
return terminal; /* Occupied. */
|
||||
}
|
||||
}
|
||||
const float2 delta = uv - island->pivot_ * scale;
|
||||
float matrix[2][2];
|
||||
island->build_transformation(scale, phi.rotation, matrix);
|
||||
float2 pivot_transformed;
|
||||
mul_v2_m2v2(pivot_transformed, matrix, island->pivot_);
|
||||
|
||||
float2 delta = phi.translation - pivot_transformed;
|
||||
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,
|
||||
delta + island->triangle_vertices_[j + 1] * scale,
|
||||
delta + island->triangle_vertices_[j + 2] * scale,
|
||||
margin,
|
||||
write);
|
||||
float2 uv0;
|
||||
float2 uv1;
|
||||
float2 uv2;
|
||||
mul_v2_m2v2(uv0, matrix, island->triangle_vertices_[j]);
|
||||
mul_v2_m2v2(uv1, matrix, island->triangle_vertices_[j + 1]);
|
||||
mul_v2_m2v2(uv2, matrix, island->triangle_vertices_[j + 2]);
|
||||
float extent = trace_triangle(uv0 + delta, uv1 + delta, uv2 + delta, margin, write);
|
||||
|
||||
if (!write && extent >= 0.0f) {
|
||||
triangle_hint_ = j;
|
||||
@@ -470,46 +523,51 @@ float Occupancy::trace_island(PackIsland *island,
|
||||
return -1.0f; /* Available. */
|
||||
}
|
||||
|
||||
static float2 find_best_fit_for_island(PackIsland *island,
|
||||
int scan_line,
|
||||
static uv_phi find_best_fit_for_island(const PackIsland *island,
|
||||
const int scan_line,
|
||||
Occupancy &occupancy,
|
||||
const float scale,
|
||||
const int angle_90_multiple,
|
||||
const float margin,
|
||||
const float target_aspect_y)
|
||||
{
|
||||
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;
|
||||
|
||||
const float sqrt_target_aspect_y = sqrtf(target_aspect_y);
|
||||
int scan_line_x = int(scan_line * sqrt_target_aspect_y);
|
||||
int scan_line_y = int(scan_line / sqrt_target_aspect_y);
|
||||
const int scan_line_x = int(scan_line * sqrt_target_aspect_y);
|
||||
const int scan_line_y = int(scan_line / sqrt_target_aspect_y);
|
||||
|
||||
uv_phi phi;
|
||||
phi.rotation = DEG2RADF(angle_90_multiple * 90);
|
||||
float matrix[2][2];
|
||||
island->build_transformation(scale, phi.rotation, matrix);
|
||||
|
||||
/* Caution, margin is zero for support_diagonal as we're tracking the top-right corner. */
|
||||
float2 support_diagonal = island->get_diagonal_support_d4(scale, phi.rotation, 0.0f);
|
||||
|
||||
/* Scan using an "Alpaca"-style search, first horizontally using "less-than". */
|
||||
int t = int(ceilf((2 * half_x_scaled + margin) * occupancy.bitmap_scale_reciprocal));
|
||||
int t = int(ceilf((2 * support_diagonal.x + margin) * occupancy.bitmap_scale_reciprocal));
|
||||
while (t < scan_line_x) {
|
||||
const float2 probe(t * bitmap_scale - half_x_scaled,
|
||||
scan_line_y * bitmap_scale - half_y_scaled);
|
||||
const float extent = occupancy.trace_island(island, scale, margin, probe, false);
|
||||
phi.translation = float2(t * bitmap_scale, scan_line_y * bitmap_scale) - support_diagonal;
|
||||
const float extent = occupancy.trace_island(island, phi, scale, margin, false);
|
||||
if (extent < 0.0f) {
|
||||
return probe; /* Success. */
|
||||
return phi; /* Success. */
|
||||
}
|
||||
t = t + std::max(1, int(extent));
|
||||
}
|
||||
|
||||
/* Then scan vertically using "less-than-or-equal" */
|
||||
t = int(ceilf((2 * half_y_scaled + margin) * occupancy.bitmap_scale_reciprocal));
|
||||
t = int(ceilf((2 * support_diagonal.y + margin) * occupancy.bitmap_scale_reciprocal));
|
||||
while (t <= scan_line_y) {
|
||||
const float2 probe(scan_line_x * bitmap_scale - half_x_scaled,
|
||||
t * bitmap_scale - half_y_scaled);
|
||||
const float extent = occupancy.trace_island(island, scale, margin, probe, false);
|
||||
phi.translation = float2(scan_line_x * bitmap_scale, t * bitmap_scale) - support_diagonal;
|
||||
const float extent = occupancy.trace_island(island, phi, scale, margin, false);
|
||||
if (extent < 0.0f) {
|
||||
return probe; /* Success. */
|
||||
return phi; /* Success. */
|
||||
}
|
||||
t = t + std::max(1, int(extent));
|
||||
}
|
||||
|
||||
return float2(-1, -1); /* Unable to find a place to fit. */
|
||||
return uv_phi(); /* Unable to find a place to fit. */
|
||||
}
|
||||
|
||||
static float guess_initial_scale(const Span<PackIsland *> islands,
|
||||
@@ -544,7 +602,7 @@ static void pack_island_xatlas(const Span<UVAABBIsland *> island_indices,
|
||||
const Span<PackIsland *> islands,
|
||||
const float scale,
|
||||
const float margin,
|
||||
const float target_aspect_y,
|
||||
const UVPackIsland_Params ¶ms,
|
||||
float *r_max_u,
|
||||
float *r_max_v)
|
||||
{
|
||||
@@ -552,6 +610,7 @@ static void pack_island_xatlas(const Span<UVAABBIsland *> island_indices,
|
||||
float max_u = 0.0f;
|
||||
float max_v = 0.0f;
|
||||
|
||||
blender::Array<uv_phi> phis(island_indices.size());
|
||||
int scan_line = 0;
|
||||
int i = 0;
|
||||
|
||||
@@ -563,12 +622,22 @@ static void pack_island_xatlas(const Span<UVAABBIsland *> island_indices,
|
||||
|
||||
while (i < island_indices.size()) {
|
||||
PackIsland *island = islands[island_indices[i]->index];
|
||||
const float2 best = find_best_fit_for_island(
|
||||
island, scan_line, occupancy, scale, margin, target_aspect_y);
|
||||
uv_phi phi;
|
||||
|
||||
if (best.x <= -1.0f) {
|
||||
int max_90_multiple = params.rotate && (i < 50) ? 4 : 1;
|
||||
for (int angle_90_multiple = 0; angle_90_multiple < max_90_multiple; angle_90_multiple++) {
|
||||
phi = find_best_fit_for_island(
|
||||
island, scan_line, occupancy, scale, angle_90_multiple, margin, params.target_aspect_y);
|
||||
if (phi.is_valid()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!phi.is_valid()) {
|
||||
/* Unable to find a fit on this scan_line. */
|
||||
|
||||
island = nullptr; /* Just mark it as null, we won't use it further. */
|
||||
|
||||
if (i < 10) {
|
||||
scan_line++;
|
||||
}
|
||||
@@ -580,8 +649,8 @@ static void pack_island_xatlas(const Span<UVAABBIsland *> island_indices,
|
||||
* choice is 2. */
|
||||
scan_line += 2;
|
||||
}
|
||||
if (scan_line <
|
||||
occupancy.bitmap_radix * sqrtf(std::min(target_aspect_y, 1.0f / target_aspect_y))) {
|
||||
if (scan_line < occupancy.bitmap_radix *
|
||||
sqrtf(std::min(params.target_aspect_y, 1.0f / params.target_aspect_y))) {
|
||||
continue; /* Try again on next scan_line. */
|
||||
}
|
||||
|
||||
@@ -591,21 +660,27 @@ 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->pre_translate + other->pivot_) * scale;
|
||||
occupancy.trace_island(islands[island_indices[j]->index], scale, margin, where, true);
|
||||
occupancy.trace_island(islands[island_indices[j]->index], phis[j], scale, margin, true);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Place island. */
|
||||
island->angle = 0.0f;
|
||||
island->pre_translate = best / scale - island->pivot_;
|
||||
occupancy.trace_island(island, scale, margin, best, true);
|
||||
phis[i] = phi; /* Place island. */
|
||||
occupancy.trace_island(island, phi, scale, margin, true);
|
||||
|
||||
i++; /* Next island. */
|
||||
|
||||
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);
|
||||
island->angle = phi.rotation;
|
||||
|
||||
float matrix_inverse[2][2];
|
||||
island->build_inverse_transformation(scale, phi.rotation, matrix_inverse);
|
||||
mul_v2_m2v2(island->pre_translate, matrix_inverse, phi.translation);
|
||||
island->pre_translate -= island->pivot_;
|
||||
|
||||
float2 support = island->get_diagonal_support(scale, phi.rotation, margin);
|
||||
float2 top_right = phi.translation + support;
|
||||
max_u = std::max(top_right.x, max_u);
|
||||
max_v = std::max(top_right.y, max_v);
|
||||
|
||||
if (i < 128 || (i & 31) == 16) {
|
||||
scan_line = 0; /* Restart completely. */
|
||||
@@ -874,7 +949,7 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
|
||||
islands,
|
||||
scale,
|
||||
margin,
|
||||
params.target_aspect_y,
|
||||
params,
|
||||
&max_u,
|
||||
&max_v);
|
||||
break;
|
||||
@@ -1089,7 +1164,9 @@ void pack_islands(const Span<PackIsland *> &islands,
|
||||
|
||||
/** \} */
|
||||
|
||||
void PackIsland::build_transformation(const float scale, const float angle, float (*r_matrix)[2])
|
||||
void PackIsland::build_transformation(const float scale,
|
||||
const float angle,
|
||||
float (*r_matrix)[2]) const
|
||||
{
|
||||
const float cos_angle = cosf(angle);
|
||||
const float sin_angle = sinf(angle);
|
||||
@@ -1101,7 +1178,7 @@ void PackIsland::build_transformation(const float scale, const float angle, floa
|
||||
|
||||
void PackIsland::build_inverse_transformation(const float scale,
|
||||
const float angle,
|
||||
float (*r_matrix)[2])
|
||||
float (*r_matrix)[2]) const
|
||||
{
|
||||
/* TODO: Generate inverse transform directly. */
|
||||
build_transformation(scale, angle, r_matrix);
|
||||
|
||||
Reference in New Issue
Block a user