Blender V5.0
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
37
38#include "MEM_guardedalloc.h"
39
40#include "BLI_bit_vector.hh"
41#include "BLI_math_base.h"
42
43#include "bmesh.hh"
44
48using blender::Span;
49using blender::Vector;
50
52 MutableBitSpan verts_tag,
53 BMVert *v)
54{
55 const int i = BM_elem_index_get(v);
56 if (!verts_tag[i]) {
57 verts_tag[i].set();
58 bmpinfo->verts.append(v);
59 return true;
60 }
61 return false;
62}
63
65 MutableBitSpan faces_tag,
66 BMFace *f)
67{
68 const int i = BM_elem_index_get(f);
69 if (!faces_tag[i]) {
70 faces_tag[i].set();
71 bmpinfo->faces.append(f);
72 return true;
73 }
74 return false;
75}
76
79 const BitSpan verts_mask,
80 const int verts_mask_count)
81{
82 /* The caller is doing something wrong if this isn't the case. */
83 BLI_assert(verts_mask_count <= bm.totvert);
84
85 BMPartialUpdate *bmpinfo = MEM_new<BMPartialUpdate>(__func__);
86
87 /* Reserve more edges than vertices since it's common for a grid topology
88 * to use around twice as many edges as vertices. */
89 const int default_verts_len_alloc = verts_mask_count;
90 const int default_faces_len_alloc = min_ii(bm.totface, verts_mask_count);
91
92 /* Allocate tags instead of using #BM_ELEM_TAG because the caller may already be using tags.
93 * Further, walking over all geometry to clear the tags isn't so efficient. */
94 BitVector<> verts_tag;
95 BitVector<> faces_tag;
96
97 /* Set vert inline. */
99
100 if (params.do_normals || params.do_tessellate) {
101 /* - Extend to all vertices connected faces:
102 * In the case of tessellation this is enough.
103 *
104 * In the case of vertex normal calculation,
105 * All the relevant connectivity data can be accessed from the faces
106 * (there is no advantage in storing connected edges or vertices in this pass).
107 *
108 * NOTE: In the future it may be useful to differentiate between vertices
109 * that are directly marked (by the filter function when looping over all vertices).
110 * And vertices marked from indirect connections.
111 * This would require an extra tag array, so avoid this unless it's needed.
112 */
113
114 /* Faces. */
115 bmpinfo->faces.reserve(default_faces_len_alloc);
116 faces_tag.resize(bm.totface);
117
118 BMVert *v;
119 BMIter iter;
120 int i;
122 BM_elem_index_set(v, i); /* set_inline */
123 if (!verts_mask[i]) {
124 continue;
125 }
126 BMEdge *e_iter = v->e;
127 if (e_iter != nullptr) {
128 /* Loop over edges. */
129 BMEdge *e_first = v->e;
130 do {
131 BMLoop *l_iter = e_iter->l;
132 if (e_iter->l != nullptr) {
133 BMLoop *l_first = e_iter->l;
134 /* Loop over radial loops. */
135 do {
136 if (l_iter->v == v) {
137 partial_elem_face_ensure(bmpinfo, faces_tag, l_iter->f);
138 }
139 } while ((l_iter = l_iter->radial_next) != l_first);
140 }
141 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
142 }
143 }
144 }
145
146 if (params.do_normals) {
147 /* - Extend to all faces vertices:
148 * Any changes to the faces normal needs to update all surrounding vertices.
149 *
150 * - Extend to all these vertices connected edges:
151 * These and needed to access those vertices edge vectors in normal calculation logic.
152 */
153
154 /* Vertices. */
155 bmpinfo->verts.reserve(default_verts_len_alloc);
156 verts_tag.resize(bm.totvert);
157
158 for (const BMFace *f : bmpinfo->faces) {
159 BMLoop *l_iter, *l_first;
160 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
161 do {
162 partial_elem_vert_ensure(bmpinfo, verts_tag, l_iter->v);
163 } while ((l_iter = l_iter->next) != l_first);
164 }
165 }
166
167 bmpinfo->params = params;
168
169 return bmpinfo;
170}
171
173 BMesh &bm,
175 const BitSpan verts_mask,
176 const int verts_mask_count)
177{
178 BMPartialUpdate *bmpinfo = MEM_new<BMPartialUpdate>(__func__);
179
180 BitVector<> verts_tag;
181 BitVector<> faces_tag;
182
183 int face_tag_loop_len = 0;
184
185 if (params.do_normals || params.do_tessellate) {
186 faces_tag.resize(bm.totface);
187
188 BMFace *f;
189 BMIter iter;
190 int i;
191 BM_ITER_MESH_INDEX (f, &iter, &bm, BM_FACES_OF_MESH, i) {
192 enum Side { SIDE_A = (1 << 0), SIDE_B = (1 << 1) } side_flag = Side(0);
193 BM_elem_index_set(f, i); /* set_inline */
194 BMLoop *l_iter, *l_first;
195 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
196 do {
197 const int j = BM_elem_index_get(l_iter->v);
198 side_flag = Side(side_flag | (verts_mask[j].test() ? SIDE_A : SIDE_B));
199 if (UNLIKELY(side_flag == (SIDE_A | SIDE_B))) {
200 partial_elem_face_ensure(bmpinfo, faces_tag, f);
201 face_tag_loop_len += f->len;
202 break;
203 }
204 } while ((l_iter = l_iter->next) != l_first);
205 }
206 }
207
208 if (params.do_normals) {
209 /* Extend to all faces vertices:
210 * Any changes to the faces normal needs to update all surrounding vertices. */
211
212 /* Over allocate using the total number of face loops. */
213 bmpinfo->verts.reserve(min_ii(bm.totvert, max_ii(1, face_tag_loop_len)));
214 verts_tag.resize(bm.totvert);
215
216 for (BMFace *f : bmpinfo->faces) {
217 BMLoop *l_iter, *l_first;
218 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
219 do {
220 partial_elem_vert_ensure(bmpinfo, verts_tag, l_iter->v);
221 } while ((l_iter = l_iter->next) != l_first);
222 }
223
224 /* Loose vertex support, these need special handling as loose normals depend on location. */
225 if (bmpinfo->verts.size() < verts_mask_count) {
226 BMVert *v;
227 BMIter iter;
228 int i;
230 if (verts_mask[i] && (BM_vert_find_first_loop(v) == nullptr)) {
231 partial_elem_vert_ensure(bmpinfo, verts_tag, v);
232 }
233 }
234 }
235 }
236
237 bmpinfo->params = params;
238
239 return bmpinfo;
240}
241
243 BMesh &bm,
245 const Span<int> verts_group,
246 const int verts_group_count)
247{
248 /* Provide a quick way of visualizing which faces are being manipulated. */
249 // #define DEBUG_MATERIAL
250
251 BMPartialUpdate *bmpinfo = MEM_new<BMPartialUpdate>(__func__);
252
253 BitVector<> verts_tag;
254 BitVector<> faces_tag;
255
256 int face_tag_loop_len = 0;
257
258 if (params.do_normals || params.do_tessellate) {
259 faces_tag.resize(bm.totface);
260
261 BMFace *f;
262 BMIter iter;
263 int i;
264 BM_ITER_MESH_INDEX (f, &iter, &bm, BM_FACES_OF_MESH, i) {
265 BM_elem_index_set(f, i); /* set_inline */
266 BMLoop *l_iter, *l_first;
267 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
268 const int group_test = verts_group[BM_elem_index_get(l_iter->prev->v)];
269#ifdef DEBUG_MATERIAL
270 f->mat_nr = 0;
271#endif
272 do {
273 const int group_iter = verts_group[BM_elem_index_get(l_iter->v)];
274 if (UNLIKELY((group_iter != group_test) || (group_iter == -1))) {
275 partial_elem_face_ensure(bmpinfo, faces_tag, f);
276 face_tag_loop_len += f->len;
277#ifdef DEBUG_MATERIAL
278 f->mat_nr = 1;
279#endif
280 break;
281 }
282 } while ((l_iter = l_iter->next) != l_first);
283 }
284 }
285
286 if (params.do_normals) {
287 /* Extend to all faces vertices:
288 * Any changes to the faces normal needs to update all surrounding vertices. */
289
290 /* Over allocate using the total number of face loops. */
291 bmpinfo->verts.reserve(min_ii(bm.totvert, max_ii(1, face_tag_loop_len)));
292 verts_tag.resize(bm.totvert);
293
294 for (BMFace *f : bmpinfo->faces) {
295 BMLoop *l_iter, *l_first;
296 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
297 do {
298 partial_elem_vert_ensure(bmpinfo, verts_tag, l_iter->v);
299 } while ((l_iter = l_iter->next) != l_first);
300 }
301
302 /* Loose vertex support, these need special handling as loose normals depend on location. */
303 if (bmpinfo->verts.size() < verts_group_count) {
304 BMVert *v;
305 BMIter iter;
306 int i;
308 if ((verts_group[i] != 0) && (BM_vert_find_first_loop(v) == nullptr)) {
309 partial_elem_vert_ensure(bmpinfo, verts_tag, v);
310 }
311 }
312 }
313 }
314
315 bmpinfo->params = params;
316
317 return bmpinfo;
318}
319
321{
322 MEM_delete(bmpinfo);
323}
#define BLI_assert(a)
Definition BLI_assert.h:46
#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
BMesh * bm
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
BLI_INLINE bool partial_elem_face_ensure(BMPartialUpdate *bmpinfo, MutableBitSpan faces_tag, BMFace *f)
BMPartialUpdate * BM_mesh_partial_create_from_verts_group_single(BMesh &bm, const BMPartialUpdate_Params &params, const BitSpan verts_mask, const int verts_mask_count)
BLI_INLINE bool partial_elem_vert_ensure(BMPartialUpdate *bmpinfo, MutableBitSpan verts_tag, BMVert *v)
BMPartialUpdate * BM_mesh_partial_create_from_verts_group_multi(BMesh &bm, const BMPartialUpdate_Params &params, const Span< int > verts_group, const int verts_group_count)
void BM_mesh_partial_destroy(BMPartialUpdate *bmpinfo)
BMPartialUpdate * BM_mesh_partial_create_from_verts(BMesh &bm, const BMPartialUpdate_Params &params, const BitSpan verts_mask, const int verts_mask_count)
#define BM_FACE
BMLoop * BM_vert_find_first_loop(BMVert *v)
ATTR_WARN_UNUSED_RESULT const BMVert * v
int64_t size() const
void append(const T &value)
void reserve(const int64_t min_capacity)
void resize(const int64_t new_size_in_bits, const bool value=false)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
struct BMLoop * l
short mat_nr
struct BMVert * v
struct BMLoop * radial_next
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
blender::Vector< BMFace * > faces
blender::Vector< BMVert * > verts
BMPartialUpdate_Params params
i
Definition text_draw.cc:230