[Bf-blender-cvs] [0e172e9732a] master: Fix T95649: Corrective Smooth can cause vertices to jump

Campbell Barton noreply at git.blender.org
Thu Sep 15 08:17:36 CEST 2022


Commit: 0e172e9732a1cab2aba324ce10ce304df7b69338
Author: Campbell Barton
Date:   Wed Apr 27 11:24:25 2022 +1000
Branches: master
https://developer.blender.org/rB0e172e9732a1cab2aba324ce10ce304df7b69338

Fix T95649: Corrective Smooth can cause vertices to jump

Calculate a tangent per loop, apply the transformation in multiple
spaces and accumulate the result weighting by the loop-angle.

Note that previously only the tangents Z axis (normal)
was properly accumulated, the X and Y axes only contained the values
from a single loop (the last one written to), meaning only two edges
contributed to the result. Now the transformation from all loops are
taken into account.

In practice the difference between both methods is minimal,
only for more extreme bending it can be noticed which is often when the
previous method didn't work as well.

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

M	source/blender/makesdna/DNA_modifier_types.h
M	source/blender/modifiers/intern/MOD_correctivesmooth.c

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

diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index 787f52f9891..7625f04fefa 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -1856,9 +1856,13 @@ enum {
 };
 
 typedef struct CorrectiveSmoothDeltaCache {
-  /* delta's between the original positions and the smoothed positions */
+  /**
+   * Delta's between the original positions and the smoothed positions,
+   * calculated loop-tangent and which is accumulated into the vertex it uses.
+   * (run-time only).
+   */
   float (*deltas)[3];
-  unsigned int totverts;
+  unsigned int deltas_num;
 
   /* Value of settings when creating the cache.
    * These are used to check if the cache should be recomputed. */
diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c
index c2320f2237f..4fdbe44281b 100644
--- a/source/blender/modifiers/intern/MOD_correctivesmooth.c
+++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c
@@ -49,10 +49,8 @@
 #include "PIL_time.h"
 #ifdef DEBUG_TIME
 #  include "PIL_time_utildefines.h"
-#endif
 
-/* minor optimization, calculate this inline */
-#define USE_TANGENT_CALC_INLINE
+#endif
 
 static void initData(ModifierData *md)
 {
@@ -79,7 +77,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
   }
 
   tcsmd->delta_cache.deltas = NULL;
-  tcsmd->delta_cache.totverts = 0;
+  tcsmd->delta_cache.deltas_num = 0;
 }
 
 static void freeBind(CorrectiveSmoothModifierData *csmd)
@@ -391,69 +389,61 @@ static void smooth_verts(CorrectiveSmoothModifierData *csmd,
 }
 
 /**
- * finalize after accumulation.
+ * Calculate an orthogonal 3x3 matrix from 2 edge vectors.
+ * \return false if this loop should be ignored (have zero influence).
  */
-static void calc_tangent_ortho(float ts[3][3])
+static bool calc_tangent_loop(const float v_dir_prev[3],
+                              const float v_dir_next[3],
+                              float r_tspace[3][3])
 {
-  float v_tan_a[3], v_tan_b[3];
-  float t_vec_a[3], t_vec_b[3];
-
-  normalize_v3(ts[2]);
-
-  copy_v3_v3(v_tan_a, ts[0]);
-  copy_v3_v3(v_tan_b, ts[1]);
-
-  cross_v3_v3v3(ts[1], ts[2], v_tan_a);
-  mul_v3_fl(ts[1], dot_v3v3(ts[1], v_tan_b) < 0.0f ? -1.0f : 1.0f);
-
-  /* Orthogonalize tangent. */
-  mul_v3_v3fl(t_vec_a, ts[2], dot_v3v3(ts[2], v_tan_a));
-  sub_v3_v3v3(ts[0], v_tan_a, t_vec_a);
-
-  /* Orthogonalize bi-tangent. */
-  mul_v3_v3fl(t_vec_a, ts[2], dot_v3v3(ts[2], ts[1]));
-  mul_v3_v3fl(t_vec_b, ts[0], dot_v3v3(ts[0], ts[1]) / dot_v3v3(v_tan_a, v_tan_a));
-  sub_v3_v3(ts[1], t_vec_a);
-  sub_v3_v3(ts[1], t_vec_b);
-
-  normalize_v3(ts[0]);
-  normalize_v3(ts[1]);
+  if (UNLIKELY(compare_v3v3(v_dir_prev, v_dir_next, FLT_EPSILON * 10.0f))) {
+    /* As there are no weights, the value doesn't matter just initialize it. */
+    unit_m3(r_tspace);
+    return false;
+  }
+
+  copy_v3_v3(r_tspace[0], v_dir_prev);
+  copy_v3_v3(r_tspace[1], v_dir_next);
+
+  cross_v3_v3v3(r_tspace[2], v_dir_prev, v_dir_next);
+  normalize_v3(r_tspace[2]);
+
+  /* Make orthogonal using `r_tspace[2]` as a basis.
+   *
+   * NOTE: while it seems more logical to use `v_dir_prev` & `v_dir_next` as separate X/Y axis
+   * (instead of combining them as is done here). It's not necessary as the directions of the
+   * axis aren't important as long as the difference between tangent matrices is equivalent.
+   * Some computations can be skipped by combining the the two directions,
+   * using the cross product for the 3rd axes. */
+  add_v3_v3(r_tspace[0], r_tspace[1]);
+  normalize_v3(r_tspace[0]);
+  cross_v3_v3v3(r_tspace[1], r_tspace[2], r_tspace[0]);
+
+  return true;
 }
 
 /**
- * accumulate edge-vectors from all polys.
+ * \param r_tangent_spaces: Loop aligned array of tangents.
+ * \param r_tangent_weights: Loop aligned array of weights (may be NULL).
+ * \param r_tangent_weights_per_vertex: Vertex aligned array, accumulating weights for each loop
+ * (may be NULL).
  */
