[Bf-blender-cvs] [09856f60810] temp-angavrilov: Shrinkwrap: support smoothing the mesh after wrapping.

Alexander Gavrilov noreply at git.blender.org
Thu Sep 8 18:18:08 CEST 2022


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

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.c
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.c b/source/blender/blenkernel/intern/shrinkwrap.c
index 4b4e3bdcfa6..3b656cf60ba 100644
--- a/source/blender/blenkernel/intern/shrinkwrap.c
+++ b/source/blender/blenkernel/intern/shrinkwrap.c
@@ -62,6 +62,8 @@ typedef struct ShrinkwrapCalcData {
   struct MVert *vert; /* Array of verts being projected. */
   const float (*vert_normals)[3];
   float (*vertexCos)[3]; /* vertexs being shrinkwraped */
+  float (*deltas)[3];    /* cached vertex deltas */
+  float *weights;        /* cached vertex weights */
   int numVerts;
 
   const struct MDeformVert *dvert; /* Pointer to mdeform array */
@@ -328,6 +330,19 @@ void BKE_shrinkwrap_compute_boundary_data(struct 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
  *
@@ -356,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);
@@ -392,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);
   }
 }
 
@@ -520,6 +539,10 @@ static void shrinkwrap_calc_normal_projection_cb_ex(void *__restrict userdata,
     return;
   }
 
+  if (calc->weights) {
+    calc->weights[i] = weight;
+  }
+
   if (calc->vert != NULL && 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
@@ -610,7 +633,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);
   }
 }
 
@@ -1120,6 +1143,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);
@@ -1164,7 +1191,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);
   }
 }
 
@@ -1365,6 +1393,136 @@ 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 = MEM_calloc_arrayN(
+      (size_t)numVerts, sizeof(*num_neighbor_verts), __func__);
+
+  /* Delta magnitudes after standard shrinkwrap before smoothing (used for clamping). */
+  float *original_mags = MEM_calloc_arrayN((size_t)numVerts, sizeof(*original_mags), __func__);
+
+  /* Accumulation buffer for smoothing. */
+  float(*accumulated_deltas)[3] = MEM_malloc_arrayN(
+      (size_t)numVerts, sizeof(*accumulated_deltas), __func__);
+
+  /* Precompute data that doesn't change during iteration. */
+  MEdge *const medges = mesh->medge;
+  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,
                                struct Scene *scene,
@@ -1396,6 +1554,11 @@ 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 = MEM_calloc_arrayN((size_t)numVerts, sizeof(*calc.deltas), __func__);
+    calc.weights = MEM_calloc_arrayN((size_t)numVerts, sizeof(*calc.weights), __func__);
+  }
+
   if (smd->target != NULL) {
     Object *ob_target = DEG_get_evaluated_object(ctx->depsgraph, smd->target);
     calc.target = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target);
@@ -1468,6 +1631,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 787f52f9891..9edee6614fa 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -1144,7 +1144,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 c5da15003e1..8f70f69ca0c 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -4368,6 +4368,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, PROP_NONE);
+  RNA_def_property_int_sdna(prop, NULL, "smoothRepeat");
+  RNA_def_pro

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list