[Bf-blender-cvs] [bfd83583b2a] temp-angavrilov-constraints: Surface Deform: fix binding vertex artifacts causing spikes.

Alexander Gavrilov noreply at git.blender.org
Sun Jan 24 15:48:32 CET 2021


Commit: bfd83583b2a913327c73a38f587a62e150180c8d
Author: Alexander Gavrilov
Date:   Thu Jan 7 20:40:29 2021 +0300
Branches: temp-angavrilov-constraints
https://developer.blender.org/rBbfd83583b2a913327c73a38f587a62e150180c8d

Surface Deform: fix binding vertex artifacts causing spikes.

There are two issues here. First, like in T81988 there are cases
where the modifier would deform some vertices immediately after
bind. This is caused by wrong assumptions in the code about the
possible relative angles between various vectors, which can cause
negative weights that don't blend correctly to appear.

Specifically, it seems originally the code assumes that the
centroid-point vector in the polygon plane lies somewhere
between the mid-edge vectors. This is however not necessarily
the case for distant vertices, because the polygon is not
guaranteed to be truly planar, so normal projection may be
a bit off. The code has to use signed angles and checks to
support all possible angular arrangements.

The second issue is very thin and long triangles, which tend
to be very spatially unstable in their thin dimension, resulting
in excess deformation. The code was weighting distance using
the distances between the centroid and the mid-edge points, which
in this case end up as nearly opposite vectors of sizable length
and don't correctly represent how thin the triangle actually is.
It is thus better to use centroid-to-line distances, and an
additional even stricter value for the midpoint that will use
only 3 vertices at evaluation time.

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

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

M	source/blender/modifiers/intern/MOD_surfacedeform.c

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

diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c
index 0fad78683eb..008e258dc7a 100644
--- a/source/blender/modifiers/intern/MOD_surfacedeform.c
+++ b/source/blender/modifiers/intern/MOD_surfacedeform.c
@@ -114,7 +114,12 @@ typedef struct SDefBindPoly {
   float weight_dist_proj;
   float weight_dist;
   float weight;
+  /** Distances from the centroid to edges flanking the corner vertex, used to penalize
+   *  small or long and narrow faces in favor of bigger and more square ones. */
   float scales[2];
+  /** Distance weight from the corner vertex to the chord line, used to penalize
+   *  cases with the three consecutive vertices being nearly in line. */
+  float scale_mid;
   /** Center of `coords` */
   float centroid[3];
   /** Center of `coords_v2` */
@@ -123,15 +128,18 @@ typedef struct SDefBindPoly {
    * The calculated normal of coords (could be shared between faces).
    */
   float normal[3];
+  /** Vectors pointing from the centroid to the midpoints of the two edges
+   *  flanking the corner vertex. */
   float cent_edgemid_vecs_v2[2][2];
-  /**
-   * The unsigned angle of this face-corner in `[0.0 .. PI]` range,
-   * where a small value is a thin corner. PI is a straight line.
-   * Take care dividing by this value as it can approach zero.
-   */
+  /** Angle between the cent_edgemid_vecs_v2 vectors. */
   float edgemid_angle;
+  /** Angles between the centroid-to-point and cent_edgemid_vecs_v2 vectors.
+   *  Positive values measured towards the corner; clamped non-negative. */
   float point_edgemid_angles[2];
+  /** Angles between the centroid-to-corner and cent_edgemid_vecs_v2 vectors. */
   float corner_edgemid_angles[2];
+  /** Weight of the bind mode based on the corner and two adjacent vertices,
+   *  versus the one based on the centroid and the dominant edge. */
   float dominant_angle_weight;
   /** Index of the input polygon. */
   uint index;
@@ -414,13 +422,14 @@ BLI_INLINE uint nearestVert(SDefBindCalcData *const data, const float point_co[3
 
 BLI_INLINE int isPolyValid(const float coords[][2], const uint nr)
 {
-  float prev_co[2];
+  float prev_co[2], prev_prev_co[2];
   float curr_vec[2], prev_vec[2];
 
   if (!is_poly_convex_v2(coords, nr)) {
     return MOD_SDEF_BIND_RESULT_CONCAVE_ERR;
   }
 
+  copy_v2_v2(prev_prev_co, coords[nr - 2]);
   copy_v2_v2(prev_co, coords[nr - 1]);
   sub_v2_v2v2(prev_vec, prev_co, coords[nr - 2]);
   normalize_v2(prev_vec);
@@ -428,15 +437,23 @@ BLI_INLINE int isPolyValid(const float coords[][2], const uint nr)
   for (int i = 0; i < nr; i++) {
     sub_v2_v2v2(curr_vec, coords[i], prev_co);
 
+    /* Check ovelap between directly adjacent vertices. */
     const float curr_len = normalize_v2(curr_vec);
     if (curr_len < FLT_EPSILON) {
       return MOD_SDEF_BIND_RESULT_OVERLAP_ERR;
     }
 
+    /* Check ovelap between vertices skipping one. */
+    if (len_squared_v2v2(prev_prev_co, coords[i]) < FLT_EPSILON * FLT_EPSILON) {
+      return MOD_SDEF_BIND_RESULT_OVERLAP_ERR;
+    }
+
+    /* Check for adjacent parallel edges. */
     if (1.0f - dot_v2v2(prev_vec, curr_vec) < FLT_EPSILON) {
       return MOD_SDEF_BIND_RESULT_CONCAVE_ERR;
     }
 
+    copy_v2_v2(prev_prev_co, prev_co);
     copy_v2_v2(prev_co, coords[i]);
     copy_v2_v2(prev_vec, curr_vec);
   }
@@ -460,9 +477,9 @@ static void freeBindData(SDefBindWeightData *const bwdata)
   MEM_freeN(bwdata);
 }
 
-BLI_INLINE float computeAngularWeight(const float point_angle)
+BLI_INLINE float computeAngularWeight(const float point_angle, const float edgemid_angle)
 {
-  return sinf(point_angle * M_PI_2);
+  return sinf(min_ff(point_angle / edgemid_angle, 1) * M_PI_2);
 }
 
 BLI_INLINE SDefBindWeightData *computeBindWeights(SDefBindCalcData *const data,
@@ -603,33 +620,51 @@ BLI_INLINE SDefBindWeightData *computeBindWeights(SDefBindCalcData *const data,
 
         avg_point_dist += bpoly->weight_dist;
 
-        /* Compute centroid to mid-edge vectors */
-        mid_v2_v2v2(bpoly->cent_edgemid_vecs_v2[0],
-                    bpoly->coords_v2[bpoly->edge_vert_inds[0]],
-                    bpoly->coords_v2[bpoly->corner_ind]);
+        /* Common vertex coordinates. */
+        const float *const vert0_v2 = bpoly->coords_v2[bpoly->edge_vert_inds[0]];
+        const float *const vert1_v2 = bpoly->coords_v2[bpoly->edge_vert_inds[1]];
+        const float *const corner_v2 = bpoly->coords_v2[bpoly->corner_ind];
 
-        mid_v2_v2v2(bpoly->cent_edgemid_vecs_v2[1],
-                    bpoly->coords_v2[bpoly->edge_vert_inds[1]],
-                    bpoly->coords_v2[bpoly->corner_ind]);
+        /* Compute centroid to mid-edge vectors */
+        mid_v2_v2v2(bpoly->cent_edgemid_vecs_v2[0], vert0_v2, corner_v2);
+        mid_v2_v2v2(bpoly->cent_edgemid_vecs_v2[1], vert1_v2, corner_v2);
 
         sub_v2_v2(bpoly->cent_edgemid_vecs_v2[0], bpoly->centroid_v2);
         sub_v2_v2(bpoly->cent_edgemid_vecs_v2[1], bpoly->centroid_v2);
 
-        /* Compute poly scales with respect to mid-edges, and normalize the vectors */
-        bpoly->scales[0] = normalize_v2(bpoly->cent_edgemid_vecs_v2[0]);
-        bpoly->scales[1] = normalize_v2(bpoly->cent_edgemid_vecs_v2[1]);
+        normalize_v2(bpoly->cent_edgemid_vecs_v2[0]);
+        normalize_v2(bpoly->cent_edgemid_vecs_v2[1]);
 
-        /* Compute the required polygon angles */
+        /* Compute poly scales with respect to the two edges. */
+        bpoly->scales[0] = dist_to_line_v2(bpoly->centroid_v2, vert0_v2, corner_v2);
+        bpoly->scales[1] = dist_to_line_v2(bpoly->centroid_v2, vert1_v2, corner_v2);
+
+        /* Compute the angle between the edge mid vectors. */
         bpoly->edgemid_angle = angle_normalized_v2v2(bpoly->cent_edgemid_vecs_v2[0],
                                                      bpoly->cent_edgemid_vecs_v2[1]);
 
-        sub_v2_v2v2(tmp_vec_v2, bpoly->coords_v2[bpoly->corner_ind], bpoly->centroid_v2);
+        /* Compute the angles between the corner and the edge mid vectors. The angles
+         * are computed signed in order to correctly clamp point_edgemid_angles later. */
+        float corner_angles[2];
+
+        sub_v2_v2v2(tmp_vec_v2, corner_v2, bpoly->centroid_v2);
         normalize_v2(tmp_vec_v2);
 
-        bpoly->corner_edgemid_angles[0] = angle_normalized_v2v2(tmp_vec_v2,
-                                                                bpoly->cent_edgemid_vecs_v2[0]);
-        bpoly->corner_edgemid_angles[1] = angle_normalized_v2v2(tmp_vec_v2,
-                                                                bpoly->cent_edgemid_vecs_v2[1]);
+        corner_angles[0] = angle_signed_v2v2(tmp_vec_v2, bpoly->cent_edgemid_vecs_v2[0]);
+        corner_angles[1] = angle_signed_v2v2(tmp_vec_v2, bpoly->cent_edgemid_vecs_v2[1]);
+
+        bpoly->corner_edgemid_angles[0] = fabsf(corner_angles[0]);
+        bpoly->corner_edgemid_angles[1] = fabsf(corner_angles[1]);
+
+        /* Verify that the computed values are valid (the polygon isn't somehow
+         * degenerate despite having passed isPolyValid). */
+        if (bpoly->scales[0] < FLT_EPSILON || bpoly->scales[1] < FLT_EPSILON ||
+            bpoly->edgemid_angle < FLT_EPSILON || bpoly->corner_edgemid_angles[0] < FLT_EPSILON ||
+            bpoly->corner_edgemid_angles[1] < FLT_EPSILON) {
+          freeBindData(bwdata);
+          data->success = MOD_SDEF_BIND_RESULT_GENERIC_ERR;
+          return NULL;
+        }
 
         /* Check for infinite weights, and compute angular data otherwise. */
         if (bpoly->weight_dist < FLT_EPSILON) {
@@ -640,15 +675,54 @@ BLI_INLINE SDefBindWeightData *computeBindWeights(SDefBindCalcData *const data,
           inf_weight_flags |= MOD_SDEF_INFINITE_WEIGHT_DIST_PROJ;
         }
         else {
-          float cent_point_vec[2];
+          /* Compute angles between the point and the edge mid vectors.  */
+          float cent_point_vec[2], point_angles[2];
 
           sub_v2_v2v2(cent_point_vec, bpoly->point_v2, bpoly->centroid_v2);
           normalize_v2(cent_point_vec);
 
-          bpoly->point_edgemid_angles[0] = angle_normalized_v2v2(cent_point_vec,
-                                                                 bpoly->cent_edgemid_vecs_v2[0]);
-          bpoly->point_edgemid_angles[1] = angle_normalized_v2v2(cent_point_vec,
-                                                                 bpoly->cent_edgemid_vecs_v2[1]);
+          point_angles[0] = angle_signed_v2v2(cent_point_vec, bpoly->cent_edgemid_vecs_v2[0]) *
+                            signf(corner_angles[0]);
+          point_angles[1] = angle_signed_v2v2(cent_point_vec, bpoly->cent_edgemid_vecs_v2[1]) *
+                            signf(corner_angles[1]);
+
+          if (point_angles[0] <= 0 && point_angles[1] <= 0) {
+            /* If the point is outside the corner formed by the edge mid vectors,
+             * choose to clamp the closest side and flip the other. */
+            if (point_angles[0] < point_angles[1]) {
+              point_angles[0] = bpoly->edgemid_angle - point_angles[1];
+            }
+            else {
+              point_angles[1] = bpoly->edgemid_angle - point_angles[0];
+            }
+          }
+
+          bpoly->point_edgemid_angles[0] = max_ff(0, point_angles[0]);
+          bpoly->point_edgemid_angles[1] = max_ff(0, point_angles[1]);
+
+          /* Compute the distance scale for the corner. The base value is the orthogonal
+           * distance from the corner to the chord, scaled by sqrt(2) to preserve the old
+           * values in case of a square grid. This doesn't use the centroid because the
+           * LOOPTRI method only uses these three vertices. */
+          bpoly->scale_mid = area_tri_v2(vert0_v2, corner_v2, vert1_v2) /
+                             len_v2v2(vert0_v2, vert1_v2) * sqrtf(2);
+
+          if (bpoly->inside) {
+            /* When inside, interpolate to centroid-based scale close to the center. */
+            float min_dist = min_ff(bpoly->scales[0], bpoly->scales[1]);
+
+            bpoly->scale_mid = interpf(bpoly->scale_mid,
+                                       (bpoly->scales[0] + bpoly->scales[1]) / 2,
+                               

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list