Blender V4.3
bmesh_decimate_dissolve.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_heap.h"
14#include "BLI_math_geom.h"
15#include "BLI_math_rotation.h"
16#include "BLI_math_vector.h"
17
18#include "BKE_customdata.hh"
19
20#include "bmesh.hh"
21#include "bmesh_decimate.hh" /* own include */
22
23/* check that collapsing a vertex between 2 edges doesn't cause a degenerate face. */
24#define USE_DEGENERATE_CHECK
25
26#define COST_INVALID FLT_MAX
27
28struct DelimitData;
29
30static bool bm_edge_is_delimiter(const BMEdge *e,
31 const BMO_Delimit delimit,
32 const DelimitData *delimit_data);
33static bool bm_vert_is_delimiter(const BMVert *v,
34 const BMO_Delimit delimit,
35 const DelimitData *delimit_data);
36
37/* multiply vertex edge angle by face angle
38 * this means we are not left with sharp corners between _almost_ planer faces
39 * convert angles [0-PI/2] -> [0-1], multiply together, then convert back to radians. */
41 const BMO_Delimit delimit,
42 const DelimitData *delimit_data)
43{
44#define UNIT_TO_ANGLE DEG2RADF(90.0f)
45#define ANGLE_TO_UNIT (1.0f / UNIT_TO_ANGLE)
46
47 const float angle = BM_vert_calc_edge_angle(v);
48 /* NOTE: could be either edge, it doesn't matter. */
49 if (v->e && BM_edge_is_manifold(v->e)) {
50 /* Checking delimited is important here,
51 * otherwise the boundary between two materials for e.g.
52 * will collapse if the faces on either side of the edge have a small angle.
53 *
54 * This way, delimiting edges are treated like boundary edges,
55 * the detail between two delimiting regions won't over-collapse. */
56 if (!bm_vert_is_delimiter(v, delimit, delimit_data)) {
57 return ((angle * ANGLE_TO_UNIT) * (BM_edge_calc_face_angle(v->e) * ANGLE_TO_UNIT)) *
59 }
60 }
61 return angle;
62
63#undef UNIT_TO_ANGLE
64#undef ANGLE_TO_UNIT
65}
66
67struct DelimitData {
72};
73
74static bool bm_edge_is_contiguous_loop_cd_all(const BMEdge *e, const DelimitData *delimit_data)
75{
76 int cd_loop_offset;
77 for (cd_loop_offset = delimit_data->cd_loop_offset;
78 cd_loop_offset < delimit_data->cd_loop_offset_end;
79 cd_loop_offset += delimit_data->cd_loop_size)
80 {
81 if (BM_edge_is_contiguous_loop_cd(e, delimit_data->cd_loop_type, cd_loop_offset) == false) {
82 return false;
83 }
84 }
85
86 return true;
87}
88
89static bool bm_edge_is_delimiter(const BMEdge *e,
90 const BMO_Delimit delimit,
91 const DelimitData *delimit_data)
92{
93 /* Caller must ensure. */
95
96 if (delimit != 0) {
97 if (delimit & BMO_DELIM_SEAM) {
99 return true;
100 }
101 }
102 if (delimit & BMO_DELIM_SHARP) {
104 return true;
105 }
106 }
107 if (delimit & BMO_DELIM_MATERIAL) {
108 if (e->l->f->mat_nr != e->l->radial_next->f->mat_nr) {
109 return true;
110 }
111 }
112 if (delimit & BMO_DELIM_NORMAL) {
113 if (!BM_edge_is_contiguous(e)) {
114 return true;
115 }
116 }
117 if (delimit & BMO_DELIM_UV) {
118 if (bm_edge_is_contiguous_loop_cd_all(e, delimit_data) == 0) {
119 return true;
120 }
121 }
122 }
123
124 return false;
125}
126
127static bool bm_vert_is_delimiter(const BMVert *v,
128 const BMO_Delimit delimit,
129 const DelimitData *delimit_data)
130{
131 BLI_assert(v->e != nullptr);
132
133 if (delimit != 0) {
134 const BMEdge *e, *e_first;
135 e = e_first = v->e;
136 do {
137 if (BM_edge_is_manifold(e)) {
138 if (bm_edge_is_delimiter(e, delimit, delimit_data)) {
139 return true;
140 }
141 }
142 } while ((e = BM_DISK_EDGE_NEXT(e, v)) != e_first);
143 }
144 return false;
145}
146
148 const BMO_Delimit delimit,
149 const DelimitData *delimit_data)
150{
151 if (BM_edge_is_manifold(e) && !bm_edge_is_delimiter(e, delimit, delimit_data)) {
152 float angle_cos_neg = dot_v3v3(e->l->f->no, e->l->radial_next->f->no);
154 angle_cos_neg *= -1;
155 }
156 return angle_cos_neg;
157 }
158
159 return COST_INVALID;
160}
161
162#ifdef USE_DEGENERATE_CHECK
163
164static void mul_v2_m3v3_center(float r[2],
165 const float m[3][3],
166 const float a[3],
167 const float center[3])
168{
169 BLI_assert(r != a);
170 BLI_assert(r != center);
171
172 float co[3];
173 sub_v3_v3v3(co, a, center);
174
175 r[0] = m[0][0] * co[0] + m[1][0] * co[1] + m[2][0] * co[2];
176 r[1] = m[0][1] * co[0] + m[1][1] * co[1] + m[2][1] * co[2];
177}
178
180{
181 /* Calculate relative to the central vertex for higher precision. */
182 const float *center = l_ear->v->co;
183
184 float tri_2d[3][2];
185 float axis_mat[3][3];
186
187 axis_dominant_v3_to_m3(axis_mat, l_ear->f->no);
188
189 {
190 mul_v2_m3v3_center(tri_2d[0], axis_mat, l_ear->prev->v->co, center);
191# if 0
192 mul_v2_m3v3_center(tri_2d[1], axis_mat, l_ear->v->co, center);
193# else
194 zero_v2(tri_2d[1]);
195# endif
196 mul_v2_m3v3_center(tri_2d[2], axis_mat, l_ear->next->v->co, center);
197 }
198
199 /* check we're not flipping face corners before or after the ear */
200 {
201 float adjacent_2d[2];
202
203 if (!BM_vert_is_edge_pair(l_ear->prev->v)) {
204 mul_v2_m3v3_center(adjacent_2d, axis_mat, l_ear->prev->prev->v->co, center);
205 if (signum_i(cross_tri_v2(adjacent_2d, tri_2d[0], tri_2d[1])) !=
206 signum_i(cross_tri_v2(adjacent_2d, tri_2d[0], tri_2d[2])))
207 {
208 return true;
209 }
210 }
211
212 if (!BM_vert_is_edge_pair(l_ear->next->v)) {
213 mul_v2_m3v3_center(adjacent_2d, axis_mat, l_ear->next->next->v->co, center);
214 if (signum_i(cross_tri_v2(adjacent_2d, tri_2d[2], tri_2d[1])) !=
215 signum_i(cross_tri_v2(adjacent_2d, tri_2d[2], tri_2d[0])))
216 {
217 return true;
218 }
219 }
220 }
221
222 /* check no existing verts are inside the triangle */
223 {
224 /* triangle may be concave, if so - flip so we can use clockwise check */
225 if (cross_tri_v2(UNPACK3(tri_2d)) < 0.0f) {
226 swap_v2_v2(tri_2d[1], tri_2d[2]);
227 }
228
229 /* skip l_ear and adjacent verts */
230 BMLoop *l_iter, *l_first;
231
232 l_iter = l_ear->next->next;
233 l_first = l_ear->prev;
234 do {
235 float co_2d[2];
236 mul_v2_m3v3_center(co_2d, axis_mat, l_iter->v->co, center);
237 if (isect_point_tri_v2_cw(co_2d, tri_2d[0], tri_2d[1], tri_2d[2])) {
238 return true;
239 }
240 } while ((l_iter = l_iter->next) != l_first);
241 }
242
243 return false;
244}
245
247{
248 BMEdge *e_pair[2];
249 BMVert *v_pair[2];
250
251 if (BM_vert_edge_pair(v, &e_pair[0], &e_pair[1])) {
252
253 /* allow wire edges */
254 if (BM_edge_is_wire(e_pair[0]) || BM_edge_is_wire(e_pair[1])) {
255 return false;
256 }
257
258 v_pair[0] = BM_edge_other_vert(e_pair[0], v);
259 v_pair[1] = BM_edge_other_vert(e_pair[1], v);
260
261 if (fabsf(cos_v3v3v3(v_pair[0]->co, v->co, v_pair[1]->co)) < (1.0f - FLT_EPSILON)) {
262 BMLoop *l_iter, *l_first;
263 l_iter = l_first = e_pair[1]->l;
264 do {
265 if (l_iter->f->len > 3) {
266 BMLoop *l_pivot = (l_iter->v == v ? l_iter : l_iter->next);
267 BLI_assert(v == l_pivot->v);
268 if (bm_loop_collapse_is_degenerate(l_pivot)) {
269 return true;
270 }
271 }
272 } while ((l_iter = l_iter->radial_next) != l_first);
273 }
274 return false;
275 }
276 return true;
277}
278#endif /* USE_DEGENERATE_CHECK */
279
281 const float angle_limit,
282 const bool do_dissolve_boundaries,
283 BMO_Delimit delimit,
284 BMVert **vinput_arr,
285 const int vinput_len,
286 BMEdge **einput_arr,
287 const int einput_len,
288 const short oflag_out)
289{
290 const float angle_limit_cos_neg = -cosf(angle_limit);
291 DelimitData delimit_data = {0};
292 const int eheap_table_len = do_dissolve_boundaries ? einput_len : max_ii(einput_len, vinput_len);
293 void *_heap_table = MEM_mallocN(sizeof(HeapNode *) * eheap_table_len, __func__);
294
295 int i;
296
297 if (delimit & BMO_DELIM_UV) {
298 const int layer_len = CustomData_number_of_layers(&bm->ldata, CD_PROP_FLOAT2);
299 if (layer_len == 0) {
300 delimit &= ~BMO_DELIM_UV;
301 }
302 else {
303 delimit_data.cd_loop_type = CD_PROP_FLOAT2;
304 delimit_data.cd_loop_size = CustomData_sizeof(eCustomDataType(delimit_data.cd_loop_type));
306 delimit_data.cd_loop_offset_end = delimit_data.cd_loop_offset +
307 delimit_data.cd_loop_size * layer_len;
308 }
309 }
310
311 /* --- first edges --- */
312 if (true) {
313 BMEdge **earray;
314 Heap *eheap;
315 HeapNode **eheap_table = static_cast<HeapNode **>(_heap_table);
316 HeapNode *enode_top;
317 int *vert_reverse_lookup;
318 BMIter iter;
319 BMEdge *e_iter;
320
321 /* --- setup heap --- */
322 eheap = BLI_heap_new_ex(einput_len);
323
324 /* wire -> tag */
325 BM_ITER_MESH (e_iter, &iter, bm, BM_EDGES_OF_MESH) {
327 BM_elem_index_set(e_iter, -1); /* set dirty */
328 }
330
331 /* build heap */
332 for (i = 0; i < einput_len; i++) {
333 BMEdge *e = einput_arr[i];
334 const float cost = bm_edge_calc_dissolve_error(e, delimit, &delimit_data);
335 eheap_table[i] = BLI_heap_insert(eheap, cost, e);
336 BM_elem_index_set(e, i); /* set dirty */
337 }
338
339 while ((BLI_heap_is_empty(eheap) == false) &&
340 (BLI_heap_node_value(enode_top = BLI_heap_top(eheap)) < angle_limit_cos_neg))
341 {
342 BMFace *f_new = nullptr;
343 BMEdge *e;
344
345 e = static_cast<BMEdge *>(BLI_heap_node_ptr(enode_top));
346 i = BM_elem_index_get(e);
347
348 if (BM_edge_is_manifold(e)) {
349 f_new = BM_faces_join_pair(bm, e->l, e->l->radial_next, false);
350
351 if (f_new) {
352 BMLoop *l_first, *l_iter;
353
354 BLI_heap_remove(eheap, enode_top);
355 eheap_table[i] = nullptr;
356
357 /* update normal */
359 if (oflag_out) {
360 BMO_face_flag_enable(bm, f_new, oflag_out);
361 }
362
363 /* re-calculate costs */
364 l_iter = l_first = BM_FACE_FIRST_LOOP(f_new);
365 do {
366 const int j = BM_elem_index_get(l_iter->e);
367 if (j != -1 && eheap_table[j]) {
368 const float cost = bm_edge_calc_dissolve_error(l_iter->e, delimit, &delimit_data);
369 BLI_heap_node_value_update(eheap, eheap_table[j], cost);
370 }
371 } while ((l_iter = l_iter->next) != l_first);
372 }
373 }
374
375 if (UNLIKELY(f_new == nullptr)) {
376 BLI_heap_node_value_update(eheap, enode_top, COST_INVALID);
377 }
378 }
379
380 /* prepare for cleanup */
382 vert_reverse_lookup = static_cast<int *>(MEM_mallocN(sizeof(int) * bm->totvert, __func__));
383 copy_vn_i(vert_reverse_lookup, bm->totvert, -1);
384 for (i = 0; i < vinput_len; i++) {
385 BMVert *v = vinput_arr[i];
386 vert_reverse_lookup[BM_elem_index_get(v)] = i;
387 }
388
389 /* --- cleanup --- */
390 earray = static_cast<BMEdge **>(MEM_mallocN(sizeof(BMEdge *) * bm->totedge, __func__));
391 BM_ITER_MESH_INDEX (e_iter, &iter, bm, BM_EDGES_OF_MESH, i) {
392 earray[i] = e_iter;
393 }
394 /* Remove all edges/verts left behind from dissolving,
395 * nulling the vertex array so we don't re-use. */
396 for (i = bm->totedge - 1; i != -1; i--) {
397 e_iter = earray[i];
398
399 if (BM_edge_is_wire(e_iter) && (BM_elem_flag_test(e_iter, BM_ELEM_TAG) == false)) {
400 /* edge has become wire */
401 int vidx_reverse;
402 BMVert *v1 = e_iter->v1;
403 BMVert *v2 = e_iter->v2;
404 BM_edge_kill(bm, e_iter);
405 if (v1->e == nullptr) {
406 vidx_reverse = vert_reverse_lookup[BM_elem_index_get(v1)];
407 if (vidx_reverse != -1) {
408 vinput_arr[vidx_reverse] = nullptr;
409 }
410 BM_vert_kill(bm, v1);
411 }
412 if (v2->e == nullptr) {
413 vidx_reverse = vert_reverse_lookup[BM_elem_index_get(v2)];
414 if (vidx_reverse != -1) {
415 vinput_arr[vidx_reverse] = nullptr;
416 }
418 }
419 }
420 }
421 MEM_freeN(vert_reverse_lookup);
422 MEM_freeN(earray);
423
424 BLI_heap_free(eheap, nullptr);
425 }
426
427 /* --- second verts --- */
428 if (do_dissolve_boundaries) {
429 /* simple version of the branch below, since we will dissolve _all_ verts that use 2 edges */
430 for (i = 0; i < vinput_len; i++) {
431 BMVert *v = vinput_arr[i];
432 if (LIKELY(v != nullptr) && BM_vert_is_edge_pair(v)) {
433 BM_vert_collapse_edge(bm, v->e, v, true, true, true); /* join edges */
434 }
435 }
436 }
437 else {
438 Heap *vheap;
439 HeapNode **vheap_table = static_cast<HeapNode **>(_heap_table);
440 HeapNode *vnode_top;
441
442 BMVert *v_iter;
443 BMIter iter;
444
445 BM_ITER_MESH (v_iter, &iter, bm, BM_VERTS_OF_MESH) {
446 BM_elem_index_set(v_iter, -1); /* set dirty */
447 }
449
450 vheap = BLI_heap_new_ex(vinput_len);
451
452 for (i = 0; i < vinput_len; i++) {
453 BMVert *v = vinput_arr[i];
454 if (LIKELY(v != nullptr)) {
455 const float cost = bm_vert_edge_face_angle(v, delimit, &delimit_data);
456 vheap_table[i] = BLI_heap_insert(vheap, cost, v);
457 BM_elem_index_set(v, i); /* set dirty */
458 }
459 }
460
461 while ((BLI_heap_is_empty(vheap) == false) &&
462 (BLI_heap_node_value(vnode_top = BLI_heap_top(vheap)) < angle_limit))
463 {
464 BMEdge *e_new = nullptr;
465 BMVert *v;
466
467 v = static_cast<BMVert *>(BLI_heap_node_ptr(vnode_top));
468 i = BM_elem_index_get(v);
469
470 if (
473#else
475#endif
476 )
477 {
478 e_new = BM_vert_collapse_edge(bm, v->e, v, true, true, true); /* join edges */
479
480 if (e_new) {
481
482 BLI_heap_remove(vheap, vnode_top);
483 vheap_table[i] = nullptr;
484
485 /* update normal */
486 if (e_new->l) {
487 BMLoop *l_first, *l_iter;
488 l_iter = l_first = e_new->l;
489 do {
490 BM_face_normal_update(l_iter->f);
491 } while ((l_iter = l_iter->radial_next) != l_first);
492 }
493
494 /* re-calculate costs */
495 BM_ITER_ELEM (v_iter, &iter, e_new, BM_VERTS_OF_EDGE) {
496 const int j = BM_elem_index_get(v_iter);
497 if (j != -1 && vheap_table[j]) {
498 const float cost = bm_vert_edge_face_angle(v_iter, delimit, &delimit_data);
499 BLI_heap_node_value_update(vheap, vheap_table[j], cost);
500 }
501 }
502
503#ifdef USE_DEGENERATE_CHECK
504 /* dissolving a vertex may mean vertices we previously weren't able to dissolve
505 * can now be re-evaluated. */
506 if (e_new->l) {
507 BMLoop *l_first, *l_iter;
508 l_iter = l_first = e_new->l;
509 do {
510 /* skip vertices part of this edge, evaluated above */
511 BMLoop *l_cycle_first, *l_cycle_iter;
512 l_cycle_iter = l_iter->next->next;
513 l_cycle_first = l_iter->prev;
514 do {
515 const int j = BM_elem_index_get(l_cycle_iter->v);
516 if (j != -1 && vheap_table[j] &&
517 (BLI_heap_node_value(vheap_table[j]) == COST_INVALID))
518 {
519 const float cost = bm_vert_edge_face_angle(
520 l_cycle_iter->v, delimit, &delimit_data);
521 BLI_heap_node_value_update(vheap, vheap_table[j], cost);
522 }
523 } while ((l_cycle_iter = l_cycle_iter->next) != l_cycle_first);
524
525 } while ((l_iter = l_iter->radial_next) != l_first);
526 }
527#endif /* USE_DEGENERATE_CHECK */
528 }
529 }
530
531 if (UNLIKELY(e_new == nullptr)) {
532 BLI_heap_node_value_update(vheap, vnode_top, COST_INVALID);
533 }
534 }
535
536 BLI_heap_free(vheap, nullptr);
537 }
538
539 MEM_freeN(_heap_table);
540}
541
543 const float angle_limit,
544 const bool do_dissolve_boundaries,
545 const BMO_Delimit delimit)
546{
547 int vinput_len;
548 int einput_len;
549
550 BMVert **vinput_arr = static_cast<BMVert **>(
551 BM_iter_as_arrayN(bm, BM_VERTS_OF_MESH, nullptr, &vinput_len, nullptr, 0));
552 BMEdge **einput_arr = static_cast<BMEdge **>(
553 BM_iter_as_arrayN(bm, BM_EDGES_OF_MESH, nullptr, &einput_len, nullptr, 0));
554
556 angle_limit,
557 do_dissolve_boundaries,
558 delimit,
559 vinput_arr,
560 vinput_len,
561 einput_arr,
562 einput_len,
563 0);
564
565 MEM_freeN(vinput_arr);
566 MEM_freeN(einput_arr);
567}
CustomData interface, see also DNA_customdata_types.h.
int CustomData_sizeof(eCustomDataType type)
int CustomData_get_n_offset(const CustomData *data, eCustomDataType type, int n)
int CustomData_number_of_layers(const CustomData *data, eCustomDataType type)
#define BLI_assert(a)
Definition BLI_assert.h:50
A min-heap / priority queue ADT.
HeapNode * BLI_heap_top(const Heap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.c:279
void BLI_heap_free(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition BLI_heap.c:192
void void float BLI_heap_node_value(const HeapNode *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.c:347
Heap * BLI_heap_new_ex(unsigned int reserve_num) ATTR_WARN_UNUSED_RESULT
Definition BLI_heap.c:172
void void bool BLI_heap_is_empty(const Heap *heap) ATTR_NONNULL(1)
Definition BLI_heap.c:269
void BLI_heap_node_value_update(Heap *heap, HeapNode *node, float value) ATTR_NONNULL(1
void * BLI_heap_node_ptr(const HeapNode *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.c:352
HeapNode * BLI_heap_insert(Heap *heap, float value, void *ptr) ATTR_NONNULL(1)
Definition BLI_heap.c:235
void void BLI_heap_remove(Heap *heap, HeapNode *node) ATTR_NONNULL(1
MINLINE int max_ii(int a, int b)
MINLINE int signum_i(float a)
MINLINE float cross_tri_v2(const float v1[2], const float v2[2], const float v3[2])
bool isect_point_tri_v2_cw(const float pt[2], const float v1[2], const float v2[2], const float v3[2])
void axis_dominant_v3_to_m3(float r_mat[3][3], const float normal[3])
Normal to x,y matrix.
MINLINE void swap_v2_v2(float a[2], float b[2])
float cos_v3v3v3(const float p1[3], const float p2[3], const float p3[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void copy_vn_i(int *array_tar, int size, int val)
MINLINE void zero_v2(float r[2])
#define UNPACK3(a)
#define UNLIKELY(x)
#define LIKELY(x)
@ CD_PROP_FLOAT2
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:125
Read Guarded memory(de)allocation.
#define BM_DISK_EDGE_NEXT(e, v)
@ BM_ELEM_SEAM
@ BM_ELEM_SMOOTH
@ BM_ELEM_TAG
#define BM_FACE_FIRST_LOOP(p)
void BM_vert_kill(BMesh *bm, BMVert *v)
void BM_edge_kill(BMesh *bm, BMEdge *e)
static bool bm_vert_is_delimiter(const BMVert *v, const BMO_Delimit delimit, const DelimitData *delimit_data)
void BM_mesh_decimate_dissolve(BMesh *bm, const float angle_limit, const bool do_dissolve_boundaries, const BMO_Delimit delimit)
static float bm_edge_calc_dissolve_error(const BMEdge *e, const BMO_Delimit delimit, const DelimitData *delimit_data)
#define ANGLE_TO_UNIT
#define UNIT_TO_ANGLE
static bool bm_edge_is_delimiter(const BMEdge *e, const BMO_Delimit delimit, const DelimitData *delimit_data)
static bool bm_loop_collapse_is_degenerate(BMLoop *l_ear)
static void mul_v2_m3v3_center(float r[2], const float m[3][3], const float a[3], const float center[3])
static float bm_vert_edge_face_angle(BMVert *v, const BMO_Delimit delimit, const DelimitData *delimit_data)
#define COST_INVALID
static bool bm_vert_collapse_is_degenerate(BMVert *v)
static bool bm_edge_is_contiguous_loop_cd_all(const BMEdge *e, const DelimitData *delimit_data)
void BM_mesh_decimate_dissolve_ex(BMesh *bm, const float angle_limit, const bool do_dissolve_boundaries, BMO_Delimit delimit, BMVert **vinput_arr, const int vinput_len, BMEdge **einput_arr, const int einput_len, const short oflag_out)
#define USE_DEGENERATE_CHECK
#define BM_elem_index_get(ele)
#define BM_elem_flag_set(ele, hflag, val)
#define BM_elem_index_set(ele, index)
#define BM_elem_flag_test(ele, hflag)
void * BM_iter_as_arrayN(BMesh *bm, const char itype, void *data, int *r_len, void **stack_array, int stack_array_size)
Iterator as Array.
#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_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_VERTS_OF_EDGE
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
BMEdge * BM_vert_collapse_edge(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool kill_degenerate_faces, const bool kill_duplicate_faces)
Vert Collapse Faces.
BMFace * BM_faces_join_pair(BMesh *bm, BMLoop *l_a, BMLoop *l_b, const bool do_del)
Faces Join Pair.
#define BM_EDGE
#define BM_VERT
#define BMO_face_flag_enable(bm, e, oflag)
@ BMO_DELIM_NORMAL
@ BMO_DELIM_MATERIAL
@ BMO_DELIM_SEAM
@ BMO_DELIM_SHARP
@ BMO_DELIM_UV
void BM_face_normal_update(BMFace *f)
float BM_vert_calc_edge_angle(const BMVert *v)
bool BM_edge_is_contiguous_loop_cd(const BMEdge *e, const int cd_loop_type, const int cd_loop_offset)
float BM_edge_calc_face_angle(const BMEdge *e)
bool BM_vert_edge_pair(BMVert *v, BMEdge **r_e_a, BMEdge **r_e_b)
bool BM_vert_is_edge_pair(const BMVert *v)
BLI_INLINE bool BM_edge_is_contiguous(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_manifold(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_wire(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
#define cosf(x)
#define fabsf(x)
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
BMVert * v1
BMVert * v2
struct BMLoop * l
float no[3]
struct BMVert * v
struct BMEdge * e
struct BMLoop * radial_next
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
float co[3]
struct BMEdge * e
float no[3]
int totvert
char elem_index_dirty
int totedge
CustomData ldata