Blender V4.5
bmesh_decimate_collapse.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
10
11#include <cstddef>
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_alloca.h"
16#include "BLI_heap.h"
17#include "BLI_linklist.h"
18#include "BLI_math_geom.h"
19#include "BLI_math_vector.h"
20#include "BLI_memarena.h"
21#include "BLI_polyfill_2d.h"
23#include "BLI_quadric.h"
25
26#include "BKE_customdata.hh"
27
28#include "bmesh.hh"
29#include "bmesh_decimate.hh" /* own include */
30
32
33#define USE_SYMMETRY
34#ifdef USE_SYMMETRY
35# include "BLI_kdtree.h"
36#endif
37
38/* defines for testing */
39#define USE_CUSTOMDATA
40#define USE_TRIANGULATE
42#define USE_VERT_NORMAL_INTERP
43
45#define USE_TOPOLOGY_FALLBACK
46#ifdef USE_TOPOLOGY_FALLBACK
48# define TOPOLOGY_FALLBACK_EPS 1e-12f
49#endif
50
51#define BOUNDARY_PRESERVE_WEIGHT 100.0f
56#define OPTIMIZE_EPS 1e-8
57#define COST_INVALID FLT_MAX
58
60 CD_DO_VERT = (1 << 0),
61 CD_DO_EDGE = (1 << 1),
62 CD_DO_LOOP = (1 << 2),
63};
65
66/* BMesh Helper Functions
67 * ********************** */
68
69
72static void bm_decim_build_quadrics(BMesh *bm, Quadric *vquadrics)
73{
74 BMIter iter;
75 BMFace *f;
76 BMEdge *e;
77
78 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
79 BMLoop *l_first;
80 BMLoop *l_iter;
81
82 float center[3];
83 double plane_db[4];
84 Quadric q;
85
87 copy_v3db_v3fl(plane_db, f->no);
88 plane_db[3] = -dot_v3db_v3fl(plane_db, center);
89
90 BLI_quadric_from_plane(&q, plane_db);
91
92 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
93 do {
94 BLI_quadric_add_qu_qu(&vquadrics[BM_elem_index_get(l_iter->v)], &q);
95 } while ((l_iter = l_iter->next) != l_first);
96 }
97
98 /* boundary edges */
101 float edge_vector[3];
102 float edge_plane[3];
103 double edge_plane_db[4];
104 sub_v3_v3v3(edge_vector, e->v2->co, e->v1->co);
105 f = e->l->f;
106
107 cross_v3_v3v3(edge_plane, edge_vector, f->no);
108 copy_v3db_v3fl(edge_plane_db, edge_plane);
109
110 if (normalize_v3_db(edge_plane_db) > double(FLT_EPSILON)) {
111 Quadric q;
112 float center[3];
113
114 mid_v3_v3v3(center, e->v1->co, e->v2->co);
115
116 edge_plane_db[3] = -dot_v3db_v3fl(edge_plane_db, center);
117 BLI_quadric_from_plane(&q, edge_plane_db);
119
120 BLI_quadric_add_qu_qu(&vquadrics[BM_elem_index_get(e->v1)], &q);
121 BLI_quadric_add_qu_qu(&vquadrics[BM_elem_index_get(e->v2)], &q);
122 }
123 }
124 }
125}
126
127static void bm_decim_calc_target_co_db(BMEdge *e, double optimize_co[3], const Quadric *vquadrics)
128{
129 /* compute an edge contraction target for edge 'e'
130 * this is computed by summing its vertices quadrics and
131 * optimizing the result. */
132 Quadric q;
133
135 &q, &vquadrics[BM_elem_index_get(e->v1)], &vquadrics[BM_elem_index_get(e->v2)]);
136
137 if (BLI_quadric_optimize(&q, optimize_co, OPTIMIZE_EPS)) {
138 /* all is good */
139 return;
140 }
141
142 optimize_co[0] = 0.5 * (double(e->v1->co[0]) + double(e->v2->co[0]));
143 optimize_co[1] = 0.5 * (double(e->v1->co[1]) + double(e->v2->co[1]));
144 optimize_co[2] = 0.5 * (double(e->v1->co[2]) + double(e->v2->co[2]));
145}
146
147static void bm_decim_calc_target_co_fl(BMEdge *e, float optimize_co[3], const Quadric *vquadrics)
148{
149 double optimize_co_db[3];
150 bm_decim_calc_target_co_db(e, optimize_co_db, vquadrics);
151 copy_v3fl_v3db(optimize_co, optimize_co_db);
152}
153
154static bool bm_edge_collapse_is_degenerate_flip(BMEdge *e, const float optimize_co[3])
155{
156 BMIter liter;
157 BMLoop *l;
158 uint i;
159
160 for (i = 0; i < 2; i++) {
161 /* loop over both verts */
162 BMVert *v = *((&e->v1) + i);
163
164 BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
165 if (l->e != e && l->prev->e != e) {
166 const float *co_prev = l->prev->v->co;
167 const float *co_next = l->next->v->co;
168 float cross_exist[3];
169 float cross_optim[3];
170
171#if 1
172 /* line between the two outer verts, re-use for both cross products */
173 float vec_other[3];
174 /* before collapse */
175 float vec_exist[3];
176 /* after collapse */
177 float vec_optim[3];
178
179 sub_v3_v3v3(vec_other, co_prev, co_next);
180 sub_v3_v3v3(vec_exist, co_prev, v->co);
181 sub_v3_v3v3(vec_optim, co_prev, optimize_co);
182
183 cross_v3_v3v3(cross_exist, vec_other, vec_exist);
184 cross_v3_v3v3(cross_optim, vec_other, vec_optim);
185
186 /* avoid normalize */
187 if (dot_v3v3(cross_exist, cross_optim) <=
188 (len_squared_v3(cross_exist) + len_squared_v3(cross_optim)) * 0.01f)
189 {
190 return true;
191 }
192#else
193 normal_tri_v3(cross_exist, v->co, co_prev, co_next);
194 normal_tri_v3(cross_optim, optimize_co, co_prev, co_next);
195
196 /* use a small value rather than zero so we don't flip a face in multiple steps
197 * (first making it zero area, then flipping again) */
198 if (dot_v3v3(cross_exist, cross_optim) <= FLT_EPSILON) {
199 // printf("no flip\n");
200 return true;
201 }
202#endif
203 }
204 }
205 }
206
207 return false;
208}
209
210#ifdef USE_TOPOLOGY_FALLBACK
218{
219 return fabsf(dot_v3v3(e->v1->no, e->v2->no)) /
220 min_ff(-len_squared_v3v3(e->v1->co, e->v2->co), -FLT_EPSILON);
221}
223{
224 return fabsf(dot_v3v3(e->v1->no, e->v2->no)) /
225 min_ff(-len_v3v3(e->v1->co, e->v2->co), -FLT_EPSILON);
226}
227
228#endif /* USE_TOPOLOGY_FALLBACK */
229
231 const Quadric *vquadrics,
232 const float *vweights,
233 const float vweight_factor,
234 Heap *eheap,
235 HeapNode **eheap_table)
236{
237 float cost;
238
239 if (UNLIKELY(vweights && ((vweights[BM_elem_index_get(e->v1)] == 0.0f) ||
240 (vweights[BM_elem_index_get(e->v2)] == 0.0f))))
241 {
242 goto clear;
243 }
244
245 /* Check we can collapse, some edges we better not touch. */
246 if (BM_edge_is_boundary(e)) {
247 if (e->l->f->len == 3) {
248 /* Pass. */
249 }
250 else {
251 /* Only collapse triangles. */
252 goto clear;
253 }
254 }
255 else if (BM_edge_is_manifold(e)) {
256 if ((e->l->f->len == 3) && (e->l->radial_next->f->len == 3)) {
257 /* Pass. */
258 }
259 else {
260 /* Only collapse triangles. */
261 goto clear;
262 }
263 }
264 else {
265 goto clear;
266 }
267 /* End sanity check. */
268
269 {
270 double optimize_co[3];
271 bm_decim_calc_target_co_db(e, optimize_co, vquadrics);
272
273 const Quadric *q1, *q2;
274 q1 = &vquadrics[BM_elem_index_get(e->v1)];
275 q2 = &vquadrics[BM_elem_index_get(e->v2)];
276
277 cost = (BLI_quadric_evaluate(q1, optimize_co) + BLI_quadric_evaluate(q2, optimize_co));
278 }
279
280 /* NOTE: 'cost' shouldn't be negative but happens sometimes with small values.
281 * this can cause faces that make up a flat surface to over-collapse, see #37121. */
282 cost = fabsf(cost);
283
284#ifdef USE_TOPOLOGY_FALLBACK
285 if (UNLIKELY(cost < TOPOLOGY_FALLBACK_EPS)) {
286 /* subtract existing cost to further differentiate edges from one another
287 *
288 * keep topology cost below 0.0 so their values don't interfere with quadric cost,
289 * (and they get handled first). */
290 if (vweights == nullptr) {
292 }
293 else {
294 /* with weights we need to use the real length so we can scale them properly */
295 const float e_weight = (vweights[BM_elem_index_get(e->v1)] +
296 vweights[BM_elem_index_get(e->v2)]);
298 /* NOTE: this is rather arbitrary max weight is 2 here,
299 * allow for skipping edges 4x the length, based on weights */
300 if (e_weight) {
301 cost *= 1.0f + (e_weight * vweight_factor);
302 }
303
304 BLI_assert(cost <= 0.0f);
305 }
306 }
307 else
308#endif
309 if (vweights)
310 {
311 const float e_weight = 2.0f - (vweights[BM_elem_index_get(e->v1)] +
312 vweights[BM_elem_index_get(e->v2)]);
313 if (e_weight) {
314 cost += (BM_edge_calc_length(e) * (e_weight * vweight_factor));
315 }
316 }
317
318 BLI_heap_insert_or_update(eheap, &eheap_table[BM_elem_index_get(e)], cost, e);
319 return;
320
321clear:
322 if (eheap_table[BM_elem_index_get(e)]) {
323 BLI_heap_remove(eheap, eheap_table[BM_elem_index_get(e)]);
324 }
325 eheap_table[BM_elem_index_get(e)] = nullptr;
326}
327
328/* use this for degenerate cases - add back to the heap with an invalid cost,
329 * this way it may be calculated again if surrounding geometry changes */
330static void bm_decim_invalid_edge_cost_single(BMEdge *e, Heap *eheap, HeapNode **eheap_table)
331{
332 BLI_assert(eheap_table[BM_elem_index_get(e)] == nullptr);
333 eheap_table[BM_elem_index_get(e)] = BLI_heap_insert(eheap, COST_INVALID, e);
334}
335
337 const Quadric *vquadrics,
338 const float *vweights,
339 const float vweight_factor,
340 Heap *eheap,
341 HeapNode **eheap_table)
342{
343 BMIter iter;
344 BMEdge *e;
345 uint i;
346
348 /* keep sanity check happy */
349 eheap_table[i] = nullptr;
350 bm_decim_build_edge_cost_single(e, vquadrics, vweights, vweight_factor, eheap, eheap_table);
351 }
352}
353
354#ifdef USE_SYMMETRY
355
357 /* pre-flipped coords */
358 float e_v1_co[3], e_v2_co[3];
359 /* Use to compare the correct endpoints in case v1/v2 are swapped. */
360 float e_dir[3];
361
363
364 /* same for all */
366 float limit_sq;
367};
368
369static bool bm_edge_symmetry_check_cb(void *user_data,
370 int index,
371 const float /*co*/[3],
372 float /*dist_sq*/)
373{
374 KD_Symmetry_Data *sym_data = static_cast<KD_Symmetry_Data *>(user_data);
375 BMEdge *e_other = sym_data->etable[index];
376 float e_other_dir[3];
377
378 sub_v3_v3v3(e_other_dir, e_other->v2->co, e_other->v1->co);
379
380 if (dot_v3v3(e_other_dir, sym_data->e_dir) > 0.0f) {
381 if ((len_squared_v3v3(sym_data->e_v1_co, e_other->v1->co) > sym_data->limit_sq) ||
382 (len_squared_v3v3(sym_data->e_v2_co, e_other->v2->co) > sym_data->limit_sq))
383 {
384 return true;
385 }
386 }
387 else {
388 if ((len_squared_v3v3(sym_data->e_v1_co, e_other->v2->co) > sym_data->limit_sq) ||
389 (len_squared_v3v3(sym_data->e_v2_co, e_other->v1->co) > sym_data->limit_sq))
390 {
391 return true;
392 }
393 }
394
395 /* exit on first-hit, this is OK since the search range is very small */
396 sym_data->e_found_index = index;
397 return false;
398}
399
400static int *bm_edge_symmetry_map(BMesh *bm, uint symmetry_axis, float limit)
401{
402 KD_Symmetry_Data sym_data;
403 BMIter iter;
404 BMEdge *e, **etable;
405 uint i;
406 int *edge_symmetry_map;
407 const float limit_sq = square_f(limit);
408 KDTree_3d *tree;
409
410 tree = BLI_kdtree_3d_new(bm->totedge);
411
412 etable = MEM_malloc_arrayN<BMEdge *>(bm->totedge, __func__);
413 edge_symmetry_map = MEM_malloc_arrayN<int>(bm->totedge, __func__);
414
416 float co[3];
417 mid_v3_v3v3(co, e->v1->co, e->v2->co);
418 BLI_kdtree_3d_insert(tree, i, co);
419 etable[i] = e;
420 edge_symmetry_map[i] = -1;
421 }
422
423 BLI_kdtree_3d_balance(tree);
424
425 sym_data.etable = etable;
426 sym_data.limit_sq = limit_sq;
427
429 if (edge_symmetry_map[i] == -1) {
430 float co[3];
431 mid_v3_v3v3(co, e->v1->co, e->v2->co);
432 co[symmetry_axis] *= -1.0f;
433
434 copy_v3_v3(sym_data.e_v1_co, e->v1->co);
435 copy_v3_v3(sym_data.e_v2_co, e->v2->co);
436 sym_data.e_v1_co[symmetry_axis] *= -1.0f;
437 sym_data.e_v2_co[symmetry_axis] *= -1.0f;
438 sub_v3_v3v3(sym_data.e_dir, sym_data.e_v2_co, sym_data.e_v1_co);
439 sym_data.e_found_index = -1;
440
441 BLI_kdtree_3d_range_search_cb(tree, co, limit, bm_edge_symmetry_check_cb, &sym_data);
442
443 if (sym_data.e_found_index != -1) {
444 const int i_other = sym_data.e_found_index;
445 edge_symmetry_map[i] = i_other;
446 edge_symmetry_map[i_other] = i;
447 }
448 }
449 }
450
451 MEM_freeN(etable);
452 BLI_kdtree_3d_free(tree);
453
454 return edge_symmetry_map;
455}
456#endif /* USE_SYMMETRY */
457
458#ifdef USE_TRIANGULATE
459/* Temp Triangulation
460 * ****************** */
461
475 BMFace *f_base,
476 LinkNode **r_faces_double,
477 int *r_edges_tri_tot,
478
479 MemArena *pf_arena,
480 /* use for MOD_TRIANGULATE_NGON_BEAUTY only! */
481 Heap *pf_heap)
482{
483 const int f_base_len = f_base->len;
484 int faces_array_tot = f_base_len - 3;
485 int edges_array_tot = f_base_len - 3;
486 BMFace **faces_array = BLI_array_alloca(faces_array, faces_array_tot);
487 BMEdge **edges_array = BLI_array_alloca(edges_array, edges_array_tot);
488 const int quad_method = 0, ngon_method = 0; /* beauty */
489
490 bool has_cut = false;
491
492 const int f_index = BM_elem_index_get(f_base);
493
495 f_base,
496 faces_array,
497 &faces_array_tot,
498 edges_array,
499 &edges_array_tot,
500 r_faces_double,
501 quad_method,
502 ngon_method,
503 false,
504 pf_arena,
505 pf_heap);
506
507 for (int i = 0; i < edges_array_tot; i++) {
508 BMLoop *l_iter, *l_first;
509 l_iter = l_first = edges_array[i]->l;
510 do {
511 BM_elem_index_set(l_iter, f_index); /* set_dirty */
512 has_cut = true;
513 } while ((l_iter = l_iter->radial_next) != l_first);
514 }
515
516 for (int i = 0; i < faces_array_tot; i++) {
517 BM_face_normal_update(faces_array[i]);
518 }
519
520 *r_edges_tri_tot += edges_array_tot;
521
522 return has_cut;
523}
524
525static bool bm_decim_triangulate_begin(BMesh *bm, int *r_edges_tri_tot)
526{
527 BMIter iter;
528 BMFace *f;
529 bool has_ngon = false;
530 bool has_cut = false;
531
532 BLI_assert((bm->elem_index_dirty & BM_VERT) == 0);
533
534 /* first clear loop index values */
535 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
536 BMLoop *l_iter;
537 BMLoop *l_first;
538
539 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
540 do {
541 BM_elem_index_set(l_iter, -1); /* set_dirty */
542 } while ((l_iter = l_iter->next) != l_first);
543
544 has_ngon |= (f->len > 4);
545 }
546
547 bm->elem_index_dirty |= BM_LOOP;
548
549 {
550 MemArena *pf_arena;
551 Heap *pf_heap;
552
553 LinkNode *faces_double = nullptr;
554
555 if (has_ngon) {
556 pf_arena = BLI_memarena_new(BLI_POLYFILL_ARENA_SIZE, __func__);
558 }
559 else {
560 pf_arena = nullptr;
561 pf_heap = nullptr;
562 }
563
564 /* Adding new faces as we loop over faces
565 * is normally best avoided, however in this case its not so bad because any face touched twice
566 * will already be triangulated. */
567 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
568 if (f->len > 3) {
569 has_cut |= bm_face_triangulate(bm,
570 f,
571 &faces_double,
572 r_edges_tri_tot,
573
574 pf_arena,
575 pf_heap);
576 }
577 }
578
579 while (faces_double) {
580 LinkNode *next = faces_double->next;
581 BM_face_kill(bm, static_cast<BMFace *>(faces_double->link));
582 MEM_freeN(faces_double);
583 faces_double = next;
584 }
585
586 if (has_ngon) {
587 BLI_memarena_free(pf_arena);
588 BLI_heap_free(pf_heap, nullptr);
589 }
590
591 BLI_assert((bm->elem_index_dirty & BM_VERT) == 0);
592
593 if (has_cut) {
594 /* now triangulation is done we need to correct index values */
596 }
597 }
598
599 return has_cut;
600}
601
602static void bm_decim_triangulate_end(BMesh *bm, const int edges_tri_tot)
603{
604 /* decimation finished, now re-join */
605 BMIter iter;
606 BMEdge *e;
607
608 /* we need to collect before merging for ngons since the loops indices will be lost */
609 BMEdge **edges_tri = static_cast<BMEdge **>(
610 MEM_mallocN(std::min(edges_tri_tot, bm->totedge) * sizeof(*edges_tri), __func__));
611 STACK_DECLARE(edges_tri);
612
613 STACK_INIT(edges_tri, std::min(edges_tri_tot, bm->totedge));
614
615 /* boundary edges */
616 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
617 BMLoop *l_a, *l_b;
618 if (BM_edge_loop_pair(e, &l_a, &l_b)) {
619 const int l_a_index = BM_elem_index_get(l_a);
620 if (l_a_index != -1) {
621 const int l_b_index = BM_elem_index_get(l_b);
622 if (l_a_index == l_b_index) {
623 if (l_a->v != l_b->v) { /* if this is the case, faces have become flipped */
624 /* check we are not making a degenerate quad */
625
626# define CAN_LOOP_MERGE(l) \
627 (BM_loop_is_manifold(l) && ((l)->v != (l)->radial_next->v) && \
628 (l_a_index == BM_elem_index_get(l)) && (l_a_index == BM_elem_index_get((l)->radial_next)))
629
630 if ((l_a->f->len == 3 && l_b->f->len == 3) && !CAN_LOOP_MERGE(l_a->next) &&
631 !CAN_LOOP_MERGE(l_a->prev) && !CAN_LOOP_MERGE(l_b->next) &&
632 !CAN_LOOP_MERGE(l_b->prev))
633 {
634 BMVert *vquad[4] = {
635 e->v1,
636 BM_vert_in_edge(e, l_a->next->v) ? l_a->prev->v : l_a->next->v,
637 e->v2,
638 BM_vert_in_edge(e, l_b->next->v) ? l_b->prev->v : l_b->next->v,
639 };
640
641 BLI_assert(ELEM(vquad[0], vquad[1], vquad[2], vquad[3]) == false);
642 BLI_assert(ELEM(vquad[1], vquad[0], vquad[2], vquad[3]) == false);
643 BLI_assert(ELEM(vquad[2], vquad[1], vquad[0], vquad[3]) == false);
644 BLI_assert(ELEM(vquad[3], vquad[1], vquad[2], vquad[0]) == false);
645
646 if (!is_quad_convex_v3(vquad[0]->co, vquad[1]->co, vquad[2]->co, vquad[3]->co)) {
647 continue;
648 }
649 }
650# undef CAN_LOOP_MERGE
651
652 /* highly unlikely to fail, but prevents possible double-ups */
653 STACK_PUSH(edges_tri, e);
654 }
655 }
656 }
657 }
658 }
659
660 for (int i = 0; i < STACK_SIZE(edges_tri); i++) {
661 BMLoop *l_a, *l_b;
662 e = edges_tri[i];
663 if (BM_edge_loop_pair(e, &l_a, &l_b)) {
664 BMFace *f_double;
665
666 BMFace *f_array[2] = {l_a->f, l_b->f};
667 BM_faces_join(bm, f_array, 2, false, &f_double);
668 /* See #BM_faces_join note on callers asserting when `r_double` is non-null. */
669 BLI_assert_msg(f_double == nullptr,
670 "Doubled face detected at " AT ". Resulting mesh may be corrupt.");
671
672 if (e->l == nullptr) {
673 BM_edge_kill(bm, e);
674 }
675 }
676 }
677 MEM_freeN(edges_tri);
678}
679
680#endif /* USE_TRIANGULATE */
681
682/* Edge Collapse Functions
683 * *********************** */
684
685#ifdef USE_CUSTOMDATA
686
691 BMesh *bm, BMLoop *l, BMVert *v_clear, BMVert *v_other, const float customdata_fac)
692{
693 /* Disable seam check - the seam check would have to be done per layer,
694 * its not really that important. */
695 // #define USE_SEAM
696 /* these don't need to be updated, since they will get removed when the edge collapses */
697 BMLoop *l_clear, *l_other;
698 const bool is_manifold = BM_edge_is_manifold(l->e);
699 int side;
700
701 /* first find the loop of 'v_other' that's attached to the face of 'l' */
702 if (l->v == v_clear) {
703 l_clear = l;
704 l_other = l->next;
705 }
706 else {
707 l_clear = l->next;
708 l_other = l;
709 }
710
711 BLI_assert(l_clear->v == v_clear);
712 BLI_assert(l_other->v == v_other);
713 /* quiet warnings for release */
714 (void)v_other;
715
716 /* now we have both corners of the face 'l->f' */
717 for (side = 0; side < 2; side++) {
718# ifdef USE_SEAM
719 bool is_seam = false;
720# endif
721 void *src[2];
722 BMFace *f_exit = is_manifold ? l->radial_next->f : nullptr;
723 BMEdge *e_prev = l->e;
724 BMLoop *l_first;
725 BMLoop *l_iter;
726 float w[2];
727
728 if (side == 0) {
729 l_iter = l_first = l_clear;
730 src[0] = l_clear->head.data;
731 src[1] = l_other->head.data;
732
733 w[0] = customdata_fac;
734 w[1] = 1.0f - customdata_fac;
735 }
736 else {
737 l_iter = l_first = l_other;
738 src[0] = l_other->head.data;
739 src[1] = l_clear->head.data;
740
741 w[0] = 1.0f - customdata_fac;
742 w[1] = customdata_fac;
743 }
744
745 // print_v2("weights", w);
746
747 /* WATCH IT! - should NOT reference (_clear or _other) vars for this while loop */
748
749 /* walk around the fan using 'e_prev' */
750 while (((l_iter = BM_vert_step_fan_loop(l_iter, &e_prev)) != l_first) && (l_iter != nullptr)) {
751 int i;
752 /* quit once we hit the opposite face, if we have one */
753 if (f_exit && UNLIKELY(f_exit == l_iter->f)) {
754 break;
755 }
756
757# ifdef USE_SEAM
758 /* break out unless we find a match */
759 is_seam = true;
760# endif
761
762 /* ok. we have a loop. now be smart with it! */
763 for (i = 0; i < bm->ldata.totlayer; i++) {
764 if (CustomData_layer_has_math(&bm->ldata, i)) {
765 const int offset = bm->ldata.layers[i].offset;
766 const int type = bm->ldata.layers[i].type;
767 const void *cd_src[2] = {
768 POINTER_OFFSET(src[0], offset),
769 POINTER_OFFSET(src[1], offset),
770 };
771 void *cd_iter = POINTER_OFFSET(l_iter->head.data, offset);
772
773 /* detect seams */
774 if (CustomData_data_equals(eCustomDataType(type), cd_src[0], cd_iter)) {
776 cd_src,
777 w,
778 nullptr,
779 ARRAY_SIZE(cd_src),
780 POINTER_OFFSET(l_iter->head.data, offset),
781 i);
782# ifdef USE_SEAM
783 is_seam = false;
784# endif
785 }
786 }
787 }
788
789# ifdef USE_SEAM
790 if (is_seam) {
791 break;
792 }
793# endif
794 }
795 }
796
797 // #undef USE_SEAM
798}
799#endif /* USE_CUSTOMDATA */
800
809
811{
814 if (e->l) {
816 if (e->l != e->l->radial_next) {
817 BM_elem_flag_enable(e->l->radial_next->f, BM_ELEM_TAG);
818 }
819 }
820}
821
823{
826 if (e->l) {
828 if (e->l != e->l->radial_next) {
829 BM_elem_flag_disable(e->l->radial_next->f, BM_ELEM_TAG);
830 }
831 }
832}
833
835{
836 /* is the edge or one of its faces tagged? */
838 (e->l &&
840 (e->l != e->l->radial_next && BM_elem_flag_test(e->l->radial_next->f, BM_ELEM_TAG)))));
841}
842
843/* takes the edges loop */
845{
846#if 0
847 /* less optimized version of check below */
848 return (BM_edge_is_manifold(l->e) || BM_edge_is_boundary(l->e);
849#else
850 /* if the edge is a boundary it points to itself, else this must be a manifold */
851 return LIKELY(l) && LIKELY(l->radial_next->radial_next == l);
852#endif
853}
854
856{
857 /* simply check that there is no overlap between faces and edges of each vert,
858 * (excluding the 2 faces attached to 'e' and 'e' itself) */
859
860 BMEdge *e_iter;
861
862 /* clear flags on both disks */
863 e_iter = e_first;
864 do {
865 if (!bm_edge_is_manifold_or_boundary(e_iter->l)) {
866 return true;
867 }
868 bm_edge_tag_disable(e_iter);
869 } while ((e_iter = bmesh_disk_edge_next(e_iter, e_first->v1)) != e_first);
870
871 e_iter = e_first;
872 do {
873 if (!bm_edge_is_manifold_or_boundary(e_iter->l)) {
874 return true;
875 }
876 bm_edge_tag_disable(e_iter);
877 } while ((e_iter = bmesh_disk_edge_next(e_iter, e_first->v2)) != e_first);
878
879 /* now enable one side... */
880 e_iter = e_first;
881 do {
882 bm_edge_tag_enable(e_iter);
883 } while ((e_iter = bmesh_disk_edge_next(e_iter, e_first->v1)) != e_first);
884
885 /* ... except for the edge we will collapse, we know that's shared,
886 * disable this to avoid false positive. We could be smart and never enable these
887 * face/edge tags in the first place but easier to do this */
888 // bm_edge_tag_disable(e_first);
889 /* do inline... */
890 {
891#if 0
892 BMIter iter;
893 BMIter liter;
894 BMLoop *l;
895 BMVert *v;
896 BM_ITER_ELEM (l, &liter, e_first, BM_LOOPS_OF_EDGE) {
898 BM_ITER_ELEM (v, &iter, l->f, BM_VERTS_OF_FACE) {
900 }
901 }
902#else
903 /* we know each face is a triangle, no looping/iterators needed here */
904
905 BMLoop *l_radial;
906 BMLoop *l_face;
907
908 l_radial = e_first->l;
909 l_face = l_radial;
910 BLI_assert(l_face->f->len == 3);
912 BM_elem_flag_disable((l_face = l_radial)->v, BM_ELEM_TAG);
913 BM_elem_flag_disable((l_face = l_face->next)->v, BM_ELEM_TAG);
915 l_face = l_radial->radial_next;
916 if (l_radial != l_face) {
917 BLI_assert(l_face->f->len == 3);
919 BM_elem_flag_disable((l_face = l_radial->radial_next)->v, BM_ELEM_TAG);
920 BM_elem_flag_disable((l_face = l_face->next)->v, BM_ELEM_TAG);
922 }
923#endif
924 }
925
926 /* and check for overlap */
927 e_iter = e_first;
928 do {
929 if (bm_edge_tag_test(e_iter)) {
930 return true;
931 }
932 } while ((e_iter = bmesh_disk_edge_next(e_iter, e_first->v2)) != e_first);
933
934 return false;
935}
936
948 BMEdge *e_clear,
949 BMVert *v_clear,
950 int r_e_clear_other[2],
951#ifdef USE_SYMMETRY
952 int *edge_symmetry_map,
953#endif
954#ifdef USE_CUSTOMDATA
955 const CD_UseFlag customdata_flag,
956 const float customdata_fac
957#else
958 const CD_UseFlag /*customdata_flag*/,
959 const float /*customdata_fac*/
960#endif
961)
962{
963 BMVert *v_other;
964
965 v_other = BM_edge_other_vert(e_clear, v_clear);
966 BLI_assert(v_other != nullptr);
967
968 if (BM_edge_is_manifold(e_clear)) {
969 BMLoop *l_a, *l_b;
970 BMEdge *e_a_other[2], *e_b_other[2];
971 bool ok;
972
973 ok = BM_edge_loop_pair(e_clear, &l_a, &l_b);
974
975 BLI_assert(ok == true);
976 BLI_assert(l_a->f->len == 3);
977 BLI_assert(l_b->f->len == 3);
979
980 /* keep 'v_clear' 0th */
981 if (BM_vert_in_edge(l_a->prev->e, v_clear)) {
982 e_a_other[0] = l_a->prev->e;
983 e_a_other[1] = l_a->next->e;
984 }
985 else {
986 e_a_other[1] = l_a->prev->e;
987 e_a_other[0] = l_a->next->e;
988 }
989
990 if (BM_vert_in_edge(l_b->prev->e, v_clear)) {
991 e_b_other[0] = l_b->prev->e;
992 e_b_other[1] = l_b->next->e;
993 }
994 else {
995 e_b_other[1] = l_b->prev->e;
996 e_b_other[0] = l_b->next->e;
997 }
998
999/* we could assert this case, but better just bail out */
1000#if 0
1001 BLI_assert(e_a_other[0] != e_b_other[0]);
1002 BLI_assert(e_a_other[0] != e_b_other[1]);
1003 BLI_assert(e_b_other[0] != e_a_other[0]);
1004 BLI_assert(e_b_other[0] != e_a_other[1]);
1005#endif
1006 /* not totally common but we want to avoid */
1007 if (ELEM(e_a_other[0], e_b_other[0], e_b_other[1]) ||
1008 ELEM(e_a_other[1], e_b_other[0], e_b_other[1]))
1009 {
1010 return false;
1011 }
1012
1013 BLI_assert(BM_edge_share_vert(e_a_other[0], e_b_other[0]));
1014 BLI_assert(BM_edge_share_vert(e_a_other[1], e_b_other[1]));
1015
1016 r_e_clear_other[0] = BM_elem_index_get(e_a_other[0]);
1017 r_e_clear_other[1] = BM_elem_index_get(e_b_other[0]);
1018
1019#ifdef USE_CUSTOMDATA
1020 /* before killing, do customdata */
1021 if (customdata_flag & CD_DO_VERT) {
1022 BM_data_interp_from_verts(bm, v_other, v_clear, v_other, customdata_fac);
1023 }
1024 if (customdata_flag & CD_DO_EDGE) {
1025 BM_data_interp_from_edges(bm, e_a_other[1], e_a_other[0], e_a_other[1], customdata_fac);
1026 BM_data_interp_from_edges(bm, e_b_other[1], e_b_other[0], e_b_other[1], customdata_fac);
1027 }
1028 if (customdata_flag & CD_DO_LOOP) {
1029 bm_edge_collapse_loop_customdata(bm, e_clear->l, v_clear, v_other, customdata_fac);
1031 bm, e_clear->l->radial_next, v_clear, v_other, customdata_fac);
1032 }
1033#endif
1034
1035 BM_edge_kill(bm, e_clear);
1036
1037 v_other->head.hflag |= v_clear->head.hflag;
1038 BM_vert_splice(bm, v_other, v_clear);
1039
1040 e_a_other[1]->head.hflag |= e_a_other[0]->head.hflag;
1041 e_b_other[1]->head.hflag |= e_b_other[0]->head.hflag;
1042 BM_edge_splice(bm, e_a_other[1], e_a_other[0]);
1043 BM_edge_splice(bm, e_b_other[1], e_b_other[0]);
1044
1045#ifdef USE_SYMMETRY
1046 /* update mirror map */
1047 if (edge_symmetry_map) {
1048 if (edge_symmetry_map[r_e_clear_other[0]] != -1) {
1049 edge_symmetry_map[edge_symmetry_map[r_e_clear_other[0]]] = BM_elem_index_get(e_a_other[1]);
1050 }
1051 if (edge_symmetry_map[r_e_clear_other[1]] != -1) {
1052 edge_symmetry_map[edge_symmetry_map[r_e_clear_other[1]]] = BM_elem_index_get(e_b_other[1]);
1053 }
1054 }
1055#endif
1056
1057 // BM_mesh_validate(bm);
1058
1059 return true;
1060 }
1061 if (BM_edge_is_boundary(e_clear)) {
1062 /* same as above but only one triangle */
1063 BMLoop *l_a;
1064 BMEdge *e_a_other[2];
1065
1066 l_a = e_clear->l;
1067
1068 BLI_assert(l_a->f->len == 3);
1069
1070 /* keep 'v_clear' 0th */
1071 if (BM_vert_in_edge(l_a->prev->e, v_clear)) {
1072 e_a_other[0] = l_a->prev->e;
1073 e_a_other[1] = l_a->next->e;
1074 }
1075 else {
1076 e_a_other[1] = l_a->prev->e;
1077 e_a_other[0] = l_a->next->e;
1078 }
1079
1080 r_e_clear_other[0] = BM_elem_index_get(e_a_other[0]);
1081 r_e_clear_other[1] = -1;
1082
1083#ifdef USE_CUSTOMDATA
1084 /* before killing, do customdata */
1085 if (customdata_flag & CD_DO_VERT) {
1086 BM_data_interp_from_verts(bm, v_other, v_clear, v_other, customdata_fac);
1087 }
1088 if (customdata_flag & CD_DO_EDGE) {
1089 BM_data_interp_from_edges(bm, e_a_other[1], e_a_other[0], e_a_other[1], customdata_fac);
1090 }
1091 if (customdata_flag & CD_DO_LOOP) {
1092 bm_edge_collapse_loop_customdata(bm, e_clear->l, v_clear, v_other, customdata_fac);
1093 }
1094#endif
1095
1096 BM_edge_kill(bm, e_clear);
1097
1098 v_other->head.hflag |= v_clear->head.hflag;
1099 BM_vert_splice(bm, v_other, v_clear);
1100
1101 e_a_other[1]->head.hflag |= e_a_other[0]->head.hflag;
1102 BM_edge_splice(bm, e_a_other[1], e_a_other[0]);
1103
1104#ifdef USE_SYMMETRY
1105 /* update mirror map */
1106 if (edge_symmetry_map) {
1107 if (edge_symmetry_map[r_e_clear_other[0]] != -1) {
1108 edge_symmetry_map[edge_symmetry_map[r_e_clear_other[0]]] = BM_elem_index_get(e_a_other[1]);
1109 }
1110 }
1111#endif
1112
1113 // BM_mesh_validate(bm);
1114
1115 return true;
1116 }
1117 return false;
1118}
1119
1126 BMEdge *e,
1127 Quadric *vquadrics,
1128 float *vweights,
1129 const float vweight_factor,
1130 Heap *eheap,
1131 HeapNode **eheap_table,
1132#ifdef USE_SYMMETRY
1133 int *edge_symmetry_map,
1134#endif
1135 const CD_UseFlag customdata_flag,
1136 float optimize_co[3],
1137 bool optimize_co_calc)
1138{
1139 int e_clear_other[2];
1140 BMVert *v_other = e->v1;
1141 const int v_other_index = BM_elem_index_get(e->v1);
1142 /* the vert is removed so only store the index */
1143 const int v_clear_index = BM_elem_index_get(e->v2);
1144 float customdata_fac;
1145
1146#ifdef USE_VERT_NORMAL_INTERP
1147 float v_clear_no[3];
1148 copy_v3_v3(v_clear_no, e->v2->no);
1149#endif
1150
1151 /* when false, use without degenerate checks */
1152 if (optimize_co_calc) {
1153 /* disallow collapsing which results in degenerate cases */
1155 /* add back with a high cost */
1156 bm_decim_invalid_edge_cost_single(e, eheap, eheap_table);
1157 return false;
1158 }
1159
1160 bm_decim_calc_target_co_fl(e, optimize_co, vquadrics);
1161
1162 /* check if this would result in an overlapping face */
1163 if (UNLIKELY(bm_edge_collapse_is_degenerate_flip(e, optimize_co))) {
1164 /* add back with a high cost */
1165 bm_decim_invalid_edge_cost_single(e, eheap, eheap_table);
1166 return false;
1167 }
1168 }
1169
1170 /* use for customdata merging */
1171 if (LIKELY(compare_v3v3(e->v1->co, e->v2->co, FLT_EPSILON) == false)) {
1172 customdata_fac = line_point_factor_v3(optimize_co, e->v1->co, e->v2->co);
1173#if 0
1174 /* simple test for stupid collapse */
1175 if (customdata_fac < 0.0 - FLT_EPSILON || customdata_fac > 1.0f + FLT_EPSILON) {
1176 return false;
1177 }
1178#endif
1179 }
1180 else {
1181 /* avoid divide by zero */
1182 customdata_fac = 0.5f;
1183 }
1184
1185 if (bm_edge_collapse(bm,
1186 e,
1187 e->v2,
1188 e_clear_other,
1189#ifdef USE_SYMMETRY
1190 edge_symmetry_map,
1191#endif
1192 customdata_flag,
1193 customdata_fac))
1194 {
1195 /* update collapse info */
1196 int i;
1197
1198 if (vweights) {
1199 float v_other_weight = interpf(
1200 vweights[v_other_index], vweights[v_clear_index], customdata_fac);
1201 CLAMP(v_other_weight, 0.0f, 1.0f);
1202 vweights[v_other_index] = v_other_weight;
1203 }
1204
1205 /* paranoid safety check */
1206 e = nullptr;
1207
1208 copy_v3_v3(v_other->co, optimize_co);
1209
1210 /* remove eheap */
1211 for (i = 0; i < 2; i++) {
1212 /* highly unlikely 'eheap_table[ke_other[i]]' would be nullptr, but do for sanity sake */
1213 if ((e_clear_other[i] != -1) && (eheap_table[e_clear_other[i]] != nullptr)) {
1214 BLI_heap_remove(eheap, eheap_table[e_clear_other[i]]);
1215 eheap_table[e_clear_other[i]] = nullptr;
1216 }
1217 }
1218
1219 /* update vertex quadric, add kept vertex from killed vertex */
1220 BLI_quadric_add_qu_qu(&vquadrics[v_other_index], &vquadrics[v_clear_index]);
1221
1222/* update connected normals */
1223
1224/* in fact face normals are not used for progressive updates, no need to update them */
1225// BM_vert_normal_update_all(v);
1226#ifdef USE_VERT_NORMAL_INTERP
1227 interp_v3_v3v3(v_other->no, v_other->no, v_clear_no, customdata_fac);
1228 normalize_v3(v_other->no);
1229#else
1230 BM_vert_normal_update(v_other);
1231#endif
1232
1233 /* update error costs and the eheap */
1234 if (LIKELY(v_other->e)) {
1235 BMEdge *e_iter;
1236 BMEdge *e_first;
1237 e_iter = e_first = v_other->e;
1238 do {
1239 BLI_assert(BM_edge_find_double(e_iter) == nullptr);
1241 e_iter, vquadrics, vweights, vweight_factor, eheap, eheap_table);
1242 } while ((e_iter = bmesh_disk_edge_next(e_iter, v_other)) != e_first);
1243 }
1244
1245/* this block used to be disabled,
1246 * but enable now since surrounding faces may have been
1247 * set to COST_INVALID because of a face overlap that no longer occurs */
1248#if 1
1249 /* optional, update edges around the vertex face fan */
1250 {
1251 BMIter liter;
1252 BMLoop *l;
1253 BM_ITER_ELEM (l, &liter, v_other, BM_LOOPS_OF_VERT) {
1254 if (l->f->len == 3) {
1255 BMEdge *e_outer;
1256 if (BM_vert_in_edge(l->prev->e, l->v)) {
1257 e_outer = l->next->e;
1258 }
1259 else {
1260 e_outer = l->prev->e;
1261 }
1262
1263 BLI_assert(BM_vert_in_edge(e_outer, l->v) == false);
1264
1266 e_outer, vquadrics, vweights, vweight_factor, eheap, eheap_table);
1267 }
1268 }
1269 }
1270 /* end optional update */
1271 return true;
1272#endif
1273 }
1274 /* add back with a high cost */
1275 bm_decim_invalid_edge_cost_single(e, eheap, eheap_table);
1276 return false;
1277}
1278
1279/* Main Decimate Function
1280 * ********************** */
1281
1283 const float factor,
1284 float *vweights,
1285 float vweight_factor,
1286 const bool do_triangulate,
1287 const int symmetry_axis,
1288 const float symmetry_eps)
1289{
1290 /* edge heap */
1291 Heap *eheap;
1292 /* edge index aligned table pointing to the eheap */
1293 HeapNode **eheap_table;
1294 /* vert index aligned quadrics */
1295 Quadric *vquadrics;
1296 int tot_edge_orig;
1297 int face_tot_target;
1298
1299 CD_UseFlag customdata_flag = CD_UseFlag(0);
1300
1301#ifdef USE_SYMMETRY
1302 bool use_symmetry = (symmetry_axis != -1);
1303 int *edge_symmetry_map;
1304#endif
1305
1306#ifdef USE_TRIANGULATE
1307 int edges_tri_tot = 0;
1308 /* temp convert quads to triangles */
1309 bool use_triangulate = bm_decim_triangulate_begin(bm, &edges_tri_tot);
1310#else
1311 UNUSED_VARS(do_triangulate);
1312#endif
1313
1314 /* Allocate variables. */
1315 vquadrics = MEM_calloc_arrayN<Quadric>(bm->totvert, __func__);
1316 /* Since some edges may be degenerate, we might be over allocating a little here. */
1317 eheap = BLI_heap_new_ex(bm->totedge);
1318 eheap_table = MEM_malloc_arrayN<HeapNode *>(bm->totedge, __func__);
1319 tot_edge_orig = bm->totedge;
1320
1321 /* build initial edge collapse cost data */
1322 bm_decim_build_quadrics(bm, vquadrics);
1323
1324 bm_decim_build_edge_cost(bm, vquadrics, vweights, vweight_factor, eheap, eheap_table);
1325
1326 face_tot_target = bm->totface * factor;
1327 bm->elem_index_dirty |= BM_ALL;
1328
1329#ifdef USE_SYMMETRY
1330 edge_symmetry_map = (use_symmetry) ? bm_edge_symmetry_map(bm, symmetry_axis, symmetry_eps) :
1331 nullptr;
1332#else
1333 UNUSED_VARS(symmetry_axis, symmetry_eps);
1334#endif
1335
1336#ifdef USE_CUSTOMDATA
1337 /* initialize customdata flag, we only need math for loops */
1338 if (CustomData_has_interp(&bm->vdata)) {
1339 customdata_flag |= CD_DO_VERT;
1340 }
1341 if (CustomData_has_interp(&bm->edata)) {
1342 customdata_flag |= CD_DO_EDGE;
1343 }
1344 if (CustomData_has_math(&bm->ldata)) {
1345 customdata_flag |= CD_DO_LOOP;
1346 }
1347#endif
1348
1349/* iterative edge collapse and maintain the eheap */
1350#ifdef USE_SYMMETRY
1351 if (use_symmetry == false)
1352#endif
1353 {
1354 /* simple non-mirror case */
1355 while ((bm->totface > face_tot_target) && (BLI_heap_is_empty(eheap) == false) &&
1356 (BLI_heap_top_value(eheap) != COST_INVALID))
1357 {
1358 // const float value = BLI_heap_node_value(BLI_heap_top(eheap));
1359 BMEdge *e = static_cast<BMEdge *>(BLI_heap_pop_min(eheap));
1360 float optimize_co[3];
1361 /* handy to detect corruptions elsewhere */
1362 BLI_assert(BM_elem_index_get(e) < tot_edge_orig);
1363
1364 /* Under normal conditions won't be accessed again,
1365 * but nullptr just in case so we don't use freed node. */
1366 eheap_table[BM_elem_index_get(e)] = nullptr;
1367
1369 e,
1370 vquadrics,
1371 vweights,
1372 vweight_factor,
1373 eheap,
1374 eheap_table,
1375#ifdef USE_SYMMETRY
1376 edge_symmetry_map,
1377#endif
1378 customdata_flag,
1379 optimize_co,
1380 true);
1381 }
1382 }
1383#ifdef USE_SYMMETRY
1384 else {
1385 while ((bm->totface > face_tot_target) && (BLI_heap_is_empty(eheap) == false) &&
1386 (BLI_heap_top_value(eheap) != COST_INVALID))
1387 {
1388 /* NOTE:
1389 * - `eheap_table[e_index_mirr]` is only removed from the heap at the last moment
1390 * since its possible (in theory) for collapsing `e` to remove `e_mirr`.
1391 * - edges sharing a vertex are ignored, so the pivot vertex isn't moved to one side.
1392 */
1393
1394 BMEdge *e = static_cast<BMEdge *>(BLI_heap_pop_min(eheap));
1395 const int e_index = BM_elem_index_get(e);
1396 const int e_index_mirr = edge_symmetry_map[e_index];
1397 BMEdge *e_mirr = nullptr;
1398 float optimize_co[3];
1399 char e_invalidate = 0;
1400
1401 BLI_assert(e_index < tot_edge_orig);
1402
1403 eheap_table[e_index] = nullptr;
1404
1405 if (e_index_mirr != -1) {
1406 if (e_index_mirr == e_index) {
1407 /* pass */
1408 }
1409 else if (eheap_table[e_index_mirr]) {
1410 e_mirr = static_cast<BMEdge *>(BLI_heap_node_ptr(eheap_table[e_index_mirr]));
1411 /* for now ignore edges with a shared vertex */
1412 if (BM_edge_share_vert_check(e, e_mirr)) {
1413 /* ignore permanently!
1414 * Otherwise we would keep re-evaluating and attempting to collapse. */
1415 // e_invalidate |= (1 | 2);
1416 goto invalidate;
1417 }
1418 }
1419 else {
1420 /* mirror edge can't be operated on (happens with asymmetrical meshes) */
1421 e_invalidate |= 1;
1422 goto invalidate;
1423 }
1424 }
1425
1426 /* when false, use without degenerate checks */
1427 {
1428 /* run both before checking (since they invalidate surrounding geometry) */
1429 bool ok_a, ok_b;
1430
1432 ok_b = e_mirr ? !bm_edge_collapse_is_degenerate_topology(e_mirr) : true;
1433
1434 /* disallow collapsing which results in degenerate cases */
1435
1436 if (UNLIKELY(!ok_a || !ok_b)) {
1437 e_invalidate |= (1 | (e_mirr ? 2 : 0));
1438 goto invalidate;
1439 }
1440
1441 bm_decim_calc_target_co_fl(e, optimize_co, vquadrics);
1442
1443 if (e_index_mirr == e_index) {
1444 optimize_co[symmetry_axis] = 0.0f;
1445 }
1446
1447 /* check if this would result in an overlapping face */
1448 if (UNLIKELY(bm_edge_collapse_is_degenerate_flip(e, optimize_co))) {
1449 e_invalidate |= (1 | (e_mirr ? 2 : 0));
1450 goto invalidate;
1451 }
1452 }
1453
1455 e,
1456 vquadrics,
1457 vweights,
1458 vweight_factor,
1459 eheap,
1460 eheap_table,
1461 edge_symmetry_map,
1462 customdata_flag,
1463 optimize_co,
1464 false))
1465 {
1466 if (e_mirr && (eheap_table[e_index_mirr])) {
1467 BLI_assert(e_index_mirr != e_index);
1468 BLI_heap_remove(eheap, eheap_table[e_index_mirr]);
1469 eheap_table[e_index_mirr] = nullptr;
1470 optimize_co[symmetry_axis] *= -1.0f;
1472 e_mirr,
1473 vquadrics,
1474 vweights,
1475 vweight_factor,
1476 eheap,
1477 eheap_table,
1478 edge_symmetry_map,
1479 customdata_flag,
1480 optimize_co,
1481 false);
1482 }
1483 }
1484 else {
1485 if (e_mirr && (eheap_table[e_index_mirr])) {
1486 e_invalidate |= 2;
1487 goto invalidate;
1488 }
1489 }
1490
1491 BLI_assert(e_invalidate == 0);
1492 continue;
1493
1494 invalidate:
1495 if (e_invalidate & 1) {
1496 bm_decim_invalid_edge_cost_single(e, eheap, eheap_table);
1497 }
1498
1499 if (e_invalidate & 2) {
1500 BLI_assert(eheap_table[e_index_mirr] != nullptr);
1501 BLI_heap_remove(eheap, eheap_table[e_index_mirr]);
1502 eheap_table[e_index_mirr] = nullptr;
1503 bm_decim_invalid_edge_cost_single(e_mirr, eheap, eheap_table);
1504 }
1505 }
1506
1507 MEM_freeN(edge_symmetry_map);
1508 }
1509#endif /* USE_SYMMETRY */
1510
1511#ifdef USE_TRIANGULATE
1512 if (do_triangulate == false) {
1513 /* its possible we only had triangles, skip this step in that case */
1514 if (LIKELY(use_triangulate)) {
1515 /* temp convert quads to triangles */
1516 bm_decim_triangulate_end(bm, edges_tri_tot);
1517 }
1518 }
1519#endif
1520
1521 /* free vars */
1522 MEM_freeN(vquadrics);
1523 MEM_freeN(eheap_table);
1524 BLI_heap_free(eheap, nullptr);
1525
1526 /* testing only */
1527 // BM_mesh_validate(bm);
1528
1529 /* quiet release build warning */
1530 (void)tot_edge_orig;
1531}
CustomData interface, see also DNA_customdata_types.h.
bool CustomData_layer_has_math(const CustomData *data, int layer_n)
bool CustomData_has_interp(const CustomData *data)
bool CustomData_data_equals(eCustomDataType type, const void *data1, const void *data2)
bool CustomData_has_math(const CustomData *data)
void CustomData_bmesh_interp_n(CustomData *data, const void **src_blocks, const float *weights, const float *sub_weights, int count, void *dst_block_ofs, int n)
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:18
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define BLI_INLINE
A min-heap / priority queue ADT.
void BLI_heap_free(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition BLI_heap.cc:191
void BLI_heap_insert_or_update(Heap *heap, HeapNode **node_p, float value, void *ptr) ATTR_NONNULL(1
Heap * BLI_heap_new_ex(unsigned int reserve_num) ATTR_WARN_UNUSED_RESULT
Definition BLI_heap.cc:171
void void bool BLI_heap_is_empty(const Heap *heap) ATTR_NONNULL(1)
Definition BLI_heap.cc:269
float BLI_heap_top_value(const Heap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.cc:284
void * BLI_heap_pop_min(Heap *heap) ATTR_NONNULL(1)
Definition BLI_heap.cc:291
void * BLI_heap_node_ptr(const HeapNode *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.cc:352
HeapNode * BLI_heap_insert(Heap *heap, float value, void *ptr) ATTR_NONNULL(1)
Definition BLI_heap.cc:234
void void BLI_heap_remove(Heap *heap, HeapNode *node) ATTR_NONNULL(1
A KD-tree for nearest neighbor search.
MINLINE float min_ff(float a, float b)
MINLINE float square_f(float a)
MINLINE float interpf(float target, float origin, float t)
bool is_quad_convex_v3(const float v1[3], const float v2[3], const float v3[3], const float v4[3])
float line_point_factor_v3(const float p[3], const float l1[3], const float l2[3])
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:41
MINLINE double normalize_v3_db(double n[3])
MINLINE float len_squared_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE double dot_v3db_v3fl(const double a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3fl_v3db(float r[3], const double a[3])
MINLINE void copy_v3db_v3fl(double r[3], const float a[3])
MINLINE bool compare_v3v3(const float v1[3], const float v2[3], float limit) ATTR_WARN_UNUSED_RESULT
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float normalize_v3(float n[3])
MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
void BLI_memarena_free(MemArena *ma) ATTR_NONNULL(1)
#define BLI_POLYFILL_ARENA_SIZE
#define BLI_POLYFILL_ALLOC_NGON_RESERVE
void BLI_quadric_mul(Quadric *a, double scalar)
Definition quadric.cc:125
bool BLI_quadric_optimize(const Quadric *q, double v[3], double epsilon)
Definition quadric.cc:142
void BLI_quadric_from_plane(Quadric *q, const double v[4])
Definition quadric.cc:31
double BLI_quadric_evaluate(const Quadric *q, const double v[3])
Definition quadric.cc:130
void BLI_quadric_add_qu_qu(Quadric *a, const Quadric *b)
Definition quadric.cc:115
void BLI_quadric_add_qu_ququ(Quadric *r, const Quadric *a, const Quadric *b)
Definition quadric.cc:120
unsigned int uint
#define CLAMP(a, b, c)
#define ARRAY_SIZE(arr)
#define UNUSED_VARS(...)
#define ENUM_OPERATORS(_type, _max)
#define UNUSED_VARS_NDEBUG(...)
#define UNLIKELY(x)
#define ELEM(...)
#define POINTER_OFFSET(v, ofs)
#define AT
#define LIKELY(x)
#define STACK_PUSH(stack, val)
#define STACK_DECLARE(stack)
#define STACK_SIZE(stack)
#define STACK_INIT(stack, stack_num)
Read Guarded memory(de)allocation.
#define BM_ALL
#define BM_FACE_FIRST_LOOP(p)
@ BM_LOOP
@ BM_ELEM_TAG
bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src)
Splice Edge.
BMFace * BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del, BMFace **r_double)
Join Connected Faces.
bool BM_vert_splice(BMesh *bm, BMVert *v_dst, BMVert *v_src)
Splice Vert.
void BM_face_kill(BMesh *bm, BMFace *f)
void BM_edge_kill(BMesh *bm, BMEdge *e)
static void bm_edge_tag_enable(BMEdge *e)
static void bm_decim_invalid_edge_cost_single(BMEdge *e, Heap *eheap, HeapNode **eheap_table)
static bool bm_edge_collapse_is_degenerate_topology(BMEdge *e_first)
static void bm_decim_build_edge_cost(BMesh *bm, const Quadric *vquadrics, const float *vweights, const float vweight_factor, Heap *eheap, HeapNode **eheap_table)
#define TOPOLOGY_FALLBACK_EPS
#define USE_CUSTOMDATA
static void bm_edge_tag_disable(BMEdge *e)
static bool bm_edge_collapse_is_degenerate_flip(BMEdge *e, const float optimize_co[3])
void BM_mesh_decimate_collapse(BMesh *bm, const float factor, float *vweights, float vweight_factor, const bool do_triangulate, const int symmetry_axis, const float symmetry_eps)
BM_mesh_decimate.
static void bm_decim_triangulate_end(BMesh *bm, const int edges_tri_tot)
static void bm_decim_calc_target_co_fl(BMEdge *e, float optimize_co[3], const Quadric *vquadrics)
static bool bm_edge_collapse(BMesh *bm, BMEdge *e_clear, BMVert *v_clear, int r_e_clear_other[2], int *edge_symmetry_map, const CD_UseFlag customdata_flag, const float customdata_fac)
static bool bm_face_triangulate(BMesh *bm, BMFace *f_base, LinkNode **r_faces_double, int *r_edges_tri_tot, MemArena *pf_arena, Heap *pf_heap)
#define BOUNDARY_PRESERVE_WEIGHT
static void bm_decim_build_edge_cost_single(BMEdge *e, const Quadric *vquadrics, const float *vweights, const float vweight_factor, Heap *eheap, HeapNode **eheap_table)
#define COST_INVALID
#define CAN_LOOP_MERGE(l)
BLI_INLINE int bm_edge_is_manifold_or_boundary(BMLoop *l)
static float bm_decim_build_edge_cost_single__topology(BMEdge *e)
static bool bm_decim_triangulate_begin(BMesh *bm, int *r_edges_tri_tot)
#define USE_SYMMETRY
static bool bm_edge_symmetry_check_cb(void *user_data, int index, const float[3], float)
static bool bm_decim_edge_collapse(BMesh *bm, BMEdge *e, Quadric *vquadrics, float *vweights, const float vweight_factor, Heap *eheap, HeapNode **eheap_table, int *edge_symmetry_map, const CD_UseFlag customdata_flag, float optimize_co[3], bool optimize_co_calc)
static void bm_decim_calc_target_co_db(BMEdge *e, double optimize_co[3], const Quadric *vquadrics)
#define OPTIMIZE_EPS
static int * bm_edge_symmetry_map(BMesh *bm, uint symmetry_axis, float limit)
static float bm_decim_build_edge_cost_single_squared__topology(BMEdge *e)
static bool bm_edge_tag_test(BMEdge *e)
static void bm_edge_collapse_loop_customdata(BMesh *bm, BMLoop *l, BMVert *v_clear, BMVert *v_other, const float customdata_fac)
static void bm_decim_build_quadrics(BMesh *bm, Quadric *vquadrics)
#define BM_elem_index_get(ele)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_index_set(ele, index)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
void BM_data_interp_from_edges(BMesh *bm, const BMEdge *e_src_1, const BMEdge *e_src_2, BMEdge *e_dst, const float fac)
Data, Interpolate From Edges.
void BM_data_interp_from_verts(BMesh *bm, const BMVert *v_src_1, const BMVert *v_src_2, BMVert *v_dst, const float fac)
Data, Interpolate From Verts.
#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_FACE
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_VERT
@ BM_LOOPS_OF_EDGE
BMesh * bm
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
void BM_vert_normal_update(BMVert *v)
void BM_face_normal_update(BMFace *f)
void BM_face_triangulate(BMesh *bm, BMFace *f, BMFace **r_faces_new, int *r_faces_new_tot, BMEdge **r_edges_new, int *r_edges_new_tot, LinkNode **r_faces_double, const int quad_method, const int ngon_method, const bool use_tag, MemArena *pf_arena, Heap *pf_heap)
void BM_face_calc_center_median(const BMFace *f, float r_cent[3])
BMVert * BM_edge_share_vert(BMEdge *e1, BMEdge *e2)
BMLoop * BM_vert_step_fan_loop(BMLoop *l, BMEdge **e_step)
bool BM_edge_loop_pair(BMEdge *e, BMLoop **r_la, BMLoop **r_lb)
BMEdge * BM_edge_find_double(BMEdge *e)
bool BM_edge_share_vert_check(BMEdge *e1, BMEdge *e2)
float BM_edge_calc_length(const BMEdge *e)
BLI_INLINE bool BM_edge_is_manifold(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_boundary(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_vert_in_edge(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMLoop * l_b
ATTR_WARN_UNUSED_RESULT const BMVert * v
BLI_INLINE BMEdge * bmesh_disk_edge_next(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
SIMD_FORCE_INLINE void invalidate()
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
#define fabsf(x)
KDTree_3d * tree
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong * next
static void clear(Message &msg)
Definition msgfmt.cc:213
BMHeader head
BMVert * v1
BMVert * v2
struct BMLoop * l
float no[3]
void * data
BMHeader head
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]
BMHeader head
void * link
struct LinkNode * next
i
Definition text_draw.cc:230