[Bf-blender-cvs] [2a35e381fd2] temp-angavrilov: Shrinkwrap: support smoothing the mesh after wrapping.
Alexander Gavrilov
noreply at git.blender.org
Sat Oct 1 15:18:40 CEST 2022
Commit: 2a35e381fd21276296c26292742c44917ae2eb9f
Author: Alexander Gavrilov
Date: Thu Sep 1 19:18:51 2022 +0300
Branches: temp-angavrilov
https://developer.blender.org/rB2a35e381fd21276296c26292742c44917ae2eb9f
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 f94836551bb..32ed04d5058 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 7625f04fefa..710d96a183b 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