[Bf-blender-cvs] [931779197a9] master: Fix T99684: Upgrade Averages Island Scale with options Scale UV and Shear

Chris Blackbourn noreply at git.blender.org
Thu Jul 14 05:42:44 CEST 2022


Commit: 931779197a9ce141eccc8b8c500f9ef726a833eb
Author: Chris Blackbourn
Date:   Thu Jul 14 14:40:07 2022 +1200
Branches: master
https://developer.blender.org/rB931779197a9ce141eccc8b8c500f9ef726a833eb

Fix T99684: Upgrade Averages Island Scale with options Scale UV and Shear

Differential Revision: https://developer.blender.org/D15421

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

M	source/blender/blenlib/BLI_math_matrix.h
M	source/blender/blenlib/intern/math_matrix.c
M	source/blender/editors/uvedit/uvedit_unwrap_ops.c
M	source/blender/geometry/GEO_uv_parametrizer.h
M	source/blender/geometry/intern/uv_parametrizer.c

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

diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h
index 2cd2a299d53..c2dafbe3a1a 100644
--- a/source/blender/blenlib/BLI_math_matrix.h
+++ b/source/blender/blenlib/BLI_math_matrix.h
@@ -238,6 +238,7 @@ bool invert_m3_ex(float m[3][3], float epsilon);
 bool invert_m3_m3_ex(float m1[3][3], const float m2[3][3], float epsilon);
 
 bool invert_m3(float R[3][3]);
+bool invert_m2_m2(float R[2][2], const float A[2][2]);
 bool invert_m3_m3(float R[3][3], const float A[3][3]);
 bool invert_m4(float R[4][4]);
 bool invert_m4_m4(float R[4][4], const float A[4][4]);
diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c
index ce9abc36cad..fcd017b3082 100644
--- a/source/blender/blenlib/intern/math_matrix.c
+++ b/source/blender/blenlib/intern/math_matrix.c
@@ -1116,6 +1116,22 @@ double determinant_m3_array_db(const double m[3][3])
           m[2][0] * (m[0][1] * m[1][2] - m[0][2] * m[1][1]));
 }
 
+bool invert_m2_m2(float m1[2][2], const float m2[2][2])
+{
+  adjoint_m2_m2(m1, m2);
+  float det = determinant_m2(m2[0][0], m2[1][0], m2[0][1], m2[1][1]);
+
+  bool success = (det != 0.0f);
+  if (success) {
+    m1[0][0] /= det;
+    m1[1][0] /= det;
+    m1[0][1] /= det;
+    m1[1][1] /= det;
+  }
+
+  return success;
+}
+
 bool invert_m3_ex(float m[3][3], const float epsilon)
 {
   float tmp[3][3];
diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
index ae81aaffeb2..7ec80a05f6d 100644
--- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c
+++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
@@ -1191,7 +1191,7 @@ void UV_OT_pack_islands(wmOperatorType *ot)
 /** \name Average UV Islands Scale Operator
  * \{ */
 
-static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op))
+static int average_islands_scale_exec(bContext *C, wmOperator *op)
 {
   const Scene *scene = CTX_data_scene(C);
   ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -1215,8 +1215,12 @@ static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op))
     return OPERATOR_CANCELLED;
   }
 
+  /* RNA props */
+  const bool scale_uv = RNA_boolean_get(op->ptr, "scale_uv");
+  const bool shear = RNA_boolean_get(op->ptr, "shear");
+
   ParamHandle *handle = construct_param_handle_multi(scene, objects, objects_len, &options);
-  GEO_uv_parametrizer_average(handle, false);
+  GEO_uv_parametrizer_average(handle, false, scale_uv, shear);
   GEO_uv_parametrizer_flush(handle);
   GEO_uv_parametrizer_delete(handle);
 
@@ -1247,6 +1251,10 @@ void UV_OT_average_islands_scale(wmOperatorType *ot)
   /* api callbacks */
   ot->exec = average_islands_scale_exec;
   ot->poll = ED_operator_uvedit;