-static void calc_tangent_loop_accum(const float v_dir_prev[3],
-                                    const float v_dir_next[3],
-                                    float r_tspace[3][3])
-{
-  add_v3_v3v3(r_tspace[1], v_dir_prev, v_dir_next);
-
-  if (compare_v3v3(v_dir_prev, v_dir_next, FLT_EPSILON * 10.0f) == false) {
-    const float weight = fabsf(acosf(dot_v3v3(v_dir_next, v_dir_prev)));
-    float nor[3];
-
-    cross_v3_v3v3(nor, v_dir_prev, v_dir_next);
-    normalize_v3(nor);
-
-    cross_v3_v3v3(r_tspace[0], r_tspace[1], nor);
-
-    mul_v3_fl(nor, weight);
-    /* accumulate weighted normals */
-    add_v3_v3(r_tspace[2], nor);
-  }
-}
-
-static void calc_tangent_spaces(Mesh *mesh, float (*vertexCos)[3], float (*r_tangent_spaces)[3][3])
+static void calc_tangent_spaces(const Mesh *mesh,
+                                const float (*vertexCos)[3],
+                                float (*r_tangent_spaces)[3][3],
+                                float *r_tangent_weights,
+                                float *r_tangent_weights_per_vertex)
 {
   const uint mpoly_num = (uint)mesh->totpoly;
-#ifndef USE_TANGENT_CALC_INLINE
-  const uint mvert_num = (uint)dm->getNumVerts(dm);
-#endif
+  const uint mvert_num = (uint)mesh->totvert;
   const MPoly *mpoly = BKE_mesh_polys(mesh);
   const MLoop *mloop = BKE_mesh_loops(mesh);
   uint i;
 
+  if (r_tangent_weights_per_vertex != NULL) {
+    copy_vn_fl(r_tangent_weights_per_vertex, (int)mvert_num, 0.0f);
+  }
+
   for (i = 0; i < mpoly_num; i++) {
     const MPoly *mp = &mpoly[i];
     const MLoop *l_next = &mloop[mp->loopstart];
@@ -469,7 +459,8 @@ static void calc_tangent_spaces(Mesh *mesh, float (*vertexCos)[3], float (*r_tan
     normalize_v3(v_dir_prev);
 
     for (; l_next != l_term; l_prev = l_curr, l_curr = l_next, l_next++) {
-      float(*ts)[3] = r_tangent_spaces[l_curr->v];
+      uint l_index = (uint)(l_curr - mloop);
+      float(*ts)[3] = r_tangent_spaces[l_index];
 
       /* re-use the previous value */
 #if 0
@@ -479,19 +470,22 @@ static void calc_tangent_spaces(Mesh *mesh, float (*vertexCos)[3], float (*r_tan
       sub_v3_v3v3(v_dir_next, vertexCos[l_curr->v], vertexCos[l_next->v]);
       normalize_v3(v_dir_next);
 
-      calc_tangent_loop_accum(v_dir_prev, v_dir_next, ts);
+      if (calc_tangent_loop(v_dir_prev, v_dir_next, ts)) {
+        if (r_tangent_weights != NULL) {
+          const float weight = fabsf(acosf(dot_v3v3(v_dir_next, v_dir_prev)));
+          r_tangent_weights[l_index] = weight;
+          r_tangent_weights_per_vertex[l_curr->v] += weight;
+        }
+      }
+      else {
+        if (r_tangent_weights != NULL) {
+          r_tangent_weights[l_index] = 0;
+        }
+      }
 
       copy_v3_v3(v_dir_prev, v_dir_next);
     }
   }
-
-  /* do inline */
-#ifndef USE_TANGENT_CALC_INLINE
-  for (i = 0; i < mvert_num; i++) {
-    float(*ts)[3] = r_tangent_spaces[i];
-    calc_tangent_ortho(ts);
-  }
-#endif
 }
 
 static void store_cache_settings(CorrectiveSmoothModifierData *csmd)
@@ -522,38 +516,42 @@ static void calc_deltas(CorrectiveSmoothModifierData *csmd,
                         const float (*rest_coords)[3],
                         uint verts_num)
 {
+  const MLoop *mloop = BKE_mesh_loops(mesh);
+  const uint loops_num = (uint)mesh->totloop;
+
   float(*smooth_vertex_coords)[3] = MEM_dupallocN(rest_coords);
   float(*tangent_spaces)[3][3];
-  uint i;
 
-  tangent_spaces = MEM_calloc_arrayN(verts_num, sizeof(float[3][3]), __func__);
+  uint l_index;
+
+  tangent_spaces = MEM_malloc_arrayN(loops_num, sizeof(float[3][3]), __func__);
 
-  if (csmd->delta_cache.totverts != verts_num) {
+  if (csmd->delta_cache.deltas_num != loops_num) {
     MEM_SAFE_FREE(csmd->delta_cache.deltas);
   }
 
   /* allocate deltas if they have not yet been allocated, otherwise we will just write over them */
   if (!csmd->delta_cache.deltas) {
-    csmd->delta_cache.totverts = verts_num;
-    csmd->delta_cache.deltas = MEM_malloc_arrayN(verts_num, sizeof(float[3]), __func__);
+    csmd->delta_cache.deltas_num = loops_num;
+    csmd->delta_cache.deltas = MEM_malloc_arrayN(loops_num, sizeof(float[3]), __func__);
   }
 
   smooth_verts(csmd, mesh, dvert, defgrp_index, smooth_vertex_coords, verts_num);
 
-  calc_tangent_spaces(mesh, smooth_vertex_coords, tangent_spaces);
+  calc_tangent_spaces(mesh, smooth_vertex_coords, tangent_spaces, NULL, NULL);
 
-  for (i = 0; i < verts_num; i++) {
-    float imat[3][3], delta[3];
+  copy_vn_fl(&csmd->delta_cache.deltas[0][0], (int)loops_num * 3, 0.0f);
 
-#ifdef USE_TANGENT_CALC_INLINE
-    calc_tangent_ortho(tangent_spaces[i]);
-#endif
+  for (l_index = 0; l_index < loops_num; l_index++) {
+    const int v_index = (int)mloop[l_index].v;
+    float delta[3];
+    sub_v3_v3v3(delta, rest_coords[v_index], smooth_vertex_coords[v_index]);
 
-    sub_v3_v3v3(delta, rest_coords[i], smooth_vertex_coords[i]);
-    if (UNLIKELY(!invert_m3_m3(imat, tangent_spaces[i]))) {
-      transpose_m3_m3(imat, tangent_spaces[i]);
+    float imat[3][3];
+    if (UNLIKELY(!invert_m3_m3(imat, tangent_spaces[l_index]))) {
+      transpose_m3_m3(imat, tangent_spaces[l_index]);
     }
-    mul_v3_m3v3(csmd->delta_cache.deltas[i], imat, delta);
+    mul_v3_m3v3(csmd->delta_cache.deltas[l_index], imat, delta);
   }
 
   MEM_freeN(tangent_spaces);
@@ -576,6 +574,9 @@ static void correctivesmooth_modifier_do(ModifierData *md,
       ((csmd->rest_source == MOD_CORRECTIVESMOOTH_RESTSOURCE_ORCO) &&
        (((ID *)ob->data)->recalc & ID_RECALC_ALL));
 
+  const MLoop *mloop = BKE_mesh_loops(mesh);
+  const uint loops_num = (uint)mesh->totloop;
+
   bool use_only_smooth = (csmd->flag & MOD_CORRECTIVESMOOTH_ONLY_SMOOTH) != 0;
   const MDeformVert *dvert = NULL;
   int defgrp_index;
@@ -638,7 +639,7 @@ static void correctivesmooth_modifier_do(ModifierData *md,
   }
 
   /* check to see if our deltas are still valid */
-  if (!csmd->delta_cache.deltas || (csmd->delta_cache.totverts != verts_num) ||
+  if (!csmd->delta_cache.deltas || (csmd->delta_cache.deltas_num != loops_num) ||
       force_delta_cache_update) {
     const float(*rest_coords)[3];
     bool is_rest_coords_alloc 

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list