diff options
author | Alexander Pinzon <apinzonf@gmail.com> | 2013-11-24 00:00:49 +0400 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2013-11-24 00:10:42 +0400 |
commit | 673bd9a009062807c16cc31a263018737c148a96 (patch) | |
tree | 4ab6bc0cf8d9a7f6fb60b4ea2d61a6374c97794d /source/blender/modifiers/intern/MOD_laplaciandeform.c | |
parent | 4c52e737df39e538d3b41a232035a4a1e240505d (diff) |
Mesh Modifiers: Added Laplacian Deform
Part of soc-2013-sketch_mesh branch
See: http://wiki.blender.org/index.php/User:Apinzonf/Doc:2.6/Manual/Modifiers/Deform/Laplacian_Deform
Diffstat (limited to 'source/blender/modifiers/intern/MOD_laplaciandeform.c')
-rw-r--r-- | source/blender/modifiers/intern/MOD_laplaciandeform.c | 866 |
1 files changed, 866 insertions, 0 deletions
diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c new file mode 100644 index 00000000000..3b909a4b209 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c @@ -0,0 +1,866 @@ +/* + * ***** 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) 2013 by the Blender Foundation. + * All rights reserved. + * + * Contributor(s): Alexander Pinzon Fernandez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_laplaciandeform.c + * \ingroup modifiers + */ + +#include "BLI_math.h" +#include "BLI_utildefines.h" +#include "BLI_string.h" + +#include "MEM_guardedalloc.h" + +#include "BKE_mesh.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_particle.h" +#include "BKE_deform.h" + +#include "MOD_util.h" + +#include "ONL_opennl.h" + +enum { + LAPDEFORM_SYSTEM_NOT_CHANGE = 0, + LAPDEFORM_SYSTEM_IS_DIFFERENT, + LAPDEFORM_SYSTEM_ONLY_CHANGE_ANCHORS, + LAPDEFORM_SYSTEM_ONLY_CHANGE_GROUP, + LAPDEFORM_SYSTEM_ONLY_CHANGE_MESH, + LAPDEFORM_SYSTEM_CHANGE_VERTEXES, + LAPDEFORM_SYSTEM_CHANGE_EDGES, + LAPDEFORM_SYSTEM_CHANGE_NOT_VALID_GROUP, +}; + +typedef struct LaplacianSystem { + bool is_matrix_computed; + bool has_solution; + int total_verts; + int total_edges; + int total_faces; + int total_anchors; + int repeat; + char anchor_grp_name[64]; /* Vertex Group name */ + float (*co)[3]; /* Original vertex coordinates */ + float (*no)[3]; /* Original vertex normal */ + float (*delta)[3]; /* Differential Coordinates */ + unsigned int (*faces)[4]; /* Copy of MFace (tessface) v1-v4 */ + int *index_anchors; /* Static vertex index list */ + int *unit_verts; /* Unit vectors of projected edges onto the plane orthogonal to n */ + int *ringf_indices; /* Indices of faces per vertex */ + int *ringv_indices; /* Indices of neighbors(vertex) per vertex */ + NLContext *context; /* System for solve general implicit rotations */ + MeshElemMap *ringf_map; /* Map of faces per vertex */ + MeshElemMap *ringv_map; /* Map of vertex per vertex */ +} LaplacianSystem; + +static LaplacianSystem *newLaplacianSystem(void) +{ + LaplacianSystem *sys; + sys = MEM_callocN(sizeof(LaplacianSystem), "DeformCache"); + + sys->is_matrix_computed = false; + sys->has_solution = false; + sys->total_verts = 0; + sys->total_edges = 0; + sys->total_anchors = 0; + sys->total_faces = 0; + sys->repeat = 1; + sys->anchor_grp_name[0] = '\0'; + + return sys; +} + +static LaplacianSystem *initLaplacianSystem(int totalVerts, int totalEdges, int totalFaces, int totalAnchors, + const char defgrpName[64], int iterations) +{ + LaplacianSystem *sys = newLaplacianSystem(); + + sys->is_matrix_computed = false; + sys->has_solution = false; + sys->total_verts = totalVerts; + sys->total_edges = totalEdges; + sys->total_faces = totalFaces; + sys->total_anchors = totalAnchors; + sys->repeat = iterations; + BLI_strncpy(sys->anchor_grp_name, defgrpName, sizeof(sys->anchor_grp_name)); + sys->co = MEM_mallocN(sizeof(float[3]) * totalVerts, "DeformCoordinates"); + sys->no = MEM_callocN(sizeof(float[3]) * totalVerts, "DeformNormals"); + sys->delta = MEM_callocN(sizeof(float[3]) * totalVerts, "DeformDeltas"); + sys->faces = MEM_mallocN(sizeof(int[4]) * totalFaces, "DeformFaces"); + sys->index_anchors = MEM_mallocN(sizeof(int) * (totalAnchors), "DeformAnchors"); + sys->unit_verts = MEM_callocN(sizeof(int) * totalVerts, "DeformUnitVerts"); + return sys; +} + +static void deleteLaplacianSystem(LaplacianSystem *sys) +{ + MEM_SAFE_FREE(sys->co); + MEM_SAFE_FREE(sys->no); + MEM_SAFE_FREE(sys->delta); + MEM_SAFE_FREE(sys->faces); + MEM_SAFE_FREE(sys->index_anchors); + MEM_SAFE_FREE(sys->unit_verts); + MEM_SAFE_FREE(sys->ringf_indices); + MEM_SAFE_FREE(sys->ringv_indices); + MEM_SAFE_FREE(sys->ringf_map); + MEM_SAFE_FREE(sys->ringv_map); + + if (sys->context) { + nlDeleteContext(sys->context); + } + MEM_SAFE_FREE(sys); +} + +static float cotan_weight(const float v1[3], const float v2[3], const float v3[3]) +{ + float a[3], b[3], c[3], clen; + + sub_v3_v3v3(a, v2, v1); + sub_v3_v3v3(b, v3, v1); + cross_v3_v3v3(c, a, b); + + clen = len_v3(c); + + if (clen > FLT_EPSILON) { + return dot_v3v3(a, b) / clen; + } + else { + return 0.0f; + } +} + +static void createFaceRingMap( + const int mvert_tot, const MFace *mface, const int mface_tot, + MeshElemMap **r_map, int **r_indices) +{ + int i, j, totalr = 0; + int *indices, *index_iter; + MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * mvert_tot, "DeformRingMap"); + const MFace *mf; + + for (i = 0, mf = mface; i < mface_tot; i++, mf++) { + bool has_4_vert; + + has_4_vert = mf->v4 ? 1 : 0; + + for (j = 0; j < (has_4_vert ? 4 : 3); j++) { + const unsigned int v_index = (*(&mf->v1 + j)); + map[v_index].count++; + totalr++; + } + } + indices = MEM_callocN(sizeof(int) * totalr, "DeformRingIndex"); + index_iter = indices; + for (i = 0; i < mvert_tot; i++) { + map[i].indices = index_iter; + index_iter += map[i].count; + map[i].count = 0; + } + for (i = 0, mf = mface; i < mface_tot; i++, mf++) { + bool has_4_vert; + + has_4_vert = mf->v4 ? 1 : 0; + + for (j = 0; j < (has_4_vert ? 4 : 3); j++) { + const unsigned int v_index = (*(&mf->v1 + j)); + map[v_index].indices[map[v_index].count] = i; + map[v_index].count++; + } + } + *r_map = map; + *r_indices = indices; +} + +static void createVertRingMap( + const int mvert_tot, const MEdge *medge, const int medge_tot, + MeshElemMap **r_map, int **r_indices) +{ + MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * mvert_tot, "DeformNeighborsMap"); + int i, vid[2], totalr = 0; + int *indices, *index_iter; + const MEdge *me; + + for (i = 0, me = medge; i < medge_tot; i++, me++) { + vid[0] = me->v1; + vid[1] = me->v2; + map[vid[0]].count++; + map[vid[1]].count++; + totalr += 2; + } + indices = MEM_callocN(sizeof(int) * totalr, "DeformNeighborsIndex"); + index_iter = indices; + for (i = 0; i < mvert_tot; i++) { + map[i].indices = index_iter; + index_iter += map[i].count; + map[i].count = 0; + } + for (i = 0, me = medge; i < medge_tot; i++, me++) { + vid[0] = me->v1; + vid[1] = me->v2; + map[vid[0]].indices[map[vid[0]].count] = vid[1]; + map[vid[0]].count++; + map[vid[1]].indices[map[vid[1]].count] = vid[0]; + map[vid[1]].count++; + } + *r_map = map; + *r_indices = indices; +} + +/** + * This method computes the Laplacian Matrix and Differential Coordinates for all vertex in the mesh. + * The Linear system is LV = d + * Where L is Laplacian Matrix, V as the vertexes in Mesh, d is the differential coordinates + * The Laplacian Matrix is computes as a + * Lij = sum(Wij) (if i == j) + * Lij = Wij (if i != j) + * Wij is weight between vertex Vi and vertex Vj, we use cotangent weight + * + * The Differential Coordinate is computes as a + * di = Vi * sum(Wij) - sum(Wij * Vj) + * Where : + * di is the Differential Coordinate i + * sum (Wij) is the sum of all weights between vertex Vi and its vertexes neighbors (Vj) + * sum (Wij * Vj) is the sum of the product between vertex neighbor Vj and weight Wij for all neighborhood. + * + * This Laplacian Matrix is described in the paper: + * Desbrun M. et.al, Implicit fairing of irregular meshes using diffusion and curvature flow, SIGGRAPH '99, pag 317-324, + * New York, USA + * + * The computation of Laplace Beltrami operator on Hybrid Triangle/Quad Meshes is described in the paper: + * Pinzon A., Romero E., Shape Inflation With an Adapted Laplacian Operator For Hybrid Quad/Triangle Meshes, + * Conference on Graphics Patterns and Images, SIBGRAPI, 2013 + * + * The computation of Differential Coordinates is described in the paper: + * Sorkine, O. Laplacian Surface Editing. Proceedings of the EUROGRAPHICS/ACM SIGGRAPH Symposium on Geometry Processing, + * 2004. p. 179-188. + */ +static void initLaplacianMatrix(LaplacianSystem *sys) +{ + float v1[3], v2[3], v3[3], v4[3], no[3]; + float w2, w3, w4; + int i, j, fi; + bool has_4_vert; + unsigned int idv1, idv2, idv3, idv4; + + for (fi = 0; fi < sys->total_faces; fi++) { + const unsigned int *vidf = sys->faces[fi]; + + idv1 = vidf[0]; + idv2 = vidf[1]; + idv3 = vidf[2]; + idv4 = vidf[3]; + + has_4_vert = vidf[3] ? 1 : 0; + if (has_4_vert) { + normal_quad_v3(no, sys->co[idv1], sys->co[idv2], sys->co[idv3], sys->co[idv4]); + add_v3_v3(sys->no[idv4], no); + i = 4; + } + else { + normal_tri_v3(no, sys->co[idv1], sys->co[idv2], sys->co[idv3]); + i = 3; + } + add_v3_v3(sys->no[idv1], no); + add_v3_v3(sys->no[idv2], no); + add_v3_v3(sys->no[idv3], no); + + for (j = 0; j < i; j++) { + idv1 = vidf[j]; + idv2 = vidf[(j + 1) % i]; + idv3 = vidf[(j + 2) % i]; + idv4 = has_4_vert ? vidf[(j + 3) % i] : 0; + + copy_v3_v3(v1, sys->co[idv1]); + copy_v3_v3(v2, sys->co[idv2]); + copy_v3_v3(v3, sys->co[idv3]); + if (has_4_vert) { + copy_v3_v3(v4, sys->co[idv4]); + } + + if (has_4_vert) { + + w2 = (cotan_weight(v4, v1, v2) + cotan_weight(v3, v1, v2)) / 2.0f; + w3 = (cotan_weight(v2, v3, v1) + cotan_weight(v4, v1, v3)) / 2.0f; + w4 = (cotan_weight(v2, v4, v1) + cotan_weight(v3, v4, v1)) / 2.0f; + + sys->delta[idv1][0] -= v4[0] * w4; + sys->delta[idv1][1] -= v4[1] * w4; + sys->delta[idv1][2] -= v4[2] * w4; + + nlRightHandSideAdd(0, idv1, -v4[0] * w4); + nlRightHandSideAdd(1, idv1, -v4[1] * w4); + nlRightHandSideAdd(2, idv1, -v4[2] * w4); + + nlMatrixAdd(idv1, idv4, -w4); + } + else { + w2 = cotan_weight(v3, v1, v2); + w3 = cotan_weight(v2, v3, v1); + w4 = 0.0f; + } + + sys->delta[idv1][0] += v1[0] * (w2 + w3 + w4); + sys->delta[idv1][1] += v1[1] * (w2 + w3 + w4); + sys->delta[idv1][2] += v1[2] * (w2 + w3 + w4); + + sys->delta[idv1][0] -= v2[0] * w2; + sys->delta[idv1][1] -= v2[1] * w2; + sys->delta[idv1][2] -= v2[2] * w2; + + sys->delta[idv1][0] -= v3[0] * w3; + sys->delta[idv1][1] -= v3[1] * w3; + sys->delta[idv1][2] -= v3[2] * w3; + + nlMatrixAdd(idv1, idv2, -w2); + nlMatrixAdd(idv1, idv3, -w3); + nlMatrixAdd(idv1, idv1, w2 + w3 + w4); + + } + } +} + +static void computeImplictRotations(LaplacianSystem *sys) +{ + int vid, *vidn = NULL; + float minj, mjt, qj[3], vj[3]; + int i, j, ln; + + for (i = 0; i < sys->total_verts; i++) { + normalize_v3(sys->no[i]); + vidn = sys->ringv_map[i].indices; + ln = sys->ringv_map[i].count; + minj = 1000000.0f; + for (j = 0; j < ln; j++) { + vid = vidn[j]; + copy_v3_v3(qj, sys->co[vid]); + sub_v3_v3v3(vj, qj, sys->co[i]); + normalize_v3(vj); + mjt = fabsf(dot_v3v3(vj, sys->no[i])); + if (mjt < minj) { + minj = mjt; + sys->unit_verts[i] = vidn[j]; + } + } + } +} + +static void rotateDifferentialCoordinates(LaplacianSystem *sys) +{ + float alpha, beta, gamma; + float pj[3], ni[3], di[3]; + float uij[3], dun[3], e2[3], pi[3], fni[3], vn[4][3]; + int i, j, lvin, num_fni, k, fi; + int *fidn; + + for (i = 0; i < sys->total_verts; i++) { + copy_v3_v3(pi, sys->co[i]); + copy_v3_v3(ni, sys->no[i]); + k = sys->unit_verts[i]; + copy_v3_v3(pj, sys->co[k]); + sub_v3_v3v3(uij, pj, pi); + mul_v3_v3fl(dun, ni, dot_v3v3(uij, ni)); + sub_v3_v3(uij, dun); + normalize_v3(uij); + cross_v3_v3v3(e2, ni, uij); + copy_v3_v3(di, sys->delta[i]); + alpha = dot_v3v3(ni, di); + beta = dot_v3v3(uij, di); + gamma = dot_v3v3(e2, di); + + pi[0] = nlGetVariable(0, i); + pi[1] = nlGetVariable(1, i); + pi[2] = nlGetVariable(2, i); + zero_v3(ni); + num_fni = 0; + num_fni = sys->ringf_map[i].count; + for (fi = 0; fi < num_fni; fi++) { + const unsigned int *vin; + fidn = sys->ringf_map[i].indices; + vin = sys->faces[fidn[fi]]; + lvin = vin[3] ? 4 : 3; + for (j = 0; j < lvin; j++) { + vn[j][0] = nlGetVariable(0, vin[j]); + vn[j][1] = nlGetVariable(1, vin[j]); + vn[j][2] = nlGetVariable(2, vin[j]); + if (vin[j] == sys->unit_verts[i]) { + copy_v3_v3(pj, vn[j]); + } + } + + if (lvin == 3) { + normal_tri_v3(fni, vn[0], vn[1], vn[2]); + } + else if (lvin == 4) { + normal_quad_v3(fni, vn[0], vn[1], vn[2], vn[3]); + } + add_v3_v3(ni, fni); + } + + normalize_v3(ni); + sub_v3_v3v3(uij, pj, pi); + mul_v3_v3fl(dun, ni, dot_v3v3(uij, ni)); + sub_v3_v3(uij, dun); + normalize_v3(uij); + cross_v3_v3v3(e2, ni, uij); + fni[0] = alpha * ni[0] + beta * uij[0] + gamma * e2[0]; + fni[1] = alpha * ni[1] + beta * uij[1] + gamma * e2[1]; + fni[2] = alpha * ni[2] + beta * uij[2] + gamma * e2[2]; + + if (len_squared_v3(fni) > FLT_EPSILON) { + nlRightHandSideSet(0, i, fni[0]); + nlRightHandSideSet(1, i, fni[1]); + nlRightHandSideSet(2, i, fni[2]); + } + else { + nlRightHandSideSet(0, i, sys->delta[i][0]); + nlRightHandSideSet(1, i, sys->delta[i][1]); + nlRightHandSideSet(2, i, sys->delta[i][2]); + } + } +} + +static void laplacianDeformPreview(LaplacianSystem *sys, float (*vertexCos)[3]) +{ + int vid, i, j, n, na; + n = sys->total_verts; + na = sys->total_anchors; + + if (!sys->is_matrix_computed) { + nlNewContext(); + sys->context = nlGetCurrent(); + + nlSolverParameteri(NL_NB_VARIABLES, n); + nlSolverParameteri(NL_SYMMETRIC, NL_FALSE); + nlSolverParameteri(NL_LEAST_SQUARES, NL_TRUE); + nlSolverParameteri(NL_NB_ROWS, n + na); + nlSolverParameteri(NL_NB_RIGHT_HAND_SIDES, 3); + nlBegin(NL_SYSTEM); + for (i = 0; i < n; i++) { + nlSetVariable(0, i, sys->co[i][0]); + nlSetVariable(1, i, sys->co[i][1]); + nlSetVariable(2, i, sys->co[i][2]); + } + for (i = 0; i < na; i++) { + vid = sys->index_anchors[i]; + nlSetVariable(0, vid, vertexCos[vid][0]); + nlSetVariable(1, vid, vertexCos[vid][1]); + nlSetVariable(2, vid, vertexCos[vid][2]); + } + nlBegin(NL_MATRIX); + + initLaplacianMatrix(sys); + computeImplictRotations(sys); + + for (i = 0; i < n; i++) { + nlRightHandSideSet(0, i, sys->delta[i][0]); + nlRightHandSideSet(1, i, sys->delta[i][1]); + nlRightHandSideSet(2, i, sys->delta[i][2]); + } + for (i = 0; i < na; i++) { + vid = sys->index_anchors[i]; + nlRightHandSideSet(0, n + i, vertexCos[vid][0]); + nlRightHandSideSet(1, n + i, vertexCos[vid][1]); + nlRightHandSideSet(2, n + i, vertexCos[vid][2]); + nlMatrixAdd(n + i, vid, 1.0f); + } + nlEnd(NL_MATRIX); + nlEnd(NL_SYSTEM); + if (nlSolveAdvanced(NULL, NL_TRUE)) { + sys->has_solution = true; + + for (j = 1; j <= sys->repeat; j++) { + nlBegin(NL_SYSTEM); + nlBegin(NL_MATRIX); + rotateDifferentialCoordinates(sys); + + for (i = 0; i < na; i++) { + vid = sys->index_anchors[i]; + nlRightHandSideSet(0, n + i, vertexCos[vid][0]); + nlRightHandSideSet(1, n + i, vertexCos[vid][1]); + nlRightHandSideSet(2, n + i, vertexCos[vid][2]); + } + + nlEnd(NL_MATRIX); + nlEnd(NL_SYSTEM); + if (!nlSolveAdvanced(NULL, NL_FALSE)) { + sys->has_solution = false; + break; + } + } + if (sys->has_solution) { + for (vid = 0; vid < sys->total_verts; vid++) { + vertexCos[vid][0] = nlGetVariable(0, vid); + vertexCos[vid][1] = nlGetVariable(1, vid); + vertexCos[vid][2] = nlGetVariable(2, vid); + } + } + else { + sys->has_solution = false; + } + + } + else { + sys->has_solution = false; + } + sys->is_matrix_computed = true; + } + else { + if (!sys->has_solution) { + return; + } + + nlBegin(NL_SYSTEM); + nlBegin(NL_MATRIX); + + for (i = 0; i < n; i++) { + nlRightHandSideSet(0, i, sys->delta[i][0]); + nlRightHandSideSet(1, i, sys->delta[i][1]); + nlRightHandSideSet(2, i, sys->delta[i][2]); + } + for (i = 0; i < na; i++) { + vid = sys->index_anchors[i]; + nlRightHandSideSet(0, n + i, vertexCos[vid][0]); + nlRightHandSideSet(1, n + i, vertexCos[vid][1]); + nlRightHandSideSet(2, n + i, vertexCos[vid][2]); + nlMatrixAdd(n + i, vid, 1.0f); + } + + nlEnd(NL_MATRIX); + nlEnd(NL_SYSTEM); + if (nlSolveAdvanced(NULL, NL_FALSE)) { + sys->has_solution = true; + for (j = 1; j <= sys->repeat; j++) { + nlBegin(NL_SYSTEM); + nlBegin(NL_MATRIX); + rotateDifferentialCoordinates(sys); + + for (i = 0; i < na; i++) { + vid = sys->index_anchors[i]; + nlRightHandSideSet(0, n + i, vertexCos[vid][0]); + nlRightHandSideSet(1, n + i, vertexCos[vid][1]); + nlRightHandSideSet(2, n + i, vertexCos[vid][2]); + } + nlEnd(NL_MATRIX); + nlEnd(NL_SYSTEM); + if (!nlSolveAdvanced(NULL, NL_FALSE)) { + sys->has_solution = false; + break; + } + } + if (sys->has_solution) { + for (vid = 0; vid < sys->total_verts; vid++) { + vertexCos[vid][0] = nlGetVariable(0, vid); + vertexCos[vid][1] = nlGetVariable(1, vid); + vertexCos[vid][2] = nlGetVariable(2, vid); + } + } + else { + sys->has_solution = false; + } + } + else { + sys->has_solution = false; + } + } +} + +static bool isValidVertexGroup(LaplacianDeformModifierData *lmd, Object *ob, DerivedMesh *dm) +{ + int defgrp_index; + MDeformVert *dvert = NULL; + + modifier_get_vgroup(ob, dm, lmd->anchor_grp_name, &dvert, &defgrp_index); + + return (dvert != NULL); +} + +static void initSystem(LaplacianDeformModifierData *lmd, Object *ob, DerivedMesh *dm, + float (*vertexCos)[3], int numVerts) +{ + int i; + int defgrp_index; + int total_anchors; + float wpaint; + MDeformVert *dvert = NULL; + MDeformVert *dv = NULL; + LaplacianSystem *sys; + if (isValidVertexGroup(lmd, ob, dm)) { + int *index_anchors = MEM_mallocN(sizeof(int) * numVerts, __func__); /* over-alloc */ + MFace *tessface; + STACK_DECLARE(index_anchors); + STACK_INIT(index_anchors); + + modifier_get_vgroup(ob, dm, lmd->anchor_grp_name, &dvert, &defgrp_index); + BLI_assert(dvert != NULL); + dv = dvert; + for (i = 0; i < numVerts; i++) { + wpaint = defvert_find_weight(dv, defgrp_index); + dv++; + if (wpaint > 0.0f) { + STACK_PUSH(index_anchors, i); + } + } + DM_ensure_tessface(dm); + total_anchors = STACK_SIZE(index_anchors); + lmd->cache_system = initLaplacianSystem(numVerts, dm->getNumEdges(dm), dm->getNumTessFaces(dm), + total_anchors, lmd->anchor_grp_name, lmd->repeat); + sys = (LaplacianSystem *)lmd->cache_system; + memcpy(sys->index_anchors, index_anchors, sizeof(int) * total_anchors); + memcpy(sys->co, vertexCos, sizeof(float[3]) * numVerts); + MEM_freeN(index_anchors); + STACK_FREE(index_anchors); + lmd->vertexco = MEM_mallocN(sizeof(float[3]) * numVerts, "ModDeformCoordinates"); + memcpy(lmd->vertexco, vertexCos, sizeof(float[3]) * numVerts); + lmd->total_verts = numVerts; + + createFaceRingMap( + dm->getNumVerts(dm), dm->getTessFaceArray(dm), dm->getNumTessFaces(dm), + &sys->ringf_map, &sys->ringf_indices); + createVertRingMap( + dm->getNumVerts(dm), dm->getEdgeArray(dm), dm->getNumEdges(dm), + &sys->ringv_map, &sys->ringv_indices); + + + tessface = dm->getTessFaceArray(dm); + + for (i = 0; i < sys->total_faces; i++) { + memcpy(&sys->faces[i], &tessface[i].v1, sizeof(*sys->faces)); + } + } +} + +static int isSystemDifferent(LaplacianDeformModifierData *lmd, Object *ob, DerivedMesh *dm, int numVerts) +{ + int i; + int defgrp_index; + int total_anchors = 0; + float wpaint; + MDeformVert *dvert = NULL; + MDeformVert *dv = NULL; + LaplacianSystem *sys = (LaplacianSystem *)lmd->cache_system; + + if (sys->total_verts != numVerts) { + return LAPDEFORM_SYSTEM_CHANGE_VERTEXES; + } + if (sys->total_edges != dm->getNumEdges(dm)) { + return LAPDEFORM_SYSTEM_CHANGE_EDGES; + } + if (!STREQ(lmd->anchor_grp_name, sys->anchor_grp_name)) { + return LAPDEFORM_SYSTEM_ONLY_CHANGE_GROUP; + } + modifier_get_vgroup(ob, dm, lmd->anchor_grp_name, &dvert, &defgrp_index); + if (!dvert) { + return LAPDEFORM_SYSTEM_CHANGE_NOT_VALID_GROUP; + } + dv = dvert; + for (i = 0; i < numVerts; i++) { + wpaint = defvert_find_weight(dv, defgrp_index); + dv++; + if (wpaint > 0.0f) { + total_anchors++; + } + } + if (sys->total_anchors != total_anchors) { + return LAPDEFORM_SYSTEM_ONLY_CHANGE_ANCHORS; + } + + return LAPDEFORM_SYSTEM_NOT_CHANGE; +} + +static void LaplacianDeformModifier_do( + LaplacianDeformModifierData *lmd, Object *ob, DerivedMesh *dm, + float (*vertexCos)[3], int numVerts) +{ + float (*filevertexCos)[3]; + int sysdif; + LaplacianSystem *sys = NULL; + filevertexCos = NULL; + if (!(lmd->flag & MOD_LAPLACIANDEFORM_BIND)) { + if (lmd->cache_system) { + sys = lmd->cache_system; + deleteLaplacianSystem(sys); + lmd->cache_system = NULL; + } + lmd->total_verts = 0; + MEM_SAFE_FREE(lmd->vertexco); + return; + } + if (lmd->cache_system) { + sysdif = isSystemDifferent(lmd, ob, dm, numVerts); + sys = lmd->cache_system; + if (sysdif) { + if (sysdif == LAPDEFORM_SYSTEM_ONLY_CHANGE_ANCHORS || sysdif == LAPDEFORM_SYSTEM_ONLY_CHANGE_GROUP) { + filevertexCos = MEM_mallocN(sizeof(float[3]) * numVerts, "TempModDeformCoordinates"); + memcpy(filevertexCos, lmd->vertexco, sizeof(float[3]) * numVerts); + MEM_SAFE_FREE(lmd->vertexco); + lmd->total_verts = 0; + deleteLaplacianSystem(sys); + initSystem(lmd, ob, dm, filevertexCos, numVerts); + sys = lmd->cache_system; /* may have been reallocated */ + MEM_SAFE_FREE(filevertexCos); + laplacianDeformPreview(sys, vertexCos); + } + else { + if (sysdif == LAPDEFORM_SYSTEM_CHANGE_VERTEXES) { + modifier_setError(&lmd->modifier, "Vertices changed from %d to %d", lmd->total_verts, numVerts); + } + else if (sysdif == LAPDEFORM_SYSTEM_CHANGE_EDGES) { + modifier_setError(&lmd->modifier, "Edges changed from %d to %d", sys->total_edges, dm->getNumEdges(dm)); + } + else if (sysdif == LAPDEFORM_SYSTEM_CHANGE_NOT_VALID_GROUP) { + modifier_setError(&lmd->modifier, "Vertex group %s is not valid", sys->anchor_grp_name); + } + } + } + else { + sys->repeat = lmd->repeat; + laplacianDeformPreview(sys, vertexCos); + } + } + else { + if (lmd->total_verts > 0 && lmd->total_verts == numVerts) { + if (isValidVertexGroup(lmd, ob, dm)) { + filevertexCos = MEM_mallocN(sizeof(float[3]) * numVerts, "TempDeformCoordinates"); + memcpy(filevertexCos, lmd->vertexco, sizeof(float[3]) * numVerts); + MEM_SAFE_FREE(lmd->vertexco); + lmd->total_verts = 0; + initSystem(lmd, ob, dm, filevertexCos, numVerts); + sys = lmd->cache_system; + MEM_SAFE_FREE(filevertexCos); + laplacianDeformPreview(sys, vertexCos); + } + } + else { + if (isValidVertexGroup(lmd, ob, dm)) { + initSystem(lmd, ob, dm, vertexCos, numVerts); + sys = lmd->cache_system; + laplacianDeformPreview(sys, vertexCos); + } + } + } + if (sys->is_matrix_computed && !sys->has_solution) { + modifier_setError(&lmd->modifier, "The system did not find a solution."); + } +} + + +static void initData(ModifierData *md) +{ + LaplacianDeformModifierData *lmd = (LaplacianDeformModifierData *)md; + lmd->anchor_grp_name[0] = '\0'; + lmd->total_verts = 0; + lmd->repeat = 1; + lmd->vertexco = NULL; + lmd->cache_system = NULL; + lmd->flag = 0; +} + +static void copyData(ModifierData *md, ModifierData *target) +{ + LaplacianDeformModifierData *lmd = (LaplacianDeformModifierData *)md; + LaplacianDeformModifierData *tlmd = (LaplacianDeformModifierData *)target; + tlmd->total_verts = lmd->total_verts; + tlmd->repeat = lmd->repeat; + BLI_strncpy(tlmd->anchor_grp_name, lmd->anchor_grp_name, sizeof(tlmd->anchor_grp_name)); + tlmd->vertexco = MEM_dupallocN(lmd->vertexco); + tlmd->cache_system = MEM_dupallocN(lmd->cache_system); + tlmd->flag = lmd->flag; +} + +static bool isDisabled(ModifierData *md, int UNUSED(useRenderParams)) +{ + LaplacianDeformModifierData *lmd = (LaplacianDeformModifierData *)md; + if (lmd->anchor_grp_name[0]) return 0; + return 1; +} + +static CustomDataMask requiredDataMask(Object *UNUSED(ob), ModifierData *md) +{ + LaplacianDeformModifierData *lmd = (LaplacianDeformModifierData *)md; + CustomDataMask dataMask = 0; + if (lmd->anchor_grp_name[0]) dataMask |= CD_MASK_MDEFORMVERT; + return dataMask; +} + +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); + + LaplacianDeformModifier_do((LaplacianDeformModifierData *)md, ob, dm, vertexCos, numVerts); + 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); + LaplacianDeformModifier_do((LaplacianDeformModifierData *)md, ob, dm, + vertexCos, numVerts); + if (dm != derivedData) { + dm->release(dm); + } +} + +static void freeData(ModifierData *md) +{ + LaplacianDeformModifierData *lmd = (LaplacianDeformModifierData *)md; + LaplacianSystem *sys = (LaplacianSystem *)lmd->cache_system; + + if (sys) { + deleteLaplacianSystem(sys); + } + MEM_SAFE_FREE(lmd->vertexco); + lmd->total_verts = 0; +} + +ModifierTypeInfo modifierType_LaplacianDeform = { + /* name */ "LaplacianDeform", + /* structName */ "LaplacianDeformModifierData", + /* structSize */ sizeof(LaplacianDeformModifierData), + /* 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 */ isDisabled, + /* updateDepgraph */ NULL, + /* dependsOnTime */ NULL, + /* dependsOnNormals */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; |