Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/modifiers/intern/MOD_correctivesmooth.c')
-rw-r--r--source/blender/modifiers/intern/MOD_correctivesmooth.c769
1 files changed, 769 insertions, 0 deletions
diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c
new file mode 100644
index 00000000000..d5b4daca3b7
--- /dev/null
+++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c
@@ -0,0 +1,769 @@
+/*
+* ***** BEGIN GPL LICENSE BLOCK *****
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software Foundation,
+* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+* The Original Code is Copyright (C) 2015 by the Blender Foundation.
+* All rights reserved.
+*
+* Contributor(s): Jack Simpson,
+* Campbell Barton
+*
+* ***** END GPL LICENSE BLOCK *****
+*
+*/
+
+/** \file blender/modifiers/intern/MOD_correctivesmooth.c
+ * \ingroup modifiers
+ *
+ * Method of smoothing deformation, also known as 'delta-mush'.
+ */
+
+#include "DNA_scene_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_mesh_types.h"
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BKE_cdderivedmesh.h"
+#include "BKE_deform.h"
+#include "BKE_mesh.h"
+#include "BKE_editmesh.h"
+
+#include "MOD_modifiertypes.h"
+#include "MOD_util.h"
+
+#include "BLI_strict_flags.h"
+
+
+// #define DEBUG_TIME
+
+#include "PIL_time.h"
+#ifdef DEBUG_TIME
+# include "PIL_time_utildefines.h"
+#endif
+
+/* minor optimization, calculate this inline */
+#define USE_TANGENT_CALC_INLINE
+
+static void initData(ModifierData *md)
+{
+ CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)md;
+
+ csmd->bind_coords = NULL;
+ csmd->bind_coords_num = 0;
+
+ csmd->lambda = 0.5f;
+ csmd->repeat = 5;
+ csmd->flag = 0;
+ csmd->smooth_type = MOD_CORRECTIVESMOOTH_SMOOTH_SIMPLE;
+
+ csmd->defgrp_name[0] = '\0';
+
+ csmd->delta_cache = NULL;
+}
+
+
+static void copyData(ModifierData *md, ModifierData *target)
+{
+ CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)md;
+ CorrectiveSmoothModifierData *tcsmd = (CorrectiveSmoothModifierData *)target;
+
+ modifier_copyData_generic(md, target);
+
+ if (csmd->bind_coords) {
+ tcsmd->bind_coords = MEM_dupallocN(csmd->bind_coords);
+ }
+
+ tcsmd->delta_cache = NULL;
+ tcsmd->delta_cache_num = 0;
+}
+
+
+static void freeBind(CorrectiveSmoothModifierData *csmd)
+{
+ MEM_SAFE_FREE(csmd->bind_coords);
+ MEM_SAFE_FREE(csmd->delta_cache);
+
+ csmd->bind_coords_num = 0;
+}
+
+
+static void freeData(ModifierData *md)
+{
+ CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)md;
+ freeBind(csmd);
+}
+
+
+static CustomDataMask requiredDataMask(Object *UNUSED(ob), ModifierData *md)
+{
+ CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)md;
+ CustomDataMask dataMask = 0;
+ /* ask for vertex groups if we need them */
+ if (csmd->defgrp_name[0]) {
+ dataMask |= CD_MASK_MDEFORMVERT;
+ }
+ return dataMask;
+}
+
+
+/* check individual weights for changes and cache values */
+static void dm_get_weights(
+ MDeformVert *dvert, const int defgrp_index,
+ const unsigned int numVerts, const bool use_invert_vgroup,
+ float *smooth_weights)
+{
+ unsigned int i;
+
+ for (i = 0; i < numVerts; i++, dvert++) {
+ const float w = defvert_find_weight(dvert, defgrp_index);
+
+ if (use_invert_vgroup == false) {
+ smooth_weights[i] = w;
+ }
+ else {
+ smooth_weights[i] = 1.0f - w;
+ }
+ }
+}
+
+
+static void dm_get_boundaries(DerivedMesh *dm, float *smooth_weights)
+{
+ const MPoly *mpoly = dm->getPolyArray(dm);
+ const MLoop *mloop = dm->getLoopArray(dm);
+ const MEdge *medge = dm->getEdgeArray(dm);
+ unsigned int mpoly_num, medge_num, i;
+ unsigned short *boundaries;
+
+ mpoly_num = (unsigned int)dm->getNumPolys(dm);
+ medge_num = (unsigned int)dm->getNumEdges(dm);
+
+ boundaries = MEM_callocN(medge_num * sizeof(*boundaries), __func__);
+
+ /* count the number of adjacent faces */
+ for (i = 0; i < mpoly_num; i++) {
+ const MPoly *p = &mpoly[i];
+ const int totloop = p->totloop;
+ int j;
+ for (j = 0; j < totloop; j++) {
+ boundaries[mloop[p->loopstart + j].e]++;
+ }
+ }
+
+ for (i = 0; i < medge_num; i++) {
+ if (boundaries[i] == 1) {
+ smooth_weights[medge[i].v1] = 0.0f;
+ smooth_weights[medge[i].v2] = 0.0f;
+ }
+ }
+
+ MEM_freeN(boundaries);
+}
+
+
+/* -------------------------------------------------------------------- */
+/* Simple Weighted Smoothing
+ *
+ * (average of surrounding verts)
+ */
+static void smooth_iter__simple(
+ CorrectiveSmoothModifierData *csmd, DerivedMesh *dm,
+ float (*vertexCos)[3], unsigned int numVerts,
+ const float *smooth_weights,
+ unsigned int iterations)
+{
+ const float lambda = csmd->lambda;
+ unsigned int i;
+
+ const unsigned int numEdges = (unsigned int)dm->getNumEdges(dm);
+ const MEdge *edges = dm->getEdgeArray(dm);
+ float *vertex_edge_count_div;
+
+ struct SmoothingData_Simple {
+ float delta[3];
+ } *smooth_data = MEM_callocN((size_t)numVerts * sizeof(*smooth_data), __func__);
+
+ vertex_edge_count_div = MEM_callocN((size_t)numVerts * sizeof(float), __func__);
+
+ /* calculate as floats to avoid int->float conversion in #smooth_iter */
+ for (i = 0; i < numEdges; i++) {
+ vertex_edge_count_div[edges[i].v1] += 1.0f;
+ vertex_edge_count_div[edges[i].v2] += 1.0f;
+ }
+
+ /* a little confusing, but we can include 'lambda' and smoothing weight
+ * here to avoid multiplying for every iteration */
+ if (smooth_weights == NULL) {
+ for (i = 0; i < numVerts; i++) {
+ vertex_edge_count_div[i] =
+ lambda * (vertex_edge_count_div[i] ? (1.0f / vertex_edge_count_div[i]) : 1.0f);
+ }
+ }
+ else {
+ for (i = 0; i < numVerts; i++) {
+ vertex_edge_count_div[i] =
+ smooth_weights[i] * lambda * (vertex_edge_count_div[i] ? (1.0f / vertex_edge_count_div[i]) : 1.0f);
+ }
+ }
+
+ /* -------------------------------------------------------------------- */
+ /* Main Smoothing Loop */
+
+ while (iterations--) {
+ for (i = 0; i < numEdges; i++) {
+ struct SmoothingData_Simple *sd_v1;
+ struct SmoothingData_Simple *sd_v2;
+ float edge_dir[3];
+
+ sub_v3_v3v3(edge_dir, vertexCos[edges[i].v2], vertexCos[edges[i].v1]);
+
+ sd_v1 = &smooth_data[edges[i].v1];
+ sd_v2 = &smooth_data[edges[i].v2];
+
+ add_v3_v3(sd_v1->delta, edge_dir);
+ sub_v3_v3(sd_v2->delta, edge_dir);
+ }
+
+
+ for (i = 0; i < numVerts; i++) {
+ struct SmoothingData_Simple *sd = &smooth_data[i];
+ madd_v3_v3fl(vertexCos[i], sd->delta, vertex_edge_count_div[i]);
+ /* zero for the next iteration (saves memset on entire array) */
+ memset(sd, 0, sizeof(*sd));
+ }
+ }
+
+ MEM_freeN(vertex_edge_count_div);
+ MEM_freeN(smooth_data);
+}
+
+
+/* -------------------------------------------------------------------- */
+/* Edge-Length Weighted Smoothing
+ */
+static void smooth_iter__length_weight(
+ CorrectiveSmoothModifierData *csmd, DerivedMesh *dm,
+ float (*vertexCos)[3], unsigned int numVerts,
+ const float *smooth_weights,
+ unsigned int iterations)
+{
+ const float eps = FLT_EPSILON * 10.0f;
+ const unsigned int numEdges = (unsigned int)dm->getNumEdges(dm);
+ /* note: the way this smoothing method works, its approx half as strong as the simple-smooth,
+ * and 2.0 rarely spikes, double the value for consistent behavior. */
+ const float lambda = csmd->lambda * 2.0f;
+ const MEdge *edges = dm->getEdgeArray(dm);
+ float *vertex_edge_count;
+ unsigned int i;
+
+ struct SmoothingData_Weighted {
+ float delta[3];
+ float edge_length_sum;
+ } *smooth_data = MEM_callocN((size_t)numVerts * sizeof(*smooth_data), __func__);
+
+
+ /* calculate as floats to avoid int->float conversion in #smooth_iter */
+ vertex_edge_count = MEM_callocN((size_t)numVerts * sizeof(float), __func__);
+ for (i = 0; i < numEdges; i++) {
+ vertex_edge_count[edges[i].v1] += 1.0f;
+ vertex_edge_count[edges[i].v2] += 1.0f;
+ }
+
+
+ /* -------------------------------------------------------------------- */
+ /* Main Smoothing Loop */
+
+ while (iterations--) {
+ for (i = 0; i < numEdges; i++) {
+ struct SmoothingData_Weighted *sd_v1;
+ struct SmoothingData_Weighted *sd_v2;
+ float edge_dir[3];
+ float edge_dist;
+
+ sub_v3_v3v3(edge_dir, vertexCos[edges[i].v2], vertexCos[edges[i].v1]);
+ edge_dist = len_v3(edge_dir);
+
+ /* weight by distance */
+ mul_v3_fl(edge_dir, edge_dist);
+
+
+ sd_v1 = &smooth_data[edges[i].v1];
+ sd_v2 = &smooth_data[edges[i].v2];
+
+ add_v3_v3(sd_v1->delta, edge_dir);
+ sub_v3_v3(sd_v2->delta, edge_dir);
+
+ sd_v1->edge_length_sum += edge_dist;
+ sd_v2->edge_length_sum += edge_dist;
+ }
+
+ if (smooth_weights == NULL) {
+ /* fast-path */
+ for (i = 0; i < numVerts; i++) {
+ struct SmoothingData_Weighted *sd = &smooth_data[i];
+ /* divide by sum of all neighbour distances (weighted) and amount of neighbours, (mean average) */
+ const float div = sd->edge_length_sum * vertex_edge_count[i];
+ if (div > eps) {
+#if 0
+ /* first calculate the new location */
+ mul_v3_fl(sd->delta, 1.0f / div);
+ /* then interpolate */
+ madd_v3_v3fl(vertexCos[i], sd->delta, lambda);
+#else
+ /* do this in one step */
+ madd_v3_v3fl(vertexCos[i], sd->delta, lambda / div);
+#endif
+ }
+ /* zero for the next iteration (saves memset on entire array) */
+ memset(sd, 0, sizeof(*sd));
+ }
+ }
+ else {
+ for (i = 0; i < numVerts; i++) {
+ struct SmoothingData_Weighted *sd = &smooth_data[i];
+ const float div = sd->edge_length_sum * vertex_edge_count[i];
+ if (div > eps) {
+ const float lambda_w = lambda * smooth_weights[i];
+ madd_v3_v3fl(vertexCos[i], sd->delta, lambda_w / div);
+ }
+
+ memset(sd, 0, sizeof(*sd));
+ }
+ }
+ }
+
+ MEM_freeN(vertex_edge_count);
+ MEM_freeN(smooth_data);
+}
+
+
+static void smooth_iter(
+ CorrectiveSmoothModifierData *csmd, DerivedMesh *dm,
+ float (*vertexCos)[3], unsigned int numVerts,
+ const float *smooth_weights,
+ unsigned int iterations)
+{
+ switch (csmd->smooth_type) {
+ case MOD_CORRECTIVESMOOTH_SMOOTH_LENGTH_WEIGHT:
+ smooth_iter__length_weight(csmd, dm, vertexCos, numVerts, smooth_weights, iterations);
+ break;
+
+ /* case MOD_CORRECTIVESMOOTH_SMOOTH_SIMPLE: */
+ default:
+ smooth_iter__simple(csmd, dm, vertexCos, numVerts, smooth_weights, iterations);
+ break;
+ }
+}
+
+static void smooth_verts(
+ CorrectiveSmoothModifierData *csmd, DerivedMesh *dm,
+ MDeformVert *dvert, const int defgrp_index,
+ float (*vertexCos)[3], unsigned int numVerts)
+{
+ float *smooth_weights = NULL;
+
+ if (dvert || (csmd->flag & MOD_CORRECTIVESMOOTH_PIN_BOUNDARY)) {
+
+ smooth_weights = MEM_mallocN(numVerts * sizeof(float), __func__);
+
+ if (dvert) {
+ dm_get_weights(
+ dvert, defgrp_index,
+ numVerts, (csmd->flag & MOD_CORRECTIVESMOOTH_INVERT_VGROUP) != 0,
+ smooth_weights);
+ }
+ else {
+ copy_vn_fl(smooth_weights, (int)numVerts, 1.0f);
+ }
+
+ if (csmd->flag & MOD_CORRECTIVESMOOTH_PIN_BOUNDARY) {
+ dm_get_boundaries(dm, smooth_weights);
+ }
+ }
+
+ smooth_iter(csmd, dm, vertexCos, numVerts, smooth_weights, (unsigned int)csmd->repeat);
+
+ if (smooth_weights) {
+ MEM_freeN(smooth_weights);
+ }
+}
+
+/**
+ * finalize after accumulation.
+ */
+static void calc_tangent_ortho(float ts[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);
+
+ /* orthognalise 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);
+
+ /* orthognalise bitangent */
+ 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]);
+}
+
+/**
+ * accumulate edge-vectors from all polys.
+ */
+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(
+ DerivedMesh *dm, float (*vertexCos)[3],
+ float (*r_tangent_spaces)[3][3])
+{
+ const unsigned int mpoly_num = (unsigned int)dm->getNumPolys(dm);
+#ifndef USE_TANGENT_CALC_INLINE
+ const unsigned int mvert_num = (unsigned int)dm->getNumVerts(dm);
+#endif
+ const MPoly *mpoly = dm->getPolyArray(dm);
+ const MLoop *mloop = dm->getLoopArray(dm);
+ unsigned int i;
+
+ for (i = 0; i < mpoly_num; i++) {
+ const MPoly *mp = &mpoly[i];
+ const MLoop *l_next = &mloop[mp->loopstart];
+ const MLoop *l_term = l_next + mp->totloop;
+ const MLoop *l_prev = l_term - 2;
+ const MLoop *l_curr = l_term - 1;
+
+ /* loop directions */
+ float v_dir_prev[3], v_dir_next[3];
+
+ /* needed entering the loop */
+ sub_v3_v3v3(v_dir_prev, vertexCos[l_prev->v], vertexCos[l_curr->v]);
+ 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];
+
+ /* re-use the previous value */
+#if 0
+ sub_v3_v3v3(v_dir_prev, vertexCos[l_prev->v], vertexCos[l_curr->v]);
+ normalize_v3(v_dir_prev);
+#endif
+ 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);
+
+ 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
+}
+
+/**
+ * This calculates #CorrectiveSmoothModifierData.delta_cache
+ * It's not run on every update (during animation for example).
+ */
+static void calc_deltas(
+ CorrectiveSmoothModifierData *csmd, DerivedMesh *dm,
+ MDeformVert *dvert, const int defgrp_index,
+ const float (*rest_coords)[3], unsigned int numVerts)
+{
+ float (*smooth_vertex_coords)[3] = MEM_dupallocN(rest_coords);
+ float (*tangent_spaces)[3][3];
+ unsigned int i;
+
+ tangent_spaces = MEM_callocN((size_t)(numVerts) * sizeof(float[3][3]), __func__);
+
+ if (csmd->delta_cache_num != numVerts) {
+ MEM_SAFE_FREE(csmd->delta_cache);
+ }
+
+ /* allocate deltas if they have not yet been allocated, otheriwse we will just write over them */
+ if (!csmd->delta_cache) {
+ csmd->delta_cache_num = numVerts;
+ csmd->delta_cache = MEM_mallocN((size_t)numVerts * sizeof(float[3]), __func__);
+ }
+
+ smooth_verts(csmd, dm, dvert, defgrp_index, smooth_vertex_coords, numVerts);
+
+ calc_tangent_spaces(dm, smooth_vertex_coords, tangent_spaces);
+
+ for (i = 0; i < numVerts; i++) {
+ float imat[3][3], delta[3];
+
+#ifdef USE_TANGENT_CALC_INLINE
+ calc_tangent_ortho(tangent_spaces[i]);
+#endif
+
+ 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]);
+ }
+ mul_v3_m3v3(csmd->delta_cache[i], imat, delta);
+ }
+
+ MEM_freeN(tangent_spaces);
+ MEM_freeN(smooth_vertex_coords);
+}
+
+
+static void correctivesmooth_modifier_do(
+ ModifierData *md, Object *ob, DerivedMesh *dm,
+ float (*vertexCos)[3], unsigned int numVerts,
+ struct BMEditMesh *em)
+{
+ CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)md;
+
+ const bool force_delta_cache_update =
+ /* XXX, take care! if mesh data its self changes we need to forcefully recalculate deltas */
+ ((csmd->rest_source == MOD_CORRECTIVESMOOTH_RESTSOURCE_ORCO) &&
+ (((ID *)ob->data)->flag & LIB_ID_RECALC));
+
+ bool use_only_smooth = (csmd->flag & MOD_CORRECTIVESMOOTH_ONLY_SMOOTH) != 0;
+ MDeformVert *dvert = NULL;
+ int defgrp_index;
+
+ modifier_get_vgroup(ob, dm, csmd->defgrp_name, &dvert, &defgrp_index);
+
+ /* if rest bind_coords not are defined, set them (only run during bind) */
+ if ((csmd->rest_source == MOD_CORRECTIVESMOOTH_RESTSOURCE_BIND) &&
+ /* signal to recalculate, whoever sets MUST also free bind coords */
+ (csmd->bind_coords_num == (unsigned int)-1))
+ {
+ BLI_assert(csmd->bind_coords == NULL);
+ csmd->bind_coords = MEM_dupallocN(vertexCos);
+ csmd->bind_coords_num = numVerts;
+ BLI_assert(csmd->bind_coords != NULL);
+ }
+
+ if (UNLIKELY(use_only_smooth)) {
+ smooth_verts(csmd, dm, dvert, defgrp_index, vertexCos, numVerts);
+ return;
+ }
+
+ if ((csmd->rest_source == MOD_CORRECTIVESMOOTH_RESTSOURCE_BIND) && (csmd->bind_coords == NULL)) {
+ modifier_setError(md, "Bind data required");
+ goto error;
+ }
+
+ /* If the number of verts has changed, the bind is invalid, so we do nothing */
+ if (csmd->rest_source == MOD_CORRECTIVESMOOTH_RESTSOURCE_BIND) {
+ if (csmd->bind_coords_num != numVerts) {
+ modifier_setError(md, "Bind vertex count mismatch: %u to %u", csmd->bind_coords_num, numVerts);
+ goto error;
+ }
+ }
+ else {
+ /* MOD_CORRECTIVESMOOTH_RESTSOURCE_ORCO */
+ if (ob->type != OB_MESH) {
+ modifier_setError(md, "Object is not a mesh");
+ goto error;
+ }
+ else {
+ unsigned int me_numVerts = (unsigned int)((em) ? em->bm->totvert : ((Mesh *)ob->data)->totvert);
+
+ if (me_numVerts != numVerts) {
+ modifier_setError(md, "Original vertex count mismatch: %u to %u", me_numVerts, numVerts);
+ goto error;
+ }
+ }
+ }
+
+ /* check to see if our deltas are still valid */
+ if (!csmd->delta_cache || (csmd->delta_cache_num != numVerts) || force_delta_cache_update) {
+ const float (*rest_coords)[3];
+ bool is_rest_coords_alloc = false;
+
+ if (csmd->rest_source == MOD_CORRECTIVESMOOTH_RESTSOURCE_BIND) {
+ /* caller needs to do sanity check here */
+ csmd->bind_coords_num = numVerts;
+ rest_coords = (const float (*)[3])csmd->bind_coords;
+ }
+ else {
+ int me_numVerts;
+ rest_coords = (const float (*)[3]) ((em) ?
+ BKE_editmesh_vertexCos_get_orco(em, &me_numVerts) :
+ BKE_mesh_vertexCos_get(ob->data, &me_numVerts));
+
+ BLI_assert((unsigned int)me_numVerts == numVerts);
+ is_rest_coords_alloc = true;
+ }
+
+#ifdef DEBUG_TIME
+ TIMEIT_START(corrective_smooth_deltas);
+#endif
+
+ calc_deltas(csmd, dm, dvert, defgrp_index, rest_coords, numVerts);
+
+#ifdef DEBUG_TIME
+ TIMEIT_END(corrective_smooth_deltas);
+#endif
+ if (is_rest_coords_alloc) {
+ MEM_freeN((void *)rest_coords);
+ }
+ }
+
+ if (csmd->rest_source == MOD_CORRECTIVESMOOTH_RESTSOURCE_BIND) {
+ /* this could be a check, but at this point it _must_ be valid */
+ BLI_assert(csmd->bind_coords_num == numVerts && csmd->delta_cache);
+ }
+
+
+#ifdef DEBUG_TIME
+ TIMEIT_START(corrective_smooth);
+#endif
+
+ /* do the actual delta mush */
+ smooth_verts(csmd, dm, dvert, defgrp_index, vertexCos, numVerts);
+
+ {
+ unsigned int i;
+
+ float (*tangent_spaces)[3][3];
+
+ /* calloc, since values are accumulated */
+ tangent_spaces = MEM_callocN((size_t)numVerts * sizeof(float[3][3]), __func__);
+
+ calc_tangent_spaces(dm, vertexCos, tangent_spaces);
+
+ for (i = 0; i < numVerts; i++) {
+ float delta[3];
+
+#ifdef USE_TANGENT_CALC_INLINE
+ calc_tangent_ortho(tangent_spaces[i]);
+#endif
+
+ mul_v3_m3v3(delta, tangent_spaces[i], csmd->delta_cache[i]);
+ add_v3_v3(vertexCos[i], delta);
+ }
+
+ MEM_freeN(tangent_spaces);
+ }
+
+#ifdef DEBUG_TIME
+ TIMEIT_END(corrective_smooth);
+#endif
+
+ return;
+
+ /* when the modifier fails to execute */
+error:
+ MEM_SAFE_FREE(csmd->delta_cache);
+ csmd->delta_cache_num = 0;
+
+}
+
+
+static void deformVerts(
+ ModifierData *md, Object *ob, DerivedMesh *derivedData,
+ float (*vertexCos)[3], int numVerts, ModifierApplyFlag UNUSED(flag))
+{
+ DerivedMesh *dm = get_dm(ob, NULL, derivedData, NULL, false, false);
+
+ correctivesmooth_modifier_do(md, ob, dm, vertexCos, (unsigned int)numVerts, NULL);
+
+ if (dm != derivedData) {
+ dm->release(dm);
+ }
+}
+
+
+static void deformVertsEM(
+ ModifierData *md, Object *ob, struct BMEditMesh *editData,
+ DerivedMesh *derivedData, float (*vertexCos)[3], int numVerts)
+{
+ DerivedMesh *dm = get_dm(ob, editData, derivedData, NULL, false, false);
+
+ correctivesmooth_modifier_do(md, ob, dm, vertexCos, (unsigned int)numVerts, editData);
+
+ if (dm != derivedData) {
+ dm->release(dm);
+ }
+}
+
+
+ModifierTypeInfo modifierType_CorrectiveSmooth = {
+ /* name */ "CorrectiveSmooth",
+ /* structName */ "CorrectiveSmoothModifierData",
+ /* structSize */ sizeof(CorrectiveSmoothModifierData),
+ /* type */ eModifierTypeType_OnlyDeform,
+ /* flags */ eModifierTypeFlag_AcceptsMesh |
+ eModifierTypeFlag_SupportsEditmode,
+
+ /* copyData */ copyData,
+ /* deformVerts */ deformVerts,
+ /* deformMatrices */ NULL,
+ /* deformVertsEM */ deformVertsEM,
+ /* deformMatricesEM */ NULL,
+ /* applyModifier */ NULL,
+ /* applyModifierEM */ NULL,
+ /* initData */ initData,
+ /* requiredDataMask */ requiredDataMask,
+ /* freeData */ freeData,
+ /* isDisabled */ NULL,
+ /* updateDepgraph */ NULL,
+ /* updateDepsgraph */ NULL,
+ /* dependsOnTime */ NULL,
+ /* dependsOnNormals */ NULL,
+ /* foreachObjectLink */ NULL,
+ /* foreachIDLink */ NULL,
+ /* foreachTexLink */ NULL,
+};