Commit: 626f791f366e9a71018bd38074ce189e68b5de73
Author: Alexander Gavrilov
Date:   Thu Sep 1 19:18:51 2022 +0300
Branches: temp-angavrilov
https://developer.blender.org/rB626f791f366e9a71018bd38074ce189e68b5de73

Shrinkwrap: support smoothing the mesh after wrapping.

Adds a smoothing post-processing option for the shrinkwrap modifier.
On any other setting than 0 iterations, the algorithm adds the laplacian
of the deltas to the base mesh to produce a smooth-looking deformed
mesh -- this makes it possible to avoid losing detail on parts of the
mesh that are not affected by the shrinkwrap displacement.

Deltas that would otherwise lose magnitude as a result of smoothing
are not updated to avoid further volume loss; this keeps the result
much closer to the surface of the target while still adjusting other
shrinkwrapped (and non-shrinkwrapped) vertexes to follow the smooth
surface.

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

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

M       source/blender/blenkernel/intern/shrinkwrap.cc
M       source/blender/makesdna/DNA_modifier_types.h
M       source/blender/makesrna/intern/rna_modifier.c
M       source/blender/modifiers/intern/MOD_shrinkwrap.c

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

diff --git a/source/blender/blenkernel/intern/shrinkwrap.cc 
b/source/blender/blenkernel/intern/shrinkwrap.cc
index d196f044c58..8a7c89ac46d 100644
--- a/source/blender/blenkernel/intern/shrinkwrap.cc
+++ b/source/blender/blenkernel/intern/shrinkwrap.cc
@@ -63,6 +63,10 @@ struct ShrinkwrapCalcData {
   const float (*vert_normals)[3];
   /* Vertices being shrink-wrapped. */
   float (*vertexCos)[3];
+  /* Cached vertex deltas. */
+  float (*deltas)[3];
+  /* Cached vertex weights */
+  float *weights;
   int numVerts;
 
   const MDeformVert *dvert; /* Pointer to mdeform array */
@@ -326,6 +330,19 @@ void BKE_shrinkwrap_compute_boundary_data(Mesh *mesh)
   mesh->runtime->shrinkwrap_data = shrinkwrap_build_boundary_data(mesh);
 }
 
+/** Output the computed vertex position either to the final coordinate or the 
delta array. */
+BLI_INLINE void shrinkwrap_save_result(
+    ShrinkwrapCalcData *calc, int i, float *co, float *result, float weight)
+{
+  if (calc->deltas) {
+    sub_v3_v3v3(calc->deltas[i], result, co);
+    mul_v3_fl(calc->deltas[i], weight);
+  }
+  else {
+    interp_v3_v3v3(co, co, result, weight); /* linear interpolation */
+  }
+}
+
 /**
  * Shrink-wrap to the nearest vertex
  *
@@ -354,6 +371,10 @@ static void shrinkwrap_calc_nearest_vertex_cb_ex(void 
*__restrict userdata,
     return;
   }
 
+  if (calc->weights) {
+    calc->weights[i] = weight;
+  }
+
   /* Convert the vertex to tree coordinates */
   if (calc->vert) {
     copy_v3_v3(tmp_co, calc->vert[i].co);
@@ -390,7 +411,7 @@ static void shrinkwrap_calc_nearest_vertex_cb_ex(void 
*__restrict userdata,
     copy_v3_v3(tmp_co, nearest->co);
     BLI_space_transform_invert(&calc->local2target, tmp_co);
 
-    interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */
+    shrinkwrap_save_result(calc, i, co, tmp_co, weight);
   }
 }
 
@@ -517,6 +538,10 @@ static void shrinkwrap_calc_normal_projection_cb_ex(void 
*__restrict userdata,
     return;
   }
 
