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. @@ _______________________________________________ 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