Cleanup: Improve precision during UV packing.

Simplify API and improve accuracy of uv packing placement
by using pre-translation and double precision internally.

Will protect against future precision problems with UDIM.

No user visible changes expected.

Maniphest Tasks: T68889
Differential Revision: https://developer.blender.org/D16362
This commit is contained in:
2022-11-09 08:37:14 +13:00
parent 96d8e5e66b
commit c6aacd718a
3 changed files with 56 additions and 47 deletions

View File

@@ -113,17 +113,6 @@ void BM_face_uv_minmax(const BMFace *f, float min[2], float max[2], const int cd
} while ((l_iter = l_iter->next) != l_first);
}
void BM_face_uv_transform(BMFace *f, const float matrix[2][2], const int cd_loop_uv_offset)
{
BMLoop *l_iter;
BMLoop *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
MLoopUV *luv = (MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
mul_m2_v2(matrix, luv->uv);
} while ((l_iter = l_iter->next) != l_first);
}
bool BM_loop_uv_share_edge_check(BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset)
{
BLI_assert(l_a->e == l_b->e);

View File

@@ -34,7 +34,6 @@ float BM_face_uv_calc_cross(const BMFace *f, int cd_loop_uv_offset) ATTR_WARN_UN
ATTR_NONNULL();
void BM_face_uv_minmax(const BMFace *f, float min[2], float max[2], int cd_loop_uv_offset);
void BM_face_uv_transform(BMFace *f, const float matrix[2][2], int cd_loop_uv_offset);
bool BM_loop_uv_share_edge_check_with_limit(BMLoop *l_a,
BMLoop *l_b,

View File

@@ -36,28 +36,47 @@
#include "bmesh.h"
/* -------------------------------------------------------------------- */
/** \name UV Face Utilities
* \{ */
static void bm_face_uv_translate_and_scale_around_pivot(BMFace *f,
const float offset[2],
const float scale[2],
const float pivot[2],
const int cd_loop_uv_offset)
static void mul_v2_m2_add_v2v2(float r[2],
const float mat[2][2],
const float a[2],
const float b[2])
{
BMLoop *l_iter;
BMLoop *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
MLoopUV *luv = static_cast<MLoopUV *>(BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset));
for (int i = 0; i < 2; i++) {
luv->uv[i] = offset[i] + (((luv->uv[i] - pivot[i]) * scale[i]) + pivot[i]);
}
} while ((l_iter = l_iter->next) != l_first);
/* Compute `r = mat * (a + b)` with high precision. */
const double x = static_cast<double>(a[0]) + static_cast<double>(b[0]);
const double y = static_cast<double>(a[1]) + static_cast<double>(b[1]);
r[0] = static_cast<float>(mat[0][0] * x + mat[1][0] * y);
r[1] = static_cast<float>(mat[0][1] * x + mat[1][1] * y);
}
/** \} */
static void island_uv_transform(FaceIsland *island,
const float matrix[2][2], /* Scale and rotation. */
const float pre_translate[2] /* (pre) Translation. */
)
{
/* Use a pre-transform to compute `A * (x+b)`
*
* \note Ordinarily, we'd use a post_transform like `A * x + b`
* In general, post-transforms are easier to work with when using homogenous co-ordinates.
*
* When UV mapping into the unit square, post-transforms can lose precision on small islands.
* Instead we're using a pre-transform to maintain precision.
*
* To convert post-transform to pre-transform, use `A * x + b == A * (x + c), c = A^-1 * b`
*/
const int cd_loop_uv_offset = island->cd_loop_uv_offset;
const int faces_len = island->faces_len;
for (int i = 0; i < faces_len; i++) {
BMFace *f = island->faces[i];
BMLoop *l;
BMIter iter;
BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) {
MLoopUV *luv = (MLoopUV *)BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
mul_v2_m2_add_v2v2(luv->uv, matrix, luv->uv, pre_translate);
}
}
}
/* -------------------------------------------------------------------- */
/** \name UV Face Array Utilities
@@ -198,13 +217,12 @@ static void face_island_uv_rotate_fit_aabb(FaceIsland *island)
/* Apply rotation back to BMesh. */
if (angle != 0.0f) {
float matrix[2][2];
float pre_translate[2] = {0, 0};
angle_to_mat2(matrix, angle);
matrix[1][0] *= 1.0f / aspect_y;
/* matrix[1][1] *= aspect_y / aspect_y; */
matrix[0][1] *= aspect_y;
for (int i = 0; i < faces_len; i++) {
BM_face_uv_transform(faces[i], matrix, cd_loop_uv_offset);
}
island_uv_transform(island, matrix, pre_translate);
}
}
@@ -769,21 +787,24 @@ void ED_uvedit_pack_islands_multi(const Scene *scene,
}
}
float matrix[2][2];
float matrix_inverse[2][2];
float pre_translate[2];
for (int i = 0; i < island_vector.size(); i++) {
FaceIsland *island = island_vector[box_array[i].index];
const float pivot[2] = {
island->bounds_rect.xmin,
island->bounds_rect.ymin,
};
const float offset[2] = {
((box_array[i].x * scale[0]) - island->bounds_rect.xmin) + base_offset[0],
((box_array[i].y * scale[1]) - island->bounds_rect.ymin) + base_offset[1],
};
for (int j = 0; j < island->faces_len; j++) {
BMFace *efa = island->faces[j];
bm_face_uv_translate_and_scale_around_pivot(
efa, offset, scale, pivot, island->cd_loop_uv_offset);
}
matrix[0][0] = scale[0];
matrix[0][1] = 0.0f;
matrix[1][0] = 0.0f;
matrix[1][1] = scale[1];
invert_m2_m2(matrix_inverse, matrix);
/* Add base_offset, post transform. */
mul_v2_m2v2(pre_translate, matrix_inverse, base_offset);
/* Translate to box_array from bounds_rect. */
pre_translate[0] += box_array[i].x - island->bounds_rect.xmin;
pre_translate[1] += box_array[i].y - island->bounds_rect.ymin;
island_uv_transform(island, matrix, pre_translate);
}
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {