Blender V4.3
bmesh_mesh_partial_update.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
38#include "MEM_guardedalloc.h"
39
40#include "BLI_bitmap.h"
41
42#include "bmesh.hh"
43
50#define GROW(len_alloc) ((len_alloc) + ((len_alloc) - ((len_alloc) / 2)))
51#define GROW_ARRAY(mem, len_alloc) \
52 { \
53 *(void **)&mem = MEM_reallocN(mem, sizeof(*mem) * ((len_alloc) = GROW(len_alloc))); \
54 } \
55 ((void)0)
56
57#define GROW_ARRAY_AS_NEEDED(mem, len_alloc, index) \
58 if (UNLIKELY(len_alloc == index)) { \
59 GROW_ARRAY(mem, len_alloc); \
60 }
61
63 BLI_bitmap *verts_tag,
64 BMVert *v)
65{
66 const int i = BM_elem_index_get(v);
67 if (!BLI_BITMAP_TEST(verts_tag, i)) {
68 BLI_BITMAP_ENABLE(verts_tag, i);
69 GROW_ARRAY_AS_NEEDED(bmpinfo->verts, bmpinfo->verts_len_alloc, bmpinfo->verts_len);
70 bmpinfo->verts[bmpinfo->verts_len++] = v;
71 return true;
72 }
73 return false;
74}
75
77 BLI_bitmap *faces_tag,
78 BMFace *f)
79{
80 const int i = BM_elem_index_get(f);
81 if (!BLI_BITMAP_TEST(faces_tag, i)) {
82 BLI_BITMAP_ENABLE(faces_tag, i);
83 GROW_ARRAY_AS_NEEDED(bmpinfo->faces, bmpinfo->faces_len_alloc, bmpinfo->faces_len);
84 bmpinfo->faces[bmpinfo->faces_len++] = f;
85 return true;
86 }
87 return false;
88}
89
92 const BLI_bitmap *verts_mask,
93 const int verts_mask_count)
94{
95 /* The caller is doing something wrong if this isn't the case. */
96 BLI_assert(verts_mask_count <= bm->totvert);
97
98 BMPartialUpdate *bmpinfo = static_cast<BMPartialUpdate *>(
99 MEM_callocN(sizeof(*bmpinfo), __func__));
100
101 /* Reserve more edges than vertices since it's common for a grid topology
102 * to use around twice as many edges as vertices. */
103 const int default_verts_len_alloc = verts_mask_count;
104 const int default_faces_len_alloc = min_ii(bm->totface, verts_mask_count);
105
106 /* Allocate tags instead of using #BM_ELEM_TAG because the caller may already be using tags.
107 * Further, walking over all geometry to clear the tags isn't so efficient. */
108 BLI_bitmap *verts_tag = nullptr;
109 BLI_bitmap *faces_tag = nullptr;
110
111 /* Set vert inline. */
113
114 if (params->do_normals || params->do_tessellate) {
115 /* - Extend to all vertices connected faces:
116 * In the case of tessellation this is enough.
117 *
118 * In the case of vertex normal calculation,
119 * All the relevant connectivity data can be accessed from the faces
120 * (there is no advantage in storing connected edges or vertices in this pass).
121 *
122 * NOTE: In the future it may be useful to differentiate between vertices
123 * that are directly marked (by the filter function when looping over all vertices).
124 * And vertices marked from indirect connections.
125 * This would require an extra tag array, so avoid this unless it's needed.
126 */
127
128 /* Faces. */
129 if (bmpinfo->faces == nullptr) {
130 bmpinfo->faces_len_alloc = default_faces_len_alloc;
131 bmpinfo->faces = static_cast<BMFace **>(
132 MEM_mallocN((sizeof(BMFace *) * bmpinfo->faces_len_alloc), __func__));
133 faces_tag = BLI_BITMAP_NEW(size_t(bm->totface), __func__);
134 }
135
136 BMVert *v;
137 BMIter iter;
138 int i;
140 BM_elem_index_set(v, i); /* set_inline */
141 if (!BLI_BITMAP_TEST(verts_mask, i)) {
142 continue;
143 }
144 BMEdge *e_iter = v->e;
145 if (e_iter != nullptr) {
146 /* Loop over edges. */
147 BMEdge *e_first = v->e;
148 do {
149 BMLoop *l_iter = e_iter->l;
150 if (e_iter->l != nullptr) {
151 BMLoop *l_first = e_iter->l;
152 /* Loop over radial loops. */
153 do {
154 if (l_iter->v == v) {
155 partial_elem_face_ensure(bmpinfo, faces_tag, l_iter->f);
156 }
157 } while ((l_iter = l_iter->radial_next) != l_first);
158 }
159 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
160 }
161 }
162 }
163
164 if (params->do_normals) {
165 /* - Extend to all faces vertices:
166 * Any changes to the faces normal needs to update all surrounding vertices.
167 *
168 * - Extend to all these vertices connected edges:
169 * These and needed to access those vertices edge vectors in normal calculation logic.
170 */
171
172 /* Vertices. */
173 if (bmpinfo->verts == nullptr) {
174 bmpinfo->verts_len_alloc = default_verts_len_alloc;
175 bmpinfo->verts = static_cast<BMVert **>(
176 MEM_mallocN((sizeof(BMVert *) * bmpinfo->verts_len_alloc), __func__));
177 verts_tag = BLI_BITMAP_NEW(size_t(bm->totvert), __func__);
178 }
179
180 for (int i = 0; i < bmpinfo->faces_len; i++) {
181 BMFace *f = bmpinfo->faces[i];
182 BMLoop *l_iter, *l_first;
183 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
184 do {
185 partial_elem_vert_ensure(bmpinfo, verts_tag, l_iter->v);
186 } while ((l_iter = l_iter->next) != l_first);
187 }
188 }
189
190 if (verts_tag) {
191 MEM_freeN(verts_tag);
192 }
193 if (faces_tag) {
194 MEM_freeN(faces_tag);
195 }
196
197 bmpinfo->params = *params;
198
199 return bmpinfo;
200}
201
203 BMesh *bm,
205 const BLI_bitmap *verts_mask,
206 const int verts_mask_count)
207{
208 BMPartialUpdate *bmpinfo = static_cast<BMPartialUpdate *>(
209 MEM_callocN(sizeof(*bmpinfo), __func__));
210
211 BLI_bitmap *verts_tag = nullptr;
212 BLI_bitmap *faces_tag = nullptr;
213
214 /* It's not worth guessing a large number as isolated regions will allocate zero faces. */
215 const int default_faces_len_alloc = 1;
216
217 int face_tag_loop_len = 0;
218
219 if (params->do_normals || params->do_tessellate) {
220
221 /* Faces. */
222 if (bmpinfo->faces == nullptr) {
223 bmpinfo->faces_len_alloc = default_faces_len_alloc;
224 bmpinfo->faces = static_cast<BMFace **>(
225 MEM_mallocN((sizeof(BMFace *) * bmpinfo->faces_len_alloc), __func__));
226 faces_tag = BLI_BITMAP_NEW(size_t(bm->totface), __func__);
227 }
228
229 BMFace *f;
230 BMIter iter;
231 int i;
232 BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
233 enum Side { SIDE_A = (1 << 0), SIDE_B = (1 << 1) } side_flag = Side(0);
234 BM_elem_index_set(f, i); /* set_inline */
235 BMLoop *l_iter, *l_first;
236 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
237 do {
238 const int j = BM_elem_index_get(l_iter->v);
239 side_flag = Side(side_flag | (BLI_BITMAP_TEST(verts_mask, j) ? SIDE_A : SIDE_B));
240 if (UNLIKELY(side_flag == (SIDE_A | SIDE_B))) {
241 partial_elem_face_ensure(bmpinfo, faces_tag, f);
242 face_tag_loop_len += f->len;
243 break;
244 }
245 } while ((l_iter = l_iter->next) != l_first);
246 }
247 }
248
249 if (params->do_normals) {
250 /* Extend to all faces vertices:
251 * Any changes to the faces normal needs to update all surrounding vertices. */
252
253 /* Over allocate using the total number of face loops. */
254 const int default_verts_len_alloc = min_ii(bm->totvert, max_ii(1, face_tag_loop_len));
255
256 /* Vertices. */
257 if (bmpinfo->verts == nullptr) {
258 bmpinfo->verts_len_alloc = default_verts_len_alloc;
259 bmpinfo->verts = static_cast<BMVert **>(
260 MEM_mallocN((sizeof(BMVert *) * bmpinfo->verts_len_alloc), __func__));
261 verts_tag = BLI_BITMAP_NEW(size_t(bm->totvert), __func__);
262 }
263
264 for (int i = 0; i < bmpinfo->faces_len; i++) {
265 BMFace *f = bmpinfo->faces[i];
266 BMLoop *l_iter, *l_first;
267 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
268 do {
269 partial_elem_vert_ensure(bmpinfo, verts_tag, l_iter->v);
270 } while ((l_iter = l_iter->next) != l_first);
271 }
272
273 /* Loose vertex support, these need special handling as loose normals depend on location. */
274 if (bmpinfo->verts_len < verts_mask_count) {
275 BMVert *v;
276 BMIter iter;
277 int i;
279 if (BLI_BITMAP_TEST(verts_mask, i) && (BM_vert_find_first_loop(v) == nullptr)) {
280 partial_elem_vert_ensure(bmpinfo, verts_tag, v);
281 }
282 }
283 }
284 }
285
286 if (verts_tag) {
287 MEM_freeN(verts_tag);
288 }
289 if (faces_tag) {
290 MEM_freeN(faces_tag);
291 }
292
293 bmpinfo->params = *params;
294
295 return bmpinfo;
296}
297
299 BMesh *bm,
301 const int *verts_group,
302 const int verts_group_count)
303{
304 /* Provide a quick way of visualizing which faces are being manipulated. */
305 // #define DEBUG_MATERIAL
306
307 BMPartialUpdate *bmpinfo = static_cast<BMPartialUpdate *>(
308 MEM_callocN(sizeof(*bmpinfo), __func__));
309
310 BLI_bitmap *verts_tag = nullptr;
311 BLI_bitmap *faces_tag = nullptr;
312
313 /* It's not worth guessing a large number as isolated regions will allocate zero faces. */
314 const int default_faces_len_alloc = 1;
315
316 int face_tag_loop_len = 0;
317
318 if (params->do_normals || params->do_tessellate) {
319
320 /* Faces. */
321 if (bmpinfo->faces == nullptr) {
322 bmpinfo->faces_len_alloc = default_faces_len_alloc;
323 bmpinfo->faces = static_cast<BMFace **>(
324 MEM_mallocN((sizeof(BMFace *) * bmpinfo->faces_len_alloc), __func__));
325 faces_tag = BLI_BITMAP_NEW(size_t(bm->totface), __func__);
326 }
327
328 BMFace *f;
329 BMIter iter;
330 int i;
331 BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
332 BM_elem_index_set(f, i); /* set_inline */
333 BMLoop *l_iter, *l_first;
334 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
335 const int group_test = verts_group[BM_elem_index_get(l_iter->prev->v)];
336#ifdef DEBUG_MATERIAL
337 f->mat_nr = 0;
338#endif
339 do {
340 const int group_iter = verts_group[BM_elem_index_get(l_iter->v)];
341 if (UNLIKELY((group_iter != group_test) || (group_iter == -1))) {
342 partial_elem_face_ensure(bmpinfo, faces_tag, f);
343 face_tag_loop_len += f->len;
344#ifdef DEBUG_MATERIAL
345 f->mat_nr = 1;
346#endif
347 break;
348 }
349 } while ((l_iter = l_iter->next) != l_first);
350 }
351 }
352
353 if (params->do_normals) {
354 /* Extend to all faces vertices:
355 * Any changes to the faces normal needs to update all surrounding vertices. */
356
357 /* Over allocate using the total number of face loops. */
358 const int default_verts_len_alloc = min_ii(bm->totvert, max_ii(1, face_tag_loop_len));
359
360 /* Vertices. */
361 if (bmpinfo->verts == nullptr) {
362 bmpinfo->verts_len_alloc = default_verts_len_alloc;
363 bmpinfo->verts = static_cast<BMVert **>(
364 MEM_mallocN((sizeof(BMVert *) * bmpinfo->verts_len_alloc), __func__));
365 verts_tag = BLI_BITMAP_NEW(size_t(bm->totvert), __func__);
366 }
367
368 for (int i = 0; i < bmpinfo->faces_len; i++) {
369 BMFace *f = bmpinfo->faces[i];
370 BMLoop *l_iter, *l_first;
371 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
372 do {
373 partial_elem_vert_ensure(bmpinfo, verts_tag, l_iter->v);
374 } while ((l_iter = l_iter->next) != l_first);
375 }
376
377 /* Loose vertex support, these need special handling as loose normals depend on location. */
378 if (bmpinfo->verts_len < verts_group_count) {
379 BMVert *v;
380 BMIter iter;
381 int i;
383 if ((verts_group[i] != 0) && (BM_vert_find_first_loop(v) == nullptr)) {
384 partial_elem_vert_ensure(bmpinfo, verts_tag, v);
385 }
386 }
387 }
388 }
389
390 if (verts_tag) {
391 MEM_freeN(verts_tag);
392 }
393 if (faces_tag) {
394 MEM_freeN(faces_tag);
395 }
396
397 bmpinfo->params = *params;
398
399 return bmpinfo;
400}
401
403{
404 if (bmpinfo->verts) {
405 MEM_freeN(bmpinfo->verts);
406 }
407 if (bmpinfo->faces) {
408 MEM_freeN(bmpinfo->faces);
409 }
410 MEM_freeN(bmpinfo);
411}
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_BITMAP_NEW(_num, _alloc_string)
Definition BLI_bitmap.h:41
#define BLI_BITMAP_TEST(_bitmap, _index)
Definition BLI_bitmap.h:65
#define BLI_BITMAP_ENABLE(_bitmap, _index)
Definition BLI_bitmap.h:82
unsigned int BLI_bitmap
Definition BLI_bitmap.h:17
#define BLI_INLINE
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
#define UNLIKELY(x)
Read Guarded memory(de)allocation.
#define BM_DISK_EDGE_NEXT(e, v)
#define BM_FACE_FIRST_LOOP(p)
#define BM_elem_index_get(ele)
#define BM_elem_index_set(ele, index)
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar)
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
BMPartialUpdate * BM_mesh_partial_create_from_verts(BMesh *bm, const BMPartialUpdate_Params *params, const BLI_bitmap *verts_mask, const int verts_mask_count)
BLI_INLINE bool partial_elem_vert_ensure(BMPartialUpdate *bmpinfo, BLI_bitmap *verts_tag, BMVert *v)
BMPartialUpdate * BM_mesh_partial_create_from_verts_group_multi(BMesh *bm, const BMPartialUpdate_Params *params, const int *verts_group, const int verts_group_count)
BLI_INLINE bool partial_elem_face_ensure(BMPartialUpdate *bmpinfo, BLI_bitmap *faces_tag, BMFace *f)
#define GROW_ARRAY_AS_NEEDED(mem, len_alloc, index)
BMPartialUpdate * BM_mesh_partial_create_from_verts_group_single(BMesh *bm, const BMPartialUpdate_Params *params, const BLI_bitmap *verts_mask, const int verts_mask_count)
void BM_mesh_partial_destroy(BMPartialUpdate *bmpinfo)
#define BM_FACE
BMLoop * BM_vert_find_first_loop(BMVert *v)
ATTR_WARN_UNUSED_RESULT const BMVert * v
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
struct BMLoop * l
short mat_nr
struct BMVert * v
struct BMLoop * radial_next
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
BMPartialUpdate_Params params
struct BMEdge * e
int totvert
int totface