diff options
-rw-r--r-- | source/blender/makesdna/DNA_modifier_types.h | 8 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_correctivesmooth.c | 200 |
2 files changed, 112 insertions, 96 deletions
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 787f52f9891..7625f04fefa 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -1856,9 +1856,13 @@ enum { }; typedef struct CorrectiveSmoothDeltaCache { - /* delta's between the original positions and the smoothed positions */ + /** + * Delta's between the original positions and the smoothed positions, + * calculated loop-tangent and which is accumulated into the vertex it uses. + * (run-time only). + */ float (*deltas)[3]; - unsigned int totverts; + unsigned int deltas_num; /* Value of settings when creating the cache. * These are used to check if the cache should be recomputed. */ 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 = { |