Blender V4.3
bmesh_intersect.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
22#include "MEM_guardedalloc.h"
23
24#include "BLI_alloca.h"
25#include "BLI_math_geom.h"
26#include "BLI_math_vector.h"
27#include "BLI_memarena.h"
28#include "BLI_sort_utils.h"
29#include "BLI_utildefines.h"
30
31#include "BLI_linklist_stack.h"
33
34#include "BLI_buffer.h"
35#include "BLI_kdopbvh.h"
36
37#include "bmesh.hh"
39
40#include "bmesh_intersect.hh" /* own include */
41
43
44#include "BLI_strict_flags.h" /* Keep last. */
45
46/*
47 * Some of these depend on each other:
48 */
49
50/* splice verts into existing edges */
51#define USE_SPLICE
52/* split faces by intersecting edges */
53#define USE_NET
54/* split resulting edges */
55#define USE_SEPARATE
56/* remove verts created by intersecting triangles */
57#define USE_DISSOLVE
58/* detect isolated holes and fill them */
59#define USE_NET_ISLAND_CONNECT
60
61/* strict asserts that may fail in practice (handy for debugging cases which should succeed) */
62// #define USE_PARANOID
63/* use accelerated overlap check */
64#define USE_BVH
65
66// #define USE_DUMP
67
68static void tri_v3_scale(float v1[3], float v2[3], float v3[3], const float t)
69{
70 float p[3];
71
72 mid_v3_v3v3v3(p, v1, v2, v3);
73
74 interp_v3_v3v3(v1, p, v1, t);
75 interp_v3_v3v3(v2, p, v2, t);
76 interp_v3_v3v3(v3, p, v3, t);
77}
78
79#ifdef USE_DISSOLVE
80/* other edge when a vert only has 2 edges */
82{
85
86 if (v->e != e) {
87 return v->e;
88 }
89 return BM_DISK_EDGE_NEXT(v->e, v);
90}
91#endif
92
101
107
110 GHash *edgetri_cache; /* int[4]: BMVert */
111 GHash *edge_verts; /* BMEdge: LinkList(of verts), new and original edges */
112 GHash *face_edges; /* BMFace-index: LinkList(of edges), only original faces */
113 GSet *wire_edges; /* BMEdge (could use tags instead) */
114 LinkNode *vert_dissolve; /* BMVert's */
115
117
119};
120
129
130static bool ghash_insert_link(GHash *gh, void *key, void *val, bool use_test, MemArena *mem_arena)
131{
132 void **ls_base_p;
133 LinkBase *ls_base;
134 LinkNode *ls;
135
136 if (!BLI_ghash_ensure_p(gh, key, &ls_base_p)) {
137 ls_base = static_cast<LinkBase *>(*ls_base_p = BLI_memarena_alloc(mem_arena,
138 sizeof(*ls_base)));
139 ls_base->list = nullptr;
140 ls_base->list_len = 0;
141 }
142 else {
143 ls_base = static_cast<LinkBase *>(*ls_base_p);
144 if (use_test && (BLI_linklist_index(ls_base->list, val) != -1)) {
145 return false;
146 }
147 }
148
149 ls = static_cast<LinkNode *>(BLI_memarena_alloc(mem_arena, sizeof(*ls)));
150 ls->next = ls_base->list;
151 ls->link = val;
152 ls_base->list = ls;
153 ls_base->list_len += 1;
154
155 return true;
156}
157
159 float val;
161};
162
163#ifdef USE_SPLICE
164static void edge_verts_sort(const float co[3], LinkBase *v_ls_base)
165{
166 /* not optimal but list will be typically < 5 */
167 uint i;
168 vert_sort_t *vert_sort = BLI_array_alloca(vert_sort, v_ls_base->list_len);
169 LinkNode *node;
170
171 BLI_assert(v_ls_base->list_len > 1);
172
173 for (i = 0, node = v_ls_base->list; i < v_ls_base->list_len; i++, node = node->next) {
174 BMVert *v = static_cast<BMVert *>(node->link);
176 vert_sort[i].val = len_squared_v3v3(co, v->co);
177 vert_sort[i].v = v;
178 }
179
180 qsort(vert_sort, v_ls_base->list_len, sizeof(*vert_sort), BLI_sortutil_cmp_float);
181
182 for (i = 0, node = v_ls_base->list; i < v_ls_base->list_len; i++, node = node->next) {
183 node->link = vert_sort[i].v;
184 }
185}
186#endif
187
188static void edge_verts_add(ISectState *s, BMEdge *e, BMVert *v, const bool use_test)
189{
192 ghash_insert_link(s->edge_verts, (void *)e, v, use_test, s->mem_arena);
193}
194
195static void face_edges_add(ISectState *s, const int f_index, BMEdge *e, const bool use_test)
196{
197 void *f_index_key = POINTER_FROM_INT(f_index);
199 BLI_assert(BM_edge_in_face(e, s->bm->ftable[f_index]) == false);
200 BLI_assert(BM_elem_index_get(s->bm->ftable[f_index]) == f_index);
201
202 ghash_insert_link(s->face_edges, f_index_key, e, use_test, s->mem_arena);
203}
204
205#ifdef USE_NET
207 BMFace *f,
208 LinkBase *e_ls_base,
209 bool use_island_connect,
210 bool use_partial_connect,
211 MemArena *mem_arena_edgenet)
212{
213 uint i;
214 uint edge_arr_len = e_ls_base->list_len;
215 BMEdge **edge_arr = BLI_array_alloca(edge_arr, edge_arr_len);
216 LinkNode *node;
218
219 for (i = 0, node = e_ls_base->list; i < e_ls_base->list_len; i++, node = node->next) {
220 edge_arr[i] = static_cast<BMEdge *>(node->link);
221 }
222 BLI_assert(node == nullptr);
223
224# ifdef USE_DUMP
225 printf("# %s: %d %u\n", __func__, BM_elem_index_get(f), e_ls_base->list_len);
226# endif
227
228# ifdef USE_NET_ISLAND_CONNECT
229 if (use_island_connect) {
230 uint edge_arr_holes_len;
231 BMEdge **edge_arr_holes;
233 f,
234 edge_arr,
235 edge_arr_len,
236 use_partial_connect,
237 mem_arena_edgenet,
238 &edge_arr_holes,
239 &edge_arr_holes_len))
240 {
241 edge_arr_len = edge_arr_holes_len;
242 edge_arr = edge_arr_holes; /* owned by the arena */
243 }
244 }
245# else
246 UNUSED_VARS(use_island_connect, mem_arena_edgenet);
247# endif
248
249 BM_face_split_edgenet(bm, f, edge_arr, int(edge_arr_len), nullptr);
250}
251#endif
252
253#ifdef USE_DISSOLVE
255{
258 BLI_assert(BLI_linklist_index(s->vert_dissolve, v) == -1);
259
261 BLI_linklist_prepend_arena(&s->vert_dissolve, v, s->mem_arena);
262}
263#endif
264
265static enum ISectType intersect_line_tri(const float p0[3],
266 const float p1[3],
267 const float *t_cos[3],
268 const float t_nor[3],
269 float r_ix[3],
270 const ISectEpsilon *e)
271{
272 float p_dir[3];
273 uint i_t0;
274 float fac;
275
276 sub_v3_v3v3(p_dir, p0, p1);
277 normalize_v3(p_dir);
278
279 for (i_t0 = 0; i_t0 < 3; i_t0++) {
280 const uint i_t1 = (i_t0 + 1) % 3;
281 float te_dir[3];
282
283 sub_v3_v3v3(te_dir, t_cos[i_t0], t_cos[i_t1]);
284 normalize_v3(te_dir);
285 if (fabsf(dot_v3v3(p_dir, te_dir)) >= 1.0f - e->eps) {
286 /* co-linear */
287 }
288 else {
289 float ix_pair[2][3];
290 int ix_pair_type;
291
292 ix_pair_type = isect_line_line_epsilon_v3(
293 p0, p1, t_cos[i_t0], t_cos[i_t1], ix_pair[0], ix_pair[1], 0.0f);
294
295 if (ix_pair_type != 0) {
296 if (ix_pair_type == 1) {
297 copy_v3_v3(ix_pair[1], ix_pair[0]);
298 }
299
300 if ((ix_pair_type == 1) || (len_squared_v3v3(ix_pair[0], ix_pair[1]) <= e->eps_margin_sq))
301 {
302 fac = line_point_factor_v3(ix_pair[1], t_cos[i_t0], t_cos[i_t1]);
303 if ((fac >= e->eps_margin) && (fac <= 1.0f - e->eps_margin)) {
304 fac = line_point_factor_v3(ix_pair[0], p0, p1);
305 if ((fac >= e->eps_margin) && (fac <= 1.0f - e->eps_margin)) {
306 copy_v3_v3(r_ix, ix_pair[0]);
307 return ISectType(IX_EDGE_TRI_EDGE0 + (enum ISectType)i_t0);
308 }
309 }
310 }
311 }
312 }
313 }
314
315 /* check ray isn't planar with tri */
316 if (fabsf(dot_v3v3(p_dir, t_nor)) >= e->eps) {
318 p0, p1, t_cos[0], t_cos[1], t_cos[2], &fac, nullptr, 0.0f))
319 {
320 if ((fac >= e->eps_margin) && (fac <= 1.0f - e->eps_margin)) {
321 interp_v3_v3v3(r_ix, p0, p1, fac);
322 if (min_fff(len_squared_v3v3(t_cos[0], r_ix),
323 len_squared_v3v3(t_cos[1], r_ix),
324 len_squared_v3v3(t_cos[2], r_ix)) >= e->eps_margin_sq)
325 {
326 return IX_EDGE_TRI;
327 }
328 }
329 }
330 }
331
332 /* r_ix may be unset */
333 return IX_NONE;
334}
335
337 BMVert *e_v0,
338 BMVert *e_v1,
339 BMVert *t[3],
340 const int t_index,
341 const float *t_cos[3],
342 const float t_nor[3],
343 enum ISectType *r_side)
344{
345 BMesh *bm = s->bm;
346 int k_arr[IX_TOT][4];
347 uint i;
348 const int ti[3] = {UNPACK3_EX(BM_elem_index_get, t, )};
349 float ix[3];
350
351 if (BM_elem_index_get(e_v0) > BM_elem_index_get(e_v1)) {
352 std::swap(e_v0, e_v1);
353 }
354
355#ifdef USE_PARANOID
356 BLI_assert(len_squared_v3v3(e_v0->co, t[0]->co) >= s->epsilon.eps_sq);
357 BLI_assert(len_squared_v3v3(e_v0->co, t[1]->co) >= s->epsilon.eps_sq);
358 BLI_assert(len_squared_v3v3(e_v0->co, t[2]->co) >= s->epsilon.eps_sq);
359 BLI_assert(len_squared_v3v3(e_v1->co, t[0]->co) >= s->epsilon.eps_sq);
360 BLI_assert(len_squared_v3v3(e_v1->co, t[1]->co) >= s->epsilon.eps_sq);
361 BLI_assert(len_squared_v3v3(e_v1->co, t[2]->co) >= s->epsilon.eps_sq);
362#endif
363
364#define KEY_SET(k, i0, i1, i2, i3) \
365 { \
366 (k)[0] = i0; \
367 (k)[1] = i1; \
368 (k)[2] = i2; \
369 (k)[3] = i3; \
370 } \
371 (void)0
372
373/* Order tri, then order (1-2, 2-3). */
374#define KEY_EDGE_TRI_ORDER(k) \
375 { \
376 if (k[2] > k[3]) { \
377 std::swap(k[2], k[3]); \
378 } \
379 if (k[0] > k[2]) { \
380 std::swap(k[0], k[2]); \
381 std::swap(k[1], k[3]); \
382 } \
383 } \
384 (void)0
385
386 KEY_SET(k_arr[IX_EDGE_TRI], BM_elem_index_get(e_v0), BM_elem_index_get(e_v1), t_index, -1);
387 /* need to order here */
388 KEY_SET(
389 k_arr[IX_EDGE_TRI_EDGE0], BM_elem_index_get(e_v0), BM_elem_index_get(e_v1), ti[0], ti[1]);
390 KEY_SET(
391 k_arr[IX_EDGE_TRI_EDGE1], BM_elem_index_get(e_v0), BM_elem_index_get(e_v1), ti[1], ti[2]);
392 KEY_SET(
393 k_arr[IX_EDGE_TRI_EDGE2], BM_elem_index_get(e_v0), BM_elem_index_get(e_v1), ti[2], ti[0]);
394
398
399#undef KEY_SET
400#undef KEY_EDGE_TRI_ORDER
401
402 for (i = 0; i < ARRAY_SIZE(k_arr); i++) {
403 BMVert *iv;
404
405 iv = static_cast<BMVert *>(BLI_ghash_lookup(s->edgetri_cache, k_arr[i]));
406
407 if (iv) {
408#ifdef USE_DUMP
409 printf("# cache hit (%d, %d, %d, %d)\n", UNPACK4(k_arr[i]));
410#endif
411 *r_side = (enum ISectType)i;
412 return iv;
413 }
414 }
415
416 *r_side = intersect_line_tri(e_v0->co, e_v1->co, t_cos, t_nor, ix, &s->epsilon);
417 if (*r_side != IX_NONE) {
418 BMVert *iv;
419 BMEdge *e;
420#ifdef USE_DUMP
421 printf("# new vertex (%.6f, %.6f, %.6f) %d\n", UNPACK3(ix), *r_side);
422#endif
423
424#ifdef USE_PARANOID
425 BLI_assert(len_squared_v3v3(ix, e_v0->co) > s->epsilon.eps_sq);
426 BLI_assert(len_squared_v3v3(ix, e_v1->co) > s->epsilon.eps_sq);
427 BLI_assert(len_squared_v3v3(ix, t[0]->co) > s->epsilon.eps_sq);
428 BLI_assert(len_squared_v3v3(ix, t[1]->co) > s->epsilon.eps_sq);
429 BLI_assert(len_squared_v3v3(ix, t[2]->co) > s->epsilon.eps_sq);
430#endif
431 iv = BM_vert_create(bm, ix, nullptr, eBMCreateFlag(0));
432
433 e = BM_edge_exists(e_v0, e_v1);
434 if (e) {
435#ifdef USE_DUMP
436 printf("# adding to edge %d\n", BM_elem_index_get(e));
437#endif
438 edge_verts_add(s, e, iv, false);
439 }
440 else {
441#ifdef USE_DISSOLVE
442 vert_dissolve_add(s, iv);
443#endif
444 }
445
446 if ((*r_side >= IX_EDGE_TRI_EDGE0) && (*r_side <= IX_EDGE_TRI_EDGE2)) {
447 i = uint(*r_side - IX_EDGE_TRI_EDGE0);
448 e = BM_edge_exists(t[i], t[(i + 1) % 3]);
449 if (e) {
450 edge_verts_add(s, e, iv, false);
451 }
452 }
453
454 {
455 int *k = static_cast<int *>(BLI_memarena_alloc(s->mem_arena, sizeof(int[4])));
456 memcpy(k, k_arr[*r_side], sizeof(int[4]));
457 BLI_ghash_insert(s->edgetri_cache, k, iv);
458 }
459
460 return iv;
461 }
462
463 *r_side = IX_NONE;
464
465 return nullptr;
466}
467
471};
472
473static bool bm_loop_filter_fn(const BMLoop *l, void *user_data)
474{
476 return false;
477 }
478
479 if (l->radial_next != l) {
480 LoopFilterWrap *data = static_cast<LoopFilterWrap *>(user_data);
481 BMLoop *l_iter = l->radial_next;
482 const int face_side = data->test_fn(l->f, data->user_data);
483 do {
484 const int face_side_other = data->test_fn(l_iter->f, data->user_data);
485 if (UNLIKELY(face_side_other == -1)) {
486 /* pass */
487 }
488 else if (face_side_other != face_side) {
489 return false;
490 }
491 } while ((l_iter = l_iter->radial_next) != l);
492 return true;
493 }
494 return false;
495}
496
501 int a_index,
502 int b_index,
503 const std::array<BMLoop *, 3> &a,
504 const std::array<BMLoop *, 3> &b,
505 bool no_shared)
506{
507 BMFace *f_a = a[0]->f;
508 BMFace *f_b = b[0]->f;
509 BMVert *fv_a[3] = {UNPACK3_EX(, a, ->v)};
510 BMVert *fv_b[3] = {UNPACK3_EX(, b, ->v)};
511 const float *f_a_cos[3] = {UNPACK3_EX(, fv_a, ->co)};
512 const float *f_b_cos[3] = {UNPACK3_EX(, fv_b, ->co)};
513 float f_a_nor[3];
514 float f_b_nor[3];
515 uint i;
516
517 /* should be enough but may need to bump */
518 BMVert *iv_ls_a[8];
519 BMVert *iv_ls_b[8];
520 STACK_DECLARE(iv_ls_a);
521 STACK_DECLARE(iv_ls_b);
522
523 if (no_shared) {
524 if (UNLIKELY(ELEM(fv_a[0], UNPACK3(fv_b)) || ELEM(fv_a[1], UNPACK3(fv_b)) ||
525 ELEM(fv_a[2], UNPACK3(fv_b))))
526 {
527 return;
528 }
529 }
530 else {
531 if (UNLIKELY(BM_face_share_edge_check(f_a, f_b))) {
532 return;
533 }
534 }
535
536 STACK_INIT(iv_ls_a, ARRAY_SIZE(iv_ls_a));
537 STACK_INIT(iv_ls_b, ARRAY_SIZE(iv_ls_b));
538
539#define VERT_VISIT_A _FLAG_WALK
540#define VERT_VISIT_B _FLAG_WALK_ALT
541
542#define STACK_PUSH_TEST_A(ele) \
543 if (BM_ELEM_API_FLAG_TEST(ele, VERT_VISIT_A) == 0) { \
544 BM_ELEM_API_FLAG_ENABLE(ele, VERT_VISIT_A); \
545 STACK_PUSH(iv_ls_a, ele); \
546 } \
547 ((void)0)
548
549#define STACK_PUSH_TEST_B(ele) \
550 if (BM_ELEM_API_FLAG_TEST(ele, VERT_VISIT_B) == 0) { \
551 BM_ELEM_API_FLAG_ENABLE(ele, VERT_VISIT_B); \
552 STACK_PUSH(iv_ls_b, ele); \
553 } \
554 ((void)0)
555
556 /* vert-vert
557 * --------- */
558 {
559 /* first check if any verts are touching
560 * (any case where we won't create new verts)
561 */
562 uint i_a;
563 for (i_a = 0; i_a < 3; i_a++) {
564 uint i_b;
565 for (i_b = 0; i_b < 3; i_b++) {
566 if (len_squared_v3v3(fv_a[i_a]->co, fv_b[i_b]->co) <= s->epsilon.eps2x_sq) {
567#ifdef USE_DUMP
568 if (BM_ELEM_API_FLAG_TEST(fv_a[i_a], VERT_VISIT_A) == 0) {
569 printf(" ('VERT-VERT-A') %u, %d),\n", i_a, BM_elem_index_get(fv_a[i_a]));
570 }
571 if (BM_ELEM_API_FLAG_TEST(fv_b[i_b], VERT_VISIT_B) == 0) {
572 printf(" ('VERT-VERT-B') %u, %d),\n", i_b, BM_elem_index_get(fv_b[i_b]));
573 }
574#endif
575 STACK_PUSH_TEST_A(fv_a[i_a]);
576 STACK_PUSH_TEST_B(fv_b[i_b]);
577 }
578 }
579 }
580 }
581
582 /* vert-edge
583 * --------- */
584 {
585 uint i_a;
586 for (i_a = 0; i_a < 3; i_a++) {
587 if (BM_ELEM_API_FLAG_TEST(fv_a[i_a], VERT_VISIT_A) == 0) {
588 uint i_b_e0;
589 for (i_b_e0 = 0; i_b_e0 < 3; i_b_e0++) {
590 uint i_b_e1 = (i_b_e0 + 1) % 3;
591
592 if (BM_ELEM_API_FLAG_TEST(fv_b[i_b_e0], VERT_VISIT_B) ||
593 BM_ELEM_API_FLAG_TEST(fv_b[i_b_e1], VERT_VISIT_B))
594 {
595 continue;
596 }
597
598 const float fac = line_point_factor_v3(
599 fv_a[i_a]->co, fv_b[i_b_e0]->co, fv_b[i_b_e1]->co);
600 if ((fac > 0.0f - s->epsilon.eps) && (fac < 1.0f + s->epsilon.eps)) {
601 float ix[3];
602 interp_v3_v3v3(ix, fv_b[i_b_e0]->co, fv_b[i_b_e1]->co, fac);
603 if (len_squared_v3v3(ix, fv_a[i_a]->co) <= s->epsilon.eps2x_sq) {
604 BMEdge *e;
605 STACK_PUSH_TEST_B(fv_a[i_a]);
606 // STACK_PUSH_TEST_A(fv_a[i_a]);
607 e = BM_edge_exists(fv_b[i_b_e0], fv_b[i_b_e1]);
608#ifdef USE_DUMP
609 printf(" ('VERT-EDGE-A', %d, %d),\n",
610 BM_elem_index_get(fv_b[i_b_e0]),
611 BM_elem_index_get(fv_b[i_b_e1]));
612#endif
613 if (e) {
614#ifdef USE_DUMP
615 printf("# adding to edge %d\n", BM_elem_index_get(e));
616#endif
617 edge_verts_add(s, e, fv_a[i_a], true);
618 }
619 break;
620 }
621 }
622 }
623 }
624 }
625 }
626
627 {
628 uint i_b;
629 for (i_b = 0; i_b < 3; i_b++) {
630 if (BM_ELEM_API_FLAG_TEST(fv_b[i_b], VERT_VISIT_B) == 0) {
631 uint i_a_e0;
632 for (i_a_e0 = 0; i_a_e0 < 3; i_a_e0++) {
633 uint i_a_e1 = (i_a_e0 + 1) % 3;
634
635 if (BM_ELEM_API_FLAG_TEST(fv_a[i_a_e0], VERT_VISIT_A) ||
636 BM_ELEM_API_FLAG_TEST(fv_a[i_a_e1], VERT_VISIT_A))
637 {
638 continue;
639 }
640
641 const float fac = line_point_factor_v3(
642 fv_b[i_b]->co, fv_a[i_a_e0]->co, fv_a[i_a_e1]->co);
643 if ((fac > 0.0f - s->epsilon.eps) && (fac < 1.0f + s->epsilon.eps)) {
644 float ix[3];
645 interp_v3_v3v3(ix, fv_a[i_a_e0]->co, fv_a[i_a_e1]->co, fac);
646 if (len_squared_v3v3(ix, fv_b[i_b]->co) <= s->epsilon.eps2x_sq) {
647 BMEdge *e;
648 STACK_PUSH_TEST_A(fv_b[i_b]);
649 // STACK_PUSH_NOTEST(iv_ls_b, fv_b[i_b]);
650 e = BM_edge_exists(fv_a[i_a_e0], fv_a[i_a_e1]);
651#ifdef USE_DUMP
652 printf(" ('VERT-EDGE-B', %d, %d),\n",
653 BM_elem_index_get(fv_a[i_a_e0]),
654 BM_elem_index_get(fv_a[i_a_e1]));
655#endif
656 if (e) {
657#ifdef USE_DUMP
658 printf("# adding to edge %d\n", BM_elem_index_get(e));
659#endif
660 edge_verts_add(s, e, fv_b[i_b], true);
661 }
662 break;
663 }
664 }
665 }
666 }
667 }
668 }
669
670 /* vert-tri
671 * -------- */
672 {
673
674 float t_scale[3][3];
675 uint i_a;
676
677 copy_v3_v3(t_scale[0], fv_b[0]->co);
678 copy_v3_v3(t_scale[1], fv_b[1]->co);
679 copy_v3_v3(t_scale[2], fv_b[2]->co);
680 tri_v3_scale(UNPACK3(t_scale), 1.0f - s->epsilon.eps2x);
681
682 /* second check for verts intersecting the triangle */
683 for (i_a = 0; i_a < 3; i_a++) {
684 if (BM_ELEM_API_FLAG_TEST(fv_a[i_a], VERT_VISIT_A)) {
685 continue;
686 }
687
688 float ix[3];
689 if (isect_point_tri_v3(fv_a[i_a]->co, UNPACK3(t_scale), ix)) {
690 if (len_squared_v3v3(ix, fv_a[i_a]->co) <= s->epsilon.eps2x_sq) {
691 STACK_PUSH_TEST_A(fv_a[i_a]);
692 STACK_PUSH_TEST_B(fv_a[i_a]);
693#ifdef USE_DUMP
694 printf(" 'VERT TRI-A',\n");
695#endif
696 }
697 }
698 }
699 }
700
701 {
702 float t_scale[3][3];
703 uint i_b;
704
705 copy_v3_v3(t_scale[0], fv_a[0]->co);
706 copy_v3_v3(t_scale[1], fv_a[1]->co);
707 copy_v3_v3(t_scale[2], fv_a[2]->co);
708 tri_v3_scale(UNPACK3(t_scale), 1.0f - s->epsilon.eps2x);
709
710 for (i_b = 0; i_b < 3; i_b++) {
711 if (BM_ELEM_API_FLAG_TEST(fv_b[i_b], VERT_VISIT_B)) {
712 continue;
713 }
714
715 float ix[3];
716 if (isect_point_tri_v3(fv_b[i_b]->co, UNPACK3(t_scale), ix)) {
717 if (len_squared_v3v3(ix, fv_b[i_b]->co) <= s->epsilon.eps2x_sq) {
718 STACK_PUSH_TEST_A(fv_b[i_b]);
719 STACK_PUSH_TEST_B(fv_b[i_b]);
720#ifdef USE_DUMP
721 printf(" 'VERT TRI-B',\n");
722#endif
723 }
724 }
725 }
726 }
727
728 if ((STACK_SIZE(iv_ls_a) >= 3) && (STACK_SIZE(iv_ls_b) >= 3)) {
729#ifdef USE_DUMP
730 printf("# OVERLAP\n");
731#endif
732 goto finally;
733 }
734
735 normal_tri_v3(f_a_nor, UNPACK3(f_a_cos));
736 normal_tri_v3(f_b_nor, UNPACK3(f_b_cos));
737
738 /* edge-tri & edge-edge
739 * -------------------- */
740 {
741 for (uint i_a_e0 = 0; i_a_e0 < 3; i_a_e0++) {
742 uint i_a_e1 = (i_a_e0 + 1) % 3;
743 enum ISectType side;
744 BMVert *iv;
745
746 if (BM_ELEM_API_FLAG_TEST(fv_a[i_a_e0], VERT_VISIT_A) ||
747 BM_ELEM_API_FLAG_TEST(fv_a[i_a_e1], VERT_VISIT_A))
748 {
749 continue;
750 }
751
753 s, fv_a[i_a_e0], fv_a[i_a_e1], fv_b, b_index, f_b_cos, f_b_nor, &side);
754 if (iv) {
757#ifdef USE_DUMP
758 printf(" ('EDGE-TRI-A', %d),\n", side);
759#endif
760 }
761 }
762
763 for (uint i_b_e0 = 0; i_b_e0 < 3; i_b_e0++) {
764 uint i_b_e1 = (i_b_e0 + 1) % 3;
765 enum ISectType side;
766 BMVert *iv;
767
768 if (BM_ELEM_API_FLAG_TEST(fv_b[i_b_e0], VERT_VISIT_B) ||
769 BM_ELEM_API_FLAG_TEST(fv_b[i_b_e1], VERT_VISIT_B))
770 {
771 continue;
772 }
773
775 s, fv_b[i_b_e0], fv_b[i_b_e1], fv_a, a_index, f_a_cos, f_a_nor, &side);
776 if (iv) {
779#ifdef USE_DUMP
780 printf(" ('EDGE-TRI-B', %d),\n", side);
781#endif
782 }
783 }
784 }
785
786 {
787 for (i = 0; i < 2; i++) {
788 BMVert **ie_vs;
789 BMFace *f;
790 bool ie_exists;
791 BMEdge *ie;
792
793 if (i == 0) {
794 if (STACK_SIZE(iv_ls_a) != 2) {
795 continue;
796 }
797 ie_vs = iv_ls_a;
798 f = f_a;
799 }
800 else {
801 if (STACK_SIZE(iv_ls_b) != 2) {
802 continue;
803 }
804 ie_vs = iv_ls_b;
805 f = f_b;
806 }
807
808 /* possible but unlikely we get this - for edge-edge intersection */
809 ie = BM_edge_exists(UNPACK2(ie_vs));
810 if (ie == nullptr) {
811 ie_exists = false;
812 /* one of the verts must be new if we are making an edge
813 * ...no, we need this in case 2x quads intersect at either ends.
814 * if not (ie_vs[0].index == -1 or ie_vs[1].index == -1):
815 * continue */
816 ie = BM_edge_create(s->bm, UNPACK2(ie_vs), nullptr, eBMCreateFlag(0));
817 BLI_gset_insert(s->wire_edges, ie);
818 }
819 else {
820 ie_exists = true;
821 /* may already exist */
822 BLI_gset_add(s->wire_edges, ie);
823
824 if (BM_edge_in_face(ie, f)) {
825 continue;
826 }
827 }
828
829 face_edges_add(s, BM_elem_index_get(f), ie, ie_exists);
830 // BLI_assert(len(ie_vs) <= 2)
831 }
832 }
833
834finally:
835 for (i = 0; i < STACK_SIZE(iv_ls_a); i++) {
837 }
838 for (i = 0; i < STACK_SIZE(iv_ls_b); i++) {
840 }
841}
842
843#ifdef USE_BVH
844
846 const float **looptris;
848};
849
850# ifdef USE_KDOPBVH_WATERTIGHT
851static const IsectRayPrecalc isect_precalc_x = {1, 2, 0, 0, 0, 1};
852# endif
853
854static void raycast_callback(void *userdata,
855 int index,
856 const BVHTreeRay *ray,
857 BVHTreeRayHit * /*hit*/)
858{
859 RaycastData *raycast_data = static_cast<RaycastData *>(userdata);
860 const float **looptris = raycast_data->looptris;
861 const float *v0 = looptris[index * 3 + 0];
862 const float *v1 = looptris[index * 3 + 1];
863 const float *v2 = looptris[index * 3 + 2];
864 float dist;
865
866 if (
868 isect_ray_tri_watertight_v3(ray->origin, &isect_precalc_x, v0, v1, v2, &dist, nullptr)
869# else
871 ray->origin, ray->direction, v0, v1, v2, &dist, nullptr, FLT_EPSILON)
872# endif
873 )
874 {
875 if (dist >= 0.0f) {
876# ifdef USE_DUMP
877 printf("%s:\n", __func__);
878 print_v3(" origin", ray->origin);
879 print_v3(" direction", ray->direction);
880 printf(" dist %f\n", dist);
881 print_v3(" v0", v0);
882 print_v3(" v1", v1);
883 print_v3(" v2", v2);
884# endif
885
886# ifdef USE_DUMP
887 printf("%s: Adding depth %f\n", __func__, dist);
888# endif
889 BLI_buffer_append(raycast_data->z_buffer, float, dist);
890 }
891 }
892}
893
894static int isect_bvhtree_point_v3(BVHTree *tree, const float **looptris, const float co[3])
895{
896 BLI_buffer_declare_static(float, z_buffer, BLI_BUFFER_NOP, 64);
897
898 RaycastData raycast_data = {
899 looptris,
900 &z_buffer,
901 };
902 BVHTreeRayHit hit = {0};
903 const float dir[3] = {1.0f, 0.0f, 0.0f};
904
905 /* Need to initialize hit even tho it's not used.
906 * This is to make it so KD-tree believes we didn't intersect anything and
907 * keeps calling the intersect callback.
908 */
909 hit.index = -1;
910 hit.dist = BVH_RAYCAST_DIST_MAX;
911
912 BLI_bvhtree_ray_cast(tree, co, dir, 0.0f, &hit, raycast_callback, &raycast_data);
913
914# ifdef USE_DUMP
915 printf("%s: Total intersections: %zu\n", __func__, z_buffer.count);
916# endif
917
918 int num_isect;
919
920 if (z_buffer.count == 0) {
921 num_isect = 0;
922 }
923 else if (z_buffer.count == 1) {
924 num_isect = 1;
925 }
926 else {
927 /* 2 or more */
928 const float eps = FLT_EPSILON * 10;
929 num_isect = 1; /* always count first */
930
931 qsort(z_buffer.data, z_buffer.count, sizeof(float), BLI_sortutil_cmp_float);
932
933 const float *depth_arr = static_cast<const float *>(z_buffer.data);
934 float depth_last = depth_arr[0];
935
936 for (uint i = 1; i < z_buffer.count; i++) {
937 if (depth_arr[i] - depth_last > eps) {
938 depth_last = depth_arr[i];
939 num_isect++;
940 }
941 }
942
943 BLI_buffer_free(&z_buffer);
944 }
945
946 // return (num_isect & 1) == 1;
947 return num_isect;
948}
949
950#endif /* USE_BVH */
951
953 const blender::Span<std::array<BMLoop *, 3>> looptris,
954 int (*test_fn)(BMFace *f, void *user_data),
955 void *user_data,
956 const bool use_self,
957 const bool use_separate,
958 const bool use_dissolve,
959 const bool use_island_connect,
960 const bool use_partial_connect,
961 const bool use_edge_tag,
962 const int boolean_mode,
963 const float eps)
964{
965 ISectState s;
966 const int totface_orig = bm->totface;
967
968 /* use to check if we made any changes */
969 bool has_edit_isect = false, has_edit_boolean = false;
970
971 /* needed for boolean, since cutting up faces moves the loops within the face */
972 const float **looptri_coords = nullptr;
973
974#ifdef USE_BVH
975 BVHTree *tree_a, *tree_b;
976 uint tree_overlap_tot;
977 BVHTreeOverlap *overlap;
978#else
979 int i_a, i_b;
980#endif
981
982 s.bm = bm;
983
984 s.edgetri_cache = BLI_ghash_new(
986
987 s.edge_verts = BLI_ghash_ptr_new(__func__);
988 s.face_edges = BLI_ghash_int_new(__func__);
989 s.wire_edges = BLI_gset_ptr_new(__func__);
990 s.vert_dissolve = nullptr;
991
992 s.mem_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
993
994 /* setup epsilon from base */
995 s.epsilon.eps = eps;
996 s.epsilon.eps2x = eps * 2.0f;
997 s.epsilon.eps_margin = s.epsilon.eps2x * 10.0f;
998
999 s.epsilon.eps_sq = s.epsilon.eps * s.epsilon.eps;
1000 s.epsilon.eps2x_sq = s.epsilon.eps2x * s.epsilon.eps2x;
1001 s.epsilon.eps_margin_sq = s.epsilon.eps_margin * s.epsilon.eps_margin;
1002
1004 BM_VERT | BM_EDGE |
1005#ifdef USE_NET
1006 BM_FACE |
1007#endif
1008 0);
1009
1011#ifdef USE_SPLICE
1012 BM_EDGE |
1013#endif
1014#ifdef USE_NET
1015 BM_FACE |
1016#endif
1017 0);
1018
1019#ifdef USE_DISSOLVE
1020 if (use_dissolve) {
1022 }
1023#else
1024 UNUSED_VARS(use_dissolve);
1025#endif
1026
1027#ifdef USE_DUMP
1028 printf("data = [\n");
1029#endif
1030
1031 if (boolean_mode != BMESH_ISECT_BOOLEAN_NONE) {
1032 /* Keep original geometry for ray-cast callbacks. */
1033 float **cos;
1034 int i, j;
1035
1036 cos = static_cast<float **>(
1037 MEM_mallocN(size_t(looptris.size()) * sizeof(*looptri_coords) * 3, __func__));
1038 for (i = 0, j = 0; i < int(looptris.size()); i++) {
1039 cos[j++] = looptris[i][0]->v->co;
1040 cos[j++] = looptris[i][1]->v->co;
1041 cos[j++] = looptris[i][2]->v->co;
1042 }
1043 looptri_coords = (const float **)cos;
1044 }
1045
1046#ifdef USE_BVH
1047 {
1048 int i;
1049 tree_a = BLI_bvhtree_new(int(looptris.size()), s.epsilon.eps_margin, 8, 8);
1050 for (i = 0; i < int(looptris.size()); i++) {
1051 if (test_fn(looptris[i][0]->f, user_data) == 0) {
1052 const float t_cos[3][3] = {
1053 {UNPACK3(looptris[i][0]->v->co)},
1054 {UNPACK3(looptris[i][1]->v->co)},
1055 {UNPACK3(looptris[i][2]->v->co)},
1056 };
1057
1058 BLI_bvhtree_insert(tree_a, i, (const float *)t_cos, 3);
1059 }
1060 }
1061 BLI_bvhtree_balance(tree_a);
1062 }
1063
1064 if (use_self == false) {
1065 int i;
1066 tree_b = BLI_bvhtree_new(int(looptris.size()), s.epsilon.eps_margin, 8, 8);
1067 for (i = 0; i < int(looptris.size()); i++) {
1068 if (test_fn(looptris[i][0]->f, user_data) == 1) {
1069 const float t_cos[3][3] = {
1070 {UNPACK3(looptris[i][0]->v->co)},
1071 {UNPACK3(looptris[i][1]->v->co)},
1072 {UNPACK3(looptris[i][2]->v->co)},
1073 };
1074
1075 BLI_bvhtree_insert(tree_b, i, (const float *)t_cos, 3);
1076 }
1077 }
1078 BLI_bvhtree_balance(tree_b);
1079 }
1080 else {
1081 tree_b = tree_a;
1082 }
1083
1084 /* For self intersection this can be useful, sometimes users generate geometry
1085 * where surfaces that seem disconnected happen to share an edge.
1086 * So when performing intersection calculation allow shared vertices,
1087 * just not shared edges. See #75946. */
1088 const bool isect_tri_tri_no_shared = (boolean_mode != BMESH_ISECT_BOOLEAN_NONE);
1089
1091# ifndef NDEBUG
1092 /* The overlap result must match that obtained in Release to succeed
1093 * in the `bmesh_boolean` test. */
1094 if (looptris.size() < 1024) {
1095 flag &= ~BVH_OVERLAP_USE_THREADING;
1096 }
1097# endif
1098 overlap = BLI_bvhtree_overlap_ex(tree_b, tree_a, &tree_overlap_tot, nullptr, nullptr, 0, flag);
1099
1100 if (overlap) {
1101 uint i;
1102
1103 for (i = 0; i < tree_overlap_tot; i++) {
1104# ifdef USE_DUMP
1105 printf(" ((%d, %d), (\n", overlap[i].indexA, overlap[i].indexB);
1106# endif
1108 overlap[i].indexA,
1109 overlap[i].indexB,
1110 looptris[overlap[i].indexA],
1111 looptris[overlap[i].indexB],
1112 isect_tri_tri_no_shared);
1113# ifdef USE_DUMP
1114 printf(")),\n");
1115# endif
1116 }
1117 MEM_freeN(overlap);
1118 }
1119
1120 if (boolean_mode == BMESH_ISECT_BOOLEAN_NONE) {
1121 /* no booleans, just free immediate */
1122 BLI_bvhtree_free(tree_a);
1123 if (tree_a != tree_b) {
1124 BLI_bvhtree_free(tree_b);
1125 }
1126 }
1127
1128#else
1129 {
1130 for (i_a = 0; i_a < looptris.size(); i_a++) {
1131 const int t_a = test_fn(looptris[i_a][0]->f, user_data);
1132 for (i_b = i_a + 1; i_b < looptris.size(); i_b++) {
1133 const int t_b = test_fn(looptris[i_b][0]->f, user_data);
1134
1135 if (use_self) {
1136 if ((t_a != 0) || (t_b != 0)) {
1137 continue;
1138 }
1139 }
1140 else {
1141 if ((t_a != t_b) && !ELEM(-1, t_a, t_b)) {
1142 continue;
1143 }
1144 }
1145
1146# ifdef USE_DUMP
1147 printf(" ((%d, %d), (", i_a, i_b);
1148# endif
1149 bm_isect_tri_tri(&s, i_a, i_b, looptris[i_a], looptris[i_b], isect_tri_tri_no_shared);
1150# ifdef USE_DUMP
1151 printf(")),\n");
1152# endif
1153 }
1154 }
1155 }
1156#endif /* USE_BVH */
1157
1158#ifdef USE_DUMP
1159 printf("]\n");
1160#endif
1161
1162 /* --------- */
1163
1164#ifdef USE_SPLICE
1165 {
1166 GHashIterator gh_iter;
1167
1168 GHASH_ITER (gh_iter, s.edge_verts) {
1169 BMEdge *e = static_cast<BMEdge *>(BLI_ghashIterator_getKey(&gh_iter));
1170 LinkBase *v_ls_base = static_cast<LinkBase *>(BLI_ghashIterator_getValue(&gh_iter));
1171
1172 BMVert *v_start;
1173 BMVert *v_end;
1174 BMVert *v_prev;
1175 bool is_wire;
1176
1177 LinkNode *node;
1178
1179 /* direction is arbitrary, could be swapped */
1180 v_start = e->v1;
1181 v_end = e->v2;
1182
1183 if (v_ls_base->list_len > 1) {
1184 edge_verts_sort(v_start->co, v_ls_base);
1185 }
1186
1187# ifdef USE_DUMP
1188 printf("# SPLITTING EDGE: %d, %u\n", BM_elem_index_get(e), v_ls_base->list_len);
1189# endif
1190 /* intersect */
1191 is_wire = BLI_gset_haskey(s.wire_edges, e);
1192
1193# ifdef USE_PARANOID
1194 for (node = v_ls_base->list; node; node = node->next) {
1195 BMVert *_v = node->link;
1196 BLI_assert(len_squared_v3v3(_v->co, e->v1->co) > s.epsilon.eps_sq);
1197 BLI_assert(len_squared_v3v3(_v->co, e->v2->co) > s.epsilon.eps_sq);
1198 }
1199# endif
1200
1201 v_prev = v_start;
1202
1203 for (node = v_ls_base->list; node; node = node->next) {
1204 BMVert *vi = static_cast<BMVert *>(node->link);
1205 const float fac = line_point_factor_v3(vi->co, e->v1->co, e->v2->co);
1206
1207 if (BM_vert_in_edge(e, v_prev)) {
1208 BMEdge *e_split;
1209 v_prev = BM_edge_split(bm, e, v_prev, &e_split, clamp_f(fac, 0.0f, 1.0f));
1210 BLI_assert(BM_vert_in_edge(e, v_end));
1211
1212 if (!BM_edge_exists(v_prev, vi) && !BM_vert_splice_check_double(v_prev, vi) &&
1213 !BM_vert_pair_share_face_check(v_prev, vi))
1214 {
1215 BM_vert_splice(bm, vi, v_prev);
1216 }
1217 else {
1218 copy_v3_v3(v_prev->co, vi->co);
1219 }
1220 v_prev = vi;
1221 if (is_wire) {
1222 BLI_gset_insert(s.wire_edges, e_split);
1223 }
1224 }
1225 }
1226 UNUSED_VARS_NDEBUG(v_end);
1227 }
1228 }
1229#endif
1230
1231/* important to handle before edgenet */
1232#ifdef USE_DISSOLVE
1233 if (use_dissolve && (boolean_mode == BMESH_ISECT_BOOLEAN_NONE)) {
1234 /* first pass */
1235 BMVert *(*splice_ls)[2];
1236 STACK_DECLARE(splice_ls);
1237 LinkNode *node;
1238
1239 for (node = s.vert_dissolve; node; node = node->next) {
1240 BMVert *v = static_cast<BMVert *>(node->link);
1242 if (!BM_vert_is_edge_pair(v)) {
1244 }
1245 }
1246 }
1247
1248 splice_ls = static_cast<BMVert *(*)[2]>(
1249 MEM_mallocN(BLI_gset_len(s.wire_edges) * sizeof(*splice_ls), __func__));
1250 STACK_INIT(splice_ls, BLI_gset_len(s.wire_edges));
1251
1252 for (node = s.vert_dissolve; node; node = node->next) {
1253 BMEdge *e_pair[2];
1254 BMVert *v = static_cast<BMVert *>(node->link);
1255 BMVert *v_a, *v_b;
1256
1258 continue;
1259 }
1260
1261 /* get chain */
1262 e_pair[0] = v->e;
1263 e_pair[1] = BM_DISK_EDGE_NEXT(v->e, v);
1264
1265 if (BM_elem_flag_test(e_pair[0], BM_ELEM_TAG) || BM_elem_flag_test(e_pair[1], BM_ELEM_TAG)) {
1266 continue;
1267 }
1268
1269 /* It's possible the vertex to dissolve is an edge on an existing face
1270 * that doesn't divide the face, therefor the edges are not wire
1271 * and shouldn't be handled here, see: #63787. */
1272 if (!BLI_gset_haskey(s.wire_edges, e_pair[0]) || !BLI_gset_haskey(s.wire_edges, e_pair[1])) {
1273 continue;
1274 }
1275
1276 v_a = BM_edge_other_vert(e_pair[0], v);
1277 v_b = BM_edge_other_vert(e_pair[1], v);
1278
1279 /* simple case */
1281 /* only start on an edge-case */
1282 /* pass */
1283 }
1284 else if (!BM_elem_flag_test(v_a, BM_ELEM_TAG) && !BM_elem_flag_test(v_b, BM_ELEM_TAG)) {
1285 /* simple case, single edge spans face */
1286 BMVert **splice_pair;
1287 BM_elem_flag_enable(e_pair[1], BM_ELEM_TAG);
1288 splice_pair = STACK_PUSH_RET(splice_ls);
1289 splice_pair[0] = v;
1290 splice_pair[1] = v_b;
1291# ifdef USE_DUMP
1292 printf("# Simple Case!\n");
1293# endif
1294 }
1295 else {
1296# ifdef USE_PARANOID
1297 BMEdge *e_keep;
1298# endif
1299 BMEdge *e;
1300 BMEdge *e_step;
1301 BMVert *v_step;
1302
1303 /* walk the chain! */
1304 if (BM_elem_flag_test(v_a, BM_ELEM_TAG)) {
1305 e = e_pair[0];
1306# ifdef USE_PARANOID
1307 e_keep = e_pair[1];
1308# endif
1309 }
1310 else {
1311 std::swap(v_a, v_b);
1312 e = e_pair[1];
1313# ifdef USE_PARANOID
1314 e_keep = e_pair[0];
1315# endif
1316 }
1317
1318 /* WALK */
1319 v_step = v;
1320 e_step = e;
1321
1322 while (true) {
1323 BMEdge *e_next;
1324 BMVert *v_next;
1325
1326 v_next = BM_edge_other_vert(e_step, v_step);
1328 if (!BM_elem_flag_test(v_next, BM_ELEM_TAG)) {
1329 BMVert **splice_pair;
1330# ifdef USE_PARANOID
1331 BLI_assert(e_step != e_keep);
1332# endif
1333 splice_pair = STACK_PUSH_RET(splice_ls);
1334 splice_pair[0] = v;
1335 splice_pair[1] = v_next;
1336 break;
1337 }
1338
1339 e_next = bm_vert_other_edge(v_next, e_step);
1340 e_step = e_next;
1341 v_step = v_next;
1343# ifdef USE_PARANOID
1344 BLI_assert(e_step != e_keep);
1345# endif
1346# ifdef USE_DUMP
1347 printf("# walk step %p %p\n", e_next, v_next);
1348# endif
1349 }
1350# ifdef USE_PARANOID
1352# endif
1353 }
1354 }
1355
1356 /* Remove edges! */
1357 {
1358 GHashIterator gh_iter;
1359
1360 GHASH_ITER (gh_iter, s.face_edges) {
1361 LinkBase *e_ls_base = static_cast<LinkBase *>(BLI_ghashIterator_getValue(&gh_iter));
1362 LinkNode **node_prev_p;
1363
1364 node_prev_p = &e_ls_base->list;
1365 for (node = e_ls_base->list; node; node = node->next) {
1366 BMEdge *e = static_cast<BMEdge *>(node->link);
1368 /* allocated by arena, don't free */
1369 *node_prev_p = node->next;
1370 e_ls_base->list_len--;
1371 }
1372 else {
1373 node_prev_p = &node->next;
1374 }
1375 }
1376 }
1377 }
1378
1379 {
1380 BMIter eiter;
1381 BMEdge *e, *e_next;
1382
1383 BM_ITER_MESH_MUTABLE (e, e_next, &eiter, bm, BM_EDGES_OF_MESH) {
1385
1386 /* in rare and annoying cases,
1387 * there can be faces from 's.face_edges' removed by the edges.
1388 * These are degenerate cases, so just make sure we don't reference the faces again. */
1389 if (e->l) {
1390 BMLoop *l_iter = e->l;
1391 BMFace **faces;
1392
1393 faces = bm->ftable;
1394
1395 do {
1396 const int f_index = BM_elem_index_get(l_iter->f);
1397 if (f_index >= 0) {
1398 BLI_assert(f_index < totface_orig);
1399 /* we could check if these are in: 's.face_edges', but easier just to remove */
1400 faces[f_index] = nullptr;
1401 }
1402 } while ((l_iter = l_iter->radial_next) != e->l);
1403 }
1404
1405 BLI_gset_remove(s.wire_edges, e, nullptr);
1406 BM_edge_kill(bm, e);
1407 }
1408 }
1409 }
1410
1411 /* Remove verts! */
1412 {
1413 GSet *verts_invalid = BLI_gset_ptr_new(__func__);
1414
1415 for (node = s.vert_dissolve; node; node = node->next) {
1416 /* arena allocated, don't free */
1417 BMVert *v = static_cast<BMVert *>(node->link);
1419 if (!v->e) {
1420 BLI_gset_add(verts_invalid, v);
1421 BM_vert_kill(bm, v);
1422 }
1423 }
1424 }
1425
1426 {
1427 uint i;
1428 for (i = 0; i < STACK_SIZE(splice_ls); i++) {
1429 if (!BLI_gset_haskey(verts_invalid, splice_ls[i][0]) &&
1430 !BLI_gset_haskey(verts_invalid, splice_ls[i][1]))
1431 {
1432 if (!BM_edge_exists(UNPACK2(splice_ls[i])) &&
1433 !BM_vert_splice_check_double(UNPACK2(splice_ls[i])))
1434 {
1435 BM_vert_splice(bm, splice_ls[i][1], splice_ls[i][0]);
1436 }
1437 }
1438 }
1439 }
1440
1441 BLI_gset_free(verts_invalid, nullptr);
1442 }
1443
1444 MEM_freeN(splice_ls);
1445 }
1446#endif /* USE_DISSOLVE */
1447
1448/* now split faces */
1449#ifdef USE_NET
1450 {
1451 GHashIterator gh_iter;
1452 BMFace **faces;
1453
1454 MemArena *mem_arena_edgenet = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
1455
1456 faces = bm->ftable;
1457
1458 GHASH_ITER (gh_iter, s.face_edges) {
1459 const int f_index = POINTER_AS_INT(BLI_ghashIterator_getKey(&gh_iter));
1460 BMFace *f;
1461 LinkBase *e_ls_base = static_cast<LinkBase *>(BLI_ghashIterator_getValue(&gh_iter));
1462
1463 BLI_assert(f_index >= 0 && f_index < totface_orig);
1464
1465 f = faces[f_index];
1466 if (UNLIKELY(f == nullptr)) {
1467 continue;
1468 }
1469
1470 BLI_assert(BM_elem_index_get(f) == f_index);
1471
1473 bm, f, e_ls_base, use_island_connect, use_partial_connect, mem_arena_edgenet);
1474
1475 BLI_memarena_clear(mem_arena_edgenet);
1476 }
1477
1478 BLI_memarena_free(mem_arena_edgenet);
1479 }
1480#else
1481 UNUSED_VARS(use_island_connect);
1482#endif /* USE_NET */
1483 (void)totface_orig;
1484
1485#ifdef USE_SEPARATE
1486 if (use_separate) {
1487 GSetIterator gs_iter;
1488
1490
1491 GSET_ITER (gs_iter, s.wire_edges) {
1492 BMEdge *e = static_cast<BMEdge *>(BLI_gsetIterator_getKey(&gs_iter));
1494 }
1495
1496 BM_mesh_edgesplit(bm, false, true, false);
1497 }
1498 else if (boolean_mode != BMESH_ISECT_BOOLEAN_NONE || use_edge_tag) {
1499 GSetIterator gs_iter;
1500
1501 /* no need to clear for boolean */
1502
1503 GSET_ITER (gs_iter, s.wire_edges) {
1504 BMEdge *e = static_cast<BMEdge *>(BLI_gsetIterator_getKey(&gs_iter));
1506 }
1507 }
1508#else
1509 (void)use_separate;
1510#endif /* USE_SEPARATE */
1511
1512 if (boolean_mode != BMESH_ISECT_BOOLEAN_NONE) {
1513 BVHTree *tree_pair[2] = {tree_a, tree_b};
1514
1515 /* group vars */
1516 int *groups_array;
1517 int(*group_index)[2];
1518 int group_tot;
1519 int i;
1520 BMFace **ftable;
1521
1523 ftable = bm->ftable;
1524
1525 /* wrap the face-test callback to make it into an edge-loop delimiter */
1526 LoopFilterWrap user_data_wrap{};
1527 user_data_wrap.test_fn = test_fn;
1528 user_data_wrap.user_data = user_data;
1529
1530 groups_array = static_cast<int *>(
1531 MEM_mallocN(sizeof(*groups_array) * size_t(bm->totface), __func__));
1532 group_tot = BM_mesh_calc_face_groups(
1533 bm, groups_array, &group_index, bm_loop_filter_fn, nullptr, &user_data_wrap, 0, BM_EDGE);
1534
1535#ifdef USE_DUMP
1536 printf("%s: Total face-groups: %d\n", __func__, group_tot);
1537#endif
1538
1539 /* Check if island is inside/outside */
1540 for (i = 0; i < group_tot; i++) {
1541 int fg = group_index[i][0];
1542 int fg_end = group_index[i][1] + fg;
1543 bool do_remove, do_flip;
1544
1545 {
1546 /* For now assume this is an OK face to test with (not degenerate!) */
1547 BMFace *f = ftable[groups_array[fg]];
1548 float co[3];
1549 int hits;
1550 int side = test_fn(f, user_data);
1551
1552 if (side == -1) {
1553 continue;
1554 }
1555 BLI_assert(ELEM(side, 0, 1));
1556 side = !side;
1557
1558 // BM_face_calc_center_median(f, co);
1560
1561 hits = isect_bvhtree_point_v3(tree_pair[side], looptri_coords, co);
1562
1563 switch (boolean_mode) {
1565 do_remove = ((hits & 1) != 1);
1566 do_flip = false;
1567 break;
1569 do_remove = ((hits & 1) == 1);
1570 do_flip = false;
1571 break;
1573 do_remove = ((hits & 1) == 1) == side;
1574 do_flip = (side == 0);
1575 break;
1576 }
1577 }
1578
1579 if (do_remove) {
1580 for (; fg != fg_end; fg++) {
1581 /* postpone killing the face since we access below, mark instead */
1582 // BM_face_kill_loose(bm, ftable[groups_array[fg]]);
1583 ftable[groups_array[fg]]->mat_nr = -1;
1584 }
1585 }
1586 else if (do_flip) {
1587 for (; fg != fg_end; fg++) {
1588 BM_face_normal_flip(bm, ftable[groups_array[fg]]);
1589 }
1590 }
1591
1592 has_edit_boolean |= (do_flip || do_remove);
1593 }
1594
1595 MEM_freeN(groups_array);
1596 MEM_freeN(group_index);
1597
1598#ifdef USE_DISSOLVE
1599 /* We have dissolve code above, this is alternative logic,
1600 * we need to do it after the boolean is executed. */
1601 if (use_dissolve) {
1602 LinkNode *node;
1603 for (node = s.vert_dissolve; node; node = node->next) {
1604 BMVert *v = static_cast<BMVert *>(node->link);
1605 if (BM_vert_is_edge_pair(v)) {
1606 /* we won't create degenerate faces from this */
1607 bool ok = true;
1608
1609 /* would we create a 2-sided-face?
1610 * if so, don't dissolve this since we may */
1611 if (v->e->l) {
1612 BMLoop *l_iter = v->e->l;
1613 do {
1614 if (l_iter->f->len == 3) {
1615 ok = false;
1616 break;
1617 }
1618 } while ((l_iter = l_iter->radial_next) != v->e->l);
1619 }
1620
1621 if (ok) {
1622 BM_vert_collapse_edge(bm, v->e, v, true, false, false);
1623 }
1624 }
1625 }
1626 }
1627#endif
1628
1629 {
1630 int tot = bm->totface;
1631 for (i = 0; i < tot; i++) {
1632 if (ftable[i]->mat_nr == -1) {
1633 BM_face_kill_loose(bm, ftable[i]);
1634 }
1635 }
1636 }
1637 }
1638
1639 if (boolean_mode != BMESH_ISECT_BOOLEAN_NONE) {
1640 MEM_freeN((void *)looptri_coords);
1641
1642 /* no booleans, just free immediate */
1643 BLI_bvhtree_free(tree_a);
1644 if (tree_a != tree_b) {
1645 BLI_bvhtree_free(tree_b);
1646 }
1647 }
1648
1649 has_edit_isect = (BLI_ghash_len(s.face_edges) != 0);
1650
1651 /* cleanup */
1652 BLI_ghash_free(s.edgetri_cache, nullptr, nullptr);
1653
1654 BLI_ghash_free(s.edge_verts, nullptr, nullptr);
1655 BLI_ghash_free(s.face_edges, nullptr, nullptr);
1656 BLI_gset_free(s.wire_edges, nullptr);
1657
1658 BLI_memarena_free(s.mem_arena);
1659
1660 /* It's unlikely the selection history is useful at this point,
1661 * if this is not called this array would need to be validated, see: #86799. */
1663
1664 return (has_edit_isect || has_edit_boolean);
1665}
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:25
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_buffer_append(buffer_, type_, val_)
Definition BLI_buffer.h:52
@ BLI_BUFFER_NOP
Definition BLI_buffer.h:23
#define BLI_buffer_declare_static(type_, name_, flag_, static_count_)
Definition BLI_buffer.h:27
#define BLI_buffer_free(name_)
Definition BLI_buffer.h:96
struct GSet GSet
Definition BLI_ghash.h:341
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.h:299
GSet * BLI_gset_ptr_new(const char *info)
bool BLI_gset_haskey(const GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:1004
#define BLI_ghashutil_inthash_v4_cmp
Definition BLI_ghash.h:602
BLI_INLINE void * BLI_ghashIterator_getValue(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.h:303
#define GHASH_ITER(gh_iter_, ghash_)
Definition BLI_ghash.h:322
GHash * BLI_ghash_new(GHashHashFP hashfp, GHashCmpFP cmpfp, const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:686
GHash * BLI_ghash_ptr_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
unsigned int BLI_gset_len(const GSet *gs) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:954
void BLI_gset_insert(GSet *gs, void *key)
Definition BLI_ghash.c:959
BLI_INLINE void * BLI_gsetIterator_getKey(GSetIterator *gsi)
Definition BLI_ghash.h:459
unsigned int BLI_ghash_len(const GHash *gh) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:702
#define GSET_ITER(gs_iter_, gset_)
Definition BLI_ghash.h:472
#define BLI_ghashutil_inthash_v4_p
Definition BLI_ghash.h:593
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:731
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition BLI_ghash.c:707
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.c:860
GHash * BLI_ghash_int_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
Definition BLI_ghash.c:1034
bool BLI_ghash_ensure_p(GHash *gh, void *key, void ***r_val) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:752
bool BLI_gset_add(GSet *gs, void *key)
Definition BLI_ghash.c:966
bool BLI_gset_remove(GSet *gs, const void *key, GSetKeyFreeFP keyfreefp)
Definition BLI_ghash.c:999
BVHTree * BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis)
@ BVH_OVERLAP_USE_THREADING
Definition BLI_kdopbvh.h:77
@ BVH_OVERLAP_RETURN_PAIRS
Definition BLI_kdopbvh.h:78
#define BVH_RAYCAST_DIST_MAX
Definition BLI_kdopbvh.h:92
#define USE_KDOPBVH_WATERTIGHT
Definition BLI_kdopbvh.h:21
void BLI_bvhtree_balance(BVHTree *tree)
void BLI_bvhtree_free(BVHTree *tree)
void BLI_bvhtree_insert(BVHTree *tree, int index, const float co[3], int numpoints)
BVHTreeOverlap * BLI_bvhtree_overlap_ex(const BVHTree *tree1, const BVHTree *tree2, uint *r_overlap_num, BVHTree_OverlapCallback callback, void *userdata, uint max_interactions, int flag)
int BLI_bvhtree_ray_cast(const BVHTree *tree, const float co[3], const float dir[3], float radius, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata)
MINLINE float clamp_f(float value, float min, float max)
MINLINE float min_fff(float a, float b, float c)
bool isect_line_segment_tri_epsilon_v3(const float p1[3], const float p2[3], const float v0[3], const float v1[3], const float v2[3], float *r_lambda, float r_uv[2], float epsilon)
int isect_line_line_epsilon_v3(const float v1[3], const float v2[3], const float v3[3], const float v4[3], float r_i1[3], float r_i2[3], float epsilon)
bool isect_point_tri_v3(const float p[3], const float v1[3], const float v2[3], const float v3[3], float r_isect_co[3])
bool isect_ray_tri_watertight_v3(const float ray_origin[3], const struct IsectRayPrecalc *isect_precalc, const float v0[3], const float v1[3], const float v2[3], float *r_lambda, float r_uv[2])
float line_point_factor_v3(const float p[3], const float l1[3], const float l2[3])
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:39
bool isect_ray_tri_epsilon_v3(const float ray_origin[3], const float ray_direction[3], const float v0[3], const float v1[3], const float v2[3], float *r_lambda, float r_uv[2], float epsilon)
void print_v3(const char *str, const float v[3])
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
Definition math_vector.c:36
MINLINE float normalize_v3(float n[3])
void mid_v3_v3v3v3(float v[3], const float v1[3], const float v2[3], const float v3[3])
void * BLI_memarena_alloc(struct MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
void BLI_memarena_free(struct MemArena *ma) ATTR_NONNULL(1)
struct MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
#define BLI_MEMARENA_STD_BUFSIZE
void void BLI_memarena_clear(MemArena *ma) ATTR_NONNULL(1)
int BLI_sortutil_cmp_float(const void *a_, const void *b_)
Definition sort_utils.c:25
unsigned int uint
#define UNPACK2(a)
#define UNPACK3_EX(pre, a, post)
#define UNPACK4(a)
#define ARRAY_SIZE(arr)
#define UNUSED_VARS(...)
#define POINTER_FROM_INT(i)
#define UNUSED_VARS_NDEBUG(...)
#define POINTER_AS_INT(i)
#define UNPACK3(a)
#define UNLIKELY(x)
#define ELEM(...)
#define STACK_DECLARE(stack)
#define STACK_SIZE(stack)
#define STACK_INIT(stack, stack_num)
#define STACK_PUSH_RET(stack)
Read Guarded memory(de)allocation.
#define BM_DISK_EDGE_NEXT(e, v)
@ BM_ELEM_TAG
bool BM_vert_splice(BMesh *bm, BMVert *v_dst, BMVert *v_src)
Splice Vert.
void BM_vert_kill(BMesh *bm, BMVert *v)
void BM_face_kill_loose(BMesh *bm, BMFace *f)
void BM_edge_kill(BMesh *bm, BMEdge *e)
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.
bool BM_vert_splice_check_double(BMVert *v_a, BMVert *v_b)
eBMCreateFlag
Definition bmesh_core.hh:23
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)
#define KEY_EDGE_TRI_ORDER(k)
static bool bm_loop_filter_fn(const BMLoop *l, void *user_data)
static void raycast_callback(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *)
static BMVert * bm_isect_edge_tri(ISectState *s, BMVert *e_v0, BMVert *e_v1, BMVert *t[3], const int t_index, const float *t_cos[3], const float t_nor[3], enum ISectType *r_side)
static void bm_isect_tri_tri(ISectState *s, int a_index, int b_index, const std::array< BMLoop *, 3 > &a, const std::array< BMLoop *, 3 > &b, bool no_shared)
static bool ghash_insert_link(GHash *gh, void *key, void *val, bool use_test, MemArena *mem_arena)
#define KEY_SET(k, i0, i1, i2, i3)
bool BM_mesh_intersect(BMesh *bm, const blender::Span< std::array< BMLoop *, 3 > > looptris, int(*test_fn)(BMFace *f, void *user_data), void *user_data, const bool use_self, const bool use_separate, const bool use_dissolve, const bool use_island_connect, const bool use_partial_connect, const bool use_edge_tag, const int boolean_mode, const float eps)
#define STACK_PUSH_TEST_B(ele)
static void face_edges_add(ISectState *s, const int f_index, BMEdge *e, const bool use_test)
#define VERT_VISIT_B
#define USE_SPLICE
static enum ISectType intersect_line_tri(const float p0[3], const float p1[3], const float *t_cos[3], const float t_nor[3], float r_ix[3], const ISectEpsilon *e)
#define STACK_PUSH_TEST_A(ele)
static void face_edges_split(BMesh *bm, BMFace *f, LinkBase *e_ls_base, bool use_island_connect, bool use_partial_connect, MemArena *mem_arena_edgenet)
static void tri_v3_scale(float v1[3], float v2[3], float v3[3], const float t)
ISectType
@ IX_EDGE_TRI_EDGE2
@ IX_EDGE_TRI_EDGE1
@ IX_EDGE_TRI_EDGE0
@ IX_TOT
@ IX_EDGE_TRI
@ IX_NONE
#define USE_NET
#define VERT_VISIT_A
static void edge_verts_sort(const float co[3], LinkBase *v_ls_base)
static void edge_verts_add(ISectState *s, BMEdge *e, BMVert *v, const bool use_test)
static void vert_dissolve_add(ISectState *s, BMVert *v)
static int isect_bvhtree_point_v3(BVHTree *tree, const float **looptris, const float co[3])
static BMEdge * bm_vert_other_edge(BMVert *v, BMEdge *e)
@ BMESH_ISECT_BOOLEAN_DIFFERENCE
@ BMESH_ISECT_BOOLEAN_NONE
@ BMESH_ISECT_BOOLEAN_UNION
@ BMESH_ISECT_BOOLEAN_ISECT
@ BM_EDGES_OF_MESH
#define BM_ITER_MESH_MUTABLE(ele, ele_next, iter, bm, itype)
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_select_history_clear(BMesh *bm)
void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
BMVert * BM_edge_split(BMesh *bm, BMEdge *e, BMVert *v, BMEdge **r_e, float fac)
Edge Split.
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.
#define BM_FACE
#define BM_EDGE
#define BM_VERT
void BM_face_calc_point_in_face(const BMFace *f, float r_co[3])
void BM_face_normal_flip(BMesh *bm, BMFace *f)
bool BM_face_split_edgenet_connect_islands(BMesh *bm, BMFace *f, BMEdge **edge_net_init, const uint edge_net_init_len, bool use_partial_connect, MemArena *mem_arena, BMEdge ***r_edge_net_new, uint *r_edge_net_new_len)
bool BM_face_split_edgenet(BMesh *bm, BMFace *f, BMEdge **edge_net, const int edge_net_len, blender::Vector< BMFace * > *r_face_arr)
#define BM_ELEM_API_FLAG_DISABLE(element, f)
#define BM_ELEM_API_FLAG_TEST(element, f)
bool BM_vert_pair_share_face_check(BMVert *v_a, BMVert *v_b)
int BM_mesh_calc_face_groups(BMesh *bm, int *r_groups_array, int(**r_group_index)[2], BMLoopFilterFunc filter_fn, BMLoopPairFilterFunc filter_pair_fn, void *user_data, const char hflag_test, const char htype_step)
bool BM_edge_in_face(const BMEdge *e, const BMFace *f)
bool BM_face_share_edge_check(BMFace *f1, BMFace *f2)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
bool BM_vert_is_edge_pair(const BMVert *v)
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_vert_in_edge(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
local_group_size(16, 16) .push_constant(Type b
#define printf
OperationNode * node
#define fabsf(x)
KDTree_3d * tree
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
static MemArena * mem_arena
Definition makesdna.cc:62
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
ccl_device_inline float3 cos(float3 v)
static char faces[256]
const btScalar eps
Definition poly34.cpp:11
struct BMLoop * l
short mat_nr
BMHeader head
struct BMEdge * e
struct BMLoop * radial_next
struct BMFace * f
float co[3]
struct BMEdge * e
BMHeader head
BMFace ** ftable
int totface
ISectEpsilon epsilon
MemArena * mem_arena
GHash * edgetri_cache
LinkNode * vert_dissolve
LinkNode * list
void * link
struct LinkNode * next
int(* test_fn)(BMFace *f, void *user_data)
BLI_Buffer * z_buffer
const float ** looptris
uint8_t flag
Definition wm_window.cc:138