Blender V5.0
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 ARRAY_SIZE(cd_src),
779 POINTER_OFFSET(l_iter->head.data, offset),
780 i);
781# ifdef USE_SEAM
782 is_seam = false;
783# endif
784 }
785 }
786 }
787
788# ifdef USE_SEAM
789 if (is_seam) {
790 break;
791 }
792# endif
793 }
794 }
795
796 // #undef USE_SEAM
797}
798#endif /* USE_CUSTOMDATA */
799
808
810{
813 if (e->l) {
815 if (e->l != e->l->radial_next) {
816 BM_elem_flag_enable(e->l->radial_next->f, BM_ELEM_TAG);
817 }
818 }
819}
820
822{
825 if (e->l) {
827 if (e->l != e->l->radial_next) {
828 BM_elem_flag_disable(e->l->radial_next->f, BM_ELEM_TAG);
829 }
830 }
831}
832
834{
835 /* is the edge or one of its faces tagged? */
837 (e->l &&
839 (e->l != e->l->radial_next && BM_elem_flag_test(e->l->radial_next->f, BM_ELEM_TAG)))));
840}
841
842/* takes the edges loop */
844{
845#if 0
846 /* less optimized version of check below */
847 return (BM_edge_is_manifold(l->e) || BM_edge_is_boundary(l->e);
848#else
849 /* if the edge is a boundary it points to itself, else this must be a manifold */
850 return LIKELY(l) && LIKELY(l->radial_next->radial_next == l);
851#endif
852}
853
855{
856 /* simply check that there is no overlap between faces and edges of each vert,
857 * (excluding the 2 faces attached to 'e' and 'e' itself) */
858
859 BMEdge *e_iter;
860
861 /* clear flags on both disks */
862 e_iter = e_first;
863 do {
864 if (!bm_edge_is_manifold_or_boundary(e_iter->l)) {
865 return true;
866 }
867 bm_edge_tag_disable(e_iter);
868 } while ((e_iter = bmesh_disk_edge_next(e_iter, e_first->v1)) != e_first);
869
870 e_iter = e_first;
871 do {
872 if (!bm_edge_is_manifold_or_boundary(e_iter->l)) {
873 return true;
874 }
875 bm_edge_tag_disable(e_iter);
876 } while ((e_iter = bmesh_disk_edge_next(e_iter, e_first->v2)) != e_first);
877
878 /* now enable one side... */
879 e_iter = e_first;
880 do {
881 bm_edge_tag_enable(e_iter);
882 } while ((e_iter = bmesh_disk_edge_next(e_iter, e_first->v1)) != e_first);
883
884 /* ... except for the edge we will collapse, we know that's shared,
885 * disable this to avoid false positive. We could be smart and never enable these
886 * face/edge tags in the first place but easier to do this */
887 // bm_edge_tag_disable(e_first);
888 /* do inline... */
889 {
890#if 0
891 BMIter iter;
892 BMIter liter;
893 BMLoop *l;
894 BMVert *v;
895 BM_ITER_ELEM (l, &liter, e_first, BM_LOOPS_OF_EDGE) {
897 BM_ITER_ELEM (v, &iter, l->f, BM_VERTS_OF_FACE) {
899 }
900 }
901#else
902 /* we know each face is a triangle, no looping/iterators needed here */
903
904 BMLoop *l_radial;
905 BMLoop *l_face;
906
907 l_radial = e_first->l;
908 l_face = l_radial;
909 BLI_assert(l_face->f->len == 3);
911 BM_elem_flag_disable((l_face = l_radial)->v, BM_ELEM_TAG);
912 BM_elem_flag_disable((l_face = l_face->next)->v, BM_ELEM_TAG);
914 l_face = l_radial->radial_next;
915 if (l_radial != l_face) {
916 BLI_assert(l_face->f->len == 3);
918 BM_elem_flag_disable((l_face = l_radial->radial_next)->v, BM_ELEM_TAG);
919 BM_elem_flag_disable((l_face = l_face->next)->v, BM_ELEM_TAG);
921 }
922#endif
923 }
924
925 /* and check for overlap */
926 e_iter = e_first;
927 do {
928 if (bm_edge_tag_test(e_iter)) {
929 return true;
930 }
931 } while ((e_iter = bmesh_disk_edge_next(e_iter, e_first->v2)) != e_first);
932
933 return false;
934}
935
947 BMEdge *e_clear,
948 BMVert *v_clear,
949 int r_e_clear_other[2],
950#ifdef USE_SYMMETRY
951 int *edge_symmetry_map,
952#endif
953#ifdef USE_CUSTOMDATA
954 const CD_UseFlag customdata_flag,
955 const float customdata_fac
956#else
957 const CD_UseFlag /*customdata_flag*/,
958 const float /*customdata_fac*/
959#endif
960)
961{
962 BMVert *v_other;
963
964 v_other = BM_edge_other_vert(e_clear, v_clear);
965 BLI_assert(v_other != nullptr);
966
967 if (BM_edge_is_manifold(e_clear)) {
968 BMLoop *l_a, *l_b;
969 BMEdge *e_a_other[2], *e_b_other[2];
970 bool ok;
971
972 ok = BM_edge_loop_pair(e_clear, &l_a, &l_b);
973
974 BLI_assert(ok == true);
975 BLI_assert(l_a->f->len == 3);
976 BLI_assert(l_b->f->len == 3);
978
979 /* keep 'v_clear' 0th */
980 if (BM_vert_in_edge(l_a->prev->e, v_clear)) {
981 e_a_other[0] = l_a->prev->e;
982 e_a_other[1] = l_a->next->e;
983 }
984 else {
985 e_a_other[1] = l_a->prev->e;
986 e_a_other[0] = l_a->next->e;
987 }
988
989 if (BM_vert_in_edge(l_b->prev->e, v_clear)) {
990 e_b_other[0] = l_b->prev->e;
991 e_b_other[1] = l_b->next->e;
992 }
993 else {
994 e_b_other[1] = l_b->prev->e;
995 e_b_other[0] = l_b->next->e;
996 }
997
998/* we could assert this case, but better just bail out */
999#if 0
1000 BLI_assert(e_a_other[0] != e_b_other[0]);
1001 BLI_assert(e_a_other[0] != e_b_other[1]);
1002 BLI_assert(e_b_other[0] != e_a_other[0]);
1003 BLI_assert(e_b_other[0] != e_a_other[1]);
1004#endif
1005 /* not totally common but we want to avoid */
1006 if (ELEM(e_a_other[0], e_b_other[0], e_b_other[1]) ||
1007 ELEM(e_a_other[1], e_b_other[0], e_b_other[1]))
1008 {
1009 return false;
1010 }
1011
1012 BLI_assert(BM_edge_share_vert(e_a_other[0], e_b_other[0]));
1013 BLI_assert(BM_edge_share_vert(e_a_other[1], e_b_other[1]));
1014
1015 r_e_clear_other[0] = BM_elem_index_get(e_a_other[0]);
1016 r_e_clear_other[1] = BM_elem_index_get(e_b_other[0]);
1017
1018#ifdef USE_CUSTOMDATA
1019 /* before killing, do customdata */
1020 if (customdata_flag & CD_DO_VERT) {
1021 BM_data_interp_from_verts(bm, v_other, v_clear, v_other, customdata_fac);
1022 }
1023 if (customdata_flag & CD_DO_EDGE) {
1024 BM_data_interp_from_edges(bm, e_a_other[1], e_a_other[0], e_a_other[1], customdata_fac);
1025 BM_data_interp_from_edges(bm, e_b_other[1], e_b_other[0], e_b_other[1], customdata_fac);
1026 }
1027 if (customdata_flag & CD_DO_LOOP) {
1028 bm_edge_collapse_loop_customdata(bm, e_clear->l, v_clear, v_other, customdata_fac);
1030 bm, e_clear->l->radial_next, v_clear, v_other, customdata_fac);
1031 }
1032#endif
1033
1034 BM_edge_kill(bm, e_clear);
1035
1036 v_other->head.hflag |= v_clear->head.hflag;
1037 BM_vert_splice(bm, v_other, v_clear);
1038
1039 e_a_other[1]->head.hflag |= e_a_other[0]->head.hflag;
1040 e_b_other[1]->head.hflag |= e_b_other[0]->head.hflag;
1041 BM_edge_splice(bm, e_a_other[1], e_a_other[0]);
1042 BM_edge_splice(bm, e_b_other[1], e_b_other[0]);
1043
1044#ifdef USE_SYMMETRY
1045 /* update mirror map */
1046 if (edge_symmetry_map) {
1047 if (edge_symmetry_map[r_e_clear_other[0]] != -1) {
1048 edge_symmetry_map[edge_symmetry_map[r_e_clear_other[0]]] = BM_elem_index_get(e_a_other[1]);
1049 }
1050 if (edge_symmetry_map[r_e_clear_other[1]] != -1) {
1051 edge_symmetry_map[edge_symmetry_map[r_e_clear_other[1]]] = BM_elem_index_get(e_b_other[1]);
1052 }
1053 }
1054#endif
1055
1056 // BM_mesh_is_valid(bm);
1057
1058 return true;
1059 }
1060 if (BM_edge_is_boundary(e_clear)) {
1061 /* same as above but only one triangle */
1062 BMLoop *l_a;
1063 BMEdge *e_a_other[2];
1064
1065 l_a = e_clear->l;
1066
1067 BLI_assert(l_a->f->len == 3);
1068
1069 /* keep 'v_clear' 0th */
1070 if (BM_vert_in_edge(l_a->prev->e, v_clear)) {
1071 e_a_other[0] = l_a->prev->e;
1072 e_a_other[1] = l_a->next->e;
1073 }
1074 else {
1075 e_a_other[1] = l_a->prev->e;
1076 e_a_other[0] = l_a->next->e;
1077 }
1078
1079 r_e_clear_other[0] = BM_elem_index_get(e_a_other[0]);
1080 r_e_clear_other[1] = -1;
1081
1082#ifdef USE_CUSTOMDATA
1083 /* before killing, do customdata */
1084 if (customdata_flag & CD_DO_VERT) {
1085 BM_data_interp_from_verts(bm, v_other, v_clear, v_other, customdata_fac);
1086 }
1087 if (customdata_flag & CD_DO_EDGE) {
1088 BM_data_interp_from_edges(bm, e_a_other[1], e_a_other[0], e_a_other[1], customdata_fac);
1089 }
1090 if (customdata_flag & CD_DO_LOOP) {
1091 bm_edge_collapse_loop_customdata(bm, e_clear->l, v_clear, v_other, customdata_fac);
1092 }
1093#endif
1094
1095 BM_edge_kill(bm, e_clear);
1096
1097 v_other->head.hflag |= v_clear->head.hflag;
1098 BM_vert_splice(bm, v_other, v_clear);
1099
1100 e_a_other[1]->head.hflag |= e_a_other[0]->head.hflag;
1101 BM_edge_splice(bm, e_a_other[1], e_a_other[0]);
1102
1103#ifdef USE_SYMMETRY
1104 /* update mirror map */
1105 if (edge_symmetry_map) {
1106 if (edge_symmetry_map[r_e_clear_other[0]] != -1) {
1107 edge_symmetry_map[edge_symmetry_map[r_e_clear_other[0]]] = BM_elem_index_get(e_a_other[1]);
1108 }
1109 }
1110#endif
1111
1112 // BM_mesh_is_valid(bm);
1113
1114 return true;
1115 }
1116 return false;
1117}
1118
1125 BMEdge *e,
1126 Quadric *vquadrics,
1127 float *vweights,
1128 const float vweight_factor,
1129 Heap *eheap,
1130 HeapNode **eheap_table,
1131#ifdef USE_SYMMETRY
1132 int *edge_symmetry_map,
1133#endif
1134 const CD_UseFlag customdata_flag,
1135 float optimize_co[3],
1136 bool optimize_co_calc)
1137{
1138 int e_clear_other[2];
1139 BMVert *v_other = e->v1;
1140 const int v_other_index = BM_elem_index_get(e->v1);
1141 /* the vert is removed so only store the index */
1142 const int v_clear_index = BM_elem_index_get(e->v2);
1143 float customdata_fac;
1144
1145#ifdef USE_VERT_NORMAL_INTERP
1146 float v_clear_no[3];
1147 copy_v3_v3(v_clear_no, e->v2->no);
1148#endif
1149
1150 /* when false, use without degenerate checks */
1151 if (optimize_co_calc) {
1152 /* disallow collapsing which results in degenerate cases */
1154 /* add back with a high cost */
1155 bm_decim_invalid_edge_cost_single(e, eheap, eheap_table);
1156 return false;
1157 }
1158
1159 bm_decim_calc_target_co_fl(e, optimize_co, vquadrics);
1160
1161 /* check if this would result in an overlapping face */
1162 if (UNLIKELY(bm_edge_collapse_is_degenerate_flip(e, optimize_co))) {
1163 /* add back with a high cost */
1164 bm_decim_invalid_edge_cost_single(e, eheap, eheap_table);
1165 return false;
1166 }
1167 }
1168
1169 /* use for customdata merging */
1170 if (LIKELY(compare_v3v3(e->v1->co, e->v2->co, FLT_EPSILON) == false)) {
1171 customdata_fac = line_point_factor_v3(optimize_co, e->v1->co, e->v2->co);
1172#if 0
1173 /* simple test for stupid collapse */
1174 if (customdata_fac < 0.0 - FLT_EPSILON || customdata_fac > 1.0f + FLT_EPSILON) {
1175 return false;
1176 }
1177#endif
1178 }
1179 else {
1180 /* avoid divide by zero */
1181 customdata_fac = 0.5f;
1182 }
1183
1184 if (bm_edge_collapse(bm,
1185 e,
1186 e->v2,
1187 e_clear_other,
1188#ifdef USE_SYMMETRY
1189 edge_symmetry_map,
1190#endif
1191 customdata_flag,
1192 customdata_fac))
1193 {
1194 /* update collapse info */
1195 int i;
1196
1197 if (vweights) {
1198 float v_other_weight = interpf(
1199 vweights[v_other_index], vweights[v_clear_index], customdata_fac);
1200 CLAMP(v_other_weight, 0.0f, 1.0f);
1201 vweights[v_other_index] = v_other_weight;
1202 }
1203
1204 /* paranoid safety check */
1205 e = nullptr;
1206
1207 copy_v3_v3(v_other->co, optimize_co);
1208
1209 /* remove eheap */
1210 for (i = 0; i < 2; i++) {
1211 /* highly unlikely 'eheap_table[ke_other[i]]' would be nullptr, but do for sanity sake */
1212 if ((e_clear_other[i] != -1) && (eheap_table[e_clear_other[i]] != nullptr)) {
1213 BLI_heap_remove(eheap, eheap_table[e_clear_other[i]]);
1214 eheap_table[e_clear_other[i]] = nullptr;
1215 }
1216 }
1217
1218 /* update vertex quadric, add kept vertex from killed vertex */
1219 BLI_quadric_add_qu_qu(&vquadrics[v_other_index], &vquadrics[v_clear_index]);
1220
1221/* update connected normals */
1222
1223/* in fact face normals are not used for progressive updates, no need to update them */
1224// BM_vert_normal_update_all(v);
1225#ifdef USE_VERT_NORMAL_INTERP
1226 interp_v3_v3v3(v_other->no, v_other->no, v_clear_no, customdata_fac);
1227 normalize_v3(v_other->no);
1228#else
1229 BM_vert_normal_update(v_other);
1230#endif
1231
1232 /* update error costs and the eheap */
1233 if (LIKELY(v_other->e)) {
1234 BMEdge *e_iter;
1235 BMEdge *e_first;
1236 e_iter = e_first = v_other->e;
1237 do {
1238 BLI_assert(BM_edge_find_double(e_iter) == nullptr);
1240 e_iter, vquadrics, vweights, vweight_factor, eheap, eheap_table);
1241 } while ((e_iter = bmesh_disk_edge_next(e_iter, v_other)) != e_first);
1242 }
1243
1244/* this block used to be disabled,
1245 * but enable now since surrounding faces may have been
1246 * set to COST_INVALID because of a face overlap that no longer occurs */
1247#if 1
1248 /* optional, update edges around the vertex face fan */
1249 {
1250 BMIter liter;
1251 BMLoop *l;
1252 BM_ITER_ELEM (l, &liter, v_other, BM_LOOPS_OF_VERT) {
1253 if (l->f->len == 3) {
1254 BMEdge *e_outer;
1255 if (BM_vert_in_edge(l->prev->e, l->v)) {
1256 e_outer = l->next->e;
1257 }
1258 else {
1259 e_outer = l->prev->e;
1260 }
1261
1262 BLI_assert(BM_vert_in_edge(e_outer, l->v) == false);
1263
1265 e_outer, vquadrics, vweights, vweight_factor, eheap, eheap_table);
1266 }
1267 }
1268 }
1269 /* end optional update */
1270 return true;
1271#endif
1272 }
1273 /* add back with a high cost */
1274 bm_decim_invalid_edge_cost_single(e, eheap, eheap_table);
1275 return false;
1276}
1277
1278/* Main Decimate Function
1279 * ********************** */
1280
1282 const float factor,
1283 float *vweights,
1284 float vweight_factor,
1285 const bool do_triangulate,
1286 const int symmetry_axis,
1287 const float symmetry_eps)
1288{
1289 /* edge heap */
1290 Heap *eheap;
1291 /* edge index aligned table pointing to the eheap */
1292 HeapNode **eheap_table;
1293 /* vert index aligned quadrics */
1294 Quadric *vquadrics;
1295 int tot_edge_orig;
1296 int face_tot_target;
1297
1298 CD_UseFlag customdata_flag = CD_UseFlag(0);
1299
1300#ifdef USE_SYMMETRY
1301 bool use_symmetry = (symmetry_axis != -1);
1302 int *edge_symmetry_map;
1303#endif
1304
1305#ifdef USE_TRIANGULATE
1306 int edges_tri_tot = 0;
1307 /* temp convert quads to triangles */
1308 bool use_triangulate = bm_decim_triangulate_begin(bm, &edges_tri_tot);
1309#else
1310 UNUSED_VARS(do_triangulate);
1311#endif
1312
1313 /* Allocate variables. */
1314 vquadrics = MEM_calloc_arrayN<Quadric>(bm->totvert, __func__);
1315 /* Since some edges may be degenerate, we might be over allocating a little here. */
1316 eheap = BLI_heap_new_ex(bm->totedge);
1317 eheap_table = MEM_malloc_arrayN<HeapNode *>(bm->totedge, __func__);
1318 tot_edge_orig = bm->totedge;
1319
1320 /* build initial edge collapse cost data */
1321 bm_decim_build_quadrics(bm, vquadrics);
1322
1323 bm_decim_build_edge_cost(bm, vquadrics, vweights, vweight_factor, eheap, eheap_table);
1324
1325 face_tot_target = bm->totface * factor;
1326 bm->elem_index_dirty |= BM_ALL;
1327
1328#ifdef USE_SYMMETRY
1329 edge_symmetry_map = (use_symmetry) ? bm_edge_symmetry_map(bm, symmetry_axis, symmetry_eps) :
1330 nullptr;
1331#else
1332 UNUSED_VARS(symmetry_axis, symmetry_eps);
1333#endif
1334
1335#ifdef USE_CUSTOMDATA
1336 /* initialize customdata flag, we only need math for loops */
1337 if (CustomData_has_interp(&bm->vdata)) {
1338 customdata_flag |= CD_DO_VERT;
1339 }
1340 if (CustomData_has_interp(&bm->edata)) {
1341 customdata_flag |= CD_DO_EDGE;
1342 }
1343 if (CustomData_has_math(&bm->ldata)) {
1344 customdata_flag |= CD_DO_LOOP;
1345 }
1346#endif
1347
1348/* iterative edge collapse and maintain the eheap */
1349#ifdef USE_SYMMETRY
1350 if (use_symmetry == false)
1351#endif
1352 {
1353 /* simple non-mirror case */
1354 while ((bm->totface > face_tot_target) && (BLI_heap_is_empty(eheap) == false) &&
1355 (BLI_heap_top_value(eheap) != COST_INVALID))
1356 {
1357 // const float value = BLI_heap_node_value(BLI_heap_top(eheap));
1358 BMEdge *e = static_cast<BMEdge *>(BLI_heap_pop_min(eheap));
1359 float optimize_co[3];
1360 /* handy to detect corruptions elsewhere */
1361 BLI_assert(BM_elem_index_get(e) < tot_edge_orig);
1362
1363 /* Under normal conditions won't be accessed again,
1364 * but nullptr just in case so we don't use freed node. */
1365 eheap_table[BM_elem_index_get(e)] = nullptr;
1366
1368 e,
1369 vquadrics,
1370 vweights,
1371 vweight_factor,
1372 eheap,
1373 eheap_table,
1374#ifdef USE_SYMMETRY
1375 edge_symmetry_map,
1376#endif
1377 customdata_flag,
1378 optimize_co,
1379 true);
1380 }
1381 }
1382#ifdef USE_SYMMETRY
1383 else {
1384 while ((bm->totface > face_tot_target) && (BLI_heap_is_empty(eheap) == false) &&
1385 (BLI_heap_top_value(eheap) != COST_INVALID))
1386 {
1387 /* NOTE:
1388 * - `eheap_table[e_index_mirr]` is only removed from the heap at the last moment
1389 * since its possible (in theory) for collapsing `e` to remove `e_mirr`.
1390 * - edges sharing a vertex are ignored, so the pivot vertex isn't moved to one side.
1391 */
1392
1393 BMEdge *e = static_cast<BMEdge *>(BLI_heap_pop_min(eheap));
1394 const int e_index = BM_elem_index_get(e);
1395 const int e_index_mirr = edge_symmetry_map[e_index];
1396 BMEdge *e_mirr = nullptr;
1397 float optimize_co[3];
1398 char e_invalidate = 0;
1399
1400 BLI_assert(e_index < tot_edge_orig);
1401
1402 eheap_table[e_index] = nullptr;
1403
1404 if (e_index_mirr != -1) {
1405 if (e_index_mirr == e_index) {
1406 /* pass */
1407 }
1408 else if (eheap_table[e_index_mirr]) {
1409 e_mirr = static_cast<BMEdge *>(BLI_heap_node_ptr(eheap_table[e_index_mirr]));
1410 /* for now ignore edges with a shared vertex */
1411 if (BM_edge_share_vert_check(e, e_mirr)) {
1412 /* ignore permanently!
1413 * Otherwise we would keep re-evaluating and attempting to collapse. */
1414 // e_invalidate |= (1 | 2);
1415 goto invalidate;
1416 }
1417 }
1418 else {
1419 /* mirror edge can't be operated on (happens with asymmetrical meshes) */
1420 e_invalidate |= 1;
1421 goto invalidate;
1422 }
1423 }
1424
1425 /* when false, use without degenerate checks */
1426 {
1427 /* run both before checking (since they invalidate surrounding geometry) */
1428 bool ok_a, ok_b;
1429
1431 ok_b = e_mirr ? !bm_edge_collapse_is_degenerate_topology(e_mirr) : true;
1432
1433 /* disallow collapsing which results in degenerate cases */
1434
1435 if (UNLIKELY(!ok_a || !ok_b)) {
1436 e_invalidate |= (1 | (e_mirr ? 2 : 0));
1437 goto invalidate;
1438 }
1439
1440 bm_decim_calc_target_co_fl(e, optimize_co, vquadrics);
1441
1442 if (e_index_mirr == e_index) {
1443 optimize_co[symmetry_axis] = 0.0f;
1444 }
1445
1446 /* check if this would result in an overlapping face */
1447 if (UNLIKELY(bm_edge_collapse_is_degenerate_flip(e, optimize_co))) {
1448 e_invalidate |= (1 | (e_mirr ? 2 : 0));
1449 goto invalidate;
1450 }
1451 }
1452
1454 e,
1455 vquadrics,
1456 vweights,
1457 vweight_factor,
1458 eheap,
1459 eheap_table,
1460 edge_symmetry_map,
1461 customdata_flag,
1462 optimize_co,
1463 false))
1464 {
1465 if (e_mirr && (eheap_table[e_index_mirr])) {
1466 BLI_assert(e_index_mirr != e_index);
1467 BLI_heap_remove(eheap, eheap_table[e_index_mirr]);
1468 eheap_table[e_index_mirr] = nullptr;
1469 optimize_co[symmetry_axis] *= -1.0f;
1471 e_mirr,
1472 vquadrics,
1473 vweights,
1474 vweight_factor,
1475 eheap,
1476 eheap_table,
1477 edge_symmetry_map,
1478 customdata_flag,
1479 optimize_co,
1480 false);
1481 }
1482 }
1483 else {
1484 if (e_mirr && (eheap_table[e_index_mirr])) {
1485 e_invalidate |= 2;
1486 goto invalidate;
1487 }
1488 }
1489
1490 BLI_assert(e_invalidate == 0);
1491 continue;
1492
1493 invalidate:
1494 if (e_invalidate & 1) {
1495 bm_decim_invalid_edge_cost_single(e, eheap, eheap_table);
1496 }
1497
1498 if (e_invalidate & 2) {
1499 BLI_assert(eheap_table[e_index_mirr] != nullptr);
1500 BLI_heap_remove(eheap, eheap_table[e_index_mirr]);
1501 eheap_table[e_index_mirr] = nullptr;
1502 bm_decim_invalid_edge_cost_single(e_mirr, eheap, eheap_table);
1503 }
1504 }
1505
1506 MEM_freeN(edge_symmetry_map);
1507 }
1508#endif /* USE_SYMMETRY */
1509
1510#ifdef USE_TRIANGULATE
1511 if (do_triangulate == false) {
1512 /* its possible we only had triangles, skip this step in that case */
1513 if (LIKELY(use_triangulate)) {
1514 /* temp convert quads to triangles */
1515 bm_decim_triangulate_end(bm, edges_tri_tot);
1516 }
1517 }
1518#endif
1519
1520 /* free vars */
1521 MEM_freeN(vquadrics);
1522 MEM_freeN(eheap_table);
1523 BLI_heap_free(eheap, nullptr);
1524
1525 /* testing only */
1526 // BM_mesh_is_valid(bm);
1527
1528 /* quiet release build warning */
1529 (void)tot_edge_orig;
1530}
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)
void CustomData_bmesh_interp_n(CustomData *data, const void **src_blocks, const float *weights, int count, void *dst_block_ofs, int n)
bool CustomData_data_equals(eCustomDataType type, const void *data1, const void *data2)
bool CustomData_has_math(const CustomData *data)
#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_ELEM_TAG
@ BM_LOOP
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
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
#define fabsf
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