+  if (calc->weights) {
+    calc->weights[i] = weight;
+  }
+
   if (calc->vert != nullptr && calc->smd->projAxis == 
MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) {
     /* calc->vert contains verts from evaluated mesh. */
     /* These coordinates are deformed by vertexCos only for normal projection
@@ -607,7 +632,7 @@ static void shrinkwrap_calc_normal_projection_cb_ex(void 
*__restrict userdata,
                                            hit->co);
     }
 
-    interp_v3_v3v3(co, co, hit->co, weight);
+    shrinkwrap_save_result(calc, i, co, hit->co, weight);
   }
 }
 
@@ -1115,6 +1140,10 @@ static void 
shrinkwrap_calc_nearest_surface_point_cb_ex(void *__restrict userdat
     return;
   }
 
+  if (calc->weights) {
+    calc->weights[i] = weight;
+  }
+
   /* Convert the vertex to tree coordinates */
   if (calc->vert) {
     copy_v3_v3(tmp_co, calc->vert[i].co);
@@ -1159,7 +1188,8 @@ static void 
shrinkwrap_calc_nearest_surface_point_cb_ex(void *__restrict userdat
 
     /* Convert the coordinates back to mesh coordinates */
     BLI_space_transform_invert(&calc->local2target, tmp_co);
-    interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */
+
+    shrinkwrap_save_result(calc, i, co, tmp_co, weight);
   }
 }
 
@@ -1359,6 +1389,137 @@ static void 
shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc)
       0, calc->numVerts, &data, shrinkwrap_calc_nearest_surface_point_cb_ex, 
&settings);
 }
 
