diff options
author | Campbell Barton <campbell@blender.org> | 2022-04-27 04:24:25 +0300 |
---|---|---|
committer | Campbell Barton <campbell@blender.org> | 2022-09-15 09:17:17 +0300 |
commit | 0e172e9732a1cab2aba324ce10ce304df7b69338 (patch) | |
tree | 19e23b7a4798553a18d8dcab1f13596ea6100915 /source/blender/modifiers/intern | |
parent | 2c53970bbfa44f58d436e9ca286875fa3919e364 (diff) |
Fix T95649: Corrective Smooth can cause vertices to jump
Calculate a tangent per loop, apply the transformation in multiple
spaces and accumulate the result weighting by the loop-angle.
Note that previously only the tangents Z axis (normal)
was properly accumulated, the X and Y axes only contained the values
from a single loop (the last one written to), meaning only two edges
contributed to the result. Now the transformation from all loops are
taken into account.
In practice the difference between both methods is minimal,
only for more extreme bending it can be noticed which is often when the
previous method didn't work as well.
Diffstat (limited to 'source/blender/modifiers/intern')
-rw-r--r-- | source/blender/modifiers/intern/MOD_correctivesmooth.c | 200 |
1 files changed, 106 insertions, 94 deletions
diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c index c2320f2237f..4fdbe44281b 100644 --- a/source/blender/modifiers/intern/MOD_correctivesmooth.c +++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c @@ -49,10 +49,8 @@ #include "PIL_time.h" #ifdef DEBUG_TIME # include "PIL_time_utildefines.h" -#endif -/* minor optimization, calculate this inline */ -#define USE_TANGENT_CALC_INLINE +#endif static void initData(ModifierData *md) { @@ -79,7 +77,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla } tcsmd->delta_cache.deltas = NULL; - tcsmd->delta_cache.totverts = 0; + tcsmd->delta_cache.deltas_num = 0; } static void freeBind(CorrectiveSmoothModifierData *csmd) @@ -391,69 +389,61 @@ static void smooth_verts(CorrectiveSmoothModifierData *csmd, } /** - * finalize after accumulation. + * Calculate an orthogonal 3x3 matrix from 2 edge vectors. + * \return false if this loop should be ignored (have zero influence). */ -static void calc_tangent_ortho(float ts[3][3]) +static bool calc_tangent_loop(const float v_dir_prev[3], + const float v_dir_next[3], + float r_tspace[3][3]) { - float v_tan_a[3], v_tan_b[3]; - float t_vec_a[3], t_vec_b[3]; - - normalize_v3(ts[2]); - - copy_v3_v3(v_tan_a, ts[0]); - copy_v3_v3(v_tan_b, ts[1]); - - cross_v3_v3v3(ts[1], ts[2], v_tan_a); - mul_v3_fl(ts[1], dot_v3v3(ts[1], v_tan_b) < 0.0f ? -1.0f : 1.0f); - - /* Orthogonalize tangent. */ - mul_v3_v3fl(t_vec_a, ts[2], dot_v3v3(ts[2], v_tan_a)); - sub_v3_v3v3(ts[0], v_tan_a, t_vec_a); - - /* Orthogonalize bi-tangent. */ - mul_v3_v3fl(t_vec_a, ts[2], dot_v3v3(ts[2], ts[1])); - mul_v3_v3fl(t_vec_b, ts[0], dot_v3v3(ts[0], ts[1]) / dot_v3v3(v_tan_a, v_tan_a)); - sub_v3_v3(ts[1], t_vec_a); - sub_v3_v3(ts[1], t_vec_b); - - normalize_v3(ts[0]); - normalize_v3(ts[1]); + if (UNLIKELY(compare_v3v3(v_dir_prev, v_dir_next, FLT_EPSILON * 10.0f))) { + /* As there are no weights, the value doesn't matter just initialize it. */ + unit_m3(r_tspace); + return false; + } + + copy_v3_v3(r_tspace[0], v_dir_prev); + copy_v3_v3(r_tspace[1], v_dir_next); + + cross_v3_v3v3(r_tspace[2], v_dir_prev, v_dir_next); + normalize_v3(r_tspace[2]); + + /* Make orthogonal using `r_tspace[2]` as a basis. + * + * NOTE: while it seems more logical to use `v_dir_prev` & `v_dir_next` as separate X/Y axis + * (instead of combining them as is done here). It's not necessary as the directions of the + * axis aren't important as long as the difference between tangent matrices is equivalent. + * Some computations can be skipped by combining the the two directions, + * using the cross product for the 3rd axes. */ + add_v3_v3(r_tspace[0], r_tspace[1]); + normalize_v3(r_tspace[0]); + cross_v3_v3v3(r_tspace[1], r_tspace[2], r_tspace[0]); + + return true; } /** - * accumulate edge-vectors from all polys. + * \param r_tangent_spaces: Loop aligned array of tangents. + * \param r_tangent_weights: Loop aligned array of weights (may be NULL). + * \param r_tangent_weights_per_vertex: Vertex aligned array, accumulating weights for each loop + * (may be NULL). */ -static void calc_tangent_loop_accum(const float v_dir_prev[3], - const float v_dir_next[3], - float r_tspace[3][3]) -{ - add_v3_v3v3(r_tspace[1], v_dir_prev, v_dir_next); - - if (compare_v3v3(v_dir_prev, v_dir_next, FLT_EPSILON * 10.0f) == false) { - const float weight = fabsf(acosf(dot_v3v3(v_dir_next, v_dir_prev))); - float nor[3]; - - cross_v3_v3v3(nor, v_dir_prev, v_dir_next); - normalize_v3(nor); - - cross_v3_v3v3(r_tspace[0], r_tspace[1], nor); - - mul_v3_fl(nor, weight); - /* accumulate weighted normals */ - add_v3_v3(r_tspace[2], nor); - } -} - -static void calc_tangent_spaces(Mesh *mesh, float (*vertexCos)[3], float (*r_tangent_spaces)[3][3]) +static void calc_tangent_spaces(const Mesh *mesh, + const float (*vertexCos)[3], + float (*r_tangent_spaces)[3][3], + float *r_tangent_weights, + float *r_tangent_weights_per_vertex) { const uint mpoly_num = (uint)mesh->totpoly; -#ifndef USE_TANGENT_CALC_INLINE - const uint mvert_num = (uint)dm->getNumVerts(dm); -#endif + const uint mvert_num = (uint)mesh->totvert; const MPoly *mpoly = BKE_mesh_polys(mesh); const MLoop *mloop = BKE_mesh_loops(mesh); uint i; + if (r_tangent_weights_per_vertex != NULL) { + copy_vn_fl(r_tangent_weights_per_vertex, (int)mvert_num, 0.0f); + } + for (i = 0; i < mpoly_num; i++) { const MPoly *mp = &mpoly[i]; const MLoop *l_next = &mloop[mp->loopstart]; @@ -469,7 +459,8 @@ static void calc_tangent_spaces(Mesh *mesh, float (*vertexCos)[3], float (*r_tan normalize_v3(v_dir_prev); for (; l_next != l_term; l_prev = l_curr, l_curr = l_next, l_next++) { - float(*ts)[3] = r_tangent_spaces[l_curr->v]; + uint l_index = (uint)(l_curr - mloop); + float(*ts)[3] = r_tangent_spaces[l_index]; /* re-use the previous value */ #if 0 @@ -479,19 +470,22 @@ static void calc_tangent_spaces(Mesh *mesh, float (*vertexCos)[3], float (*r_tan sub_v3_v3v3(v_dir_next, vertexCos[l_curr->v], vertexCos[l_next->v]); normalize_v3(v_dir_next); - calc_tangent_loop_accum(v_dir_prev, v_dir_next, ts); + if (calc_tangent_loop(v_dir_prev, v_dir_next, ts)) { + if (r_tangent_weights != NULL) { + const float weight = fabsf(acosf(dot_v3v3(v_dir_next, v_dir_prev))); + r_tangent_weights[l_index] = weight; + r_tangent_weights_per_vertex[l_curr->v] += weight; + } + } + else { + if (r_tangent_weights != NULL) { + r_tangent_weights[l_index] = 0; + } + } copy_v3_v3(v_dir_prev, v_dir_next); } } - - /* do inline */ -#ifndef USE_TANGENT_CALC_INLINE - for (i = 0; i < mvert_num; i++) { - float(*ts)[3] = r_tangent_spaces[i]; - calc_tangent_ortho(ts); - } -#endif } static void store_cache_settings(CorrectiveSmoothModifierData *csmd) @@ -522,38 +516,42 @@ static void calc_deltas(CorrectiveSmoothModifierData *csmd, const float (*rest_coords)[3], uint verts_num) { + const MLoop *mloop = BKE_mesh_loops(mesh); + const uint loops_num = (uint)mesh->totloop; + float(*smooth_vertex_coords)[3] = MEM_dupallocN(rest_coords); float(*tangent_spaces)[3][3]; - uint i; - tangent_spaces = MEM_calloc_arrayN(verts_num, sizeof(float[3][3]), __func__); + uint l_index; + + tangent_spaces = MEM_malloc_arrayN(loops_num, sizeof(float[3][3]), __func__); - if (csmd->delta_cache.totverts != verts_num) { + if (csmd->delta_cache.deltas_num != loops_num) { MEM_SAFE_FREE(csmd->delta_cache.deltas); } /* allocate deltas if they have not yet been allocated, otherwise we will just write over them */ if (!csmd->delta_cache.deltas) { - csmd->delta_cache.totverts = verts_num; - csmd->delta_cache.deltas = MEM_malloc_arrayN(verts_num, sizeof(float[3]), __func__); + csmd->delta_cache.deltas_num = loops_num; + csmd->delta_cache.deltas = MEM_malloc_arrayN(loops_num, sizeof(float[3]), __func__); } smooth_verts(csmd, mesh, dvert, defgrp_index, smooth_vertex_coords, verts_num); - calc_tangent_spaces(mesh, smooth_vertex_coords, tangent_spaces); + calc_tangent_spaces(mesh, smooth_vertex_coords, tangent_spaces, NULL, NULL); - for (i = 0; i < verts_num; i++) { - float imat[3][3], delta[3]; + copy_vn_fl(&csmd->delta_cache.deltas[0][0], (int)loops_num * 3, 0.0f); -#ifdef USE_TANGENT_CALC_INLINE - calc_tangent_ortho(tangent_spaces[i]); -#endif + for (l_index = 0; l_index < loops_num; l_index++) { + const int v_index = (int)mloop[l_index].v; + float delta[3]; + sub_v3_v3v3(delta, rest_coords[v_index], smooth_vertex_coords[v_index]); - sub_v3_v3v3(delta, rest_coords[i], smooth_vertex_coords[i]); - if (UNLIKELY(!invert_m3_m3(imat, tangent_spaces[i]))) { - transpose_m3_m3(imat, tangent_spaces[i]); + float imat[3][3]; + if (UNLIKELY(!invert_m3_m3(imat, tangent_spaces[l_index]))) { + transpose_m3_m3(imat, tangent_spaces[l_index]); } - mul_v3_m3v3(csmd->delta_cache.deltas[i], imat, delta); + mul_v3_m3v3(csmd->delta_cache.deltas[l_index], imat, delta); } MEM_freeN(tangent_spaces); @@ -576,6 +574,9 @@ static void correctivesmooth_modifier_do(ModifierData *md, ((csmd->rest_source == MOD_CORRECTIVESMOOTH_RESTSOURCE_ORCO) && (((ID *)ob->data)->recalc & ID_RECALC_ALL)); + const MLoop *mloop = BKE_mesh_loops(mesh); + const uint loops_num = (uint)mesh->totloop; + bool use_only_smooth = (csmd->flag & MOD_CORRECTIVESMOOTH_ONLY_SMOOTH) != 0; const MDeformVert *dvert = NULL; int defgrp_index; @@ -638,7 +639,7 @@ static void correctivesmooth_modifier_do(ModifierData *md, } /* check to see if our deltas are still valid */ - if (!csmd->delta_cache.deltas || (csmd->delta_cache.totverts != verts_num) || + if (!csmd->delta_cache.deltas || (csmd->delta_cache.deltas_num != loops_num) || force_delta_cache_update) { const float(*rest_coords)[3]; bool is_rest_coords_alloc = false; @@ -686,27 +687,38 @@ static void correctivesmooth_modifier_do(ModifierData *md, smooth_verts(csmd, mesh, dvert, defgrp_index, vertexCos, verts_num); { - uint i; + uint l_index; float(*tangent_spaces)[3][3]; + float *tangent_weights; + + float *tangent_weights_per_vertex; const float scale = csmd->scale; - /* calloc, since values are accumulated */ - tangent_spaces = MEM_calloc_arrayN(verts_num, sizeof(float[3][3]), __func__); - calc_tangent_spaces(mesh, vertexCos, tangent_spaces); + tangent_spaces = MEM_malloc_arrayN(loops_num, sizeof(float[3][3]), __func__); + tangent_weights = MEM_malloc_arrayN(loops_num, sizeof(float), __func__); + tangent_weights_per_vertex = MEM_malloc_arrayN(loops_num, sizeof(float), __func__); - for (i = 0; i < verts_num; i++) { - float delta[3]; + calc_tangent_spaces( + mesh, vertexCos, tangent_spaces, tangent_weights, tangent_weights_per_vertex); -#ifdef USE_TANGENT_CALC_INLINE - calc_tangent_ortho(tangent_spaces[i]); -#endif + for (l_index = 0; l_index < loops_num; l_index++) { + const uint v_index = mloop[l_index].v; + const float weight = tangent_weights[l_index] / tangent_weights_per_vertex[v_index]; + if (UNLIKELY(!(weight > 0.0f))) { + /* Catches zero & divide by zero. */ + continue; + } - mul_v3_m3v3(delta, tangent_spaces[i], csmd->delta_cache.deltas[i]); - madd_v3_v3fl(vertexCos[i], delta, scale); + float delta[3]; + mul_v3_m3v3(delta, tangent_spaces[l_index], csmd->delta_cache.deltas[l_index]); + mul_v3_fl(delta, weight); + madd_v3_v3fl(vertexCos[v_index], delta, scale); } MEM_freeN(tangent_spaces); + MEM_freeN(tangent_weights); + MEM_freeN(tangent_weights_per_vertex); } #ifdef DEBUG_TIME @@ -718,7 +730,7 @@ static void correctivesmooth_modifier_do(ModifierData *md, /* when the modifier fails to execute */ error: MEM_SAFE_FREE(csmd->delta_cache.deltas); - csmd->delta_cache.totverts = 0; + csmd->delta_cache.deltas_num = 0; } static void deformVerts(ModifierData *md, @@ -827,7 +839,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) /* runtime only */ csmd->delta_cache.deltas = NULL; - csmd->delta_cache.totverts = 0; + csmd->delta_cache.deltas_num = 0; } ModifierTypeInfo modifierType_CorrectiveSmooth = { |