+
+  /* properties */
+  RNA_def_boolean(ot->srna, "scale_uv", false, "Non-Uniform", "Scale U and V independently");
+  RNA_def_boolean(ot->srna, "shear", false, "Shear", "Reduce shear within islands");
 }
 
 /** \} */
@@ -1845,7 +1853,7 @@ static void uvedit_unwrap(const Scene *scene,
                                  result_info ? &result_info->count_failed : NULL);
   GEO_uv_parametrizer_lscm_end(handle);
 
-  GEO_uv_parametrizer_average(handle, true);
+  GEO_uv_parametrizer_average(handle, true, false, false);
 
   GEO_uv_parametrizer_flush(handle);
 
diff --git a/source/blender/geometry/GEO_uv_parametrizer.h b/source/blender/geometry/GEO_uv_parametrizer.h
index 2181f95945e..5285aefbd4c 100644
--- a/source/blender/geometry/GEO_uv_parametrizer.h
+++ b/source/blender/geometry/GEO_uv_parametrizer.h
@@ -103,7 +103,10 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle,
 /** \name Average area for all charts
  * \{ */
 
-void GEO_uv_parametrizer_average(ParamHandle *handle, bool ignore_pinned);
+void GEO_uv_parametrizer_average(ParamHandle *handle,
+                                 bool ignore_pinned,
+                                 bool scale_uv,
+                                 bool shear);
 
 /** \} */
 
diff --git a/source/blender/geometry/intern/uv_parametrizer.c b/source/blender/geometry/intern/uv_parametrizer.c
index c75a302f8dc..38eb50722cf 100644
--- a/source/blender/geometry/intern/uv_parametrizer.c
+++ b/source/blender/geometry/intern/uv_parametrizer.c
@@ -148,7 +148,8 @@ typedef struct PChart {
     } lscm;
     struct PChartPack {
       float rescale, area;
-      float size[2] /* , trans[2] */;
+      float size[2];
+      float origin[2];
     } pack;
   } u;
 
@@ -4243,7 +4244,10 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle,
   }
 }
 
-void GEO_uv_parametrizer_average(ParamHandle *phandle, bool ignore_pinned)
+void GEO_uv_parametrizer_average(ParamHandle *phandle,
+                                 bool ignore_pinned,
+                                 bool scale_uv,
+                                 bool shear)
 {
   PChart *chart;
   int i;
@@ -4263,6 +4267,83 @@ void GEO_uv_parametrizer_average(ParamHandle *phandle, bool ignore_pinned)
       continue;
     }
 
