diff options
author | Daniel Genrich <daniel.genrich@gmx.net> | 2012-10-24 14:39:11 +0400 |
---|---|---|
committer | Daniel Genrich <daniel.genrich@gmx.net> | 2012-10-24 14:39:11 +0400 |
commit | 3ce334e188affdeec3c492408075558e65f8244f (patch) | |
tree | 9e851da8755544d56a9b9ff0b0e9fb19904dfe46 | |
parent | d739536103caada312a3681d106b9cc78af2d1fe (diff) |
Merge GSoC project from branch: Laplacian Smooth (Operator & Modifier)
by Alexander Pinzon Fernandez (apinzonf)
Supported by Google Summer of Code 2012
Project Documentation:
http://wiki.blender.org/index.php/User:Apinzonf
Manual Page:
http://wiki.blender.org/index.php/User:Apinzonf/Doc:2.6/Manual/Modifiers/Deform/Laplacian_Smooth
19 files changed, 1523 insertions, 5 deletions
diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py index c205edc0c3c..28d20bcb2d6 100644 --- a/release/scripts/startup/bl_ui/properties_data_modifier.py +++ b/release/scripts/startup/bl_ui/properties_data_modifier.py @@ -331,6 +331,20 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): row.operator("object.hook_select", text="Select") row.operator("object.hook_assign", text="Assign") + def LAPLACIANSMOOTH(self, layout, ob, md): + layout.prop(md, "iterations") + layout.prop(md, "lamb") + layout.prop(md, "lambdaborder") + row = layout.row() + row.label(text="Axis: ") + row.prop(md, "use_x") + row.prop(md, "use_y") + row.prop(md, "use_z") + row = layout.row() + row.prop(md, "volume_preservation") + layout.label(text="Vertex Group:") + layout.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + def LATTICE(self, layout, ob, md): split = layout.split() diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 6c7f2d1ed52..d44213c199a 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -1711,6 +1711,7 @@ class VIEW3D_MT_edit_mesh_specials(Menu): layout.operator("mesh.select_all", text="Select Inverse").action = 'INVERT' layout.operator("mesh.flip_normals") layout.operator("mesh.vertices_smooth", text="Smooth") + layout.operator("mesh.vertices_smooth_laplacian", text="Laplacian Smooth") layout.operator("mesh.inset") layout.operator("mesh.bevel", text="Bevel") layout.operator("mesh.bridge_edge_loops") diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 1e39e7f77d3..1753fd3650f 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -29,8 +29,7 @@ set(INC ../blenlib ../makesdna ../../../intern/guardedalloc - ../../../extern/bullet2/src -) + ../../../extern/bullet2/src ../../../intern/opennl/extern) set(INC_SYS @@ -52,6 +51,7 @@ set(SRC operators/bmo_mirror.c operators/bmo_primitive.c operators/bmo_removedoubles.c + operators/bmo_smooth_laplacian.c operators/bmo_symmetrize.c operators/bmo_subdivide.c operators/bmo_subdivide.h diff --git a/source/blender/bmesh/SConscript b/source/blender/bmesh/SConscript index be6332be478..356ee9b41b0 100644 --- a/source/blender/bmesh/SConscript +++ b/source/blender/bmesh/SConscript @@ -13,8 +13,7 @@ incs = [ '../makesdna', '../blenkernel', '#/intern/guardedalloc', - '#/extern/bullet2/src' - ] + '#/extern/bullet2/src' '#/intern/opennl/extern', ] defs = [] diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index b527cffbfdc..e12e28f8cff 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -114,6 +114,26 @@ static BMOpDefine bmo_smooth_vert_def = { }; /* + * Vertext Smooth Laplacian + * Smooths vertices by using Laplacian smoothing propose by. + * Desbrun, et al. Implicit Fairing of Irregular Meshes using Diffusion and Curvature Flow +*/ +static BMOpDefine bmo_smooth_laplacian_vert_def = { + "smooth_laplacian_vert", + {{BMO_OP_SLOT_ELEMENT_BUF, "verts"}, //input vertices + {BMO_OP_SLOT_FLT, "lambda"}, //lambda param + {BMO_OP_SLOT_FLT, "lambda_border"}, //lambda param in border + {BMO_OP_SLOT_BOOL, "use_x"}, //Smooth object along X axis + {BMO_OP_SLOT_BOOL, "use_y"}, //Smooth object along Y axis + {BMO_OP_SLOT_BOOL, "use_z"}, //Smooth object along Z axis + {BMO_OP_SLOT_BOOL, "volume_preservation"}, //Apply volume preservation after smooth + {0} /* null-terminating sentinel */, + }, + bmo_smooth_laplacian_vert_exec, + 0 +}; + +/* * Right-Hand Faces * * Computes an "outside" normal for the specified input faces. @@ -1278,6 +1298,7 @@ BMOpDefine *opdefines[] = { &bmo_similar_verts_def, &bmo_slide_vert_def, &bmo_smooth_vert_def, + &bmo_smooth_laplacian_vert_def, &bmo_solidify_def, &bmo_spin_def, &bmo_split_def, diff --git a/source/blender/bmesh/intern/bmesh_operators_private.h b/source/blender/bmesh/intern/bmesh_operators_private.h index d6135efe19a..65c9cf0c421 100644 --- a/source/blender/bmesh/intern/bmesh_operators_private.h +++ b/source/blender/bmesh/intern/bmesh_operators_private.h @@ -91,6 +91,7 @@ void bmo_similar_faces_exec(BMesh *bm, BMOperator *op); void bmo_similar_verts_exec(BMesh *bm, BMOperator *op); void bmo_slide_vert_exec(BMesh *bm, BMOperator *op); void bmo_smooth_vert_exec(BMesh *bm, BMOperator *op); +void bmo_smooth_laplacian_vert_exec(BMesh *bm, BMOperator *op); void bmo_solidify_face_region_exec(BMesh *bm, BMOperator *op); void bmo_spin_exec(BMesh *bm, BMOperator *op); void bmo_split_edges_exec(BMesh *bm, BMOperator *op); diff --git a/source/blender/bmesh/operators/bmo_smooth_laplacian.c b/source/blender/bmesh/operators/bmo_smooth_laplacian.c new file mode 100644 index 00000000000..ccbc85a617c --- /dev/null +++ b/source/blender/bmesh/operators/bmo_smooth_laplacian.c @@ -0,0 +1,613 @@ +/* + * ***** 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. + * + * Contributor(s): Alexander Pinzon + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/operators/bmo_smooth_laplacian.c + * \ingroup bmesh + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_meshdata_types.h" + +#include "BLI_array.h" +#include "BLI_heap.h" +#include "BLI_math.h" +#include "BLI_math_geom.h" +#include "BLI_smallhash.h" + +#include "BKE_customdata.h" +#include "BKE_mesh.h" + +#include "bmesh.h" + +#include "ONL_opennl.h" + +#include "intern/bmesh_operators_private.h" /* own include */ + +#define SMOOTH_LAPLACIAN_AREA_FACTOR 4.0f +#define SMOOTH_LAPLACIAN_EDGE_FACTOR 2.0f +#define SMOOTH_LAPLACIAN_MAX_EDGE_PERCENTAGE 1.8 +#define SMOOTH_LAPLACIAN_MIN_EDGE_PERCENTAGE 0.15 + +struct BLaplacianSystem { + float *eweights; /* Length weights per Edge */ + float (*fweights)[3]; /* Cotangent weights per face */ + float *ring_areas; /* Total area per ring*/ + float *vlengths; /* Total sum of lengths(edges) per vertice*/ + float *vweights; /* Total sum of weights per vertice*/ + int numEdges; /* Number of edges*/ + int numFaces; /* Number of faces*/ + int numVerts; /* Number of verts*/ + short *zerola; /* Is zero area or length*/ + + /* Pointers to data*/ + BMesh *bm; + BMOperator *op; + NLContext *context; + + /*Data*/ + float min_area; +}; +typedef struct BLaplacianSystem LaplacianSystem; + +static float compute_volume(BMesh *bm, BMOperator *op); +static float cotan_weight(float *v1, float *v2, float *v3); +static int vert_is_boundary(BMVert *v); +static LaplacianSystem * init_laplacian_system( int a_numEdges, int a_numFaces, int a_numVerts); +static void init_laplacian_matrix(LaplacianSystem * sys); +static void delete_laplacian_system(LaplacianSystem * sys); +static void delete_void_pointer(void * data); +static void fill_laplacian_matrix(LaplacianSystem * sys); +static void memset_laplacian_system(LaplacianSystem *sys, int val); +static void validate_solution(LaplacianSystem * sys, int usex, int usey, int usez, float lambda, float lambda_border, int volumepreservation); +static void volume_preservation(BMesh *bm, BMOperator *op, float vini, float vend, int usex, int usey, int usez); + +static void delete_void_pointer(void * data) +{ + if (data) { + MEM_freeN(data); + data = NULL; + } +} + +static void delete_laplacian_system(LaplacianSystem * sys) +{ + delete_void_pointer(sys->eweights); + delete_void_pointer(sys->fweights); + delete_void_pointer(sys->ring_areas); + delete_void_pointer(sys->vlengths); + delete_void_pointer(sys->vweights); + delete_void_pointer(sys->zerola); + if (sys->context) { + nlDeleteContext(sys->context); + } + sys->bm = NULL; + sys->op = NULL; + MEM_freeN(sys); +} + +static void memset_laplacian_system(LaplacianSystem *sys, int val) +{ + memset(sys->eweights , val, sizeof(float) * sys->numEdges); + memset(sys->fweights , val, sizeof(float) * sys->numFaces * 3); + memset(sys->ring_areas , val, sizeof(float) * sys->numVerts); + memset(sys->vlengths , val, sizeof(float) * sys->numVerts); + memset(sys->vweights , val, sizeof(float) * sys->numVerts); + memset(sys->zerola , val, sizeof(short) * sys->numVerts); +} + +static LaplacianSystem * init_laplacian_system( int a_numEdges, int a_numFaces, int a_numVerts) +{ + LaplacianSystem * sys; + sys = MEM_callocN(sizeof(LaplacianSystem), "ModLaplSmoothSystem"); + sys->numEdges = a_numEdges; + sys->numFaces = a_numFaces; + sys->numVerts = a_numVerts; + + sys->eweights = MEM_callocN(sizeof(float) * sys->numEdges, "ModLaplSmoothEWeight"); + if (!sys->eweights) { + delete_laplacian_system(sys); + return NULL; + } + + sys->fweights = MEM_callocN(sizeof(float) * 3 * sys->numFaces, "ModLaplSmoothFWeight"); + if (!sys->fweights) { + delete_laplacian_system(sys); + return NULL; + } + + sys->ring_areas = MEM_callocN(sizeof(float) * sys->numVerts, "ModLaplSmoothRingAreas"); + if (!sys->ring_areas) { + delete_laplacian_system(sys); + return NULL; + } + + sys->vlengths = MEM_callocN(sizeof(float) * sys->numVerts, "ModLaplSmoothVlengths"); + if (!sys->vlengths) { + delete_laplacian_system(sys); + return NULL; + } + + sys->vweights = MEM_callocN(sizeof(float) * sys->numVerts, "ModLaplSmoothVweights"); + if (!sys->vweights) { + delete_laplacian_system(sys); + return NULL; + } + + sys->zerola = MEM_callocN(sizeof(short) * sys->numVerts, "ModLaplSmoothZeloa"); + if (!sys->zerola) { + delete_laplacian_system(sys); + return NULL; + } + + return sys; +} + +/* Compute weigth between vertice v_i and all your neighbors + * weight between v_i and v_neighbor + * Wij = cot(alpha) + cot(beta) / (4.0 * total area of all faces * sum all weight) + * v_i * + * / | \ + * / | \ + * v_beta* | * v_alpha + * \ | / + * \ | / + * * v_neighbor +*/ + +static void init_laplacian_matrix(LaplacianSystem * sys) +{ + float areaf; + float *v1, *v2, *v3, *v4; + float w1, w2, w3, w4; + int i, j; + int has_4_vert ; + unsigned int idv1, idv2, idv3, idv4, idv[4]; + BMEdge *e; + BMFace *f; + BMIter eiter; + BMIter fiter; + BMIter vi; + BMVert *vn; + BMVert *vf[4]; + + BM_ITER_MESH_INDEX (e, &eiter, sys->bm, BM_EDGES_OF_MESH, j) { + if (!BM_elem_flag_test(e, BM_ELEM_SELECT) && BM_edge_is_boundary(e)) { + v1 = e->v1->co; + v2 = e->v2->co; + idv1 = BM_elem_index_get(e->v1); + idv2 = BM_elem_index_get(e->v2); + + w1 = len_v3v3(v1, v2); + if (w1 > sys->min_area) { + w1 = 1.0f / w1; + i = BM_elem_index_get(e); + sys->eweights[i] = w1; + sys->vlengths[idv1] += w1; + sys->vlengths[idv2] += w1; + }else{ + sys->zerola[idv1] = 1; + sys->zerola[idv2] = 1; + } + } + } + + BM_ITER_MESH (f, &fiter, sys->bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + + BM_ITER_ELEM_INDEX (vn, &vi, f, BM_VERTS_OF_FACE, i) { + vf[i] = vn; + } + has_4_vert = (i == 4) ? 1 : 0; + idv1 = BM_elem_index_get(vf[0]); + idv2 = BM_elem_index_get(vf[1]); + idv3 = BM_elem_index_get(vf[2]); + idv4 = has_4_vert ? BM_elem_index_get(vf[3]) : 0; + + v1 = vf[0]->co; + v2 = vf[1]->co; + v3 = vf[2]->co; + v4 = has_4_vert ? vf[3]->co : 0; + + if (has_4_vert) { + areaf = area_quad_v3(v1, v2, v3, v4); + } else { + areaf = area_tri_v3(v1, v2, v3); + } + + if (fabs(areaf) < sys->min_area) { + sys->zerola[idv1] = 1; + sys->zerola[idv2] = 1; + sys->zerola[idv3] = 1; + if (has_4_vert) sys->zerola[idv4] = 1; + } + + sys->ring_areas[idv1] += areaf; + sys->ring_areas[idv2] += areaf; + sys->ring_areas[idv3] += areaf; + if (has_4_vert) sys->ring_areas[idv4] += areaf; + + if (has_4_vert) { + + idv[0] = idv1; + idv[1] = idv2; + idv[2] = idv3; + idv[3] = idv4; + + for (j = 0; j < 4; j++) { + idv1 = idv[j]; + idv2 = idv[(j + 1) % 4]; + idv3 = idv[(j + 2) % 4]; + idv4 = idv[(j + 3) % 4]; + + v1 = vf[j]->co; + v2 = vf[(j + 1) % 4]->co; + v3 = vf[(j + 2) % 4]->co; + v4 = vf[(j + 3) % 4]->co; + + w2 = cotan_weight(v4, v1, v2) + cotan_weight(v3, v1, v2); + w3 = cotan_weight(v2, v3, v1) + cotan_weight(v4, v1, v3); + w4 = cotan_weight(v2, v4, v1) + cotan_weight(v3, v4, v1); + + sys->vweights[idv1] += (w2 + w3 + w4) / 4.0f; + } + } else { + i = BM_elem_index_get(f); + + w1 = cotan_weight(v1, v2, v3); + w2 = cotan_weight(v2, v3, v1); + w3 = cotan_weight(v3, v1, v2); + + sys->fweights[i][0] += w1; + sys->fweights[i][1] += w2; + sys->fweights[i][2] += w3; + + sys->vweights[idv1] += w2 + w3; + sys->vweights[idv2] += w1 + w3; + sys->vweights[idv3] += w1 + w2; + } + } + } +} + +static void fill_laplacian_matrix(LaplacianSystem * sys) +{ + float *v1, *v2, *v3, *v4; + float w2, w3, w4; + int i, j; + int has_4_vert ; + unsigned int idv1, idv2, idv3, idv4, idv[4]; + + BMEdge *e; + BMFace *f; + BMIter eiter; + BMIter fiter; + BMIter vi; + BMVert *vn; + BMVert *vf[4]; + + BM_ITER_MESH (f, &fiter, sys->bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + BM_ITER_ELEM_INDEX (vn, &vi, f, BM_VERTS_OF_FACE, i) { + vf[i] = vn; + } + has_4_vert = (i == 4) ? 1 : 0; + if (has_4_vert) { + idv[0] = BM_elem_index_get(vf[0]); + idv[1] = BM_elem_index_get(vf[1]); + idv[2] = BM_elem_index_get(vf[2]); + idv[3] = BM_elem_index_get(vf[3]); + for (j = 0; j < 4; j++) { + idv1 = idv[j]; + idv2 = idv[(j + 1) % 4]; + idv3 = idv[(j + 2) % 4]; + idv4 = idv[(j + 3) % 4]; + + v1 = vf[j]->co; + v2 = vf[(j + 1) % 4]->co; + v3 = vf[(j + 2) % 4]->co; + v4 = vf[(j + 3) % 4]->co; + + w2 = cotan_weight(v4, v1, v2) + cotan_weight(v3, v1, v2); + w3 = cotan_weight(v2, v3, v1) + cotan_weight(v4, v1, v3); + w4 = cotan_weight(v2, v4, v1) + cotan_weight(v3, v4, v1); + + w2 = w2 / 4.0f; + w3 = w3 / 4.0f; + w4 = w4 / 4.0f; + + if (!vert_is_boundary(vf[j]) && sys->zerola[idv1] == 0) { + nlMatrixAdd(idv1, idv2, w2 * sys->vweights[idv1]); + nlMatrixAdd(idv1, idv3, w3 * sys->vweights[idv1]); + nlMatrixAdd(idv1, idv4, w4 * sys->vweights[idv1]); + } + } + } else { + idv1 = BM_elem_index_get(vf[0]); + idv2 = BM_elem_index_get(vf[1]); + idv3 = BM_elem_index_get(vf[2]); + /* Is ring if number of faces == number of edges around vertice*/ + i = BM_elem_index_get(f); + if (!vert_is_boundary(vf[0]) && sys->zerola[idv1] == 0) { + nlMatrixAdd(idv1, idv2, sys->fweights[i][2] * sys->vweights[idv1]); + nlMatrixAdd(idv1, idv3, sys->fweights[i][1] * sys->vweights[idv1]); + } + if (!vert_is_boundary(vf[1]) && sys->zerola[idv2] == 0) { + nlMatrixAdd(idv2, idv1, sys->fweights[i][2] * sys->vweights[idv2]); + nlMatrixAdd(idv2, idv3, sys->fweights[i][0] * sys->vweights[idv2]); + } + if (!vert_is_boundary(vf[2]) && sys->zerola[idv3] == 0) { + nlMatrixAdd(idv3, idv1, sys->fweights[i][1] * sys->vweights[idv3]); + nlMatrixAdd(idv3, idv2, sys->fweights[i][0] * sys->vweights[idv3]); + } + } + } + } + BM_ITER_MESH (e, &eiter, sys->bm, BM_EDGES_OF_MESH) { + if (!BM_elem_flag_test(e, BM_ELEM_SELECT) && BM_edge_is_boundary(e) ) { + v1 = e->v1->co; + v2 = e->v2->co; + idv1 = BM_elem_index_get(e->v1); + idv2 = BM_elem_index_get(e->v2); + if (sys->zerola[idv1] == 0 && sys->zerola[idv2] == 0) { + i = BM_elem_index_get(e); + nlMatrixAdd(idv1, idv2, sys->eweights[i] * sys->vlengths[idv1]); + nlMatrixAdd(idv2, idv1, sys->eweights[i] * sys->vlengths[idv2]); + } + } + } +} + +static float cotan_weight(float *v1, float *v2, float *v3) +{ + 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 == 0.0f) + return 0.0f; + + return dot_v3v3(a, b) / clen; +} + +static int vert_is_boundary(BMVert *v) +{ + BMEdge *ed; + BMFace *f; + BMIter ei; + BMIter fi; + BM_ITER_ELEM(ed, &ei, v, BM_EDGES_OF_VERT) { + if (BM_edge_is_boundary(ed)) { + return 1; + } + } + BM_ITER_ELEM (f, &fi, v, BM_FACES_OF_VERT) { + if (!BM_elem_flag_test(f, BM_ELEM_SELECT)) { + return 1; + } + } + return 0; +} + +static float compute_volume(BMesh *bm, BMOperator *op) +{ + float vol = 0.0f; + float x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4; + int i; + BMFace *f; + BMIter fiter; + BMIter vi; + BMVert *vn; + BMVert *vf[4]; + + BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { + BM_ITER_ELEM_INDEX (vn, &vi, f, BM_VERTS_OF_FACE, i) { + vf[i] = vn; + } + x1 = vf[0]->co[0]; + y1 = vf[0]->co[1]; + z1 = vf[0]->co[2]; + + x2 = vf[1]->co[0]; + y2 = vf[1]->co[1]; + z2 = vf[1]->co[2]; + + x3 = vf[2]->co[0]; + y3 = vf[2]->co[1]; + z3 = vf[2]->co[2]; + + vol += (1.0 / 6.0) * (0.0 - x3*y2*z1 + x2*y3*z1 + x3*y1*z2 - x1*y3*z2 - x2*y1*z3 + x1*y2*z3); + + if (i == 4) { + x4 = vf[3]->co[0]; + y4 = vf[3]->co[1]; + z4 = vf[3]->co[2]; + vol += (1.0 / 6.0) * (x1*y3*z4 - x1*y4*z3 - x3*y1*z4 + x3*z1*y4 + y1*x4*z3 - x4*y3*z1); + } + } + return fabs(vol); +} + +static void volume_preservation(BMesh *bm, BMOperator *op, float vini, float vend, int usex, int usey, int usez) +{ + float beta; + BMOIter siter; + BMVert *v; + + if (vend != 0.0f) { + beta = pow (vini / vend, 1.0f / 3.0f); + BMO_ITER (v, &siter, bm, op, "verts", BM_VERT) { + if (usex) { + v->co[0] *= beta; + } + if (usey) { + v->co[1] *= beta; + } + if (usez) { + v->co[2] *= beta; + } + + } + } +} + +static void validate_solution(LaplacianSystem * sys, int usex, int usey, int usez, float lambda, float lambda_border, int volumepreservation) +{ + int m_vertex_id; + float leni, lene; + float vini, vend; + float *vi1, *vi2, ve1[3], ve2[3]; + unsigned int idv1, idv2; + BMOIter siter; + BMVert *v; + BMEdge *e; + BMIter eiter; + + BM_ITER_MESH (e, &eiter, sys->bm, BM_EDGES_OF_MESH) { + idv1 = BM_elem_index_get(e->v1); + idv2 = BM_elem_index_get(e->v2); + vi1 = e->v1->co; + vi2 = e->v2->co; + ve1[0] = nlGetVariable(0, idv1); + ve1[1] = nlGetVariable(1, idv1); + ve1[2] = nlGetVariable(2, idv1); + ve2[0] = nlGetVariable(0, idv2); + ve2[1] = nlGetVariable(1, idv2); + ve2[2] = nlGetVariable(2, idv2); + leni = len_v3v3(vi1, vi2); + lene = len_v3v3(ve1, ve2); + if ( lene > leni* SMOOTH_LAPLACIAN_MAX_EDGE_PERCENTAGE || lene < leni*SMOOTH_LAPLACIAN_MIN_EDGE_PERCENTAGE) { + sys->zerola[idv1] = 1; + sys->zerola[idv2] = 1; + } + } + + if (volumepreservation) { + vini = compute_volume(sys->bm, sys->op); + } + BMO_ITER (v, &siter, sys->bm, sys->op, "verts", BM_VERT) { + m_vertex_id = BM_elem_index_get(v); + if (sys->zerola[m_vertex_id] == 0) { + if (usex) { + v->co[0] = nlGetVariable(0, m_vertex_id); + } + if (usey) { + v->co[1] = nlGetVariable(1, m_vertex_id); + } + if (usez) { + v->co[2] = nlGetVariable(2, m_vertex_id); + } + } + } + if (volumepreservation) { + vend = compute_volume(sys->bm, sys->op); + volume_preservation(sys->bm, sys->op, vini, vend, usex, usey, usez); + } + +} + +void bmo_smooth_laplacian_vert_exec(BMesh *bm, BMOperator *op) +{ + int i; + int m_vertex_id; + int usex, usey, usez, volumepreservation; + float lambda, lambda_border; + float w; + BMOIter siter; + BMVert *v; + LaplacianSystem * sys; + + sys = init_laplacian_system(bm->totedge, bm->totface, bm->totvert); + if (!sys) return; + sys->bm = bm; + sys->op = op; + + memset_laplacian_system(sys, 0); + + BM_mesh_elem_index_ensure(bm, BM_VERT); + lambda = BMO_slot_float_get(op, "lambda"); + lambda_border = BMO_slot_float_get(op, "lambda_border"); + sys->min_area = 0.00001f; + usex = BMO_slot_bool_get(op, "use_x"); + usey = BMO_slot_bool_get(op, "use_y"); + usez = BMO_slot_bool_get(op, "use_z"); + volumepreservation = BMO_slot_bool_get(op, "volume_preservation"); + + + nlNewContext(); + sys->context = nlGetCurrent(); + + nlSolverParameteri(NL_NB_VARIABLES, bm->totvert); + nlSolverParameteri(NL_LEAST_SQUARES, NL_TRUE); + nlSolverParameteri(NL_NB_ROWS, bm->totvert); + nlSolverParameteri(NL_NB_RIGHT_HAND_SIDES, 3); + + nlBegin(NL_SYSTEM); + for (i=0; i < bm->totvert; i++) { + nlLockVariable(i); + } + BMO_ITER (v, &siter, bm, op, "verts", BM_VERT) { + m_vertex_id = BM_elem_index_get(v); + nlUnlockVariable(m_vertex_id); + nlSetVariable(0,m_vertex_id, v->co[0]); + nlSetVariable(1,m_vertex_id, v->co[1]); + nlSetVariable(2,m_vertex_id, v->co[2]); + } + + nlBegin(NL_MATRIX); + init_laplacian_matrix(sys); + BMO_ITER (v, &siter, bm, op, "verts", BM_VERT) { + m_vertex_id = BM_elem_index_get(v); + nlRightHandSideAdd(0, m_vertex_id, v->co[0]); + nlRightHandSideAdd(1, m_vertex_id, v->co[1]); + nlRightHandSideAdd(2, m_vertex_id, v->co[2]); + i = m_vertex_id; + if (sys->zerola[i] == 0) { + w = sys->vweights[i] * sys->ring_areas[i]; + sys->vweights[i] = (w == 0.0f) ? 0.0f : - lambda / (4.0f * w); + w = sys->vlengths[i]; + sys->vlengths[i] = (w == 0.0f) ? 0.0f : - lambda_border * 2.0f / w; + + if (!vert_is_boundary(v)) { + nlMatrixAdd(i, i, 1.0f + lambda / (4.0f * sys->ring_areas[i])); + } else { + nlMatrixAdd(i, i, 1.0f + lambda_border * 2.0f); + } + } else { + nlMatrixAdd(i, i, 1.0f); + } + } + fill_laplacian_matrix(sys); + + nlEnd(NL_MATRIX); + nlEnd(NL_SYSTEM); + + if (nlSolveAdvanced(NULL, NL_TRUE) ) { + validate_solution(sys, usex, usey, usez, lambda, lambda_border, volumepreservation); + } + + delete_laplacian_system(sys); +} diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 44933f6a1c0..4e13833d85c 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -1666,6 +1666,88 @@ void MESH_OT_vertices_smooth(wmOperatorType *ot) RNA_def_boolean(ot->srna, "zaxis", 1, "Z-Axis", "Smooth along the Z axis"); } +static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BMEdit_FromObject(obedit); + int usex = TRUE, usey = TRUE, usez = TRUE, volume_preservation = TRUE; + int i, repeat; + float lambda = 0.1f; + float lambda_border = 0.1f; + BMIter fiter; + BMFace *f; + + /* Check if select faces are triangles */ + BM_ITER_MESH (f, &fiter, em->bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + if(f->len > 4) { + BKE_report(op->reports, RPT_WARNING, "Selected faces must be triangles or quads"); + return OPERATOR_CANCELLED; + } + } + } + + /* mirror before smooth */ + if (((Mesh *)obedit->data)->editflag & ME_EDIT_MIRROR_X) { + EDBM_verts_mirror_cache_begin(em, TRUE); + } + + repeat = RNA_int_get(op->ptr, "repeat"); + lambda = RNA_float_get(op->ptr, "lambda"); + lambda_border = RNA_float_get(op->ptr, "lambda_border"); + usex = RNA_boolean_get(op->ptr, "use_x"); + usey = RNA_boolean_get(op->ptr, "use_y"); + usez = RNA_boolean_get(op->ptr, "use_z"); + volume_preservation = RNA_boolean_get(op->ptr, "volume_preservation"); + if (!repeat) + repeat = 1; + + for (i = 0; i < repeat; i++) { + if (!EDBM_op_callf(em, op, + "smooth_laplacian_vert verts=%hv lambda=%f lambda_border=%f use_x=%b use_y=%b use_z=%b volume_preservation=%b", + BM_ELEM_SELECT, lambda, lambda_border, usex, usey, usez, volume_preservation)) + { + return OPERATOR_CANCELLED; + } + } + + /* apply mirror */ + if (((Mesh *)obedit->data)->editflag & ME_EDIT_MIRROR_X) { + EDBM_verts_mirror_apply(em, BM_ELEM_SELECT, 0); + EDBM_verts_mirror_cache_end(em); + } + + EDBM_update_generic(C, em, TRUE); + + return OPERATOR_FINISHED; +} + +void MESH_OT_vertices_smooth_laplacian(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Laplacian Smooth Vertex"; + ot->description = "Laplacian smooth of selected vertices"; + ot->idname = "MESH_OT_vertices_smooth_laplacian"; + + /* api callbacks */ + ot->exec = edbm_do_smooth_laplacian_vertex_exec; + ot->poll = ED_operator_editmesh; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_int(ot->srna, "repeat", 1, 1, 200, + "Number of iterations to smooth the mesh", "", 1, 200); + RNA_def_float(ot->srna, "lambda", 0.00005f, 0.0000001f, 1000.0f, + "Lambda factor", "", 0.0000001f, 1000.0f); + RNA_def_float(ot->srna, "lambda_border", 0.00005f, 0.0000001f, 1000.0f, + "Lambda factor in border", "", 0.0000001f, 1000.0f); + RNA_def_boolean(ot->srna, "use_x", 1, "Smooth X Axis", "Smooth object along X axis"); + RNA_def_boolean(ot->srna, "use_y", 1, "Smooth Y Axis", "Smooth object along Y axis"); + RNA_def_boolean(ot->srna, "use_z", 1, "Smooth Z Axis", "Smooth object along Z axis"); + RNA_def_boolean(ot->srna, "volume_preservation", 1, "Preserve Volume", "Apply volume preservation after smooth"); +} + /********************** Smooth/Solid Operators *************************/ static void mesh_set_smooth_faces(BMEditMesh *em, short smooth) diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 5c782dc2266..8b56109202a 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -134,6 +134,7 @@ void MESH_OT_loop_multi_select(struct wmOperatorType *ot); void MESH_OT_mark_seam(struct wmOperatorType *ot); void MESH_OT_mark_sharp(struct wmOperatorType *ot); void MESH_OT_vertices_smooth(struct wmOperatorType *ot); +void MESH_OT_vertices_smooth_laplacian(struct wmOperatorType *ot); void MESH_OT_noise(struct wmOperatorType *ot); void MESH_OT_flip_normals(struct wmOperatorType *ot); void MESH_OT_solidify(struct wmOperatorType *ot); diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index 864db7f096d..716af1d938b 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -133,6 +133,7 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_mark_seam); WM_operatortype_append(MESH_OT_mark_sharp); WM_operatortype_append(MESH_OT_vertices_smooth); + WM_operatortype_append(MESH_OT_vertices_smooth_laplacian); WM_operatortype_append(MESH_OT_noise); WM_operatortype_append(MESH_OT_flip_normals); WM_operatortype_append(MESH_OT_rip); diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 51935b24de3..f6a167aefe6 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -1000,6 +1000,7 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto case eModifierType_Bevel: UI_icon_draw(x, y, ICON_MOD_BEVEL); break; case eModifierType_Smooth: + case eModifierType_LaplacianSmooth: UI_icon_draw(x, y, ICON_MOD_SMOOTH); break; case eModifierType_SimpleDeform: UI_icon_draw(x, y, ICON_MOD_SIMPLEDEFORM); break; diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index af8716b0fb5..c9e929ce41b 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -75,6 +75,7 @@ typedef enum ModifierType { eModifierType_DynamicPaint = 40, eModifierType_Remesh = 41, eModifierType_Skin = 42, + eModifierType_LaplacianSmooth = 43, NUM_MODIFIER_TYPES } ModifierType; @@ -1112,4 +1113,17 @@ enum { MOD_SKIN_SMOOTH_SHADING = 1 }; +/* Smooth modifier flags */ +#define MOD_LAPLACIANSMOOTH_X (1<<1) +#define MOD_LAPLACIANSMOOTH_Y (1<<2) +#define MOD_LAPLACIANSMOOTH_Z (1<<3) +#define MOD_LAPLACIANSMOOTH_VOLUME_PRESERVATION (1<<4) + +typedef struct LaplacianSmoothModifierData { + ModifierData modifier; + float lambda, lambda_border, pad1; + char defgrp_name[64]; /* MAX_VGROUP_NAME */ + short flag, repeat; +} LaplacianSmoothModifierData; + #endif diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 042acbd5b11..60ab231ffc9 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -286,6 +286,7 @@ extern StructRNA RNA_KinematicConstraint; extern StructRNA RNA_Lamp; extern StructRNA RNA_LampSkySettings; extern StructRNA RNA_LampTextureSlot; +extern StructRNA RNA_LaplacianSmoothModifier; extern StructRNA RNA_Lattice; extern StructRNA RNA_LatticeModifier; extern StructRNA RNA_LatticePoint; diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 1ec2c391efc..f94fbc8b1d6 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -84,6 +84,7 @@ EnumPropertyItem modifier_type_items[] = { {eModifierType_Curve, "CURVE", ICON_MOD_CURVE, "Curve", ""}, {eModifierType_Displace, "DISPLACE", ICON_MOD_DISPLACE, "Displace", ""}, {eModifierType_Hook, "HOOK", ICON_HOOK, "Hook", ""}, + {eModifierType_LaplacianSmooth, "LAPLACIANSMOOTH", ICON_MOD_SMOOTH, "Laplacian Smooth", ""}, {eModifierType_Lattice, "LATTICE", ICON_MOD_LATTICE, "Lattice", ""}, {eModifierType_MeshDeform, "MESH_DEFORM", ICON_MOD_MESHDEFORM, "Mesh Deform", ""}, {eModifierType_Shrinkwrap, "SHRINKWRAP", ICON_MOD_SHRINKWRAP, "Shrinkwrap", ""}, @@ -210,6 +211,8 @@ static StructRNA *rna_Modifier_refine(struct PointerRNA *ptr) return &RNA_RemeshModifier; case eModifierType_Skin: return &RNA_SkinModifier; + case eModifierType_LaplacianSmooth: + return &RNA_LaplacianSmoothModifier; default: return &RNA_Modifier; } @@ -359,6 +362,12 @@ static void rna_SmoothModifier_vgroup_set(PointerRNA *ptr, const char *value) rna_object_vgroup_name_set(ptr, value, lmd->defgrp_name, sizeof(lmd->defgrp_name)); } +static void rna_LaplacianSmoothModifier_vgroup_set(PointerRNA *ptr, const char *value) +{ + LaplacianSmoothModifierData *lmd = (LaplacianSmoothModifierData *)ptr->data; + rna_object_vgroup_name_set(ptr, value, lmd->defgrp_name, sizeof(lmd->defgrp_name)); +} + static void rna_WaveModifier_vgroup_set(PointerRNA *ptr, const char *value) { WaveModifierData *lmd = (WaveModifierData *)ptr->data; @@ -1780,6 +1789,64 @@ static void rna_def_modifier_smooth(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Modifier_update"); } +static void rna_def_modifier_laplaciansmooth(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "LaplacianSmoothModifier", "Modifier"); + RNA_def_struct_ui_text(srna, "Laplacian Smooth Modifier", "Smoothing effect modifier"); + RNA_def_struct_sdna(srna, "LaplacianSmoothModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_SMOOTH); + + prop = RNA_def_property(srna, "use_x", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_LAPLACIANSMOOTH_X); + RNA_def_property_ui_text(prop, "X", "Smooth object along X axis"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_y", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_LAPLACIANSMOOTH_Y); + RNA_def_property_ui_text(prop, "Y", "Smooth object along Y axis"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_z", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_LAPLACIANSMOOTH_Z); + RNA_def_property_ui_text(prop, "Z", "Smooth object along Z axis"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "volume_preservation", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_LAPLACIANSMOOTH_VOLUME_PRESERVATION); + RNA_def_property_ui_text(prop, "Preserve Volume", "Apply volume preservation after smooth"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "lamb", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "lambda"); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0000001, 1000.0, 0.0000001, 8); + RNA_def_property_ui_text(prop, "Lambda Factor", "Smooth factor effect"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "lambdaborder", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "lambda_border"); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0000001, 1000.0, 0.0000001, 8); + RNA_def_property_ui_text(prop, "Lambda border", "Lambda factor in border"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "iterations", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "repeat"); + RNA_def_property_ui_range(prop, 0, 200, 1, 0); + RNA_def_property_ui_text(prop, "Repeat", ""); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "defgrp_name"); + RNA_def_property_ui_text(prop, "Vertex Group", + "Name of Vertex Group which determines influence of modifier per point"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_LaplacianSmoothModifier_vgroup_set"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); +} + static void rna_def_modifier_cast(BlenderRNA *brna) { StructRNA *srna; @@ -3394,6 +3461,7 @@ void RNA_def_modifier(BlenderRNA *brna) rna_def_modifier_ocean(brna); rna_def_modifier_remesh(brna); rna_def_modifier_skin(brna); + rna_def_modifier_laplaciansmooth(brna); } #endif diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 2d07dda7895..3a7066ff41a 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -38,6 +38,7 @@ set(INC ../render/extern/include ../../../intern/elbeem/extern ../../../intern/guardedalloc + ../../../intern/opennl/extern ) set(INC_SYS @@ -64,6 +65,7 @@ set(SRC intern/MOD_fluidsim_util.c intern/MOD_hook.c intern/MOD_lattice.c + intern/MOD_laplaciansmooth.c intern/MOD_mask.c intern/MOD_meshdeform.c intern/MOD_mirror.c diff --git a/source/blender/modifiers/MOD_modifiertypes.h b/source/blender/modifiers/MOD_modifiertypes.h index 51e574e276d..a4817ff775d 100644 --- a/source/blender/modifiers/MOD_modifiertypes.h +++ b/source/blender/modifiers/MOD_modifiertypes.h @@ -75,6 +75,7 @@ extern ModifierTypeInfo modifierType_WeightVGProximity; extern ModifierTypeInfo modifierType_DynamicPaint; extern ModifierTypeInfo modifierType_Remesh; extern ModifierTypeInfo modifierType_Skin; +extern ModifierTypeInfo modifierType_LaplacianSmooth; /* MOD_util.c */ void modifier_type_init(ModifierTypeInfo *types[]); diff --git a/source/blender/modifiers/SConscript b/source/blender/modifiers/SConscript index 9059ae210e3..62fd9ba2de1 100644 --- a/source/blender/modifiers/SConscript +++ b/source/blender/modifiers/SConscript @@ -4,7 +4,7 @@ Import ('env') sources = env.Glob('intern/*.c') incs = '. ./intern' -incs += ' #/intern/guardedalloc #/intern/bsp/extern #/intern/elbeem/extern #/extern/glew/include' +incs += ' #/intern/guardedalloc #/intern/bsp/extern #/intern/elbeem/extern #/extern/glew/include #/intern/opennl/extern' incs += ' ../render/extern/include ../blenloader ../bmesh' incs += ' ../include ../blenlib ../blenfont ../makesdna ../makesrna ../blenkernel ../blenkernel/intern' incs += ' ../gpu' diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c new file mode 100644 index 00000000000..103d959dfc6 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c @@ -0,0 +1,697 @@ +/* + * ***** 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) 2005 by the Blender Foundation. + * All rights reserved. + * + * Contributor(s): Alexander Pinzon + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_laplaciansmooth.c + * \ingroup modifiers + */ + + +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" + +#include "BLI_math.h" +#include "BLI_utildefines.h" +#include "BLI_string.h" + +#include "MEM_guardedalloc.h" + +#include "BKE_cdderivedmesh.h" +#include "BKE_deform.h" +#include "BKE_displist.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_object.h" +#include "BKE_particle.h" +#include "BKE_tessmesh.h" + +#include "MOD_modifiertypes.h" +#include "MOD_util.h" + +#include "ONL_opennl.h" + +#define MOD_LAPLACIANSMOOTH_MAX_EDGE_PERCENTAGE 1.8 +#define MOD_LAPLACIANSMOOTH_MIN_EDGE_PERCENTAGE 0.02 + +struct BLaplacianSystem { + float *eweights; /* Length weights per Edge */ + float (*fweights)[3]; /* Cotangent weights per face */ + float *ring_areas; /* Total area per ring*/ + float *vlengths; /* Total sum of lengths(edges) per vertice*/ + float *vweights; /* Total sum of weights per vertice*/ + int numEdges; /* Number of edges*/ + int numFaces; /* Number of faces*/ + int numVerts; /* Number of verts*/ + short *numNeFa; /* Number of neighboors faces around vertice*/ + short *numNeEd; /* Number of neighboors Edges around vertice*/ + short *zerola; /* Is zero area or length*/ + + /* Pointers to data*/ + float (*vertexCos)[3]; + MFace *mfaces; + MEdge *medges; + NLContext *context; + + /*Data*/ + float min_area; + float vert_centroid[3]; +}; +typedef struct BLaplacianSystem LaplacianSystem; + +static CustomDataMask required_data_mask(Object *UNUSED(ob), ModifierData *md); +static int is_disabled(ModifierData *md, int UNUSED(useRenderParams)); +static float compute_volume(float (*vertexCos)[3], MFace *mfaces, int numFaces); +static float cotan_weight(float *v1, float *v2, float *v3); +static LaplacianSystem * init_laplacian_system( int a_numEdges, int a_numFaces, int a_numVerts); +static void copy_data(ModifierData *md, ModifierData *target); +static void delete_laplacian_system(LaplacianSystem * sys); +static void delete_void_pointer(void * data); +static void fill_laplacian_matrix(LaplacianSystem * sys); +static void init_data(ModifierData *md); +static void init_laplacian_matrix(LaplacianSystem * sys); +static void memset_laplacian_system(LaplacianSystem *sys, int val); +static void volume_preservation(LaplacianSystem *sys, float vini, float vend, short flag); +static void validate_solution(LaplacianSystem * sys, short flag, float lambda, float lambda_border); + +static void delete_void_pointer(void * data) +{ + if (data) { + MEM_freeN(data); + data = NULL; + } +} + +static void delete_laplacian_system(LaplacianSystem * sys) +{ + delete_void_pointer(sys->eweights); + delete_void_pointer(sys->fweights); + delete_void_pointer(sys->numNeEd); + delete_void_pointer(sys->numNeFa); + delete_void_pointer(sys->ring_areas); + delete_void_pointer(sys->vlengths); + delete_void_pointer(sys->vweights); + delete_void_pointer(sys->zerola); + if (sys->context) { + nlDeleteContext(sys->context); + } + sys->vertexCos = NULL; + sys->mfaces = NULL; + sys->medges = NULL; + MEM_freeN(sys); +} + +static void memset_laplacian_system(LaplacianSystem *sys, int val) +{ + memset(sys->eweights , val, sizeof(float) * sys->numEdges); + memset(sys->fweights , val, sizeof(float) * sys->numFaces * 3); + memset(sys->numNeEd , val, sizeof(short) * sys->numVerts); + memset(sys->numNeFa , val, sizeof(short) * sys->numVerts); + memset(sys->ring_areas , val, sizeof(float) * sys->numVerts); + memset(sys->vlengths , val, sizeof(float) * sys->numVerts); + memset(sys->vweights , val, sizeof(float) * sys->numVerts); + memset(sys->zerola , val, sizeof(short) * sys->numVerts); +} + +static LaplacianSystem * init_laplacian_system( int a_numEdges, int a_numFaces, int a_numVerts) +{ + LaplacianSystem * sys; + sys = MEM_callocN(sizeof(LaplacianSystem), "ModLaplSmoothSystem"); + sys->numEdges = a_numEdges; + sys->numFaces = a_numFaces; + sys->numVerts = a_numVerts; + + sys->eweights = MEM_callocN(sizeof(float) * sys->numEdges, "ModLaplSmoothEWeight"); + if (!sys->eweights) { + delete_laplacian_system(sys); + return NULL; + } + + sys->fweights = MEM_callocN(sizeof(float) * 3 * sys->numFaces, "ModLaplSmoothFWeight"); + if (!sys->fweights) { + delete_laplacian_system(sys); + return NULL; + } + + sys->numNeEd = MEM_callocN(sizeof(short) * sys->numVerts, "ModLaplSmoothNumNeEd"); + if (!sys->numNeEd) { + delete_laplacian_system(sys); + return NULL; + } + + sys->numNeFa = MEM_callocN(sizeof(short) * sys->numVerts, "ModLaplSmoothNumNeFa"); + if (!sys->numNeFa) { + delete_laplacian_system(sys); + return NULL; + } + + sys->ring_areas = MEM_callocN(sizeof(float) * sys->numVerts, "ModLaplSmoothRingAreas"); + if (!sys->ring_areas) { + delete_laplacian_system(sys); + return NULL; + } + + sys->vlengths = MEM_callocN(sizeof(float) * sys->numVerts, "ModLaplSmoothVlengths"); + if (!sys->vlengths) { + delete_laplacian_system(sys); + return NULL; + } + + sys->vweights = MEM_callocN(sizeof(float) * sys->numVerts, "ModLaplSmoothVweights"); + if (!sys->vweights) { + delete_laplacian_system(sys); + return NULL; + } + + sys->zerola = MEM_callocN(sizeof(short) * sys->numVerts, "ModLaplSmoothZeloa"); + if (!sys->zerola) { + delete_laplacian_system(sys); + return NULL; + } + + return sys; +} + +static void init_data(ModifierData *md) +{ + LaplacianSmoothModifierData *smd = (LaplacianSmoothModifierData *) md; + smd->lambda = 0.00001f; + smd->lambda_border = 0.00005f; + smd->repeat = 1; + smd->flag = MOD_LAPLACIANSMOOTH_X | MOD_LAPLACIANSMOOTH_Y | MOD_LAPLACIANSMOOTH_Z | MOD_LAPLACIANSMOOTH_VOLUME_PRESERVATION; + smd->defgrp_name[0] = '\0'; +} + +static void copy_data(ModifierData *md, ModifierData *target) +{ + LaplacianSmoothModifierData *smd = (LaplacianSmoothModifierData *) md; + LaplacianSmoothModifierData *tsmd = (LaplacianSmoothModifierData *) target; + + tsmd->lambda = smd->lambda; + tsmd->lambda_border = smd->lambda_border; + tsmd->repeat = smd->repeat; + tsmd->flag = smd->flag; + BLI_strncpy(tsmd->defgrp_name, smd->defgrp_name, sizeof(tsmd->defgrp_name)); +} + +static int is_disabled(ModifierData *md, int UNUSED(useRenderParams)) +{ + LaplacianSmoothModifierData *smd = (LaplacianSmoothModifierData *) md; + short flag; + + flag = smd->flag & (MOD_LAPLACIANSMOOTH_X | MOD_LAPLACIANSMOOTH_Y | MOD_LAPLACIANSMOOTH_Z); + + /* disable if modifier is off for X, Y and Z or if factor is 0 */ + if ( flag == 0) return 1; + + return 0; +} + +static CustomDataMask required_data_mask(Object *UNUSED(ob), ModifierData *md) +{ + LaplacianSmoothModifierData *smd = (LaplacianSmoothModifierData *)md; + CustomDataMask dataMask = 0; + + /* ask for vertexgroups if we need them */ + if (smd->defgrp_name[0]) dataMask |= CD_MASK_MDEFORMVERT; + + return dataMask; +} + +static float cotan_weight(float *v1, float *v2, float *v3) +{ + 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 == 0.0f) + return 0.0f; + + return dot_v3v3(a, b) / clen; +} + +static float compute_volume(float (*vertexCos)[3], MFace *mfaces, int numFaces) +{ + float vol = 0.0f; + float x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4; + int i; + float *vf[4]; + for (i = 0; i<numFaces; i++) { + vf[0] = vertexCos[mfaces[i].v1]; + vf[1] = vertexCos[mfaces[i].v2]; + vf[2] = vertexCos[mfaces[i].v3]; + + x1 = vf[0][0]; + y1 = vf[0][1]; + z1 = vf[0][2]; + + x2 = vf[1][0]; + y2 = vf[1][1]; + z2 = vf[1][2]; + + x3 = vf[2][0]; + y3 = vf[2][1]; + z3 = vf[2][2]; + + + vol += (1.0 / 6.0) * (x2*y3*z1 + x3*y1*z2 - x1*y3*z2 - x2*y1*z3 + x1*y2*z3 - x3*y2*z1); + if ((&mfaces[i])->v4) { + vf[3] = vertexCos[mfaces[i].v4]; + x4 = vf[3][0]; + y4 = vf[3][1]; + z4 = vf[3][2]; + vol += (1.0 / 6.0) * (x1*y3*z4 - x1*y4*z3 - x3*y1*z4 + x3*z1*y4 + y1*x4*z3 - x4*y3*z1); + } + } + return fabs(vol); +} + +static void volume_preservation(LaplacianSystem *sys, float vini, float vend, short flag) +{ + float beta; + int i; + + if (vend != 0.0f) { + beta = pow (vini / vend, 1.0f / 3.0f); + for (i = 0; i < sys->numVerts; i++) { + if (flag & MOD_LAPLACIANSMOOTH_X) { + sys->vertexCos[i][0] = (sys->vertexCos[i][0] - sys->vert_centroid[0])* beta + sys->vert_centroid[0]; + } + if (flag & MOD_LAPLACIANSMOOTH_Y) { + sys->vertexCos[i][1] = (sys->vertexCos[i][1] - sys->vert_centroid[1])* beta + sys->vert_centroid[1]; + } + if (flag & MOD_LAPLACIANSMOOTH_Z) { + sys->vertexCos[i][2] = (sys->vertexCos[i][2] - sys->vert_centroid[2])* beta + sys->vert_centroid[2]; + } + + } + } +} + +static void init_laplacian_matrix(LaplacianSystem * sys) +{ + float *v1, *v2, *v3, *v4; + float w1, w2, w3, w4; + float areaf; + int i, j; + unsigned int idv1, idv2, idv3, idv4, idv[4]; + int has_4_vert ; + for ( i = 0; i < sys->numEdges; i++) { + idv1 = sys->medges[i].v1; + idv2 = sys->medges[i].v2; + + v1 = sys->vertexCos[idv1]; + v2 = sys->vertexCos[idv2]; + + sys->numNeEd[idv1] = sys->numNeEd[idv1] + 1; + sys->numNeEd[idv2] = sys->numNeEd[idv2] + 1; + w1 = len_v3v3(v1, v2); + if (w1 < sys->min_area) { + sys->zerola[idv1] = 1; + sys->zerola[idv2] = 1; + } else { + w1 = 1.0f / w1; + } + + sys->eweights[i] = w1; + } + for ( i = 0; i < sys->numFaces; i++) { + has_4_vert = ((&sys->mfaces[i])->v4) ? 1 : 0; + + idv1 = sys->mfaces[i].v1; + idv2 = sys->mfaces[i].v2; + idv3 = sys->mfaces[i].v3; + idv4 = has_4_vert ? sys->mfaces[i].v4 : 0; + + sys->numNeFa[idv1] += 1; + sys->numNeFa[idv2] += 1; + sys->numNeFa[idv3] += 1; + if (has_4_vert) sys->numNeFa[idv4] += 1; + + v1 = sys->vertexCos[idv1]; + v2 = sys->vertexCos[idv2]; + v3 = sys->vertexCos[idv3]; + v4 = has_4_vert ? sys->vertexCos[idv4] : 0; + + if (has_4_vert) { + areaf = area_quad_v3(v1, v2, v3, sys->vertexCos[sys->mfaces[i].v4]); + } else { + areaf = area_tri_v3(v1, v2, v3); + } + if (fabs(areaf) < sys->min_area) { + sys->zerola[idv1] = 1; + sys->zerola[idv2] = 1; + sys->zerola[idv3] = 1; + if (has_4_vert) sys->zerola[idv4] = 1; + } + + sys->ring_areas[idv1] += areaf; + sys->ring_areas[idv2] += areaf; + sys->ring_areas[idv3] += areaf; + if (has_4_vert) sys->ring_areas[idv4] += areaf; + + if (has_4_vert) { + + idv[0] = idv1; + idv[1] = idv2; + idv[2] = idv3; + idv[3] = idv4; + + for (j = 0; j < 4; j++) { + idv1 = idv[j]; + idv2 = idv[(j + 1) % 4]; + idv3 = idv[(j + 2) % 4]; + idv4 = idv[(j + 3) % 4]; + + v1 = sys->vertexCos[idv1]; + v2 = sys->vertexCos[idv2]; + v3 = sys->vertexCos[idv3]; + v4 = sys->vertexCos[idv4]; + + w2 = cotan_weight(v4, v1, v2) + cotan_weight(v3, v1, v2); + w3 = cotan_weight(v2, v3, v1) + cotan_weight(v4, v1, v3); + w4 = cotan_weight(v2, v4, v1) + cotan_weight(v3, v4, v1); + + sys->vweights[idv1] += (w2 + w3 + w4) / 4.0f; + } + } else { + w1 = cotan_weight(v1, v2, v3); + w2 = cotan_weight(v2, v3, v1); + w3 = cotan_weight(v3, v1, v2); + + sys->fweights[i][0] = sys->fweights[i][0] + w1; + sys->fweights[i][1] = sys->fweights[i][1] + w2; + sys->fweights[i][2] = sys->fweights[i][2] + w3; + + sys->vweights[idv1] = sys->vweights[idv1] + w2 + w3; + sys->vweights[idv2] = sys->vweights[idv2] + w1 + w3; + sys->vweights[idv3] = sys->vweights[idv3] + w1 + w2; + } + } + for ( i = 0; i < sys->numEdges; i++) { + idv1 = sys->medges[i].v1; + idv2 = sys->medges[i].v2; + /* if is boundary, apply scale-dependent umbrella operator only with neighboors in boundary */ + if (sys->numNeEd[idv1] != sys->numNeFa[idv1] && sys->numNeEd[idv2] != sys->numNeFa[idv2]) { + sys->vlengths[idv1] += sys->eweights[i]; + sys->vlengths[idv2] += sys->eweights[i]; + } + } + +} + +static void fill_laplacian_matrix(LaplacianSystem * sys) +{ + float *v1, *v2, *v3, *v4; + float w2, w3, w4; + int i, j; + int has_4_vert ; + unsigned int idv1, idv2, idv3, idv4, idv[4]; + + for ( i = 0; i < sys->numFaces; i++) { + idv1 = sys->mfaces[i].v1; + idv2 = sys->mfaces[i].v2; + idv3 = sys->mfaces[i].v3; + has_4_vert = ((&sys->mfaces[i])->v4) ? 1 : 0; + + if (has_4_vert) { + idv[0] = sys->mfaces[i].v1; + idv[1] = sys->mfaces[i].v2; + idv[2] = sys->mfaces[i].v3; + idv[3] = sys->mfaces[i].v4; + for (j = 0; j < 4; j++) { + idv1 = idv[j]; + idv2 = idv[(j + 1) % 4]; + idv3 = idv[(j + 2) % 4]; + idv4 = idv[(j + 3) % 4]; + + v1 = sys->vertexCos[idv1]; + v2 = sys->vertexCos[idv2]; + v3 = sys->vertexCos[idv3]; + v4 = sys->vertexCos[idv4]; + + w2 = cotan_weight(v4, v1, v2) + cotan_weight(v3, v1, v2); + w3 = cotan_weight(v2, v3, v1) + cotan_weight(v4, v1, v3); + w4 = cotan_weight(v2, v4, v1) + cotan_weight(v3, v4, v1); + + w2 = w2 / 4.0f; + w3 = w3 / 4.0f; + w4 = w4 / 4.0f; + + if (sys->numNeEd[idv1] == sys->numNeFa[idv1] && sys->zerola[idv1] == 0) { + nlMatrixAdd(idv1, idv2, w2 * sys->vweights[idv1]); + nlMatrixAdd(idv1, idv3, w3 * sys->vweights[idv1]); + nlMatrixAdd(idv1, idv4, w4 * sys->vweights[idv1]); + } + } + } else { + /* Is ring if number of faces == number of edges around vertice*/ + if (sys->numNeEd[idv1] == sys->numNeFa[idv1] && sys->zerola[idv1] == 0) { + nlMatrixAdd(idv1, idv2, sys->fweights[i][2] * sys->vweights[idv1]); + nlMatrixAdd(idv1, idv3, sys->fweights[i][1] * sys->vweights[idv1]); + } + if (sys->numNeEd[idv2] == sys->numNeFa[idv2] && sys->zerola[idv2] == 0) { + nlMatrixAdd(idv2, idv1, sys->fweights[i][2] * sys->vweights[idv2]); + nlMatrixAdd(idv2, idv3, sys->fweights[i][0] * sys->vweights[idv2]); + } + if (sys->numNeEd[idv3] == sys->numNeFa[idv3] && sys->zerola[idv3] == 0) { + nlMatrixAdd(idv3, idv1, sys->fweights[i][1] * sys->vweights[idv3]); + nlMatrixAdd(idv3, idv2, sys->fweights[i][0] * sys->vweights[idv3]); + } + } + } + + for ( i = 0; i < sys->numEdges; i++) { + idv1 = sys->medges[i].v1; + idv2 = sys->medges[i].v2; + /* Is boundary */ + if (sys->numNeEd[idv1] != sys->numNeFa[idv1] && + sys->numNeEd[idv2] != sys->numNeFa[idv2] && + sys->zerola[idv1] == 0 && + sys->zerola[idv2] == 0) + { + nlMatrixAdd(idv1, idv2, sys->eweights[i] * sys->vlengths[idv1]); + nlMatrixAdd(idv2, idv1, sys->eweights[i] * sys->vlengths[idv2]); + } + } +} + +static void validate_solution(LaplacianSystem * sys, short flag, float lambda, float lambda_border) +{ + int i, idv1, idv2; + float leni, lene; + float vini, vend; + float *vi1, *vi2, ve1[3], ve2[3]; + if (flag & MOD_LAPLACIANSMOOTH_VOLUME_PRESERVATION) { + vini = compute_volume(sys->vertexCos, sys->mfaces, sys->numFaces); + } + for ( i = 0; i < sys->numEdges; i++) { + idv1 = sys->medges[i].v1; + idv2 = sys->medges[i].v2; + vi1 = sys->vertexCos[idv1]; + vi2 = sys->vertexCos[idv2]; + ve1[0] = nlGetVariable(0, idv1); + ve1[1] = nlGetVariable(1, idv1); + ve1[2] = nlGetVariable(2, idv1); + ve2[0] = nlGetVariable(0, idv2); + ve2[1] = nlGetVariable(1, idv2); + ve2[2] = nlGetVariable(2, idv2); + leni = len_v3v3(vi1, vi2); + lene = len_v3v3(ve1, ve2); + if ( lene > leni*MOD_LAPLACIANSMOOTH_MAX_EDGE_PERCENTAGE || lene < leni*MOD_LAPLACIANSMOOTH_MIN_EDGE_PERCENTAGE ){ + sys->zerola[idv1] = 1; + sys->zerola[idv2] = 1; + } + } + for (i = 0; i < sys->numVerts; i++) { + if (sys->zerola[i] == 0) { + if (flag & MOD_LAPLACIANSMOOTH_X) { + sys->vertexCos[i][0] = nlGetVariable(0, i); + } + if (flag & MOD_LAPLACIANSMOOTH_Y) { + sys->vertexCos[i][1] = nlGetVariable(1, i); + } + if (flag & MOD_LAPLACIANSMOOTH_Z) { + sys->vertexCos[i][2] = nlGetVariable(2, i); + } + } + } + if (flag & MOD_LAPLACIANSMOOTH_VOLUME_PRESERVATION) { + vend = compute_volume(sys->vertexCos, sys->mfaces, sys->numFaces); + volume_preservation(sys, vini, vend, flag); + } +} + +static void laplaciansmoothModifier_do( + LaplacianSmoothModifierData *smd, Object *ob, DerivedMesh *dm, + float (*vertexCos)[3], int numVerts) +{ + LaplacianSystem *sys; + MDeformVert *dvert = NULL; + MDeformVert *dv = NULL; + float w, wpaint; + int i, iter; + int defgrp_index; + + DM_ensure_tessface(dm); + + sys = init_laplacian_system(dm->getNumEdges(dm), dm->getNumTessFaces(dm), numVerts); + if(!sys) return; + + sys->mfaces = dm->getTessFaceArray(dm); + sys->medges = dm->getEdgeArray(dm); + sys->vertexCos = vertexCos; + sys->min_area = 0.00001f; + modifier_get_vgroup(ob, dm, smd->defgrp_name, &dvert, &defgrp_index); + + sys->vert_centroid[0] = 0.0f; + sys->vert_centroid[1] = 0.0f; + sys->vert_centroid[2] = 0.0f; + for (iter = 0; iter < smd->repeat; iter++) { + memset_laplacian_system(sys, 0); + nlNewContext(); + sys->context = nlGetCurrent(); + nlSolverParameteri(NL_NB_VARIABLES, numVerts); + nlSolverParameteri(NL_LEAST_SQUARES, NL_TRUE); + nlSolverParameteri(NL_NB_ROWS, numVerts); + nlSolverParameteri(NL_NB_RIGHT_HAND_SIDES, 3); + + init_laplacian_matrix(sys); + + nlBegin(NL_SYSTEM); + for (i = 0; i < numVerts; i++) { + nlSetVariable(0, i, vertexCos[i][0]); + nlSetVariable(1, i, vertexCos[i][1]); + nlSetVariable(2, i, vertexCos[i][2]); + if (iter == 0) { + sys->vert_centroid[0] += vertexCos[i][0]; + sys->vert_centroid[1] += vertexCos[i][1]; + sys->vert_centroid[2] += vertexCos[i][2]; + } + } + if (iter == 0 && numVerts >0) { + sys->vert_centroid[0] = sys->vert_centroid[0] / numVerts; + sys->vert_centroid[1] = sys->vert_centroid[1] / numVerts; + sys->vert_centroid[2] = sys->vert_centroid[2] / numVerts; + } + + nlBegin(NL_MATRIX); + dv = dvert; + for (i = 0; i < numVerts; i++) { + nlRightHandSideAdd(0, i, vertexCos[i][0]); + nlRightHandSideAdd(1, i, vertexCos[i][1]); + nlRightHandSideAdd(2, i, vertexCos[i][2]); + if (dv) { + wpaint = defvert_find_weight(dv, defgrp_index); + dv++; + } else { + wpaint = 1.0f; + } + if (sys->zerola[i] == 0) { + w = sys->vweights[i] * sys->ring_areas[i]; + sys->vweights[i] = (w == 0.0f) ? 0.0f : - smd->lambda * wpaint / (4.0f * w); + w = sys->vlengths[i]; + sys->vlengths[i] = (w == 0.0f) ? 0.0f : - smd->lambda_border * wpaint * 2.0f / w; + + if (sys->numNeEd[i] == sys->numNeFa[i]) { + nlMatrixAdd(i, i, 1.0f + smd->lambda * wpaint / (4.0f * sys->ring_areas[i])); + } else { + nlMatrixAdd(i, i, 1.0f + smd->lambda_border * wpaint * 2.0f); + } + } else { + nlMatrixAdd(i, i, 1.0f); + } + } + + fill_laplacian_matrix(sys); + + nlEnd(NL_MATRIX); + nlEnd(NL_SYSTEM); + + if (nlSolveAdvanced(NULL, NL_TRUE)) { + validate_solution(sys, smd->flag, smd->lambda, smd->lambda_border); + } + nlDeleteContext(sys->context); + sys->context = NULL; + } + delete_laplacian_system(sys); + +} + +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, 0); + + laplaciansmoothModifier_do((LaplacianSmoothModifierData *)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, 0); + + laplaciansmoothModifier_do((LaplacianSmoothModifierData *)md, ob, dm, + vertexCos, numVerts); + + if (dm != derivedData) + dm->release(dm); +} + + +ModifierTypeInfo modifierType_LaplacianSmooth = { + /* name */ "Laplacian Smooth", + /* structName */ "LaplacianSmoothModifierData", + /* structSize */ sizeof(LaplacianSmoothModifierData), + /* type */ eModifierTypeType_OnlyDeform, + /* flags */ eModifierTypeFlag_AcceptsMesh | + eModifierTypeFlag_SupportsEditmode, + + /* copy_data */ copy_data, + /* deformVerts */ deformVerts, + /* deformMatrices */ NULL, + /* deformVertsEM */ deformVertsEM, + /* deformMatricesEM */ NULL, + /* applyModifier */ NULL, + /* applyModifierEM */ NULL, + /* init_data */ init_data, + /* required_data_mask */ required_data_mask, + /* freeData */ NULL, + /* is_disabled */ is_disabled, + /* updateDepgraph */ NULL, + /* dependsOnTime */ NULL, + /* dependsOnNormals */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index 6443a88a222..3b769a30994 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -277,5 +277,6 @@ void modifier_type_init(ModifierTypeInfo *types[]) INIT_TYPE(DynamicPaint); INIT_TYPE(Remesh); INIT_TYPE(Skin); + INIT_TYPE(LaplacianSmooth); #undef INIT_TYPE } |