Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/mesh/editmesh_intersect.c')
-rw-r--r--source/blender/editors/mesh/editmesh_intersect.c594
1 files changed, 539 insertions, 55 deletions
diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c
index e2e4638254b..69588928253 100644
--- a/source/blender/editors/mesh/editmesh_intersect.c
+++ b/source/blender/editors/mesh/editmesh_intersect.c
@@ -27,10 +27,13 @@
#include "DNA_object_types.h"
#include "BLI_math.h"
-#include "BLI_array.h"
+#include "BLI_memarena.h"
+#include "BLI_stack.h"
+#include "BLI_buffer.h"
+#include "BLI_kdopbvh.h"
#include "BLI_linklist_stack.h"
-
+#include "BKE_editmesh_bvh.h"
#include "BKE_context.h"
#include "BKE_report.h"
#include "BKE_editmesh.h"
@@ -50,8 +53,8 @@
#include "tools/bmesh_intersect.h"
-/* -------------------------------------------------------------------- */
-/* Cut intersections into geometry */
+/* detect isolated holes and fill them */
+#define USE_NET_ISLAND_CONNECT
/**
* Compare selected with its self.
@@ -75,6 +78,23 @@ static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
return -1;
}
else if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+/**
+ * A flipped version of #bm_face_isect_pair
+ * use for boolean 'difference', which depends on order.
+ */
+static int bm_face_isect_pair_swap(BMFace *f, void *UNUSED(user_data))
+{
+ if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
+ return -1;
+ }
+ else if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
return 0;
}
else {
@@ -82,19 +102,41 @@ static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
}
}
+/**
+ * Use for intersect and boolean.
+ */
+static void edbm_intersect_select(BMEditMesh *em)
+{
+ BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false);
+
+ if (em->bm->selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) {
+ BMIter iter;
+ BMEdge *e;
+
+ BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
+ if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
+ BM_edge_select_set(em->bm, e, true);
+ }
+ }
+ }
+
+ EDBM_mesh_normals_update(em);
+ EDBM_update_generic(em, true, true);
+
+}
+
+/* -------------------------------------------------------------------- */
+/* Cut intersections into geometry */
+
+/** \name Simple Intersect (self-intersect)
+ * \{
+ */
+
enum {
ISECT_SEL = 0,
ISECT_SEL_UNSEL = 1,
};
-static EnumPropertyItem isect_mode_items[] = {
- {ISECT_SEL, "SELECT", 0, "Self Intersect",
- "Self intersect selected faces"},
- {ISECT_SEL_UNSEL, "SELECT_UNSELECT", 0, "Selected/Unselected",
- "Intersect selected with unselected faces"},
- {0, NULL, 0, NULL, NULL}
-};
-
static int edbm_intersect_exec(bContext *C, wmOperator *op)
{
Object *obedit = CTX_data_edit_object(C);
@@ -123,26 +165,13 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op)
bm,
em->looptris, em->tottri,
test_fn, NULL,
- use_self, use_separate,
+ use_self, use_separate, true, true,
+ -1,
eps);
if (has_isect) {
- BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false);
-
- if (em->bm->selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) {
- BMIter iter;
- BMEdge *e;
-
- BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
- if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
- BM_edge_select_set(bm, e, true);
- }
- }
- }
-
- EDBM_mesh_normals_update(em);
- EDBM_update_generic(em, true, true);
+ edbm_intersect_select(em);
}
else {
BKE_report(op->reports, RPT_WARNING, "No intersections found");
@@ -153,8 +182,16 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op)
void MESH_OT_intersect(struct wmOperatorType *ot)
{
+ static EnumPropertyItem isect_mode_items[] = {
+ {ISECT_SEL, "SELECT", 0, "Self Intersect",
+ "Self intersect selected faces"},
+ {ISECT_SEL_UNSEL, "SELECT_UNSELECT", 0, "Selected/Unselected",
+ "Intersect selected with unselected faces"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
/* identifiers */
- ot->name = "Intersect";
+ ot->name = "Intersect (Knife)";
ot->description = "Cut an intersection into faces";
ot->idname = "MESH_OT_intersect";
@@ -165,25 +202,98 @@ void MESH_OT_intersect(struct wmOperatorType *ot)
/* props */
RNA_def_enum(ot->srna, "mode", isect_mode_items, ISECT_SEL_UNSEL, "Source", "");
RNA_def_boolean(ot->srna, "use_separate", true, "Separate", "");
- RNA_def_float(ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge threshold", "", 0.0, 0.001);
+ RNA_def_float_distance(ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge threshold", "", 0.0, 0.001);
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* Face Split by Edges */
+/* Boolean (a kind of intersect) */
+
+/** \name Boolean Intersect
+ *
+ * \note internally this is nearly exactly the same as 'MESH_OT_intersect',
+ * however from a user perspective they are quite different, so expose as different tools.
+ *
+ * \{
+ */
+
+static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit = CTX_data_edit_object(C);
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ BMesh *bm = em->bm;
+ const int boolean_operation = RNA_enum_get(op->ptr, "operation");
+ bool use_swap = RNA_boolean_get(op->ptr, "use_swap");
+ const float eps = RNA_float_get(op->ptr, "threshold");
+ int (*test_fn)(BMFace *, void *);
+ bool has_isect;
+
+ test_fn = use_swap ? bm_face_isect_pair_swap : bm_face_isect_pair;
+
+ has_isect = BM_mesh_intersect(
+ bm,
+ em->looptris, em->tottri,
+ test_fn, NULL,
+ false, false, true, true,
+ boolean_operation,
+ eps);
+
+
+ if (has_isect) {
+ edbm_intersect_select(em);
+ }
+ else {
+ BKE_report(op->reports, RPT_WARNING, "No intersections found");
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void MESH_OT_intersect_boolean(struct wmOperatorType *ot)
+{
+ static EnumPropertyItem isect_boolean_operation_items[] = {
+ {BMESH_ISECT_BOOLEAN_ISECT, "INTERSECT", 0, "Intersect", ""},
+ {BMESH_ISECT_BOOLEAN_UNION, "UNION", 0, "Union", ""},
+ {BMESH_ISECT_BOOLEAN_DIFFERENCE, "DIFFERENCE", 0, "Difference", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+ /* identifiers */
+ ot->name = "Intersect (Boolean)";
+ ot->description = "Cut solid geometry from selected to unselected";
+ ot->idname = "MESH_OT_intersect_boolean";
+
+ /* api callbacks */
+ ot->exec = edbm_intersect_boolean_exec;
+ ot->poll = ED_operator_editmesh;
+
+ /* props */
+ RNA_def_enum(ot->srna, "operation", isect_boolean_operation_items, BMESH_ISECT_BOOLEAN_DIFFERENCE, "Boolean", "");
+ RNA_def_boolean(ot->srna, "use_swap", false, "Swap", "Use with difference intersection to swap which side is kept");
+ RNA_def_float_distance(ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge threshold", "", 0.0, 0.001);
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/* Face Split by Edges */
/** \name Face/Edge Split
* \{ */
-static void bm_face_split_by_edges(BMesh *bm, BMFace *f, const char hflag)
+static void bm_face_split_by_edges(
+ BMesh *bm, BMFace *f, const char hflag,
+ /* reusable memory buffer */
+ BLI_Buffer *edge_net_temp_buf)
{
- BMEdge **edge_net = NULL;
- BLI_array_declare(edge_net);
-
const int f_index = BM_elem_index_get(f);
BMLoop *l_iter;
@@ -198,6 +308,7 @@ static void bm_face_split_by_edges(BMesh *bm, BMFace *f, const char hflag)
BLI_SMALLSTACK_DECLARE(vert_stack, BMVert *);
BLI_SMALLSTACK_DECLARE(vert_stack_next, BMVert *);
+ BLI_assert(edge_net_temp_buf->count == 0);
/* collect all edges */
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
@@ -213,7 +324,7 @@ static void bm_face_split_by_edges(BMesh *bm, BMFace *f, const char hflag)
v->e = e;
BLI_SMALLSTACK_PUSH(vert_stack, v);
- BLI_array_append(edge_net, e);
+ BLI_buffer_append(edge_net_temp_buf, BMEdge *, e);
}
}
} while ((l_iter = l_iter->next) != l_first);
@@ -234,7 +345,7 @@ static void bm_face_split_by_edges(BMesh *bm, BMFace *f, const char hflag)
v_next = BM_edge_other_vert(e_next, v);
BM_elem_index_set(e_next, f_index);
BLI_SMALLSTACK_PUSH(vert_stack_next, v_next);
- BLI_array_append(edge_net, e_next);
+ BLI_buffer_append(edge_net_temp_buf, BMEdge *, e_next);
}
}
@@ -243,8 +354,11 @@ static void bm_face_split_by_edges(BMesh *bm, BMFace *f, const char hflag)
}
}
- BM_face_split_edgenet(bm, f, edge_net, BLI_array_count(edge_net), &face_arr, &face_arr_len);
- BLI_array_free(edge_net);
+ BM_face_split_edgenet(
+ bm, f, edge_net_temp_buf->data, edge_net_temp_buf->count,
+ &face_arr, &face_arr_len);
+
+ BLI_buffer_empty(edge_net_temp_buf);
if (face_arr_len) {
int i;
@@ -259,6 +373,241 @@ static void bm_face_split_by_edges(BMesh *bm, BMFace *f, const char hflag)
}
}
+/**
+ * Check if a vert is in any of the faces connected to the edge,
+ * \a f_ignore is a face we happen to know isn't shared by the vertex.
+ */
+static bool bm_vert_in_faces_radial(BMVert *v, BMEdge *e_radial, BMFace *f_ignore)
+{
+ BLI_assert(BM_vert_in_face(v, f_ignore) == false);
+ if (e_radial->l) {
+ BMLoop *l_iter = e_radial->l;
+ do {
+ if (l_iter->f != f_ignore) {
+ if (BM_vert_in_face(v, l_iter->f)) {
+ return true;
+ }
+ }
+ } while ((l_iter = l_iter->radial_next) != e_radial->l);
+ }
+ return false;
+}
+
+#ifdef USE_NET_ISLAND_CONNECT
+
+struct LinkBase {
+ LinkNode *list;
+ unsigned int list_len;
+};
+
+static void ghash_insert_face_edge_link(
+ GHash *gh, BMFace *f_key, BMEdge *e_val,
+ MemArena *mem_arena)
+{
+ void **ls_base_p;
+ struct LinkBase *ls_base;
+ LinkNode *ls;
+
+ if (!BLI_ghash_ensure_p(gh, f_key, &ls_base_p)) {
+ ls_base = *ls_base_p = BLI_memarena_alloc(mem_arena, sizeof(*ls_base));
+ ls_base->list = NULL;
+ ls_base->list_len = 0;
+ }
+ else {
+ ls_base = *ls_base_p;
+ }
+
+ ls = BLI_memarena_alloc(mem_arena, sizeof(*ls));
+ ls->next = ls_base->list;
+ ls->link = e_val;
+ ls_base->list = ls;
+ ls_base->list_len += 1;
+}
+
+static int bm_edge_sort_length_cb(const void *e_a_v, const void *e_b_v)
+{
+ const float val_a = -BM_edge_calc_length_squared(*((BMEdge **)e_a_v));
+ const float val_b = -BM_edge_calc_length_squared(*((BMEdge **)e_b_v));
+
+ if (val_a > val_b) return 1;
+ else if (val_a < val_b) return -1;
+ else return 0;
+}
+
+static void bm_face_split_by_edges_island_connect(
+ BMesh *bm, BMFace *f,
+ LinkNode *e_link, const int e_link_len,
+ MemArena *mem_arena_edgenet)
+{
+ BMEdge **edge_arr = BLI_memarena_alloc(mem_arena_edgenet, sizeof(BMEdge **) * e_link_len);
+ int edge_arr_len = 0;
+
+ while (e_link) {
+ edge_arr[edge_arr_len++] = e_link->link;
+ e_link = e_link->next;
+ }
+
+ {
+ unsigned int edge_arr_holes_len;
+ BMEdge **edge_arr_holes;
+ if (BM_face_split_edgenet_connect_islands(
+ bm, f,
+ edge_arr, e_link_len,
+ true,
+ 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 */
+ }
+ }
+
+ BM_face_split_edgenet(
+ bm, f, edge_arr, edge_arr_len,
+ NULL, NULL);
+
+ for (int i = e_link_len; i < edge_arr_len; i++) {
+ BM_edge_select_set(bm, edge_arr[i], true);
+ }
+
+ if (e_link_len != edge_arr_len) {
+ /* connecting partial islands can add redundant edges
+ * sort before removal to give deterministic outcome */
+ qsort(edge_arr, edge_arr_len - e_link_len, sizeof(*edge_arr), bm_edge_sort_length_cb);
+ for (int i = e_link_len; i < edge_arr_len; i++) {
+ BMFace *f_pair[2];
+ if (BM_edge_face_pair(edge_arr[i], &f_pair[0], &f_pair[1])) {
+ if (BM_face_share_vert_count(f_pair[0], f_pair[1]) == 2) {
+ BMFace *f_new = BM_faces_join(bm, f_pair, 2, true);
+ if (f_new) {
+ BM_face_select_set(bm, f_new, true);
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Check if \a v_pivot should be spliced into an existing edge.
+ *
+ * Detect one of 3 cases:
+ *
+ * - \a v_pivot is shared by 2+ edges from different faces.
+ * in this case return the closest edge shared by all faces.
+ *
+ * - \a v_pivot is an end-point of an edge which has no other edges connected.
+ * in this case return the closest edge in \a f_a to the \a v_pivot.
+ *
+ * - \a v_pivot has only edges from the same face connected,
+ * in this case return NULL. This is the most common case - no action is needed.
+ *
+ * \return the edge to be split.
+ *
+ * \note Currently we don't snap to verts or split chains by verts on-edges.
+ */
+static BMEdge *bm_face_split_edge_find(
+ BMEdge *e_a, BMFace *f_a, BMVert *v_pivot, BMFace **ftable, const int ftable_len,
+ float r_v_pivot_co[3], float *r_v_pivot_fac)
+{
+ const int f_a_index = BM_elem_index_get(e_a);
+ bool found_other_self = false;
+ int found_other_face = 0;
+ BLI_SMALLSTACK_DECLARE(face_stack, BMFace *);
+
+ /* loop over surrounding edges to check if we're part of a chain or a delimiter vertex */
+ BMEdge *e_b = v_pivot->e;
+ do {
+ if (e_b != e_a) {
+ const int f_b_index = BM_elem_index_get(e_b);
+ if (f_b_index == f_a_index) {
+ /* not an endpoint */
+ found_other_self = true;
+ }
+ else if (f_b_index != -1) {
+ BLI_assert(f_b_index < ftable_len);
+ UNUSED_VARS_NDEBUG(ftable_len);
+
+ /* 'v_pivot' spans 2+ faces,
+ * tag to ensure we pick an edge that includes this face */
+ BMFace *f_b = ftable[f_b_index];
+ if (!BM_elem_flag_test(f_b, BM_ELEM_INTERNAL_TAG)) {
+ BM_elem_flag_enable(f_b, BM_ELEM_INTERNAL_TAG);
+ BLI_SMALLSTACK_PUSH(face_stack, f_b);
+ found_other_face++;
+ }
+ }
+ }
+ } while ((e_b = BM_DISK_EDGE_NEXT(e_b, v_pivot)) != v_pivot->e);
+
+ BMEdge *e_split = NULL;
+
+ /* if we have no others or the other edge is outside this face,
+ * we're an endpoint to connect to a boundary */
+ if ((found_other_self == false) || found_other_face) {
+
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f_a);
+ float dist_best_sq = FLT_MAX;
+
+ do {
+ float v_pivot_co_test[3];
+ float v_pivot_fac = line_point_factor_v3(v_pivot->co, l_iter->e->v1->co, l_iter->e->v2->co);
+ CLAMP(v_pivot_fac, 0.0f, 1.0f);
+ interp_v3_v3v3(v_pivot_co_test, l_iter->e->v1->co, l_iter->e->v2->co, v_pivot_fac);
+
+ float dist_test_sq = len_squared_v3v3(v_pivot_co_test, v_pivot->co);
+ if ((dist_test_sq < dist_best_sq) || (e_split == NULL)) {
+ bool ok = true;
+
+ if (UNLIKELY(BM_edge_exists(v_pivot, l_iter->e->v1) ||
+ BM_edge_exists(v_pivot, l_iter->e->v2)))
+ {
+ /* very unlikley but will cause complications splicing the verts together,
+ * so just skip this case */
+ ok = false;
+ }
+ else if (found_other_face) {
+ /* double check that _all_ the faces used by v_pivot's edges are attached to this edge
+ * otherwise don't attempt the split since it will give non-deterministic results */
+ BMLoop *l_radial_iter = l_iter->radial_next;
+ int other_face_shared = 0;
+ if (l_radial_iter != l_iter) {
+ do {
+ if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_INTERNAL_TAG)) {
+ other_face_shared++;
+ }
+ } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter);
+ }
+ if (other_face_shared != found_other_face) {
+ ok = false;
+ }
+ }
+
+ if (ok) {
+ e_split = l_iter->e;
+ dist_best_sq = dist_test_sq;
+ copy_v3_v3(r_v_pivot_co, v_pivot_co_test);
+ *r_v_pivot_fac = v_pivot_fac;
+ }
+ }
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+
+ {
+ /* reset the flag, for future use */
+ BMFace *f;
+ while ((f = BLI_SMALLSTACK_POP(face_stack))) {
+ BM_elem_flag_disable(f, BM_ELEM_INTERNAL_TAG);
+ }
+ }
+
+ return e_split;
+}
+
+#endif /* USE_NET_ISLAND_CONNECT */
+
+
static int edbm_face_split_by_edges_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *obedit = CTX_data_edit_object(C);
@@ -266,15 +615,16 @@ static int edbm_face_split_by_edges_exec(bContext *C, wmOperator *UNUSED(op))
BMesh *bm = em->bm;
const char hflag = BM_ELEM_TAG;
- BMVert *v;
BMEdge *e;
- BMFace *f;
BMIter iter;
BLI_SMALLSTACK_DECLARE(loop_stack, BMLoop *);
- BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
- BM_elem_flag_disable(v, hflag);
+ {
+ BMVert *v;
+ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ BM_elem_flag_disable(v, hflag);
+ }
}
/* edge index is set to -1 then used to assosiate them with faces */
@@ -291,19 +641,28 @@ static int edbm_face_split_by_edges_exec(bContext *C, wmOperator *UNUSED(op))
}
BM_elem_index_set(e, -1); /* set_dirty */
}
+ bm->elem_index_dirty |= BM_EDGE;
- BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
- if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
- BM_elem_flag_enable(f, hflag);
- }
- else {
- BM_elem_flag_disable(f, hflag);
+ {
+ BMFace *f;
+ int i;
+ BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
+ if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
+ BM_elem_flag_enable(f, hflag);
+ }
+ else {
+ BM_elem_flag_disable(f, hflag);
+ }
+ BM_elem_flag_disable(f, BM_ELEM_INTERNAL_TAG);
+ BM_elem_index_set(f, i); /* set_ok */
}
}
+ bm->elem_index_dirty &= ~BM_FACE;
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
if (BM_elem_flag_test(e, hflag)) {
BMIter viter;
+ BMVert *v;
BM_ITER_ELEM (v, &viter, e, BM_VERTS_OF_EDGE) {
BMIter liter;
BMLoop *l;
@@ -365,18 +724,143 @@ static int edbm_face_split_by_edges_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- bm->elem_index_dirty |= BM_EDGE;
+ {
+ BMFace *f;
+ BLI_buffer_declare_static(BMEdge **, edge_net_temp_buf, 0, 128);
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ if (BM_elem_flag_test(f, hflag)) {
+ bm_face_split_by_edges(bm, f, hflag, &edge_net_temp_buf);
+ }
+ }
+ BLI_buffer_free(&edge_net_temp_buf);
+ }
- BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
- if (BM_elem_flag_test(f, hflag)) {
- bm_face_split_by_edges(bm, f, hflag);
+#ifdef USE_NET_ISLAND_CONNECT
+ /* before overwriting edge index values, collect edges left untouched */
+ BLI_Stack *edges_loose = BLI_stack_new(sizeof(BMEdge * ), __func__);
+ BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+ if (BM_elem_flag_test(e, BM_ELEM_SELECT) && BM_edge_is_wire(e)) {
+ BLI_stack_push(edges_loose, &e);
}
}
+#endif
EDBM_mesh_normals_update(em);
EDBM_update_generic(em, true, true);
+
+#ifdef USE_NET_ISLAND_CONNECT
+ /* we may have remaining isolated regions remaining,
+ * these will need to have connecting edges created */
+ if (!BLI_stack_is_empty(edges_loose)) {
+ GHash *face_edge_map = BLI_ghash_ptr_new(__func__);
+
+ MemArena *mem_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+
+ BM_mesh_elem_index_ensure(bm, BM_FACE);
+
+ {
+ BMBVHTree *bmbvh = BKE_bmbvh_new(bm, em->looptris, em->tottri, BMBVH_RESPECT_SELECT, NULL, false);
+
+ BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+ BM_elem_index_set(e, -1); /* set_dirty */
+ }
+
+ while (!BLI_stack_is_empty(edges_loose)) {
+ BLI_stack_pop(edges_loose, &e);
+ float e_center[3];
+ mid_v3_v3v3(e_center, e->v1->co, e->v2->co);
+
+ BMFace *f = BKE_bmbvh_find_face_closest(bmbvh, e_center, FLT_MAX);
+ if (f) {
+ ghash_insert_face_edge_link(face_edge_map, f, e, mem_arena);
+ BM_elem_index_set(e, BM_elem_index_get(f)); /* set_dirty */
+ }
+ }
+
+ BKE_bmbvh_free(bmbvh);
+ }
+
+ bm->elem_index_dirty |= BM_EDGE;
+
+ BM_mesh_elem_table_ensure(bm, BM_FACE);
+
+ /* detect edges chains that span faces
+ * and splice vertices into the closest edges */
+ {
+ GHashIterator gh_iter;
+
+ GHASH_ITER(gh_iter, face_edge_map) {
+ BMFace *f = BLI_ghashIterator_getKey(&gh_iter);
+ struct LinkBase *e_ls_base = BLI_ghashIterator_getValue(&gh_iter);
+ LinkNode *e_link = e_ls_base->list;
+
+ do {
+ e = e_link->link;
+
+ for (int j = 0; j < 2; j++) {
+ BMVert *v_pivot = (&e->v1)[j];
+ /* checking that \a v_pivot isn't in the face
+ * prevents attempting to splice the same vertex into an edge from multiple faces */
+ if (!BM_vert_in_face(v_pivot, f)) {
+ float v_pivot_co[3];
+ float v_pivot_fac;
+ BMEdge *e_split = bm_face_split_edge_find(
+ e, f, v_pivot, bm->ftable, bm->totface,
+ v_pivot_co, &v_pivot_fac);
+
+ if (e_split) {
+ /* for degenerate cases this vertex may be in one of this edges radial faces */
+ if (!bm_vert_in_faces_radial(v_pivot, e_split, f)) {
+ BMEdge *e_new;
+ BMVert *v_new = BM_edge_split(bm, e_split, e_split->v1, &e_new, v_pivot_fac);
+ if (v_new) {
+ /* we _know_ these don't share an edge */
+ BM_vert_splice(bm, v_pivot, v_new);
+ BM_elem_index_set(e_new, BM_elem_index_get(e_split));
+ }
+ }
+ }
+ }
+ }
+
+ } while ((e_link = e_link->next));
+ }
+ }
+
+
+ {
+ MemArena *mem_arena_edgenet = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+
+ GHashIterator gh_iter;
+
+ GHASH_ITER(gh_iter, face_edge_map) {
+ BMFace *f = BLI_ghashIterator_getKey(&gh_iter);
+ struct LinkBase *e_ls_base = BLI_ghashIterator_getValue(&gh_iter);
+
+ bm_face_split_by_edges_island_connect(
+ bm, f,
+ e_ls_base->list, e_ls_base->list_len,
+ mem_arena_edgenet);
+
+ BLI_memarena_clear(mem_arena_edgenet);
+ }
+
+ BLI_memarena_free(mem_arena_edgenet);
+ }
+
+ BLI_memarena_free(mem_arena);
+
+ BLI_ghash_free(face_edge_map, NULL, NULL);
+
+ EDBM_mesh_normals_update(em);
+ EDBM_update_generic(em, true, true);
+ }
+
+ BLI_stack_free(edges_loose);
+#endif /* USE_NET_ISLAND_CONNECT */
+
return OPERATOR_FINISHED;
}
@@ -384,8 +868,8 @@ static int edbm_face_split_by_edges_exec(bContext *C, wmOperator *UNUSED(op))
void MESH_OT_face_split_by_edges(struct wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Split by Edges";
- ot->description = "Split faces by loose edges";
+ ot->name = "Weld Edges into Faces";
+ ot->description = "Weld loose edges into faces (splitting them into new faces)";
ot->idname = "MESH_OT_face_split_by_edges";
/* api callbacks */