+    p_chart_uv_bbox(chart, minv, maxv);
+    mid_v2_v2v2(chart->u.pack.origin, minv, maxv);
+
+    if (scale_uv || shear) {
+      /* It's possible that for some "bad" inputs, the following iteration will converge slowly or
+       * perhaps even diverge. Rather than infinite loop, we only iterate a maximum of `max_iter`
+       * times. (Also useful when making changes to the calculation.) */
+      int max_iter = 10;
+      for (int j = 0; j < max_iter; j++) {
+        /* An island could contain millions of polygons. When summing many small values, we need to
+         * use double precision in the accumulator to maintain accuracy. Note that the individual
+         * calculations only need to be at single precision.*/
+        double scale_cou = 0;
+        double scale_cov = 0;
+        double scale_cross = 0;
+        double weight_sum = 0;
+        for (PFace *f = chart->faces; f; f = f->nextlink) {
+          float m[2][2], s[2][2];
+          PVert *va = f->edge->vert;
+          PVert *vb = f->edge->next->vert;
+          PVert *vc = f->edge->next->next->vert;
+          s[0][0] = va->uv[0] - vc->uv[0];
+          s[0][1] = va->uv[1] - vc->uv[1];
+          s[1][0] = vb->uv[0] - vc->uv[0];
+          s[1][1] = vb->uv[1] - vc->uv[1];
+          /* Find the "U" axis and "V" axis in triangle co-ordinates. Normally this would require
+           * SVD, but in 2D we can use a cheaper matrix inversion instead.*/
+          if (!invert_m2_m2(m, s)) {
+            continue;
+          }
+          float cou[3], cov[3]; /* i.e. Texture "U" and texture "V" in 3D co-ordinates.*/
+          for (int i = 0; i < 3; i++) {
+            cou[i] = m[0][0] * (va->co[i] - vc->co[i]) + m[0][1] * (vb->co[i] - vc->co[i]);
+            cov[i] = m[1][0] * (va->co[i] - vc->co[i]) + m[1][1] * (vb->co[i] - vc->co[i]);
+          }
+          const float weight = p_face_area(f);
+          scale_cou += len_v3(cou) * weight;
+          scale_cov += len_v3(cov) * weight;
+          if (shear) {
+            normalize_v3(cov);
+            normalize_v3(cou);
+
+            /* Why is scale_cross called `cross` when we call `dot`? The next line calculates:
+             * `scale_cross += length(cross(cross(cou, face_normal), cov))`
+             * By construction, both `cou` and `cov` are orthogonal to the face normal.
+             * By definition, the normal vector has unit length. */
+            scale_cross += dot_v3v3(cou, cov) * weight;
+          }
+          weight_sum += weight;
+        }
+        if (scale_cou * scale_cov < 1e-10f) {
+          break;
+        }
+        const float scale_factor_u = scale_uv ? sqrtf(scale_cou / scale_cov) : 1.0f;
+
+        /* Compute correction transform. */
+        float t[2][2];
+        t[0][0] = scale_factor_u;
+        t[1][0] = clamp_f((float)(scale_cross / weight_sum), -0.5f, 0.5f);
+        t[0][1] = 0;
+        t[1][1] = 1.0f / scale_factor_u;
+
+        /* Apply the correction. */
+        p_chart_uv_transform(chart, t);
+
+        /* How far from the identity transform are we? [[1,0],[0,1]] */
+        const float err = fabsf(t[0][0] - 1.0f) + fabsf(t[1][0]) + fabsf(t[0][1]) +
+                          fabsf(t[1][1] - 1.0f);
+
+        const float tolerance = 1e-6f; /* Trade accuracy for performance. */
+        if (err < tolerance) {
+          /* Too slow? Use Richardson Extrapolation to accelerate the convergence.*/
+          break;
+        }
+      }
+    }
+
     chart->u.pack.area = 0.0f;    /* 3d area */
     chart->u.pack.rescale = 0.0f; /* UV area, abusing rescale for tmp storage, oh well :/ */
 
@@ -4292,18 +4373,16 @@ void GEO_uv_parametrizer_average(ParamHandle *phandle, bool ignore_pinned)
     if (chart->u.pack.area != 0.0f && chart->u.pack.rescale != 0.0f) {
       fac = chart->u.pack.area / chart->u.pack.rescale;
 
-      /* Get the island center */
-      p_chart_uv_bbox(chart, minv, maxv);
-      trans[0] = (minv[0] + maxv[0]) / -2.0f;
-      trans[1] = (minv[1] + maxv[1]) / -2.0f;
-
-      /* Move center to 0,0 */
-      p_chart_uv_translate(chart, trans);
+      /* Average scale. */
       p_chart_uv_scale(chart, sqrtf(fac / tot_fac));
 
-      /* Move to original center */
-      trans[0] = -trans[0];
-      trans[1] = -trans[1];
+      /* Get the current island center. */
+      p_chart_uv_bbox(chart, minv, maxv);
+
+      /* Move to original center. */
+      mid_v2_v2v2(trans, minv, maxv);
+      negate_v2(trans);
+      add_v2_v2(trans, chart->u.pack.origin);
       p_chart_uv_translate(chart, trans);
     }
   }



More information about the Bf-blender-cvs mailing list