Blender V4.3
bmesh_boolean.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 <functional>
12
13#include "BLI_array.hh"
14#include "BLI_math_mpq.hh"
15#include "BLI_mesh_boolean.hh"
16#include "BLI_mesh_intersect.hh"
17
18#include "bmesh.hh"
19#include "bmesh_boolean.hh"
20#include "bmesh_edgesplit.hh"
21
22// #define PERF_DEBUG
23
24namespace blender::meshintersect {
25
26#ifdef WITH_GMP
27
37static IMesh mesh_from_bm(BMesh *bm,
38 const Span<std::array<BMLoop *, 3>> looptris,
39 IMesh *r_triangulated,
40 IMeshArena *arena)
41{
42 BLI_assert(r_triangulated != nullptr);
45 /* Account for triangulation and intersects. */
46 const int estimate_num_outv = 3 * bm->totvert;
47 const int estimate_num_outf = 4 * bm->totface;
48 arena->reserve(estimate_num_outv, estimate_num_outf);
49 Array<const Vert *> vert(bm->totvert);
50 for (int v = 0; v < bm->totvert; ++v) {
51 BMVert *bmv = BM_vert_at_index(bm, v);
52 vert[v] = arena->add_or_find_vert(mpq3(bmv->co[0], bmv->co[1], bmv->co[2]), v);
53 }
54 Array<Face *> face(bm->totface);
55 constexpr int estimated_max_facelen = 100;
56 Vector<const Vert *, estimated_max_facelen> face_vert;
57 Vector<int, estimated_max_facelen> face_edge_orig;
58 for (int f = 0; f < bm->totface; ++f) {
59 BMFace *bmf = BM_face_at_index(bm, f);
60 int flen = bmf->len;
61 face_vert.clear();
62 face_edge_orig.clear();
63 BMLoop *l = bmf->l_first;
64 for (int i = 0; i < flen; ++i) {
65 const Vert *v = vert[BM_elem_index_get(l->v)];
66 face_vert.append(v);
67 int e_index = BM_elem_index_get(l->e);
68 face_edge_orig.append(e_index);
69 l = l->next;
70 }
71 face[f] = arena->add_face(face_vert, f, face_edge_orig);
72 }
73 /* Now do the triangulation mesh.
74 * The loop_tris have accurate v and f members for the triangles,
75 * but their next and e pointers are not correct for the loops
76 * that start added-diagonal edges. */
77 Array<Face *> tri_face(looptris.size());
78 face_vert.resize(3);
79 face_edge_orig.resize(3);
80 for (const int i : looptris.index_range()) {
81 BMFace *bmf = looptris[i][0]->f;
82 int f = BM_elem_index_get(bmf);
83 for (int j = 0; j < 3; ++j) {
84 BMLoop *l = looptris[i][j];
85 int v_index = BM_elem_index_get(l->v);
86 int e_index;
87 if (l->next->v == looptris[i][(j + 1) % 3]->v) {
88 e_index = BM_elem_index_get(l->e);
89 }
90 else {
91 e_index = NO_INDEX;
92 }
93 face_vert[j] = vert[v_index];
94 face_edge_orig[j] = e_index;
95 }
96 tri_face[i] = arena->add_face(face_vert, f, face_edge_orig);
97 }
98 r_triangulated->set_faces(tri_face);
99 return IMesh(face);
100}
101
102static bool bmvert_attached_to_wire(const BMVert *bmv)
103{
104 /* This is not quite right. It returns true if the only edges
105 * Attached to \a bmv are wire edges. TODO: iterate through edges
106 * attached to \a bmv and check #BM_edge_is_wire. */
107 return BM_vert_is_wire(bmv);
108}
109
110static bool bmvert_attached_to_hidden_face(BMVert *bmv)
111{
112 BMIter iter;
113 for (BMFace *bmf = static_cast<BMFace *>(BM_iter_new(&iter, nullptr, BM_FACES_OF_VERT, bmv));
114 bmf;
115 bmf = static_cast<BMFace *>(BM_iter_step(&iter)))
116 {
118 return true;
119 }
120 }
121 return false;
122}
123
124static bool face_has_verts_in_order(BMesh *bm, BMFace *bmf, const BMVert *v1, const BMVert *v2)
125{
126 BMIter liter;
127 BMLoop *l = static_cast<BMLoop *>(BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, bmf));
128 while (l != nullptr) {
129 if (l->v == v1 && l->next->v == v2) {
130 return true;
131 }
132 l = static_cast<BMLoop *>(BM_iter_step(&liter));
133 }
134 return false;
135}
136
138constexpr uint KEEP_FLAG = (1 << 6);
139
147static bool apply_mesh_output_to_bmesh(BMesh *bm, IMesh &m_out, bool keep_hidden)
148{
149 bool any_change = false;
150
151 m_out.populate_vert();
152
153 /* Initially mark all existing verts as "don't keep", except hidden verts
154 * (if keep_hidden is true), and verts attached to wire edges. */
155 for (int v = 0; v < bm->totvert; ++v) {
156 BMVert *bmv = BM_vert_at_index(bm, v);
157 if ((keep_hidden &&
158 (BM_elem_flag_test(bmv, BM_ELEM_HIDDEN) || bmvert_attached_to_hidden_face(bmv))) ||
159 bmvert_attached_to_wire(bmv))
160 {
161 BM_elem_flag_enable(bmv, KEEP_FLAG);
162 }
163 else {
164 BM_elem_flag_disable(bmv, KEEP_FLAG);
165 }
166 }
167
168 /* Reuse old or make new #BMVert's, depending on if there's an orig or not.
169 * For those reused, mark them "keep".
170 * Store needed old #BMVert's in new_bmvs first, as the table may be unusable after
171 * creating a new #BMVert. */
172 Array<BMVert *> new_bmvs(m_out.vert_size());
173 for (int v : m_out.vert_index_range()) {
174 const Vert *vertp = m_out.vert(v);
175 int orig = vertp->orig;
176 if (orig != NO_INDEX) {
177 BLI_assert(orig >= 0 && orig < bm->totvert);
178 BMVert *bmv = BM_vert_at_index(bm, orig);
179 new_bmvs[v] = bmv;
180 BM_elem_flag_enable(bmv, KEEP_FLAG);
181 }
182 else {
183 new_bmvs[v] = nullptr;
184 }
185 }
186 for (int v : m_out.vert_index_range()) {
187 const Vert *vertp = m_out.vert(v);
188 if (new_bmvs[v] == nullptr) {
189 float co[3];
190 const double3 &d_co = vertp->co;
191 for (int i = 0; i < 3; ++i) {
192 co[i] = float(d_co[i]);
193 }
194 BMVert *bmv = BM_vert_create(bm, co, nullptr, BM_CREATE_NOP);
195 new_bmvs[v] = bmv;
196 BM_elem_flag_enable(bmv, KEEP_FLAG);
197 any_change = true;
198 }
199 }
200
201 /* Initially mark all existing faces as "don't keep", except hidden faces (if keep_hidden).
202 * Also, save current #BMFace pointers as creating faces will disturb the table. */
203 Array<BMFace *> old_bmfs(bm->totface);
205 for (int f = 0; f < bm->totface; ++f) {
206 BMFace *bmf = BM_face_at_index(bm, f);
207 old_bmfs[f] = bmf;
208 if (keep_hidden && BM_elem_flag_test(bmf, BM_ELEM_HIDDEN)) {
209 BM_elem_flag_enable(bmf, KEEP_FLAG);
210 }
211 else {
212 BM_elem_flag_disable(bmf, KEEP_FLAG);
213 }
214 }
215
216 /* Save the original #BMEdge's so we can use them as examples. */
217 Array<BMEdge *> old_edges(bm->totedge);
218 std::copy_n(bm->etable, bm->totedge, old_edges.begin());
219
220 /* Reuse or make new #BMFace's, as the faces are identical to old ones or not.
221 * If reusing, mark them as "keep". First find the maximum face length
222 * so we can declare some arrays outside of the face-creating loop. */
223 int maxflen = 0;
224 for (const Face *f : m_out.faces()) {
225 maxflen = max_ii(maxflen, f->size());
226 }
227 Array<BMVert *> face_bmverts(maxflen);
228 Array<BMEdge *> face_bmedges(maxflen);
229 for (const Face *f : m_out.faces()) {
230 const Face &face = *f;
231 int flen = face.size();
232 for (int i = 0; i < flen; ++i) {
233 const Vert *v = face[i];
234 int v_index = m_out.lookup_vert(v);
235 BLI_assert(v_index < new_bmvs.size());
236 face_bmverts[i] = new_bmvs[v_index];
237 }
238 BMFace *bmf = BM_face_exists(face_bmverts.data(), flen);
239 /* #BM_face_exists checks if the face exists with the vertices in either order.
240 * We can only reuse the face if the orientations are the same. */
241 if (bmf != nullptr && face_has_verts_in_order(bm, bmf, face_bmverts[0], face_bmverts[1])) {
242 BM_elem_flag_enable(bmf, KEEP_FLAG);
243 }
244 else {
245 int orig = face.orig;
246 BMFace *orig_face;
247 /* There should always be an orig face, but just being extra careful here. */
248 if (orig != NO_INDEX) {
249 orig_face = old_bmfs[orig];
250 }
251 else {
252 orig_face = nullptr;
253 }
254 /* Make or find #BMEdge's. */
255 for (int i = 0; i < flen; ++i) {
256 BMVert *bmv1 = face_bmverts[i];
257 BMVert *bmv2 = face_bmverts[(i + 1) % flen];
258 BMEdge *bme = BM_edge_exists(bmv1, bmv2);
259 if (bme == nullptr) {
260 BMEdge *orig_edge = nullptr;
261 if (face.edge_orig[i] != NO_INDEX) {
262 orig_edge = old_edges[face.edge_orig[i]];
263 }
264 bme = BM_edge_create(bm, bmv1, bmv2, orig_edge, BM_CREATE_NOP);
265 if (orig_edge != nullptr) {
266 BM_elem_select_copy(bm, bme, orig_edge);
267 }
268 }
269 face_bmedges[i] = bme;
270 if (face.is_intersect[i]) {
272 }
273 else {
275 }
276 }
277 BMFace *bmf = BM_face_create(
278 bm, face_bmverts.data(), face_bmedges.data(), flen, orig_face, BM_CREATE_NOP);
279 if (orig_face != nullptr) {
280 BM_elem_select_copy(bm, bmf, orig_face);
281 }
282 BM_elem_flag_enable(bmf, KEEP_FLAG);
283 /* Now do interpolation of loop data (e.g., UVs) using the example face. */
284 if (orig_face != nullptr) {
285 BMIter liter;
286 BMLoop *l = static_cast<BMLoop *>(BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, bmf));
287 while (l != nullptr) {
288 BM_loop_interp_from_face(bm, l, orig_face, false, true);
289 l = static_cast<BMLoop *>(BM_iter_step(&liter));
290 }
291 }
292 any_change = true;
293 }
294 }
295
296 /* Now kill the unused faces and verts, and clear flags for kept ones. */
297 /* #BM_ITER_MESH_MUTABLE macro needs type casts for C++, so expand here.
298 * TODO(howard): make some nice C++ iterators for #BMesh. */
299 BMIter iter;
300 BMFace *bmf = static_cast<BMFace *>(BM_iter_new(&iter, bm, BM_FACES_OF_MESH, nullptr));
301 while (bmf != nullptr) {
302# ifndef NDEBUG
304# endif
305 BMFace *bmf_next = static_cast<BMFace *>(BM_iter_step(&iter));
306 if (BM_elem_flag_test(bmf, KEEP_FLAG)) {
307 BM_elem_flag_disable(bmf, KEEP_FLAG);
308 }
309 else {
311# if 0
312 BM_face_kill(bm, bmf);
313# endif
314 any_change = true;
315 }
316 bmf = bmf_next;
317 }
318 BMVert *bmv = static_cast<BMVert *>(BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, nullptr));
319 while (bmv != nullptr) {
320# ifndef NDEBUG
322# endif
323 BMVert *bmv_next = static_cast<BMVert *>(BM_iter_step(&iter));
324 if (BM_elem_flag_test(bmv, KEEP_FLAG)) {
325 BM_elem_flag_disable(bmv, KEEP_FLAG);
326 }
327 else {
328 BM_vert_kill(bm, bmv);
329 any_change = true;
330 }
331 bmv = bmv_next;
332 }
333
334 return any_change;
335}
336
337static bool bmesh_boolean(BMesh *bm,
338 const Span<std::array<BMLoop *, 3>> looptris,
339 int (*test_fn)(BMFace *f, void *user_data),
340 void *user_data,
341 int nshapes,
342 const bool use_self,
343 const bool use_separate_all,
344 const bool keep_hidden,
345 const bool hole_tolerant,
346 const BoolOpType boolean_mode)
347{
348 IMeshArena arena;
349 IMesh m_triangulated;
350# ifdef PERF_DEBUG
351 double start_time = BLI_time_now_seconds();
352# endif
353 IMesh m_in = mesh_from_bm(bm, looptris, &m_triangulated, &arena);
354# ifdef PERF_DEBUG
355 double mesh_time = BLI_time_now_seconds();
356 std::cout << "bmesh_boolean, imesh_from_bm done, time = " << mesh_time - start_time << "\n";
357# endif
358 std::function<int(int)> shape_fn;
359 if (use_self && boolean_mode == BoolOpType::None) {
360 /* Unary knife operation. Want every face where test_fn doesn't return -1. */
361 BLI_assert(nshapes == 1);
362 shape_fn = [bm, test_fn, user_data](int f) {
363 BMFace *bmf = BM_face_at_index(bm, f);
364 if (test_fn(bmf, user_data) != -1) {
365 return 0;
366 }
367 return -1;
368 };
369 }
370 else {
371 shape_fn = [bm, test_fn, user_data](int f) {
372 BMFace *bmf = BM_face_at_index(bm, f);
373 int test_val = test_fn(bmf, user_data);
374 if (test_val >= 0) {
375 return test_val;
376 }
377 return -1;
378 };
379 }
380 IMesh m_out = boolean_mesh(
381 m_in, boolean_mode, nshapes, shape_fn, use_self, hole_tolerant, &m_triangulated, &arena);
382# ifdef PERF_DEBUG
383 double boolean_time = BLI_time_now_seconds();
384 std::cout << "boolean done, time = " << boolean_time - mesh_time << "\n";
385# endif
386 bool any_change = apply_mesh_output_to_bmesh(bm, m_out, keep_hidden);
387# ifdef PERF_DEBUG
388 double apply_mesh_time = BLI_time_now_seconds();
389 std::cout << "applied boolean output to bmesh, time = " << apply_mesh_time - boolean_time
390 << "\n";
391# endif
392 if (use_separate_all) {
393 /* We are supposed to separate all faces that are incident on intersection edges. */
394 BM_mesh_edgesplit(bm, false, true, false);
395 }
396 return any_change;
397}
398
399#endif // WITH_GMP
400
401} // namespace blender::meshintersect
402
419#ifdef WITH_GMP
421 const blender::Span<std::array<BMLoop *, 3>> looptris,
422 int (*test_fn)(BMFace *f, void *user_data),
423 void *user_data,
424 const int nshapes,
425 const bool use_self,
426 const bool keep_hidden,
427 const bool hole_tolerant,
428 const int boolean_mode)
429{
430 return blender::meshintersect::bmesh_boolean(
431 bm,
432 looptris,
433 test_fn,
434 user_data,
435 nshapes,
436 use_self,
437 false,
438 keep_hidden,
439 hole_tolerant,
440 static_cast<blender::meshintersect::BoolOpType>(boolean_mode));
441}
442
444 const blender::Span<std::array<BMLoop *, 3>> looptris,
445 int (*test_fn)(BMFace *f, void *user_data),
446 void *user_data,
447 const int nshapes,
448 const bool use_self,
449 const bool use_separate_all,
450 const bool hole_tolerant,
451 const bool keep_hidden)
452{
453 return blender::meshintersect::bmesh_boolean(bm,
454 looptris,
455 test_fn,
456 user_data,
457 nshapes,
458 use_self,
459 use_separate_all,
460 keep_hidden,
461 hole_tolerant,
462 blender::meshintersect::BoolOpType::None);
463}
464#else
465bool BM_mesh_boolean(BMesh * /*bm*/,
466 blender::Span<std::array<BMLoop *, 3>> looptris,
467 int (*test_fn)(BMFace *, void *),
468 void * /*user_data*/,
469 const int /*nshapes*/,
470 const bool /*use_self*/,
471 const bool /*keep_hidden*/,
472 const bool /*hole_tolerant*/,
473 const int /*boolean_mode*/)
474{
475 UNUSED_VARS(looptris, test_fn);
476 return false;
477}
478
488 blender::Span<std::array<BMLoop *, 3>> looptris,
489 int (*test_fn)(BMFace *, void *),
490 void * /*user_data*/,
491 const int /*nshapes*/,
492 const bool /*use_self*/,
493 const bool /*use_separate_all*/,
494 const bool /*hole_tolerant*/,
495 const bool /*keep_hidden*/)
496{
497 UNUSED_VARS(looptris, test_fn);
498 return false;
499}
500#endif
#define BLI_assert(a)
Definition BLI_assert.h:50
MINLINE int max_ii(int a, int b)
unsigned int uint
double BLI_time_now_seconds(void)
Definition time.c:65
#define UNUSED_VARS(...)
bool BM_mesh_boolean_knife(BMesh *, blender::Span< std::array< BMLoop *, 3 > > looptris, int(*test_fn)(BMFace *, void *), void *, const int, const bool, const bool, const bool, const bool)
bool BM_mesh_boolean(BMesh *, blender::Span< std::array< BMLoop *, 3 > > looptris, int(*test_fn)(BMFace *, void *), void *, const int, const bool, const bool, const bool, const int)
@ BM_ELEM_HIDDEN
@ BM_ELEM_TAG
void BM_elem_select_copy(BMesh *bm_dst, void *ele_dst_v, const void *ele_src_v)
void BM_vert_kill(BMesh *bm, BMVert *v)
void BM_face_kill(BMesh *bm, BMFace *f)
void BM_face_kill_loose(BMesh *bm, BMFace *f)
BMFace * BM_face_create(BMesh *bm, BMVert *const *verts, BMEdge *const *edges, const int len, const BMFace *f_example, const eBMCreateFlag create_flag)
BMVert * BM_vert_create(BMesh *bm, const float co[3], const BMVert *v_example, const eBMCreateFlag create_flag)
Main function for creating a new vertex.
Definition bmesh_core.cc:43
BMEdge * BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *e_example, const eBMCreateFlag create_flag)
Main function for creating a new edge.
@ BM_CREATE_NOP
Definition bmesh_core.hh:24
void BM_mesh_edgesplit(BMesh *bm, const bool use_verts, const bool tag_only, const bool copy_select)
#define BM_elem_index_get(ele)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
void BM_loop_interp_from_face(BMesh *bm, BMLoop *l_dst, const BMFace *f_src, const bool do_vertex, const bool do_multires)
int BM_iter_mesh_count(const char itype, BMesh *bm)
@ BM_FACES_OF_VERT
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_FACE
#define BM_iter_new(iter, bm, itype, data)
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
BLI_INLINE BMVert * BM_vert_at_index(BMesh *bm, const int index)
BLI_INLINE BMFace * BM_face_at_index(BMesh *bm, const int index)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
bool BM_vert_is_wire(const BMVert *v)
BMFace * BM_face_exists(BMVert *const *varr, int len)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert * v
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
VecBase< double, 3 > double3
BMLoop * l_first
struct BMVert * v
struct BMEdge * e
struct BMLoop * next
float co[3]
int totvert
BMEdge ** etable
int totedge
int totface