+static void shrinkwrap_smooth(
+    ShrinkwrapCalcData *calc, Object *ob, Mesh *mesh, float (*vertexCos)[3], 
int numVerts)
+{
+  if (mesh == NULL) {
+    return;
+  }
+
+  /* Number of neighboring vertices for the given vertex. */
+  uint *num_neighbor_verts = static_cast<uint *>(
+      MEM_calloc_arrayN((size_t)numVerts, sizeof(*num_neighbor_verts), 
__func__));
+
+  /* Delta magnitudes after standard shrinkwrap before smoothing (used for 
clamping). */
+  float *original_mags = static_cast<float *>(
+      MEM_calloc_arrayN((size_t)numVerts, sizeof(*original_mags), __func__));
+
+  /* Accumulation buffer for smoothing. */
+  float(*accumulated_deltas)[3] = static_cast<float(*)[3]>(
+      MEM_malloc_arrayN((size_t)numVerts, sizeof(*accumulated_deltas), 
__func__));
+
+  /* Precompute data that doesn't change during iteration. */
+  const MEdge *const medges = BKE_mesh_edges(mesh);
+  const int num_edges = mesh->totedge;
+
+  for (int i = 0; i < num_edges; i++) {
+    num_neighbor_verts[medges[i].v1]++;
+    num_neighbor_verts[medges[i].v2]++;
+  }
+
+  for (int i = 0; i < numVerts; i++) {
+    original_mags[i] = len_v3(calc->deltas[i]);
+  }
+
+  /* Iterative smoothing. */
+  for (int j = 0; j < calc->smd->smoothRepeat; j++) {
+    /* Clear the accumulation array. */
+    memset(accumulated_deltas, 0, sizeof(*accumulated_deltas) * 
(size_t)numVerts);
+
+    /* Accumulate data from edges into vertices. */
+    for (int i = 0; i < num_edges; i++) {
+      const uint idx1 = medges[i].v1;
+      const uint idx2 = medges[i].v2;
+
+      const float weight1 = calc->weights[idx1];
+      const float weight2 = calc->weights[idx2];
+
+      /* Zero weight vertices always have zero offsets. */
+      if (weight1 <= 0 || weight2 <= 0) {
+        break;
+      }
+
+      /* Proportionally reduce offsets when transitioning from higher to lower 
weight. */
+      const float fac1 = min_ff(1.0f, weight1 / weight2);
+
+      madd_v3_v3fl(accumulated_deltas[idx1], calc->deltas[idx2], fac1);
+
+      const float fac2 = min_ff(1.0f, weight2 / weight1);
+
+      madd_v3_v3fl(accumulated_deltas[idx2], calc->deltas[idx1], fac2);
+    }
+
+    /* Update vertex offsets. */
+    for (int i = 0; i < numVerts; i++) {
+      if (num_neighbor_verts[i] == 0) {
+        continue;
+      }
+
+      /* Final accumulated vector and its length. */
+      float accum[3];
+
+      mul_v3_v3fl(accum, accumulated_deltas[i], 1.0f / 
(float)num_neighbor_verts[i]);
+
+      const float mag_accum = len_v3(accum);
+
+      /* Zero accumulated magnitude will never cause an update. */
+      if (mag_accum == 0.0f) {
+        continue;
+      }
+
+      /* Apply interpolation with clamping to shrink result. */
+      const float fac_strength = 0.5f;
+
+      float *delta = calc->deltas[i];
+
+      if (original_mags[i] > 0) {
+        /* Fade out influence on direction if smoothing tries to reduce the 
magnitude.
+         * This can't be a hard cutoff to avoid vertices jumping sideways in 
animation.
+         * When magnitude increase is above the threshold, transitions to 
regular smooth. */
+        const float dir_lock_threshold = 1.5f;
+        const float fac_direction = smoothstep(
+            1.0f, dir_lock_threshold, mag_accum / original_mags[i]);
+
+        if (fac_direction == 0.0f) {
+          /* Decreasing the delta magnitude is completely blocked. */
+          continue;
+        }
+
+        if (fac_direction == 1.0f) {
+          /* Regular smoothing because the smoothed delta increase is big 
enough. */
+          interp_v3_v3v3(delta, delta, accum, fac_strength);
+        }
+        else {
+          /* Regular smoothing result. */
+          interp_v3_v3v3(accum, delta, accum, fac_strength);
+
+          /* Only interpolate the delta magnitude while keeping direction. */
+          const float mag_delta = len_v3(delta);
+          const float mag_interp = interpf(mag_accum, mag_delta, fac_strength);
+
+          mul_v3_fl(delta, max_ff(mag_interp, original_mags[i]) / mag_delta);
+
+          /* Apply the fade out by mixing between the two. */
+          interp_v3_v3v3(delta, delta, accum, fac_direction);
+        }
+      }
+      else {
+        /* Regular smoothing because the original delta is zero. */
+        interp_v3_v3v3(delta, delta, accum, fac_strength);
+      }
+    }
+  }
+
+  /* Apply vertex offsets to the final coordinates. */
+  for (int i = 0; i < numVerts; i++) {
+    add_v3_v3(vertexCos[i], calc->deltas[i]);
+  }
+
+  MEM_freeN(accumulated_deltas);
+  MEM_freeN(num_neighbor_verts);
+  MEM_freeN(original_mags);
+}
+
 void shrinkwrapModifier_deform(ShrinkwrapModifierData *smd,
                                const ModifierEvalContext *ctx,
                                Scene *scene,
@@ -1390,6 +1551,13 @@ void shrinkwrapModifier_deform(ShrinkwrapModifierData 
*smd,
   calc.vgroup = defgrp_index;
   calc.invert_vgroup = (smd->shrinkOpts & MOD_SHRINKWRAP_INVERT_VGROUP) != 0;
 
+  if (smd->smoothRepeat > 0) {
+    calc.deltas = static_cast<float(*)[3]>(
+        MEM_calloc_arrayN((size_t)numVerts, sizeof(*calc.deltas), __func__));
+    calc.weights = static_cast<float *>(
+        MEM_calloc_arrayN((size_t)numVerts, sizeof(*calc.weights), __func__));
+  }
+
   if (smd->target != nullptr) {
     Object *ob_target = DEG_get_evaluated_object(ctx->depsgraph, smd->target);
     calc.target = 
BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target);
@@ -1466,6 +1634,13 @@ void shrinkwrapModifier_deform(ShrinkwrapModifierData 
*smd,
     BKE_shrinkwrap_free_tree(&tree);
   }
 
+  if (smd->smoothRepeat > 0) {
+    shrinkwrap_smooth(&calc, ob, mesh, vertexCos, numVerts);
+
+    MEM_freeN(calc.deltas);
+    MEM_freeN(calc.weights);
+  }
+
   /* free memory */
   if (ss_mesh) {
     ss_mesh->release(ss_mesh);
diff --git a/source/blender/makesdna/DNA_modifier_types.h 
b/source/blender/makesdna/DNA_modifier_types.h
index 97e42efd5ac..ee6554557d3 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -1152,7 +1152,10 @@ typedef struct ShrinkwrapModifierData {
    */
   char subsurfLevels;
 
-  char _pad[2];
+  /** Number of smoothing iterations to apply. */
+  char smoothRepeat;
+
+  char _pad[1];
 } ShrinkwrapModifierData;
 
 /** #ShrinkwrapModifierData.shrinkType */
diff --git a/source/blender/makesrna/intern/rna_modifier.c 
b/source/blender/makesrna/intern/rna_modifier.c
index 24db2136af9..6eb6053bd79 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -4392,6 +4392,14 @@ static void rna_def_modifier_shrinkwrap(BlenderRNA *brna)
   RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence");
   RNA_def_property_update(prop, 0, "rna_Modifier_update");
 
+  prop = RNA_def_property(srna, "smooth_steps", PROP_INT

@@ Diff output truncated at 10240 characters. @@

_______________________________________________
Bf-blender-cvs mailing list
Bf-blender-cvs@blender.org
List details, subscription details or unsubscribe:
https://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to