Blender V4.3
bmesh_decimate_unsubdivide.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#include "MEM_guardedalloc.h"
12
13#include "bmesh.hh"
14#include "bmesh_decimate.hh" /* own include */
15
17{
18 /* check if we should walk over these verts */
19 BMIter iter;
20 BMEdge *e;
21
22 BMVert *varr[4];
23
24 uint tot_edge = 0;
25 uint tot_edge_boundary = 0;
26 uint tot_edge_manifold = 0;
27 uint tot_edge_wire = 0;
28
29 BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
31 tot_edge_boundary++;
32 }
33 else if (BM_edge_is_manifold(e)) {
34 tot_edge_manifold++;
35 }
36 else if (BM_edge_is_wire(e)) {
37 tot_edge_wire++;
38 }
39
40 /* bail out early */
41 if (tot_edge == 4) {
42 return false;
43 }
44
45 /* used to check overlapping faces */
46 varr[tot_edge] = BM_edge_other_vert(e, v);
47
48 tot_edge++;
49 }
50
51 if (((tot_edge == 4) && (tot_edge_boundary == 0) && (tot_edge_manifold == 4)) ||
52 ((tot_edge == 3) && (tot_edge_boundary == 0) && (tot_edge_manifold == 3)) ||
53 ((tot_edge == 3) && (tot_edge_boundary == 2) && (tot_edge_manifold == 1)))
54 {
55 if (!BM_face_exists(varr, tot_edge)) {
56 return true;
57 }
58 }
59 else if ((tot_edge == 2) && (tot_edge_wire == 2)) {
60 return true;
61 }
62 return false;
63}
64
66{
67 /* Collapse under 2 conditions:
68 * - vert connects to 4 manifold edges (and 4 faces).
69 * - vert connects to 1 manifold edge, 2 boundary edges (and 2 faces).
70 *
71 * This covers boundary verts of a quad grid and center verts.
72 * note that surrounding faces don't have to be quads.
73 */
74
75 BMIter iter;
76 BMEdge *e;
77
78 uint tot_loop = 0;
79 uint tot_edge = 0;
80 uint tot_edge_boundary = 0;
81 uint tot_edge_manifold = 0;
82 uint tot_edge_wire = 0;
83
84 BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
86 tot_edge_boundary++;
87 }
88 else if (BM_edge_is_manifold(e)) {
89 tot_edge_manifold++;
90 }
91 else if (BM_edge_is_wire(e)) {
92 tot_edge_wire++;
93 }
94 tot_edge++;
95 }
96
97 if (tot_edge == 2) {
98 /* check for 2 wire verts only */
99 if (tot_edge_wire == 2) {
100 return (BM_vert_collapse_edge(bm, v->e, v, true, true, true) != nullptr);
101 }
102 }
103 else if (tot_edge == 4) {
104 /* check for 4 faces surrounding */
105 if (tot_edge_boundary == 0 && tot_edge_manifold == 4) {
106 /* good to go! */
107 tot_loop = 4;
108 }
109 }
110 else if (tot_edge == 3) {
111 /* check for 2 faces surrounding at a boundary */
112 if (tot_edge_boundary == 2 && tot_edge_manifold == 1) {
113 /* good to go! */
114 tot_loop = 2;
115 }
116 else if (tot_edge_boundary == 0 && tot_edge_manifold == 3) {
117 /* good to go! */
118 tot_loop = 3;
119 }
120 }
121
122 if (tot_loop) {
123 BMLoop *f_loop[4];
124 uint i;
125
126 /* ensure there are exactly tot_loop loops */
127 BLI_assert(BM_iter_at_index(bm, BM_LOOPS_OF_VERT, v, tot_loop) == nullptr);
128 BM_iter_as_array(bm, BM_LOOPS_OF_VERT, v, (void **)f_loop, tot_loop);
129
130 for (i = 0; i < tot_loop; i++) {
131 BMLoop *l = f_loop[i];
132 if (l->f->len > 3) {
133 BMLoop *l_new;
134 BLI_assert(l->prev->v != l->next->v);
135 BM_face_split(bm, l->f, l->prev, l->next, &l_new, nullptr, true);
136 BM_elem_flag_merge_into(l_new->e, l->e, l->prev->e);
137 }
138 }
139
140 return BM_vert_dissolve(bm, v);
141 }
142
143 return false;
144}
145
146enum {
150};
151
152// #define USE_WALKER /* gives uneven results, disable for now */
153
154/* - BMVert.flag & BM_ELEM_TAG: shows we touched this vert
155 * - BMVert.index == -1: shows we will remove this vert
156 */
157
158void BM_mesh_decimate_unsubdivide_ex(BMesh *bm, const int iterations, const bool tag_only)
159{
160#ifdef USE_WALKER
161# define ELE_VERT_TAG 1
162#else
163 BMVert **vert_seek_a = static_cast<BMVert **>(
164 MEM_mallocN(sizeof(BMVert *) * bm->totvert, __func__));
165 BMVert **vert_seek_b = static_cast<BMVert **>(
166 MEM_mallocN(sizeof(BMVert *) * bm->totvert, __func__));
167 uint vert_seek_a_tot = 0;
168 uint vert_seek_b_tot = 0;
169#endif
170
171 BMIter iter;
172
173 const uint offset = 0;
174 const uint nth = 2;
175
176 int iter_step;
177
178 /* if tag_only is set, we assume the caller knows what verts to tag
179 * needed for the operator */
180 if (tag_only == false) {
181 BMVert *v;
182 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
184 }
185 }
186
187 for (iter_step = 0; iter_step < iterations; iter_step++) {
188 BMVert *v, *v_next;
189 bool iter_done;
190
191 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
193#ifdef USE_WALKER
194 BMO_vert_flag_enable(bm, v, ELE_VERT_TAG);
195#endif
196 BM_elem_index_set(v, VERT_INDEX_INIT); /* set_dirty! */
197 }
198 else {
199 BM_elem_index_set(v, VERT_INDEX_IGNORE); /* set_dirty! */
200 }
201 }
202 /* done with selecting tagged verts */
203
204 /* main loop, keep tagging until we can't tag any more islands */
205 while (true) {
206#ifdef USE_WALKER
207 BMWalker walker;
208#else
209 uint depth = 1;
210 uint i;
211#endif
212 BMVert *v_first = nullptr;
213
214 /* we could avoid iterating from the start each time */
215 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
216 if (v->e && (BM_elem_index_get(v) == VERT_INDEX_INIT)) {
217#ifdef USE_WALKER
218 if (BMO_vert_flag_test(bm, v, ELE_VERT_TAG))
219#endif
220 {
221 /* Check again in case the topology changed. */
223 v_first = v;
224 }
225 break;
226 }
227 }
228 }
229 if (v_first == nullptr) {
230 break;
231 }
232
233#ifdef USE_WALKER
234 /* Walk over selected elements starting at active */
235 BMW_init(
236 &walker,
237 bm,
239 ELE_VERT_TAG,
242 BMW_FLAG_NOP, /* don't use #BMW_FLAG_TEST_HIDDEN here since we want to deselect all. */
244
246 for (v = BMW_begin(&walker, v_first); v != nullptr; v = BMW_step(&walker)) {
247 /* Deselect elements that aren't at "nth" depth from active */
249 if ((offset + BMW_current_depth(&walker)) % nth) {
250 /* tag for removal */
251 BM_elem_index_set(v, VERT_INDEX_DO_COLLAPSE); /* set_dirty! */
252 }
253 else {
254 /* works better to allow these verts to be checked again */
255 // BM_elem_index_set(v, VERT_INDEX_IGNORE); /* set_dirty! */
256 }
257 }
258 }
259 BMW_end(&walker);
260#else
261
262 BM_elem_index_set(v_first,
263 ((offset + depth) % nth) ? VERT_INDEX_IGNORE :
264 VERT_INDEX_DO_COLLAPSE); /* set_dirty! */
265
266 vert_seek_b_tot = 0;
267 vert_seek_b[vert_seek_b_tot++] = v_first;
268
269 while (true) {
270 BMEdge *e;
271
272 if ((offset + depth) % nth) {
273 vert_seek_a_tot = 0;
274 for (i = 0; i < vert_seek_b_tot; i++) {
275 v = vert_seek_b[i];
277 BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
278 BMVert *v_other = BM_edge_other_vert(e, v);
279 if (BM_elem_index_get(v_other) == VERT_INDEX_INIT) {
280 BM_elem_index_set(v_other, VERT_INDEX_DO_COLLAPSE); /* set_dirty! */
281 vert_seek_a[vert_seek_a_tot++] = v_other;
282 }
283 }
284 }
285 if (vert_seek_a_tot == 0) {
286 break;
287 }
288 }
289 else {
290 vert_seek_b_tot = 0;
291 for (i = 0; i < vert_seek_a_tot; i++) {
292 v = vert_seek_a[i];
294 BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
295 BMVert *v_other = BM_edge_other_vert(e, v);
296 if (BM_elem_index_get(v_other) == VERT_INDEX_INIT) {
297 BM_elem_index_set(v_other, VERT_INDEX_IGNORE); /* set_dirty! */
298 vert_seek_b[vert_seek_b_tot++] = v_other;
299 }
300 }
301 }
302 if (vert_seek_b_tot == 0) {
303 break;
304 }
305 }
306
307 depth++;
308 }
309#endif /* USE_WALKER */
310 }
311
312 /* now we tagged all verts -1 for removal, lets loop over and rebuild faces */
313 iter_done = false;
314 BM_ITER_MESH_MUTABLE (v, v_next, &iter, bm, BM_VERTS_OF_MESH) {
316 if (bm_vert_dissolve_fan(bm, v)) {
317 iter_done = true;
318 }
319 }
320 }
321
322 if (iter_done == false) {
323 break;
324 }
325 }
326
328
329#ifndef USE_WALKER
330 MEM_freeN(vert_seek_a);
331 MEM_freeN(vert_seek_b);
332#endif
333}
334
335void BM_mesh_decimate_unsubdivide(BMesh *bm, const int iterations)
336{
337 BM_mesh_decimate_unsubdivide_ex(bm, iterations, false);
338}
#define BLI_assert(a)
Definition BLI_assert.h:50
unsigned int uint
Read Guarded memory(de)allocation.
@ BM_ELEM_TAG
static bool bm_vert_dissolve_fan_test(BMVert *v)
static bool bm_vert_dissolve_fan(BMesh *bm, BMVert *v)
void BM_mesh_decimate_unsubdivide(BMesh *bm, const int iterations)
void BM_mesh_decimate_unsubdivide_ex(BMesh *bm, const int iterations, const bool tag_only)
#define BM_elem_index_get(ele)
#define BM_elem_flag_merge_into(ele, ele_a, ele_b)
#define BM_elem_index_set(ele, index)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
void * BM_iter_at_index(BMesh *bm, const char itype, void *data, int index)
int BM_iter_as_array(BMesh *bm, const char itype, void *data, void **array, const int len)
Iterator as Array.
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_VERTS_OF_MESH
@ BM_LOOPS_OF_VERT
@ BM_EDGES_OF_VERT
#define BM_ITER_MESH_MUTABLE(ele, ele_next, iter, bm, itype)
ATTR_WARN_UNUSED_RESULT BMesh * bm
bool BM_vert_dissolve(BMesh *bm, BMVert *v)
Dissolve Vert.
Definition bmesh_mods.cc:24
BMEdge * BM_vert_collapse_edge(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool kill_degenerate_faces, const bool kill_duplicate_faces)
Vert Collapse Faces.
BMFace * BM_face_split(BMesh *bm, BMFace *f, BMLoop *l_a, BMLoop *l_b, BMLoop **r_l, BMEdge *example, const bool no_double)
Face Split.
#define BM_VERT
#define BMO_vert_flag_enable(bm, e, oflag)
#define BMO_vert_flag_test(bm, e, oflag)
BMFace * BM_face_exists(BMVert *const *varr, int len)
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_edge_is_wire(const BMEdge *e) 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 BMVert * v
void * BMW_begin(BMWalker *walker, void *start)
void BMW_init(BMWalker *walker, BMesh *bm, int type, short mask_vert, short mask_edge, short mask_face, BMWFlag flag, int layer)
Init Walker.
void BMW_end(BMWalker *walker)
End Walker.
void * BMW_step(BMWalker *walker)
Step Walker.
int BMW_current_depth(BMWalker *walker)
Walker Current Depth.
@ BMW_BREADTH_FIRST
#define BMW_NIL_LAY
@ BMW_FLAG_NOP
#define BMW_MASK_NOP
@ BMW_CONNECTED_VERTEX
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
struct BMVert * v
struct BMEdge * e
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
struct BMEdge * e
BMWOrder order
int totvert
char elem_index_dirty