[Bf-blender-cvs] [c6aacd718a5] master: Cleanup: Improve precision during UV packing.

Chris Blackbourn noreply at git.blender.org
Tue Nov 8 20:51:59 CET 2022


Commit: c6aacd718a51dea2f0736280a6bd605898a320f2
Author: Chris Blackbourn
Date:   Wed Nov 9 08:37:14 2022 +1300
Branches: master
https://developer.blender.org/rBc6aacd718a51dea2f0736280a6bd605898a320f2

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

===================================================================

M	source/blender/bmesh/intern/bmesh_query_uv.cc
M	source/blender/bmesh/intern/bmesh_query_uv.h
M	source/blender/editors/uvedit/uvedit_islands.cc

===================================================================

diff --git a/source/blender/bmesh/intern/bmesh_query_uv.cc b/source/blender/bmesh/intern/bmesh_query_uv.cc
index 33b2ca7a828..0e2385ff4e2 100644
--- a/source/blender/bmesh/intern/bmesh_query_uv.cc
+++ b/source/blender/bmesh/intern/bmesh_query_uv.cc
@@ -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);
diff --git a/source/blender/bmesh/intern/bmesh_query_uv.h b/source/blender/bmesh/intern/bmesh_query_uv.h
index 2b0833f9185..6aa82653535 100644
--- a/source/blender/bmesh/intern/bmesh_query_uv.h
+++ b/source/blender/bmesh/intern/bmesh_query_uv.h
@@ -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,
diff --git a/source/blender/editors/uvedit/uvedit_islands.cc b/source/blender/editors/uvedit/uvedit_islands.cc
index 92745667505..d8e10435146 100644
--- a/source/blender/editors/uvedit/uvedit_islands.cc
+++ b/source/blender/editors/uvedit/uvedit_islands.cc
@@ -36,29 +36,48 @@
 
 #include "bmesh.h"
 
-/* -------------------------------------------------------------------- */
-/** \name UV Face Utilities
- * \{ */
+static 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. */
+  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 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 island_uv_transform(FaceIsland *island,
+                                const float matrix[2][2],    /* Scale and rotation. */
+                                const float pre_translate[2] /* (pre) Translation. */
+)
 {
-  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]);
+  /* 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);
     }
-  } while ((l_iter = l_iter->next) != l_first);
+  }
 }
 
-/** \} */
-
 /* -------------------------------------------------------------------- */
 /** \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++) {



More information about the Bf-blender-cvs mailing list