Blender V5.0
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
10
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
150enum {
154};
155
170static void bm_tag_untagged_neighbors(BMVert *verts_start[],
171 const uint verts_start_num,
172 const int desired_tag,
173 BMVert *r_verts_tagged[],
174 uint &r_verts_tagged_num)
175{
176
177 BMEdge *e;
178 BMIter iter;
179 r_verts_tagged_num = 0;
180
181 for (int i = 0; i < verts_start_num; i++) {
182 BMVert *v = verts_start[i];
183
184 /* Since DO_COLLAPSE and IGNORE are -1 and +1, inverting the sign finds the other. */
185 BLI_assert(BM_elem_index_get(v) == -desired_tag);
186
187 BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
188 BMVert *v_other = BM_edge_other_vert(e, v);
189 if (BM_elem_index_get(v_other) == VERT_INDEX_INIT) {
190 BM_elem_index_set(v_other, desired_tag); /* set_dirty! */
191 r_verts_tagged[r_verts_tagged_num++] = v_other;
192 }
193 }
194 }
195}
196
197/* - BMVert.flag & BM_ELEM_TAG: shows we touched this vert
198 * - BMVert.index == -1: shows we will remove this vert
199 */
200
201void BM_mesh_decimate_unsubdivide_ex(BMesh *bm, const int iterations, const bool tag_only)
202{
203 /* NOTE: while #BMWalker seems like a logical choice, it results in uneven geometry. */
204
205 BMVert **verts_collapse = MEM_malloc_arrayN<BMVert *>(bm->totvert, __func__);
206 BMVert **verts_ignore = MEM_malloc_arrayN<BMVert *>(bm->totvert, __func__);
207 uint verts_collapse_num = 0;
208 uint verts_ignore_num = 0;
209
210 BMIter iter;
211
212 int iter_step;
213
214 /* if tag_only is set, we assume the caller knows what verts to tag
215 * needed for the operator */
216 if (tag_only == false) {
217 BMVert *v;
218 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
220 }
221 }
222
223 /* Perform the number of iteration steps which the user requested. */
224 for (iter_step = 0; iter_step < iterations; iter_step++) {
225 BMVert *v, *v_next;
226 bool verts_were_marked_for_dissolve = false;
227
228 /* Tag all verts which are eligible to be dissolved on this iteration. */
229 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
231 BM_elem_index_set(v, VERT_INDEX_INIT); /* set_dirty! */
232 }
233 else {
234 BM_elem_index_set(v, VERT_INDEX_IGNORE); /* set_dirty! */
235 }
236 }
237
238 /* main loop, keep tagging until we can't tag any more islands */
239 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
240
241 /* Only process verts which are eligible for dissolve and which have not yet been tagged. */
243 continue;
244 }
245
246 /* Set the first #VERT_INDEX_INIT vert as the first starting vert. */
247 BM_elem_index_set(v, VERT_INDEX_IGNORE); /* set_dirty! */
248 verts_ignore[0] = v;
249 verts_ignore_num = 1;
250
251 /* Starting at v, expand outwards, tagging any currently untagged neighbors.
252 * verts will be alternately tagged for collapse or ignore.
253 * Stop when there are no neighbors left to expand to. */
254 while (true) {
255
256 bm_tag_untagged_neighbors(verts_ignore,
257 verts_ignore_num,
258 VERT_INDEX_DO_COLLAPSE, /* set_dirty! */
259 verts_collapse,
260 verts_collapse_num);
261 if (verts_collapse_num == 0) {
262 break;
263 }
264 verts_were_marked_for_dissolve = true;
265
266 bm_tag_untagged_neighbors(verts_collapse,
267 verts_collapse_num,
268 VERT_INDEX_IGNORE, /* set_dirty! */
269 verts_ignore,
270 verts_ignore_num);
271 if (verts_ignore_num == 0) {
272 break;
273 }
274 }
275 }
276
277 /* At high iteration levels, later steps can run out of verts that are eligible for dissolve.
278 * If this occurs, stop. Future iterations won't find any verts that this iteration didn't. */
279 if (!verts_were_marked_for_dissolve) {
280 break;
281 }
282
283 /* Remove all verts tagged for removal. */
284 BM_ITER_MESH_MUTABLE (v, v_next, &iter, bm, BM_VERTS_OF_MESH) {
287 }
288 }
289 }
290
291 /* Ensure the vert index values will be recomputed. */
292 bm->elem_index_dirty |= BM_VERT;
293
294 MEM_freeN(verts_collapse);
295 MEM_freeN(verts_ignore);
296}
297
298void BM_mesh_decimate_unsubdivide(BMesh *bm, const int iterations)
299{
300 BM_mesh_decimate_unsubdivide_ex(bm, iterations, false);
301}
#define BLI_assert(a)
Definition BLI_assert.h:46
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)
static void bm_tag_untagged_neighbors(BMVert *verts_start[], const uint verts_start_num, const int desired_tag, BMVert *r_verts_tagged[], uint &r_verts_tagged_num)
#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)
BMesh * bm
bool BM_vert_dissolve(BMesh *bm, BMVert *v)
Dissolve Vert.
Definition bmesh_mods.cc:22
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
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 * 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
struct BMEdge * e
i
Definition text_draw.cc:230