Files
test/source/blender/bmesh/tools/bmesh_intersect.cc
Campbell Barton e955c94ed3 License Headers: Set copyright to "Blender Authors", add AUTHORS
Listing the "Blender Foundation" as copyright holder implied the Blender
Foundation holds copyright to files which may include work from many
developers.

While keeping copyright on headers makes sense for isolated libraries,
Blender's own code may be refactored or moved between files in a way
that makes the per file copyright holders less meaningful.

Copyright references to the "Blender Foundation" have been replaced with
"Blender Authors", with the exception of `./extern/` since these this
contains libraries which are more isolated, any changed to license
headers there can be handled on a case-by-case basis.

Some directories in `./intern/` have also been excluded:

- `./intern/cycles/` it's own `AUTHORS` file is planned.
- `./intern/opensubdiv/`.

An "AUTHORS" file has been added, using the chromium projects authors
file as a template.

Design task: #110784

Ref !110783.
2023-08-16 00:20:26 +10:00

1663 lines
45 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bmesh
*
* Cut meshes along intersections.
*
* Boolean-like modeling operation (without calculating inside/outside).
*
* Supported:
* - Concave faces.
* - Non-planar faces.
* - Custom-data (UVs etc).
*
* Unsupported:
* - Intersecting between different meshes.
* - No support for holes (cutting a hole into a single face).
*/
#include "MEM_guardedalloc.h"
#include "BLI_alloca.h"
#include "BLI_math_geom.h"
#include "BLI_math_vector.h"
#include "BLI_memarena.h"
#include "BLI_sort_utils.h"
#include "BLI_utildefines.h"
#include "BLI_linklist_stack.h"
#include "BLI_utildefines_stack.h"
#ifndef NDEBUG
#endif
#include "BLI_buffer.h"
#include "BLI_kdopbvh.h"
#include "bmesh.h"
#include "intern/bmesh_private.h"
#include "bmesh_intersect.h" /* own include */
#include "tools/bmesh_edgesplit.h"
#include "BLI_strict_flags.h"
/*
* Some of these depend on each other:
*/
/* splice verts into existing edges */
#define USE_SPLICE
/* split faces by intersecting edges */
#define USE_NET
/* split resulting edges */
#define USE_SEPARATE
/* remove verts created by intersecting triangles */
#define USE_DISSOLVE
/* detect isolated holes and fill them */
#define USE_NET_ISLAND_CONNECT
/* strict asserts that may fail in practice (handy for debugging cases which should succeed) */
// #define USE_PARANOID
/* use accelerated overlap check */
#define USE_BVH
// #define USE_DUMP
static void tri_v3_scale(float v1[3], float v2[3], float v3[3], const float t)
{
float p[3];
mid_v3_v3v3v3(p, v1, v2, v3);
interp_v3_v3v3(v1, p, v1, t);
interp_v3_v3v3(v2, p, v2, t);
interp_v3_v3v3(v3, p, v3, t);
}
#ifdef USE_DISSOLVE
/* other edge when a vert only has 2 edges */
static BMEdge *bm_vert_other_edge(BMVert *v, BMEdge *e)
{
BLI_assert(BM_vert_is_edge_pair(v));
BLI_assert(BM_vert_in_edge(e, v));
if (v->e != e) {
return v->e;
}
return BM_DISK_EDGE_NEXT(v->e, v);
}
#endif
enum ISectType {
IX_NONE = -1,
IX_EDGE_TRI_EDGE0,
IX_EDGE_TRI_EDGE1,
IX_EDGE_TRI_EDGE2,
IX_EDGE_TRI,
IX_TOT,
};
struct ISectEpsilon {
float eps, eps_sq;
float eps2x, eps2x_sq;
float eps_margin, eps_margin_sq;
};
struct ISectState {
BMesh *bm;
GHash *edgetri_cache; /* int[4]: BMVert */
GHash *edge_verts; /* BMEdge: LinkList(of verts), new and original edges */
GHash *face_edges; /* BMFace-index: LinkList(of edges), only original faces */
GSet *wire_edges; /* BMEdge (could use tags instead) */
LinkNode *vert_dissolve; /* BMVert's */
MemArena *mem_arena;
ISectEpsilon epsilon;
};
/**
* Store as value in GHash so we can get list-length without counting every time.
* Also means we don't need to update the GHash value each time.
*/
struct LinkBase {
LinkNode *list;
uint list_len;
};
static bool ghash_insert_link(GHash *gh, void *key, void *val, bool use_test, MemArena *mem_arena)
{
void **ls_base_p;
LinkBase *ls_base;
LinkNode *ls;
if (!BLI_ghash_ensure_p(gh, key, &ls_base_p)) {
ls_base = static_cast<LinkBase *>(*ls_base_p = BLI_memarena_alloc(mem_arena,
sizeof(*ls_base)));
ls_base->list = nullptr;
ls_base->list_len = 0;
}
else {
ls_base = static_cast<LinkBase *>(*ls_base_p);
if (use_test && (BLI_linklist_index(ls_base->list, val) != -1)) {
return false;
}
}
ls = static_cast<LinkNode *>(BLI_memarena_alloc(mem_arena, sizeof(*ls)));
ls->next = ls_base->list;
ls->link = val;
ls_base->list = ls;
ls_base->list_len += 1;
return true;
}
struct vert_sort_t {
float val;
BMVert *v;
};
#ifdef USE_SPLICE
static void edge_verts_sort(const float co[3], LinkBase *v_ls_base)
{
/* not optimal but list will be typically < 5 */
uint i;
vert_sort_t *vert_sort = BLI_array_alloca(vert_sort, v_ls_base->list_len);
LinkNode *node;
BLI_assert(v_ls_base->list_len > 1);
for (i = 0, node = v_ls_base->list; i < v_ls_base->list_len; i++, node = node->next) {
BMVert *v = static_cast<BMVert *>(node->link);
BLI_assert(v->head.htype == BM_VERT);
vert_sort[i].val = len_squared_v3v3(co, v->co);
vert_sort[i].v = v;
}
qsort(vert_sort, v_ls_base->list_len, sizeof(*vert_sort), BLI_sortutil_cmp_float);
for (i = 0, node = v_ls_base->list; i < v_ls_base->list_len; i++, node = node->next) {
node->link = vert_sort[i].v;
}
}
#endif
static void edge_verts_add(ISectState *s, BMEdge *e, BMVert *v, const bool use_test)
{
BLI_assert(e->head.htype == BM_EDGE);
BLI_assert(v->head.htype == BM_VERT);
ghash_insert_link(s->edge_verts, (void *)e, v, use_test, s->mem_arena);
}
static void face_edges_add(ISectState *s, const int f_index, BMEdge *e, const bool use_test)
{
void *f_index_key = POINTER_FROM_INT(f_index);
BLI_assert(e->head.htype == BM_EDGE);
BLI_assert(BM_edge_in_face(e, s->bm->ftable[f_index]) == false);
BLI_assert(BM_elem_index_get(s->bm->ftable[f_index]) == f_index);
ghash_insert_link(s->face_edges, f_index_key, e, use_test, s->mem_arena);
}
#ifdef USE_NET
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)
{
uint i;
uint edge_arr_len = e_ls_base->list_len;
BMEdge **edge_arr = BLI_array_alloca(edge_arr, edge_arr_len);
LinkNode *node;
BLI_assert(f->head.htype == BM_FACE);
for (i = 0, node = e_ls_base->list; i < e_ls_base->list_len; i++, node = node->next) {
edge_arr[i] = static_cast<BMEdge *>(node->link);
}
BLI_assert(node == nullptr);
# ifdef USE_DUMP
printf("# %s: %d %u\n", __func__, BM_elem_index_get(f), e_ls_base->list_len);
# endif
# ifdef USE_NET_ISLAND_CONNECT
if (use_island_connect) {
uint edge_arr_holes_len;
BMEdge **edge_arr_holes;
if (BM_face_split_edgenet_connect_islands(bm,
f,
edge_arr,
edge_arr_len,
use_partial_connect,
mem_arena_edgenet,
&edge_arr_holes,
&edge_arr_holes_len))
{
edge_arr_len = edge_arr_holes_len;
edge_arr = edge_arr_holes; /* owned by the arena */
}
}
# else
UNUSED_VARS(use_island_connect, mem_arena_edgenet);
# endif
BM_face_split_edgenet(bm, f, edge_arr, int(edge_arr_len), nullptr, nullptr);
}
#endif
#ifdef USE_DISSOLVE
static void vert_dissolve_add(ISectState *s, BMVert *v)
{
BLI_assert(v->head.htype == BM_VERT);
BLI_assert(!BM_elem_flag_test(v, BM_ELEM_TAG));
BLI_assert(BLI_linklist_index(s->vert_dissolve, v) == -1);
BM_elem_flag_enable(v, BM_ELEM_TAG);
BLI_linklist_prepend_arena(&s->vert_dissolve, v, s->mem_arena);
}
#endif
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)
{
float p_dir[3];
uint i_t0;
float fac;
sub_v3_v3v3(p_dir, p0, p1);
normalize_v3(p_dir);
for (i_t0 = 0; i_t0 < 3; i_t0++) {
const uint i_t1 = (i_t0 + 1) % 3;
float te_dir[3];
sub_v3_v3v3(te_dir, t_cos[i_t0], t_cos[i_t1]);
normalize_v3(te_dir);
if (fabsf(dot_v3v3(p_dir, te_dir)) >= 1.0f - e->eps) {
/* co-linear */
}
else {
float ix_pair[2][3];
int ix_pair_type;
ix_pair_type = isect_line_line_epsilon_v3(
p0, p1, t_cos[i_t0], t_cos[i_t1], ix_pair[0], ix_pair[1], 0.0f);
if (ix_pair_type != 0) {
if (ix_pair_type == 1) {
copy_v3_v3(ix_pair[1], ix_pair[0]);
}
if ((ix_pair_type == 1) || (len_squared_v3v3(ix_pair[0], ix_pair[1]) <= e->eps_margin_sq))
{
fac = line_point_factor_v3(ix_pair[1], t_cos[i_t0], t_cos[i_t1]);
if ((fac >= e->eps_margin) && (fac <= 1.0f - e->eps_margin)) {
fac = line_point_factor_v3(ix_pair[0], p0, p1);
if ((fac >= e->eps_margin) && (fac <= 1.0f - e->eps_margin)) {
copy_v3_v3(r_ix, ix_pair[0]);
return ISectType(IX_EDGE_TRI_EDGE0 + (enum ISectType)i_t0);
}
}
}
}
}
}
/* check ray isn't planar with tri */
if (fabsf(dot_v3v3(p_dir, t_nor)) >= e->eps) {
if (isect_line_segment_tri_epsilon_v3(
p0, p1, t_cos[0], t_cos[1], t_cos[2], &fac, nullptr, 0.0f)) {
if ((fac >= e->eps_margin) && (fac <= 1.0f - e->eps_margin)) {
interp_v3_v3v3(r_ix, p0, p1, fac);
if (min_fff(len_squared_v3v3(t_cos[0], r_ix),
len_squared_v3v3(t_cos[1], r_ix),
len_squared_v3v3(t_cos[2], r_ix)) >= e->eps_margin_sq)
{
return IX_EDGE_TRI;
}
}
}
}
/* r_ix may be unset */
return IX_NONE;
}
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)
{
BMesh *bm = s->bm;
int k_arr[IX_TOT][4];
uint i;
const int ti[3] = {UNPACK3_EX(BM_elem_index_get, t, )};
float ix[3];
if (BM_elem_index_get(e_v0) > BM_elem_index_get(e_v1)) {
SWAP(BMVert *, e_v0, e_v1);
}
#ifdef USE_PARANOID
BLI_assert(len_squared_v3v3(e_v0->co, t[0]->co) >= s->epsilon.eps_sq);
BLI_assert(len_squared_v3v3(e_v0->co, t[1]->co) >= s->epsilon.eps_sq);
BLI_assert(len_squared_v3v3(e_v0->co, t[2]->co) >= s->epsilon.eps_sq);
BLI_assert(len_squared_v3v3(e_v1->co, t[0]->co) >= s->epsilon.eps_sq);
BLI_assert(len_squared_v3v3(e_v1->co, t[1]->co) >= s->epsilon.eps_sq);
BLI_assert(len_squared_v3v3(e_v1->co, t[2]->co) >= s->epsilon.eps_sq);
#endif
#define KEY_SET(k, i0, i1, i2, i3) \
{ \
(k)[0] = i0; \
(k)[1] = i1; \
(k)[2] = i2; \
(k)[3] = i3; \
} \
(void)0
/* Order tri, then order (1-2, 2-3). */
#define KEY_EDGE_TRI_ORDER(k) \
{ \
if (k[2] > k[3]) { \
SWAP(int, k[2], k[3]); \
} \
if (k[0] > k[2]) { \
SWAP(int, k[0], k[2]); \
SWAP(int, k[1], k[3]); \
} \
} \
(void)0
KEY_SET(k_arr[IX_EDGE_TRI], BM_elem_index_get(e_v0), BM_elem_index_get(e_v1), t_index, -1);
/* need to order here */
KEY_SET(
k_arr[IX_EDGE_TRI_EDGE0], BM_elem_index_get(e_v0), BM_elem_index_get(e_v1), ti[0], ti[1]);
KEY_SET(
k_arr[IX_EDGE_TRI_EDGE1], BM_elem_index_get(e_v0), BM_elem_index_get(e_v1), ti[1], ti[2]);
KEY_SET(
k_arr[IX_EDGE_TRI_EDGE2], BM_elem_index_get(e_v0), BM_elem_index_get(e_v1), ti[2], ti[0]);
KEY_EDGE_TRI_ORDER(k_arr[IX_EDGE_TRI_EDGE0]);
KEY_EDGE_TRI_ORDER(k_arr[IX_EDGE_TRI_EDGE1]);
KEY_EDGE_TRI_ORDER(k_arr[IX_EDGE_TRI_EDGE2]);
#undef KEY_SET
#undef KEY_EDGE_TRI_ORDER
for (i = 0; i < ARRAY_SIZE(k_arr); i++) {
BMVert *iv;
iv = static_cast<BMVert *>(BLI_ghash_lookup(s->edgetri_cache, k_arr[i]));
if (iv) {
#ifdef USE_DUMP
printf("# cache hit (%d, %d, %d, %d)\n", UNPACK4(k_arr[i]));
#endif
*r_side = (enum ISectType)i;
return iv;
}
}
*r_side = intersect_line_tri(e_v0->co, e_v1->co, t_cos, t_nor, ix, &s->epsilon);
if (*r_side != IX_NONE) {
BMVert *iv;
BMEdge *e;
#ifdef USE_DUMP
printf("# new vertex (%.6f, %.6f, %.6f) %d\n", UNPACK3(ix), *r_side);
#endif
#ifdef USE_PARANOID
BLI_assert(len_squared_v3v3(ix, e_v0->co) > s->epsilon.eps_sq);
BLI_assert(len_squared_v3v3(ix, e_v1->co) > s->epsilon.eps_sq);
BLI_assert(len_squared_v3v3(ix, t[0]->co) > s->epsilon.eps_sq);
BLI_assert(len_squared_v3v3(ix, t[1]->co) > s->epsilon.eps_sq);
BLI_assert(len_squared_v3v3(ix, t[2]->co) > s->epsilon.eps_sq);
#endif
iv = BM_vert_create(bm, ix, nullptr, eBMCreateFlag(0));
e = BM_edge_exists(e_v0, e_v1);
if (e) {
#ifdef USE_DUMP
printf("# adding to edge %d\n", BM_elem_index_get(e));
#endif
edge_verts_add(s, e, iv, false);
}
else {
#ifdef USE_DISSOLVE
vert_dissolve_add(s, iv);
#endif
}
if ((*r_side >= IX_EDGE_TRI_EDGE0) && (*r_side <= IX_EDGE_TRI_EDGE2)) {
i = uint(*r_side - IX_EDGE_TRI_EDGE0);
e = BM_edge_exists(t[i], t[(i + 1) % 3]);
if (e) {
edge_verts_add(s, e, iv, false);
}
}
{
int *k = static_cast<int *>(BLI_memarena_alloc(s->mem_arena, sizeof(int[4])));
memcpy(k, k_arr[*r_side], sizeof(int[4]));
BLI_ghash_insert(s->edgetri_cache, k, iv);
}
return iv;
}
*r_side = IX_NONE;
return nullptr;
}
struct LoopFilterWrap {
int (*test_fn)(BMFace *f, void *user_data);
void *user_data;
};
static bool bm_loop_filter_fn(const BMLoop *l, void *user_data)
{
if (BM_elem_flag_test(l->e, BM_ELEM_TAG)) {
return false;
}
if (l->radial_next != l) {
LoopFilterWrap *data = static_cast<LoopFilterWrap *>(user_data);
BMLoop *l_iter = l->radial_next;
const int face_side = data->test_fn(l->f, data->user_data);
do {
const int face_side_other = data->test_fn(l_iter->f, data->user_data);
if (UNLIKELY(face_side_other == -1)) {
/* pass */
}
else if (face_side_other != face_side) {
return false;
}
} while ((l_iter = l_iter->radial_next) != l);
return true;
}
return false;
}
/**
* Return true if we have any intersections.
*/
static void bm_isect_tri_tri(
ISectState *s, int a_index, int b_index, BMLoop **a, BMLoop **b, bool no_shared)
{
BMFace *f_a = (*a)->f;
BMFace *f_b = (*b)->f;
BMVert *fv_a[3] = {UNPACK3_EX(, a, ->v)};
BMVert *fv_b[3] = {UNPACK3_EX(, b, ->v)};
const float *f_a_cos[3] = {UNPACK3_EX(, fv_a, ->co)};
const float *f_b_cos[3] = {UNPACK3_EX(, fv_b, ->co)};
float f_a_nor[3];
float f_b_nor[3];
uint i;
/* should be enough but may need to bump */
BMVert *iv_ls_a[8];
BMVert *iv_ls_b[8];
STACK_DECLARE(iv_ls_a);
STACK_DECLARE(iv_ls_b);
if (no_shared) {
if (UNLIKELY(ELEM(fv_a[0], UNPACK3(fv_b)) || ELEM(fv_a[1], UNPACK3(fv_b)) ||
ELEM(fv_a[2], UNPACK3(fv_b))))
{
return;
}
}
else {
if (UNLIKELY(BM_face_share_edge_check(f_a, f_b))) {
return;
}
}
STACK_INIT(iv_ls_a, ARRAY_SIZE(iv_ls_a));
STACK_INIT(iv_ls_b, ARRAY_SIZE(iv_ls_b));
#define VERT_VISIT_A _FLAG_WALK
#define VERT_VISIT_B _FLAG_WALK_ALT
#define STACK_PUSH_TEST_A(ele) \
if (BM_ELEM_API_FLAG_TEST(ele, VERT_VISIT_A) == 0) { \
BM_ELEM_API_FLAG_ENABLE(ele, VERT_VISIT_A); \
STACK_PUSH(iv_ls_a, ele); \
} \
((void)0)
#define STACK_PUSH_TEST_B(ele) \
if (BM_ELEM_API_FLAG_TEST(ele, VERT_VISIT_B) == 0) { \
BM_ELEM_API_FLAG_ENABLE(ele, VERT_VISIT_B); \
STACK_PUSH(iv_ls_b, ele); \
} \
((void)0)
/* vert-vert
* --------- */
{
/* first check if any verts are touching
* (any case where we won't create new verts)
*/
uint i_a;
for (i_a = 0; i_a < 3; i_a++) {
uint i_b;
for (i_b = 0; i_b < 3; i_b++) {
if (len_squared_v3v3(fv_a[i_a]->co, fv_b[i_b]->co) <= s->epsilon.eps2x_sq) {
#ifdef USE_DUMP
if (BM_ELEM_API_FLAG_TEST(fv_a[i_a], VERT_VISIT_A) == 0) {
printf(" ('VERT-VERT-A') %u, %d),\n", i_a, BM_elem_index_get(fv_a[i_a]));
}
if (BM_ELEM_API_FLAG_TEST(fv_b[i_b], VERT_VISIT_B) == 0) {
printf(" ('VERT-VERT-B') %u, %d),\n", i_b, BM_elem_index_get(fv_b[i_b]));
}
#endif
STACK_PUSH_TEST_A(fv_a[i_a]);
STACK_PUSH_TEST_B(fv_b[i_b]);
}
}
}
}
/* vert-edge
* --------- */
{
uint i_a;
for (i_a = 0; i_a < 3; i_a++) {
if (BM_ELEM_API_FLAG_TEST(fv_a[i_a], VERT_VISIT_A) == 0) {
uint i_b_e0;
for (i_b_e0 = 0; i_b_e0 < 3; i_b_e0++) {
uint i_b_e1 = (i_b_e0 + 1) % 3;
if (BM_ELEM_API_FLAG_TEST(fv_b[i_b_e0], VERT_VISIT_B) ||
BM_ELEM_API_FLAG_TEST(fv_b[i_b_e1], VERT_VISIT_B))
{
continue;
}
const float fac = line_point_factor_v3(
fv_a[i_a]->co, fv_b[i_b_e0]->co, fv_b[i_b_e1]->co);
if ((fac > 0.0f - s->epsilon.eps) && (fac < 1.0f + s->epsilon.eps)) {
float ix[3];
interp_v3_v3v3(ix, fv_b[i_b_e0]->co, fv_b[i_b_e1]->co, fac);
if (len_squared_v3v3(ix, fv_a[i_a]->co) <= s->epsilon.eps2x_sq) {
BMEdge *e;
STACK_PUSH_TEST_B(fv_a[i_a]);
// STACK_PUSH_TEST_A(fv_a[i_a]);
e = BM_edge_exists(fv_b[i_b_e0], fv_b[i_b_e1]);
#ifdef USE_DUMP
printf(" ('VERT-EDGE-A', %d, %d),\n",
BM_elem_index_get(fv_b[i_b_e0]),
BM_elem_index_get(fv_b[i_b_e1]));
#endif
if (e) {
#ifdef USE_DUMP
printf("# adding to edge %d\n", BM_elem_index_get(e));
#endif
edge_verts_add(s, e, fv_a[i_a], true);
}
break;
}
}
}
}
}
}
{
uint i_b;
for (i_b = 0; i_b < 3; i_b++) {
if (BM_ELEM_API_FLAG_TEST(fv_b[i_b], VERT_VISIT_B) == 0) {
uint i_a_e0;
for (i_a_e0 = 0; i_a_e0 < 3; i_a_e0++) {
uint i_a_e1 = (i_a_e0 + 1) % 3;
if (BM_ELEM_API_FLAG_TEST(fv_a[i_a_e0], VERT_VISIT_A) ||
BM_ELEM_API_FLAG_TEST(fv_a[i_a_e1], VERT_VISIT_A))
{
continue;
}
const float fac = line_point_factor_v3(
fv_b[i_b]->co, fv_a[i_a_e0]->co, fv_a[i_a_e1]->co);
if ((fac > 0.0f - s->epsilon.eps) && (fac < 1.0f + s->epsilon.eps)) {
float ix[3];
interp_v3_v3v3(ix, fv_a[i_a_e0]->co, fv_a[i_a_e1]->co, fac);
if (len_squared_v3v3(ix, fv_b[i_b]->co) <= s->epsilon.eps2x_sq) {
BMEdge *e;
STACK_PUSH_TEST_A(fv_b[i_b]);
// STACK_PUSH_NOTEST(iv_ls_b, fv_b[i_b]);
e = BM_edge_exists(fv_a[i_a_e0], fv_a[i_a_e1]);
#ifdef USE_DUMP
printf(" ('VERT-EDGE-B', %d, %d),\n",
BM_elem_index_get(fv_a[i_a_e0]),
BM_elem_index_get(fv_a[i_a_e1]));
#endif
if (e) {
#ifdef USE_DUMP
printf("# adding to edge %d\n", BM_elem_index_get(e));
#endif
edge_verts_add(s, e, fv_b[i_b], true);
}
break;
}
}
}
}
}
}
/* vert-tri
* -------- */
{
float t_scale[3][3];
uint i_a;
copy_v3_v3(t_scale[0], fv_b[0]->co);
copy_v3_v3(t_scale[1], fv_b[1]->co);
copy_v3_v3(t_scale[2], fv_b[2]->co);
tri_v3_scale(UNPACK3(t_scale), 1.0f - s->epsilon.eps2x);
/* second check for verts intersecting the triangle */
for (i_a = 0; i_a < 3; i_a++) {
if (BM_ELEM_API_FLAG_TEST(fv_a[i_a], VERT_VISIT_A)) {
continue;
}
float ix[3];
if (isect_point_tri_v3(fv_a[i_a]->co, UNPACK3(t_scale), ix)) {
if (len_squared_v3v3(ix, fv_a[i_a]->co) <= s->epsilon.eps2x_sq) {
STACK_PUSH_TEST_A(fv_a[i_a]);
STACK_PUSH_TEST_B(fv_a[i_a]);
#ifdef USE_DUMP
printf(" 'VERT TRI-A',\n");
#endif
}
}
}
}
{
float t_scale[3][3];
uint i_b;
copy_v3_v3(t_scale[0], fv_a[0]->co);
copy_v3_v3(t_scale[1], fv_a[1]->co);
copy_v3_v3(t_scale[2], fv_a[2]->co);
tri_v3_scale(UNPACK3(t_scale), 1.0f - s->epsilon.eps2x);
for (i_b = 0; i_b < 3; i_b++) {
if (BM_ELEM_API_FLAG_TEST(fv_b[i_b], VERT_VISIT_B)) {
continue;
}
float ix[3];
if (isect_point_tri_v3(fv_b[i_b]->co, UNPACK3(t_scale), ix)) {
if (len_squared_v3v3(ix, fv_b[i_b]->co) <= s->epsilon.eps2x_sq) {
STACK_PUSH_TEST_A(fv_b[i_b]);
STACK_PUSH_TEST_B(fv_b[i_b]);
#ifdef USE_DUMP
printf(" 'VERT TRI-B',\n");
#endif
}
}
}
}
if ((STACK_SIZE(iv_ls_a) >= 3) && (STACK_SIZE(iv_ls_b) >= 3)) {
#ifdef USE_DUMP
printf("# OVERLAP\n");
#endif
goto finally;
}
normal_tri_v3(f_a_nor, UNPACK3(f_a_cos));
normal_tri_v3(f_b_nor, UNPACK3(f_b_cos));
/* edge-tri & edge-edge
* -------------------- */
{
for (uint i_a_e0 = 0; i_a_e0 < 3; i_a_e0++) {
uint i_a_e1 = (i_a_e0 + 1) % 3;
enum ISectType side;
BMVert *iv;
if (BM_ELEM_API_FLAG_TEST(fv_a[i_a_e0], VERT_VISIT_A) ||
BM_ELEM_API_FLAG_TEST(fv_a[i_a_e1], VERT_VISIT_A))
{
continue;
}
iv = bm_isect_edge_tri(
s, fv_a[i_a_e0], fv_a[i_a_e1], fv_b, b_index, f_b_cos, f_b_nor, &side);
if (iv) {
STACK_PUSH_TEST_A(iv);
STACK_PUSH_TEST_B(iv);
#ifdef USE_DUMP
printf(" ('EDGE-TRI-A', %d),\n", side);
#endif
}
}
for (uint i_b_e0 = 0; i_b_e0 < 3; i_b_e0++) {
uint i_b_e1 = (i_b_e0 + 1) % 3;
enum ISectType side;
BMVert *iv;
if (BM_ELEM_API_FLAG_TEST(fv_b[i_b_e0], VERT_VISIT_B) ||
BM_ELEM_API_FLAG_TEST(fv_b[i_b_e1], VERT_VISIT_B))
{
continue;
}
iv = bm_isect_edge_tri(
s, fv_b[i_b_e0], fv_b[i_b_e1], fv_a, a_index, f_a_cos, f_a_nor, &side);
if (iv) {
STACK_PUSH_TEST_A(iv);
STACK_PUSH_TEST_B(iv);
#ifdef USE_DUMP
printf(" ('EDGE-TRI-B', %d),\n", side);
#endif
}
}
}
{
for (i = 0; i < 2; i++) {
BMVert **ie_vs;
BMFace *f;
bool ie_exists;
BMEdge *ie;
if (i == 0) {
if (STACK_SIZE(iv_ls_a) != 2) {
continue;
}
ie_vs = iv_ls_a;
f = f_a;
}
else {
if (STACK_SIZE(iv_ls_b) != 2) {
continue;
}
ie_vs = iv_ls_b;
f = f_b;
}
/* possible but unlikely we get this - for edge-edge intersection */
ie = BM_edge_exists(UNPACK2(ie_vs));
if (ie == nullptr) {
ie_exists = false;
/* one of the verts must be new if we are making an edge
* ...no, we need this in case 2x quads intersect at either ends.
* if not (ie_vs[0].index == -1 or ie_vs[1].index == -1):
* continue */
ie = BM_edge_create(s->bm, UNPACK2(ie_vs), nullptr, eBMCreateFlag(0));
BLI_gset_insert(s->wire_edges, ie);
}
else {
ie_exists = true;
/* may already exist */
BLI_gset_add(s->wire_edges, ie);
if (BM_edge_in_face(ie, f)) {
continue;
}
}
face_edges_add(s, BM_elem_index_get(f), ie, ie_exists);
// BLI_assert(len(ie_vs) <= 2)
}
}
finally:
for (i = 0; i < STACK_SIZE(iv_ls_a); i++) {
BM_ELEM_API_FLAG_DISABLE(iv_ls_a[i], VERT_VISIT_A);
}
for (i = 0; i < STACK_SIZE(iv_ls_b); i++) {
BM_ELEM_API_FLAG_DISABLE(iv_ls_b[i], VERT_VISIT_B);
}
}
#ifdef USE_BVH
struct RaycastData {
const float **looptris;
BLI_Buffer *z_buffer;
};
# ifdef USE_KDOPBVH_WATERTIGHT
static const IsectRayPrecalc isect_precalc_x = {1, 2, 0, 0, 0, 1};
# endif
static void raycast_callback(void *userdata,
int index,
const BVHTreeRay *ray,
BVHTreeRayHit * /*hit*/)
{
RaycastData *raycast_data = static_cast<RaycastData *>(userdata);
const float **looptris = raycast_data->looptris;
const float *v0 = looptris[index * 3 + 0];
const float *v1 = looptris[index * 3 + 1];
const float *v2 = looptris[index * 3 + 2];
float dist;
if (
# ifdef USE_KDOPBVH_WATERTIGHT
isect_ray_tri_watertight_v3(ray->origin, &isect_precalc_x, v0, v1, v2, &dist, nullptr)
# else
isect_ray_tri_epsilon_v3(
ray->origin, ray->direction, v0, v1, v2, &dist, nullptr, FLT_EPSILON)
# endif
)
{
if (dist >= 0.0f) {
# ifdef USE_DUMP
printf("%s:\n", __func__);
print_v3(" origin", ray->origin);
print_v3(" direction", ray->direction);
printf(" dist %f\n", dist);
print_v3(" v0", v0);
print_v3(" v1", v1);
print_v3(" v2", v2);
# endif
# ifdef USE_DUMP
printf("%s: Adding depth %f\n", __func__, dist);
# endif
BLI_buffer_append(raycast_data->z_buffer, float, dist);
}
}
}
static int isect_bvhtree_point_v3(BVHTree *tree, const float **looptris, const float co[3])
{
BLI_buffer_declare_static(float, z_buffer, BLI_BUFFER_NOP, 64);
RaycastData raycast_data = {
looptris,
&z_buffer,
};
BVHTreeRayHit hit = {0};
const float dir[3] = {1.0f, 0.0f, 0.0f};
/* Need to initialize hit even tho it's not used.
* This is to make it so KD-tree believes we didn't intersect anything and
* keeps calling the intersect callback.
*/
hit.index = -1;
hit.dist = BVH_RAYCAST_DIST_MAX;
BLI_bvhtree_ray_cast(tree, co, dir, 0.0f, &hit, raycast_callback, &raycast_data);
# ifdef USE_DUMP
printf("%s: Total intersections: %zu\n", __func__, z_buffer.count);
# endif
int num_isect;
if (z_buffer.count == 0) {
num_isect = 0;
}
else if (z_buffer.count == 1) {
num_isect = 1;
}
else {
/* 2 or more */
const float eps = FLT_EPSILON * 10;
num_isect = 1; /* always count first */
qsort(z_buffer.data, z_buffer.count, sizeof(float), BLI_sortutil_cmp_float);
const float *depth_arr = static_cast<const float *>(z_buffer.data);
float depth_last = depth_arr[0];
for (uint i = 1; i < z_buffer.count; i++) {
if (depth_arr[i] - depth_last > eps) {
depth_last = depth_arr[i];
num_isect++;
}
}
BLI_buffer_free(&z_buffer);
}
// return (num_isect & 1) == 1;
return num_isect;
}
#endif /* USE_BVH */
bool BM_mesh_intersect(BMesh *bm,
BMLoop *(*looptris)[3],
const int looptris_tot,
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)
{
ISectState s;
const int totface_orig = bm->totface;
/* use to check if we made any changes */
bool has_edit_isect = false, has_edit_boolean = false;
/* needed for boolean, since cutting up faces moves the loops within the face */
const float **looptri_coords = nullptr;
#ifdef USE_BVH
BVHTree *tree_a, *tree_b;
uint tree_overlap_tot;
BVHTreeOverlap *overlap;
#else
int i_a, i_b;
#endif
s.bm = bm;
s.edgetri_cache = BLI_ghash_new(
BLI_ghashutil_inthash_v4_p, BLI_ghashutil_inthash_v4_cmp, __func__);
s.edge_verts = BLI_ghash_ptr_new(__func__);
s.face_edges = BLI_ghash_int_new(__func__);
s.wire_edges = BLI_gset_ptr_new(__func__);
s.vert_dissolve = nullptr;
s.mem_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
/* setup epsilon from base */
s.epsilon.eps = eps;
s.epsilon.eps2x = eps * 2.0f;
s.epsilon.eps_margin = s.epsilon.eps2x * 10.0f;
s.epsilon.eps_sq = s.epsilon.eps * s.epsilon.eps;
s.epsilon.eps2x_sq = s.epsilon.eps2x * s.epsilon.eps2x;
s.epsilon.eps_margin_sq = s.epsilon.eps_margin * s.epsilon.eps_margin;
BM_mesh_elem_index_ensure(bm,
BM_VERT | BM_EDGE |
#ifdef USE_NET
BM_FACE |
#endif
0);
BM_mesh_elem_table_ensure(bm,
#ifdef USE_SPLICE
BM_EDGE |
#endif
#ifdef USE_NET
BM_FACE |
#endif
0);
#ifdef USE_DISSOLVE
if (use_dissolve) {
BM_mesh_elem_hflag_disable_all(bm, BM_EDGE | BM_VERT, BM_ELEM_TAG, false);
}
#else
UNUSED_VARS(use_dissolve);
#endif
#ifdef USE_DUMP
printf("data = [\n");
#endif
if (boolean_mode != BMESH_ISECT_BOOLEAN_NONE) {
/* Keep original geometry for ray-cast callbacks. */
float **cos;
int i, j;
cos = static_cast<float **>(
MEM_mallocN(size_t(looptris_tot) * sizeof(*looptri_coords) * 3, __func__));
for (i = 0, j = 0; i < looptris_tot; i++) {
cos[j++] = looptris[i][0]->v->co;
cos[j++] = looptris[i][1]->v->co;
cos[j++] = looptris[i][2]->v->co;
}
looptri_coords = (const float **)cos;
}
#ifdef USE_BVH
{
int i;
tree_a = BLI_bvhtree_new(looptris_tot, s.epsilon.eps_margin, 8, 8);
for (i = 0; i < looptris_tot; i++) {
if (test_fn(looptris[i][0]->f, user_data) == 0) {
const float t_cos[3][3] = {
{UNPACK3(looptris[i][0]->v->co)},
{UNPACK3(looptris[i][1]->v->co)},
{UNPACK3(looptris[i][2]->v->co)},
};
BLI_bvhtree_insert(tree_a, i, (const float *)t_cos, 3);
}
}
BLI_bvhtree_balance(tree_a);
}
if (use_self == false) {
int i;
tree_b = BLI_bvhtree_new(looptris_tot, s.epsilon.eps_margin, 8, 8);
for (i = 0; i < looptris_tot; i++) {
if (test_fn(looptris[i][0]->f, user_data) == 1) {
const float t_cos[3][3] = {
{UNPACK3(looptris[i][0]->v->co)},
{UNPACK3(looptris[i][1]->v->co)},
{UNPACK3(looptris[i][2]->v->co)},
};
BLI_bvhtree_insert(tree_b, i, (const float *)t_cos, 3);
}
}
BLI_bvhtree_balance(tree_b);
}
else {
tree_b = tree_a;
}
/* For self intersection this can be useful, sometimes users generate geometry
* where surfaces that seem disconnected happen to share an edge.
* So when performing intersection calculation allow shared vertices,
* just not shared edges. See #75946. */
const bool isect_tri_tri_no_shared = (boolean_mode != BMESH_ISECT_BOOLEAN_NONE);
int flag = BVH_OVERLAP_USE_THREADING | BVH_OVERLAP_RETURN_PAIRS;
# ifdef DEBUG
/* The overlap result must match that obtained in Release to succeed
* in the `bmesh_boolean` test. */
if (looptris_tot < 1024) {
flag &= ~BVH_OVERLAP_USE_THREADING;
}
# endif
overlap = BLI_bvhtree_overlap_ex(tree_b, tree_a, &tree_overlap_tot, nullptr, nullptr, 0, flag);
if (overlap) {
uint i;
for (i = 0; i < tree_overlap_tot; i++) {
# ifdef USE_DUMP
printf(" ((%d, %d), (\n", overlap[i].indexA, overlap[i].indexB);
# endif
bm_isect_tri_tri(&s,
overlap[i].indexA,
overlap[i].indexB,
looptris[overlap[i].indexA],
looptris[overlap[i].indexB],
isect_tri_tri_no_shared);
# ifdef USE_DUMP
printf(")),\n");
# endif
}
MEM_freeN(overlap);
}
if (boolean_mode == BMESH_ISECT_BOOLEAN_NONE) {
/* no booleans, just free immediate */
BLI_bvhtree_free(tree_a);
if (tree_a != tree_b) {
BLI_bvhtree_free(tree_b);
}
}
#else
{
for (i_a = 0; i_a < looptris_tot; i_a++) {
const int t_a = test_fn(looptris[i_a][0]->f, user_data);
for (i_b = i_a + 1; i_b < looptris_tot; i_b++) {
const int t_b = test_fn(looptris[i_b][0]->f, user_data);
if (use_self) {
if ((t_a != 0) || (t_b != 0)) {
continue;
}
}
else {
if ((t_a != t_b) && !ELEM(-1, t_a, t_b)) {
continue;
}
}
# ifdef USE_DUMP
printf(" ((%d, %d), (", i_a, i_b);
# endif
bm_isect_tri_tri(&s, i_a, i_b, looptris[i_a], looptris[i_b], isect_tri_tri_no_shared);
# ifdef USE_DUMP
printf(")),\n");
# endif
}
}
}
#endif /* USE_BVH */
#ifdef USE_DUMP
printf("]\n");
#endif
/* --------- */
#ifdef USE_SPLICE
{
GHashIterator gh_iter;
GHASH_ITER (gh_iter, s.edge_verts) {
BMEdge *e = static_cast<BMEdge *>(BLI_ghashIterator_getKey(&gh_iter));
LinkBase *v_ls_base = static_cast<LinkBase *>(BLI_ghashIterator_getValue(&gh_iter));
BMVert *v_start;
BMVert *v_end;
BMVert *v_prev;
bool is_wire;
LinkNode *node;
/* direction is arbitrary, could be swapped */
v_start = e->v1;
v_end = e->v2;
if (v_ls_base->list_len > 1) {
edge_verts_sort(v_start->co, v_ls_base);
}
# ifdef USE_DUMP
printf("# SPLITTING EDGE: %d, %u\n", BM_elem_index_get(e), v_ls_base->list_len);
# endif
/* intersect */
is_wire = BLI_gset_haskey(s.wire_edges, e);
# ifdef USE_PARANOID
for (node = v_ls_base->list; node; node = node->next) {
BMVert *_v = node->link;
BLI_assert(len_squared_v3v3(_v->co, e->v1->co) > s.epsilon.eps_sq);
BLI_assert(len_squared_v3v3(_v->co, e->v2->co) > s.epsilon.eps_sq);
}
# endif
v_prev = v_start;
for (node = v_ls_base->list; node; node = node->next) {
BMVert *vi = static_cast<BMVert *>(node->link);
const float fac = line_point_factor_v3(vi->co, e->v1->co, e->v2->co);
if (BM_vert_in_edge(e, v_prev)) {
BMEdge *e_split;
v_prev = BM_edge_split(bm, e, v_prev, &e_split, clamp_f(fac, 0.0f, 1.0f));
BLI_assert(BM_vert_in_edge(e, v_end));
if (!BM_edge_exists(v_prev, vi) && !BM_vert_splice_check_double(v_prev, vi) &&
!BM_vert_pair_share_face_check(v_prev, vi))
{
BM_vert_splice(bm, vi, v_prev);
}
else {
copy_v3_v3(v_prev->co, vi->co);
}
v_prev = vi;
if (is_wire) {
BLI_gset_insert(s.wire_edges, e_split);
}
}
}
UNUSED_VARS_NDEBUG(v_end);
}
}
#endif
/* important to handle before edgenet */
#ifdef USE_DISSOLVE
if (use_dissolve && (boolean_mode == BMESH_ISECT_BOOLEAN_NONE)) {
/* first pass */
BMVert *(*splice_ls)[2];
STACK_DECLARE(splice_ls);
LinkNode *node;
for (node = s.vert_dissolve; node; node = node->next) {
BMVert *v = static_cast<BMVert *>(node->link);
if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
if (!BM_vert_is_edge_pair(v)) {
BM_elem_flag_disable(v, BM_ELEM_TAG);
}
}
}
splice_ls = static_cast<BMVert *(*)[2]>(
MEM_mallocN(BLI_gset_len(s.wire_edges) * sizeof(*splice_ls), __func__));
STACK_INIT(splice_ls, BLI_gset_len(s.wire_edges));
for (node = s.vert_dissolve; node; node = node->next) {
BMEdge *e_pair[2];
BMVert *v = static_cast<BMVert *>(node->link);
BMVert *v_a, *v_b;
if (!BM_elem_flag_test(v, BM_ELEM_TAG)) {
continue;
}
/* get chain */
e_pair[0] = v->e;
e_pair[1] = BM_DISK_EDGE_NEXT(v->e, v);
if (BM_elem_flag_test(e_pair[0], BM_ELEM_TAG) || BM_elem_flag_test(e_pair[1], BM_ELEM_TAG)) {
continue;
}
/* It's possible the vertex to dissolve is an edge on an existing face
* that doesn't divide the face, therefor the edges are not wire
* and shouldn't be handled here, see: #63787. */
if (!BLI_gset_haskey(s.wire_edges, e_pair[0]) || !BLI_gset_haskey(s.wire_edges, e_pair[1])) {
continue;
}
v_a = BM_edge_other_vert(e_pair[0], v);
v_b = BM_edge_other_vert(e_pair[1], v);
/* simple case */
if (BM_elem_flag_test(v_a, BM_ELEM_TAG) && BM_elem_flag_test(v_b, BM_ELEM_TAG)) {
/* only start on an edge-case */
/* pass */
}
else if (!BM_elem_flag_test(v_a, BM_ELEM_TAG) && !BM_elem_flag_test(v_b, BM_ELEM_TAG)) {
/* simple case, single edge spans face */
BMVert **splice_pair;
BM_elem_flag_enable(e_pair[1], BM_ELEM_TAG);
splice_pair = STACK_PUSH_RET(splice_ls);
splice_pair[0] = v;
splice_pair[1] = v_b;
# ifdef USE_DUMP
printf("# Simple Case!\n");
# endif
}
else {
# ifdef USE_PARANOID
BMEdge *e_keep;
# endif
BMEdge *e;
BMEdge *e_step;
BMVert *v_step;
/* walk the chain! */
if (BM_elem_flag_test(v_a, BM_ELEM_TAG)) {
e = e_pair[0];
# ifdef USE_PARANOID
e_keep = e_pair[1];
# endif
}
else {
SWAP(BMVert *, v_a, v_b);
e = e_pair[1];
# ifdef USE_PARANOID
e_keep = e_pair[0];
# endif
}
/* WALK */
v_step = v;
e_step = e;
while (true) {
BMEdge *e_next;
BMVert *v_next;
v_next = BM_edge_other_vert(e_step, v_step);
BM_elem_flag_enable(e_step, BM_ELEM_TAG);
if (!BM_elem_flag_test(v_next, BM_ELEM_TAG)) {
BMVert **splice_pair;
# ifdef USE_PARANOID
BLI_assert(e_step != e_keep);
# endif
splice_pair = STACK_PUSH_RET(splice_ls);
splice_pair[0] = v;
splice_pair[1] = v_next;
break;
}
e_next = bm_vert_other_edge(v_next, e_step);
e_step = e_next;
v_step = v_next;
BM_elem_flag_enable(e_step, BM_ELEM_TAG);
# ifdef USE_PARANOID
BLI_assert(e_step != e_keep);
# endif
# ifdef USE_DUMP
printf("# walk step %p %p\n", e_next, v_next);
# endif
}
# ifdef USE_PARANOID
BLI_assert(BM_elem_flag_test(e_keep, BM_ELEM_TAG) == 0);
# endif
}
}
/* Remove edges! */
{
GHashIterator gh_iter;
GHASH_ITER (gh_iter, s.face_edges) {
LinkBase *e_ls_base = static_cast<LinkBase *>(BLI_ghashIterator_getValue(&gh_iter));
LinkNode **node_prev_p;
node_prev_p = &e_ls_base->list;
for (node = e_ls_base->list; node; node = node->next) {
BMEdge *e = static_cast<BMEdge *>(node->link);
if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
/* allocated by arena, don't free */
*node_prev_p = node->next;
e_ls_base->list_len--;
}
else {
node_prev_p = &node->next;
}
}
}
}
{
BMIter eiter;
BMEdge *e, *e_next;
BM_ITER_MESH_MUTABLE (e, e_next, &eiter, bm, BM_EDGES_OF_MESH) {
if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
/* in rare and annoying cases,
* there can be faces from 's.face_edges' removed by the edges.
* These are degenerate cases, so just make sure we don't reference the faces again. */
if (e->l) {
BMLoop *l_iter = e->l;
BMFace **faces;
faces = bm->ftable;
do {
const int f_index = BM_elem_index_get(l_iter->f);
if (f_index >= 0) {
BLI_assert(f_index < totface_orig);
/* we could check if these are in: 's.face_edges', but easier just to remove */
faces[f_index] = nullptr;
}
} while ((l_iter = l_iter->radial_next) != e->l);
}
BLI_gset_remove(s.wire_edges, e, nullptr);
BM_edge_kill(bm, e);
}
}
}
/* Remove verts! */
{
GSet *verts_invalid = BLI_gset_ptr_new(__func__);
for (node = s.vert_dissolve; node; node = node->next) {
/* arena allocated, don't free */
BMVert *v = static_cast<BMVert *>(node->link);
if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
if (!v->e) {
BLI_gset_add(verts_invalid, v);
BM_vert_kill(bm, v);
}
}
}
{
uint i;
for (i = 0; i < STACK_SIZE(splice_ls); i++) {
if (!BLI_gset_haskey(verts_invalid, splice_ls[i][0]) &&
!BLI_gset_haskey(verts_invalid, splice_ls[i][1]))
{
if (!BM_edge_exists(UNPACK2(splice_ls[i])) &&
!BM_vert_splice_check_double(UNPACK2(splice_ls[i]))) {
BM_vert_splice(bm, splice_ls[i][1], splice_ls[i][0]);
}
}
}
}
BLI_gset_free(verts_invalid, nullptr);
}
MEM_freeN(splice_ls);
}
#endif /* USE_DISSOLVE */
/* now split faces */
#ifdef USE_NET
{
GHashIterator gh_iter;
BMFace **faces;
MemArena *mem_arena_edgenet = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
faces = bm->ftable;
GHASH_ITER (gh_iter, s.face_edges) {
const int f_index = POINTER_AS_INT(BLI_ghashIterator_getKey(&gh_iter));
BMFace *f;
LinkBase *e_ls_base = static_cast<LinkBase *>(BLI_ghashIterator_getValue(&gh_iter));
BLI_assert(f_index >= 0 && f_index < totface_orig);
f = faces[f_index];
if (UNLIKELY(f == nullptr)) {
continue;
}
BLI_assert(BM_elem_index_get(f) == f_index);
face_edges_split(
bm, f, e_ls_base, use_island_connect, use_partial_connect, mem_arena_edgenet);
BLI_memarena_clear(mem_arena_edgenet);
}
BLI_memarena_free(mem_arena_edgenet);
}
#else
UNUSED_VARS(use_island_connect);
#endif /* USE_NET */
(void)totface_orig;
#ifdef USE_SEPARATE
if (use_separate) {
GSetIterator gs_iter;
BM_mesh_elem_hflag_disable_all(bm, BM_EDGE, BM_ELEM_TAG, false);
GSET_ITER (gs_iter, s.wire_edges) {
BMEdge *e = static_cast<BMEdge *>(BLI_gsetIterator_getKey(&gs_iter));
BM_elem_flag_enable(e, BM_ELEM_TAG);
}
BM_mesh_edgesplit(bm, false, true, false);
}
else if (boolean_mode != BMESH_ISECT_BOOLEAN_NONE || use_edge_tag) {
GSetIterator gs_iter;
/* no need to clear for boolean */
GSET_ITER (gs_iter, s.wire_edges) {
BMEdge *e = static_cast<BMEdge *>(BLI_gsetIterator_getKey(&gs_iter));
BM_elem_flag_enable(e, BM_ELEM_TAG);
}
}
#else
(void)use_separate;
#endif /* USE_SEPARATE */
if (boolean_mode != BMESH_ISECT_BOOLEAN_NONE) {
BVHTree *tree_pair[2] = {tree_a, tree_b};
/* group vars */
int *groups_array;
int(*group_index)[2];
int group_tot;
int i;
BMFace **ftable;
BM_mesh_elem_table_ensure(bm, BM_FACE);
ftable = bm->ftable;
/* wrap the face-test callback to make it into an edge-loop delimiter */
LoopFilterWrap user_data_wrap{};
user_data_wrap.test_fn = test_fn;
user_data_wrap.user_data = user_data;
groups_array = static_cast<int *>(
MEM_mallocN(sizeof(*groups_array) * size_t(bm->totface), __func__));
group_tot = BM_mesh_calc_face_groups(
bm, groups_array, &group_index, bm_loop_filter_fn, nullptr, &user_data_wrap, 0, BM_EDGE);
#ifdef USE_DUMP
printf("%s: Total face-groups: %d\n", __func__, group_tot);
#endif
/* Check if island is inside/outside */
for (i = 0; i < group_tot; i++) {
int fg = group_index[i][0];
int fg_end = group_index[i][1] + fg;
bool do_remove, do_flip;
{
/* For now assume this is an OK face to test with (not degenerate!) */
BMFace *f = ftable[groups_array[fg]];
float co[3];
int hits;
int side = test_fn(f, user_data);
if (side == -1) {
continue;
}
BLI_assert(ELEM(side, 0, 1));
side = !side;
// BM_face_calc_center_median(f, co);
BM_face_calc_point_in_face(f, co);
hits = isect_bvhtree_point_v3(tree_pair[side], looptri_coords, co);
switch (boolean_mode) {
case BMESH_ISECT_BOOLEAN_ISECT:
do_remove = ((hits & 1) != 1);
do_flip = false;
break;
case BMESH_ISECT_BOOLEAN_UNION:
do_remove = ((hits & 1) == 1);
do_flip = false;
break;
case BMESH_ISECT_BOOLEAN_DIFFERENCE:
do_remove = ((hits & 1) == 1) == side;
do_flip = (side == 0);
break;
}
}
if (do_remove) {
for (; fg != fg_end; fg++) {
/* postpone killing the face since we access below, mark instead */
// BM_face_kill_loose(bm, ftable[groups_array[fg]]);
ftable[groups_array[fg]]->mat_nr = -1;
}
}
else if (do_flip) {
for (; fg != fg_end; fg++) {
BM_face_normal_flip(bm, ftable[groups_array[fg]]);
}
}
has_edit_boolean |= (do_flip || do_remove);
}
MEM_freeN(groups_array);
MEM_freeN(group_index);
#ifdef USE_DISSOLVE
/* We have dissolve code above, this is alternative logic,
* we need to do it after the boolean is executed. */
if (use_dissolve) {
LinkNode *node;
for (node = s.vert_dissolve; node; node = node->next) {
BMVert *v = static_cast<BMVert *>(node->link);
if (BM_vert_is_edge_pair(v)) {
/* we won't create degenerate faces from this */
bool ok = true;
/* would we create a 2-sided-face?
* if so, don't dissolve this since we may */
if (v->e->l) {
BMLoop *l_iter = v->e->l;
do {
if (l_iter->f->len == 3) {
ok = false;
break;
}
} while ((l_iter = l_iter->radial_next) != v->e->l);
}
if (ok) {
BM_vert_collapse_edge(bm, v->e, v, true, false, false);
}
}
}
}
#endif
{
int tot = bm->totface;
for (i = 0; i < tot; i++) {
if (ftable[i]->mat_nr == -1) {
BM_face_kill_loose(bm, ftable[i]);
}
}
}
}
if (boolean_mode != BMESH_ISECT_BOOLEAN_NONE) {
MEM_freeN((void *)looptri_coords);
/* no booleans, just free immediate */
BLI_bvhtree_free(tree_a);
if (tree_a != tree_b) {
BLI_bvhtree_free(tree_b);
}
}
has_edit_isect = (BLI_ghash_len(s.face_edges) != 0);
/* cleanup */
BLI_ghash_free(s.edgetri_cache, nullptr, nullptr);
BLI_ghash_free(s.edge_verts, nullptr, nullptr);
BLI_ghash_free(s.face_edges, nullptr, nullptr);
BLI_gset_free(s.wire_edges, nullptr);
BLI_memarena_free(s.mem_arena);
/* It's unlikely the selection history is useful at this point,
* if this is not called this array would need to be validated, see: #86799. */
BM_select_history_clear(bm);
return (has_edit_isect || has_edit_boolean);
}