Blender V4.3
bmo_smooth_laplacian.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#include "MEM_guardedalloc.h"
12
13#include "BLI_math_geom.h"
14#include "BLI_math_vector.h"
15
16#include "eigen_capi.h"
17
18#include "bmesh.hh"
19
20#include "intern/bmesh_operators_private.hh" /* own include */
21
22// #define SMOOTH_LAPLACIAN_AREA_FACTOR 4.0f /* UNUSED */
23// #define SMOOTH_LAPLACIAN_EDGE_FACTOR 2.0f /* UNUSED */
24#define SMOOTH_LAPLACIAN_MAX_EDGE_PERCENTAGE 1.8f
25#define SMOOTH_LAPLACIAN_MIN_EDGE_PERCENTAGE 0.15f
26
28 float *eweights; /* Length weights per Edge. */
29 float (*fweights)[3]; /* Cotangent weights per loop. */
30 float *ring_areas; /* Total area per ring. */
31 float *vlengths; /* Total sum of lengths(edges) per vertex. */
32 float *vweights; /* Total sum of weights per vertex. */
33 int numEdges; /* Number of edges. */
34 int numLoops; /* Number of loops. */
35 int numVerts; /* Number of verts. */
36 bool *zerola; /* Is zero area or length. */
37
38 /* Pointers to data. */
42
43 /* Data. */
44 float min_area;
45};
47
48static bool vert_is_boundary(BMVert *v);
49static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numLoops, int a_numVerts);
52static void delete_void_pointer(void *data);
54static void memset_laplacian_system(LaplacianSystem *sys, int val);
55static void validate_solution(
56 LaplacianSystem *sys, int usex, int usey, int usez, int preserve_volume);
57static void volume_preservation(
58 BMOperator *op, float vini, float vend, int usex, int usey, int usez);
59
60static void delete_void_pointer(void *data)
61{
62 if (data) {
63 MEM_freeN(data);
64 }
65}
66
68{
75 if (sys->context) {
77 }
78 sys->bm = nullptr;
79 sys->op = nullptr;
80 MEM_freeN(sys);
81}
82
83static void memset_laplacian_system(LaplacianSystem *sys, int val)
84{
85 memset(sys->eweights, val, sizeof(float) * sys->numEdges);
86 memset(sys->fweights, val, sizeof(float[3]) * sys->numLoops);
87 memset(sys->ring_areas, val, sizeof(float) * sys->numVerts);
88 memset(sys->vlengths, val, sizeof(float) * sys->numVerts);
89 memset(sys->vweights, val, sizeof(float) * sys->numVerts);
90 memset(sys->zerola, val, sizeof(bool) * sys->numVerts);
91}
92
93static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numLoops, int a_numVerts)
94{
95 LaplacianSystem *sys;
96 sys = static_cast<LaplacianSystem *>(
97 MEM_callocN(sizeof(LaplacianSystem), "ModLaplSmoothSystem"));
98 sys->numEdges = a_numEdges;
99 sys->numLoops = a_numLoops;
100 sys->numVerts = a_numVerts;
101
102 sys->eweights = static_cast<float *>(
103 MEM_callocN(sizeof(float) * sys->numEdges, "ModLaplSmoothEWeight"));
104 if (!sys->eweights) {
106 return nullptr;
107 }
108
109 sys->fweights = static_cast<float(*)[3]>(
110 MEM_callocN(sizeof(float[3]) * sys->numLoops, "ModLaplSmoothFWeight"));
111 if (!sys->fweights) {
113 return nullptr;
114 }
115
116 sys->ring_areas = static_cast<float *>(
117 MEM_callocN(sizeof(float) * sys->numVerts, "ModLaplSmoothRingAreas"));
118 if (!sys->ring_areas) {
120 return nullptr;
121 }
122
123 sys->vlengths = static_cast<float *>(
124 MEM_callocN(sizeof(float) * sys->numVerts, "ModLaplSmoothVlengths"));
125 if (!sys->vlengths) {
127 return nullptr;
128 }
129
130 sys->vweights = static_cast<float *>(
131 MEM_callocN(sizeof(float) * sys->numVerts, "ModLaplSmoothVweights"));
132 if (!sys->vweights) {
134 return nullptr;
135 }
136
137 sys->zerola = static_cast<bool *>(
138 MEM_callocN(sizeof(bool) * sys->numVerts, "ModLaplSmoothZeloa"));
139 if (!sys->zerola) {
141 return nullptr;
142 }
143
144 return sys;
145}
146
164{
165 BMEdge *e;
166 BMFace *f;
167 BMIter eiter;
168 BMIter fiter;
169 uint i;
170
171 BM_ITER_MESH_INDEX (e, &eiter, sys->bm, BM_EDGES_OF_MESH, i) {
173 continue;
174 }
175
176 const float *v1 = e->v1->co;
177 const float *v2 = e->v2->co;
178 const int idv1 = BM_elem_index_get(e->v1);
179 const int idv2 = BM_elem_index_get(e->v2);
180
181 float w1 = len_v3v3(v1, v2);
182 if (w1 > sys->min_area) {
183 w1 = 1.0f / w1;
184 sys->eweights[i] = w1;
185 sys->vlengths[idv1] += w1;
186 sys->vlengths[idv2] += w1;
187 }
188 else {
189 sys->zerola[idv1] = true;
190 sys->zerola[idv2] = true;
191 }
192 }
193
194 uint l_curr_index = 0;
195
196 BM_ITER_MESH (f, &fiter, sys->bm, BM_FACES_OF_MESH) {
198 l_curr_index += f->len;
199 continue;
200 }
201
202 BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
203 BMLoop *l_iter;
204
205 l_iter = l_first;
206 do {
207 const int vi_prev = BM_elem_index_get(l_iter->prev->v);
208 const int vi_curr = BM_elem_index_get(l_iter->v);
209 const int vi_next = BM_elem_index_get(l_iter->next->v);
210
211 const float *co_prev = l_iter->prev->v->co;
212 const float *co_curr = l_iter->v->co;
213 const float *co_next = l_iter->next->v->co;
214
215 const float areaf = area_tri_v3(co_prev, co_curr, co_next);
216
217 if (areaf < sys->min_area) {
218 sys->zerola[vi_curr] = true;
219 }
220
221 sys->ring_areas[vi_prev] += areaf;
222 sys->ring_areas[vi_curr] += areaf;
223 sys->ring_areas[vi_next] += areaf;
224
225 const float w1 = cotangent_tri_weight_v3(co_curr, co_next, co_prev) / 2.0f;
226 const float w2 = cotangent_tri_weight_v3(co_next, co_prev, co_curr) / 2.0f;
227 const float w3 = cotangent_tri_weight_v3(co_prev, co_curr, co_next) / 2.0f;
228
229 sys->fweights[l_curr_index][0] += w1;
230 sys->fweights[l_curr_index][1] += w2;
231 sys->fweights[l_curr_index][2] += w3;
232
233 sys->vweights[vi_prev] += w1 + w2;
234 sys->vweights[vi_curr] += w2 + w3;
235 sys->vweights[vi_next] += w1 + w3;
236 } while ((void)(l_curr_index += 1), (l_iter = l_iter->next) != l_first);
237 }
238}
239
241{
242 BMEdge *e;
243 BMFace *f;
244 BMIter eiter;
245 BMIter fiter;
246 int i;
247
248 uint l_curr_index = 0;
249
250 BM_ITER_MESH (f, &fiter, sys->bm, BM_FACES_OF_MESH) {
252 l_curr_index += f->len;
253 continue;
254 }
255
256 BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
257 BMLoop *l_iter = l_first;
258
259 int vi_prev = BM_elem_index_get(l_iter->prev->v);
260 int vi_curr = BM_elem_index_get(l_iter->v);
261
262 bool ok_prev = (sys->zerola[vi_prev] == false) && !vert_is_boundary(l_iter->prev->v);
263 bool ok_curr = (sys->zerola[vi_curr] == false) && !vert_is_boundary(l_iter->v);
264
265 do {
266 const int vi_next = BM_elem_index_get(l_iter->next->v);
267 const bool ok_next = (sys->zerola[vi_next] == false) && !vert_is_boundary(l_iter->next->v);
268
269 if (ok_prev) {
271 vi_prev,
272 vi_curr,
273 sys->fweights[l_curr_index][1] * sys->vweights[vi_prev]);
275 vi_prev,
276 vi_next,
277 sys->fweights[l_curr_index][0] * sys->vweights[vi_prev]);
278 }
279 if (ok_curr) {
281 vi_curr,
282 vi_next,
283 sys->fweights[l_curr_index][2] * sys->vweights[vi_curr]);
285 vi_curr,
286 vi_prev,
287 sys->fweights[l_curr_index][1] * sys->vweights[vi_curr]);
288 }
289 if (ok_next) {
291 vi_next,
292 vi_curr,
293 sys->fweights[l_curr_index][2] * sys->vweights[vi_next]);
295 vi_next,
296 vi_prev,
297 sys->fweights[l_curr_index][0] * sys->vweights[vi_next]);
298 }
299
300 vi_prev = vi_curr;
301 vi_curr = vi_next;
302
303 ok_prev = ok_curr;
304 ok_curr = ok_next;
305
306 } while ((void)(l_curr_index += 1), (l_iter = l_iter->next) != l_first);
307 }
308 BM_ITER_MESH_INDEX (e, &eiter, sys->bm, BM_EDGES_OF_MESH, i) {
310 continue;
311 }
312 const uint idv1 = BM_elem_index_get(e->v1);
313 const uint idv2 = BM_elem_index_get(e->v2);
314 if (sys->zerola[idv1] == false && sys->zerola[idv2] == false) {
316 sys->context, idv1, idv2, sys->eweights[i] * sys->vlengths[idv1]);
318 sys->context, idv2, idv1, sys->eweights[i] * sys->vlengths[idv2]);
319 }
320 }
321}
322
324{
325 BMEdge *ed;
326 BMFace *f;
327 BMIter ei;
328 BMIter fi;
329 BM_ITER_ELEM (ed, &ei, v, BM_EDGES_OF_VERT) {
330 if (BM_edge_is_boundary(ed)) {
331 return true;
332 }
333 }
334 BM_ITER_ELEM (f, &fi, v, BM_FACES_OF_VERT) {
336 return true;
337 }
338 }
339 return false;
340}
341
343 BMOperator *op, float vini, float vend, int usex, int usey, int usez)
344{
345 float beta;
346 BMOIter siter;
347 BMVert *v;
348
349 if (vend != 0.0f) {
350 beta = pow(vini / vend, 1.0f / 3.0f);
351 BMO_ITER (v, &siter, op->slots_in, "verts", BM_VERT) {
352 if (usex) {
353 v->co[0] *= beta;
354 }
355 if (usey) {
356 v->co[1] *= beta;
357 }
358 if (usez) {
359 v->co[2] *= beta;
360 }
361 }
362 }
363}
364
366 LaplacianSystem *sys, int usex, int usey, int usez, int preserve_volume)
367{
368 int m_vertex_id;
369 float leni, lene;
370 float vini, vend;
371 float *vi1, *vi2, ve1[3], ve2[3];
372 uint idv1, idv2;
373 BMOIter siter;
374 BMVert *v;
375 BMEdge *e;
376 BMIter eiter;
377
378 BM_ITER_MESH (e, &eiter, sys->bm, BM_EDGES_OF_MESH) {
379 idv1 = BM_elem_index_get(e->v1);
380 idv2 = BM_elem_index_get(e->v2);
381 vi1 = e->v1->co;
382 vi2 = e->v2->co;
383 ve1[0] = EIG_linear_solver_variable_get(sys->context, 0, idv1);
384 ve1[1] = EIG_linear_solver_variable_get(sys->context, 1, idv1);
385 ve1[2] = EIG_linear_solver_variable_get(sys->context, 2, idv1);
386 ve2[0] = EIG_linear_solver_variable_get(sys->context, 0, idv2);
387 ve2[1] = EIG_linear_solver_variable_get(sys->context, 1, idv2);
388 ve2[2] = EIG_linear_solver_variable_get(sys->context, 2, idv2);
389 leni = len_v3v3(vi1, vi2);
390 lene = len_v3v3(ve1, ve2);
391 if (lene > leni * SMOOTH_LAPLACIAN_MAX_EDGE_PERCENTAGE ||
393 {
394 sys->zerola[idv1] = true;
395 sys->zerola[idv2] = true;
396 }
397 }
398
399 if (preserve_volume) {
400 vini = BM_mesh_calc_volume(sys->bm, false);
401 }
402 BMO_ITER (v, &siter, sys->op->slots_in, "verts", BM_VERT) {
403 m_vertex_id = BM_elem_index_get(v);
404 if (sys->zerola[m_vertex_id] == false) {
405 if (usex) {
406 v->co[0] = EIG_linear_solver_variable_get(sys->context, 0, m_vertex_id);
407 }
408 if (usey) {
409 v->co[1] = EIG_linear_solver_variable_get(sys->context, 1, m_vertex_id);
410 }
411 if (usez) {
412 v->co[2] = EIG_linear_solver_variable_get(sys->context, 2, m_vertex_id);
413 }
414 }
415 }
416 if (preserve_volume) {
417 vend = BM_mesh_calc_volume(sys->bm, false);
418 volume_preservation(sys->op, vini, vend, usex, usey, usez);
419 }
420}
421
423{
424 int i;
425 int m_vertex_id;
426 bool usex, usey, usez, preserve_volume;
427 float lambda_factor, lambda_border;
428 float w;
429 BMOIter siter;
430 BMVert *v;
431 LaplacianSystem *sys;
432
433 if (bm->totface == 0) {
434 return;
435 }
437 if (!sys) {
438 return;
439 }
440 sys->bm = bm;
441 sys->op = op;
442
444
446 lambda_factor = BMO_slot_float_get(op->slots_in, "lambda_factor");
447 lambda_border = BMO_slot_float_get(op->slots_in, "lambda_border");
448 sys->min_area = 0.00001f;
449 usex = BMO_slot_bool_get(op->slots_in, "use_x");
450 usey = BMO_slot_bool_get(op->slots_in, "use_y");
451 usez = BMO_slot_bool_get(op->slots_in, "use_z");
452 preserve_volume = BMO_slot_bool_get(op->slots_in, "preserve_volume");
453
455
456 for (i = 0; i < bm->totvert; i++) {
458 }
459 BMO_ITER (v, &siter, op->slots_in, "verts", BM_VERT) {
460 m_vertex_id = BM_elem_index_get(v);
462 EIG_linear_solver_variable_set(sys->context, 0, m_vertex_id, v->co[0]);
463 EIG_linear_solver_variable_set(sys->context, 1, m_vertex_id, v->co[1]);
464 EIG_linear_solver_variable_set(sys->context, 2, m_vertex_id, v->co[2]);
465 }
466
468 BMO_ITER (v, &siter, op->slots_in, "verts", BM_VERT) {
469 m_vertex_id = BM_elem_index_get(v);
470 EIG_linear_solver_right_hand_side_add(sys->context, 0, m_vertex_id, v->co[0]);
471 EIG_linear_solver_right_hand_side_add(sys->context, 1, m_vertex_id, v->co[1]);
472 EIG_linear_solver_right_hand_side_add(sys->context, 2, m_vertex_id, v->co[2]);
473 i = m_vertex_id;
474 if ((sys->zerola[i] == false) &&
475 /* Non zero check is to account for vertices that aren't connected to a selected face.
476 * Without this wire edges become `nan`, see #89214. */
477 (sys->ring_areas[i] != 0.0f))
478 {
479 w = sys->vweights[i] * sys->ring_areas[i];
480 sys->vweights[i] = (w == 0.0f) ? 0.0f : -lambda_factor / (4.0f * w);
481 w = sys->vlengths[i];
482 sys->vlengths[i] = (w == 0.0f) ? 0.0f : -lambda_border * 2.0f / w;
483
484 if (!vert_is_boundary(v)) {
486 sys->context, i, i, 1.0f + lambda_factor / (4.0f * sys->ring_areas[i]));
487 }
488 else {
489 EIG_linear_solver_matrix_add(sys->context, i, i, 1.0f + lambda_border * 2.0f);
490 }
491 }
492 else {
493 EIG_linear_solver_matrix_add(sys->context, i, i, 1.0f);
494 }
495 }
497
499 validate_solution(sys, usex, usey, usez, preserve_volume);
500 }
501
503}
float area_tri_v3(const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:98
float cotangent_tri_weight_v3(const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:196
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
unsigned int uint
Read Guarded memory(de)allocation.
@ BM_ELEM_SELECT
#define BM_FACE_FIRST_LOOP(p)
#define BM_elem_index_get(ele)
#define BM_elem_flag_test(ele, hflag)
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar)
@ BM_FACES_OF_VERT
@ BM_EDGES_OF_MESH
@ BM_FACES_OF_MESH
@ BM_EDGES_OF_VERT
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
#define BM_VERT
float BMO_slot_float_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
#define BMO_ITER(ele, iter, slot_args, slot_name, restrict_flag)
bool BMO_slot_bool_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
double BM_mesh_calc_volume(BMesh *bm, bool is_signed)
BLI_INLINE bool BM_edge_is_boundary(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
static void delete_void_pointer(void *data)
void bmo_smooth_laplacian_vert_exec(BMesh *bm, BMOperator *op)
static void init_laplacian_matrix(LaplacianSystem *sys)
static void delete_laplacian_system(LaplacianSystem *sys)
static LaplacianSystem * init_laplacian_system(int a_numEdges, int a_numLoops, int a_numVerts)
static bool vert_is_boundary(BMVert *v)
static void validate_solution(LaplacianSystem *sys, int usex, int usey, int usez, int preserve_volume)
#define SMOOTH_LAPLACIAN_MAX_EDGE_PERCENTAGE
static void memset_laplacian_system(LaplacianSystem *sys, int val)
#define SMOOTH_LAPLACIAN_MIN_EDGE_PERCENTAGE
static void volume_preservation(BMOperator *op, float vini, float vend, int usex, int usey, int usez)
static void fill_laplacian_matrix(LaplacianSystem *sys)
BLaplacianSystem LaplacianSystem
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
pow(value.r - subtrahend, 2.0)") .do_static_compilation(true)
draw_view in_light_buf[] float
LinearSolver * EIG_linear_least_squares_solver_new(int num_rows, int num_columns, int num_rhs)
void EIG_linear_solver_variable_set(LinearSolver *solver, int rhs, int index, double value)
void EIG_linear_solver_right_hand_side_add(LinearSolver *solver, int rhs, int index, double value)
void EIG_linear_solver_variable_unlock(LinearSolver *solver, int index)
void EIG_linear_solver_delete(LinearSolver *solver)
double EIG_linear_solver_variable_get(LinearSolver *solver, int rhs, int index)
void EIG_linear_solver_matrix_add(LinearSolver *solver, int row, int col, double value)
bool EIG_linear_solver_solve(LinearSolver *solver)
void EIG_linear_solver_variable_lock(LinearSolver *solver, int index)
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
struct BMVert * v
struct BMLoop * prev
struct BMLoop * next
struct BMOpSlot slots_in[BMO_OP_MAX_SLOTS]
float co[3]
int totvert
int totedge
int totloop
int totface
LinearSolver * context
float(* fweights)[3]
ccl_device_inline float beta(float x, float y)
Definition util/math.h:833