Blender V4.3
editmesh_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
9#include "MEM_guardedalloc.h"
10
11#include "DNA_object_types.h"
12
13#include "BLI_buffer.h"
14#include "BLI_linklist_stack.h"
15#include "BLI_math_geom.h"
16#include "BLI_math_vector.h"
17#include "BLI_memarena.h"
18#include "BLI_stack.h"
19#include "BLI_vector.hh"
20
21#include "BKE_context.hh"
22#include "BKE_editmesh.hh"
23#include "BKE_editmesh_bvh.hh"
24#include "BKE_layer.hh"
25#include "BKE_report.hh"
26
27#include "RNA_access.hh"
28#include "RNA_define.hh"
29
30#include "WM_types.hh"
31
32#include "UI_interface.hh"
33#include "UI_resources.hh"
34
35#include "ED_mesh.hh"
36#include "ED_screen.hh"
37
38#include "mesh_intern.hh" /* own include */
39
43
44/* detect isolated holes and fill them */
45#define USE_NET_ISLAND_CONNECT
46
47using blender::Vector;
48
52static int bm_face_isect_self(BMFace *f, void * /*user_data*/)
53{
55 return 0;
56 }
57 return -1;
58}
59
63static int bm_face_isect_pair(BMFace *f, void * /*user_data*/)
64{
66 return -1;
67 }
69 return 1;
70 }
71 return 0;
72}
73
78static int bm_face_isect_pair_swap(BMFace *f, void * /*user_data*/)
79{
81 return -1;
82 }
84 return 0;
85 }
86 return 1;
87}
88
92static void edbm_intersect_select(BMEditMesh *em, Mesh *mesh, bool do_select)
93{
94 if (do_select) {
96
98 BMIter iter;
99 BMEdge *e;
100
101 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
103 BM_edge_select_set(em->bm, e, true);
104 }
105 }
107 }
108 }
109
111 params.calc_looptris = true;
112 params.calc_normals = true;
113 params.is_destructive = true;
114 EDBM_update(mesh, &params);
115}
116
117/* -------------------------------------------------------------------- */
123enum {
126};
127
128enum {
132};
133
134enum {
137};
138
140{
141 const int mode = RNA_enum_get(op->ptr, "mode");
142 int (*test_fn)(BMFace *, void *);
143 bool use_separate_all = false;
144 bool use_separate_cut = false;
145 const int separate_mode = RNA_enum_get(op->ptr, "separate_mode");
146 const float eps = RNA_float_get(op->ptr, "threshold");
147#ifdef WITH_GMP
148 const bool exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT;
149#else
150 if (RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT) {
151 BKE_report(op->reports, RPT_WARNING, "Compiled without GMP, using fast solver");
152 }
153 const bool exact = false;
154#endif
155 bool use_self;
156 bool has_isect;
157
158 switch (mode) {
159 case ISECT_SEL:
160 test_fn = bm_face_isect_self;
161 use_self = true;
162 break;
163 default: /* ISECT_SEL_UNSEL */
164 test_fn = bm_face_isect_pair;
165 use_self = false;
166 break;
167 }
168
169 switch (separate_mode) {
171 use_separate_all = true;
172 break;
174 if (use_self == false) {
175 use_separate_cut = true;
176 }
177 else {
178 /* we could support this but would require more advanced logic inside 'BM_mesh_intersect'
179 * for now just separate all */
180 use_separate_all = true;
181 }
182 break;
183 default: /* ISECT_SEPARATE_NONE */
184 break;
185 }
186 const Scene *scene = CTX_data_scene(C);
187 ViewLayer *view_layer = CTX_data_view_layer(C);
188 uint isect_len = 0;
190 scene, view_layer, CTX_wm_view3d(C));
191 for (Object *obedit : objects) {
193
194 if (em->bm->totfacesel == 0) {
195 continue;
196 }
197
198 if (exact) {
199 int nshapes = use_self ? 1 : 2;
200 has_isect = BM_mesh_boolean_knife(em->bm,
201 em->looptris,
202 test_fn,
203 nullptr,
204 nshapes,
205 use_self,
206 use_separate_all,
207 false,
208 true);
209 }
210 else {
211 has_isect = BM_mesh_intersect(em->bm,
212 em->looptris,
213 test_fn,
214 nullptr,
215 use_self,
216 use_separate_all,
217 true,
218 true,
219 true,
220 true,
221 -1,
222 eps);
223 }
224
225 if (use_separate_cut) {
226 /* detach selected/un-selected faces */
229 }
230
231 edbm_intersect_select(em, static_cast<Mesh *>(obedit->data), has_isect);
232
233 if (!has_isect) {
234 isect_len++;
235 }
236 }
237
238 if (isect_len == objects.size()) {
239 BKE_report(op->reports, RPT_WARNING, "No intersections found");
240 }
241 return OPERATOR_FINISHED;
242}
243
244static void edbm_intersect_ui(bContext * /*C*/, wmOperator *op)
245{
246 uiLayout *layout = op->layout;
247 uiLayout *row;
248
249 bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT;
250
251 uiLayoutSetPropSep(layout, true);
252 uiLayoutSetPropDecorate(layout, false);
253 row = uiLayoutRow(layout, false);
254 uiItemR(row, op->ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
255 uiItemS(layout);
256 row = uiLayoutRow(layout, false);
257 uiItemR(row, op->ptr, "separate_mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
258 uiItemS(layout);
259
260 row = uiLayoutRow(layout, false);
261 uiItemR(row, op->ptr, "solver", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
262 uiItemS(layout);
263
264 if (!use_exact) {
265 uiItemR(layout, op->ptr, "threshold", UI_ITEM_NONE, nullptr, ICON_NONE);
266 }
267}
268
270{
271 static const EnumPropertyItem isect_mode_items[] = {
272 {ISECT_SEL, "SELECT", 0, "Self Intersect", "Self intersect selected faces"},
274 "SELECT_UNSELECT",
275 0,
276 "Selected/Unselected",
277 "Intersect selected with unselected faces"},
278 {0, nullptr, 0, nullptr, nullptr},
279 };
280
281 static const EnumPropertyItem isect_separate_items[] = {
282 {ISECT_SEPARATE_ALL, "ALL", 0, "All", "Separate all geometry from intersections"},
284 "CUT",
285 0,
286 "Cut",
287 "Cut into geometry keeping each side separate (Selected/Unselected only)"},
288 {ISECT_SEPARATE_NONE, "NONE", 0, "Merge", "Merge all geometry from the intersection"},
289 {0, nullptr, 0, nullptr, nullptr},
290 };
291
292 static const EnumPropertyItem isect_intersect_solver_items[] = {
293 {ISECT_SOLVER_FAST, "FAST", 0, "Fast", "Faster solver, some limitations"},
294 {ISECT_SOLVER_EXACT, "EXACT", 0, "Exact", "Exact solver, slower, handles more cases"},
295 {0, nullptr, 0, nullptr, nullptr},
296 };
297
298 /* identifiers */
299 ot->name = "Intersect (Knife)";
300 ot->description = "Cut an intersection into faces";
301 ot->idname = "MESH_OT_intersect";
302
303 /* api callbacks */
307
308 /* props */
309 RNA_def_enum(ot->srna, "mode", isect_mode_items, ISECT_SEL_UNSEL, "Source", "");
311 ot->srna, "separate_mode", isect_separate_items, ISECT_SEPARATE_CUT, "Separate Mode", "");
313 ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge Threshold", "", 0.0, 0.001);
315 "solver",
316 isect_intersect_solver_items,
318 "Solver",
319 "Which Intersect solver to use");
320
321 /* flags */
323}
324
327/* -------------------------------------------------------------------- */
335{
336 const int boolean_operation = RNA_enum_get(op->ptr, "operation");
337 bool use_swap = RNA_boolean_get(op->ptr, "use_swap");
338 bool use_self = RNA_boolean_get(op->ptr, "use_self");
339#ifdef WITH_GMP
340 const bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT;
341#else
342 if (RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT) {
343 BKE_report(op->reports, RPT_WARNING, "Compiled without GMP, using fast solver");
344 }
345 const bool use_exact = false;
346#endif
347 const float eps = RNA_float_get(op->ptr, "threshold");
348 int (*test_fn)(BMFace *, void *);
349 bool has_isect;
350
351 test_fn = use_swap ? bm_face_isect_pair_swap : bm_face_isect_pair;
352 const Scene *scene = CTX_data_scene(C);
353 ViewLayer *view_layer = CTX_data_view_layer(C);
354 uint isect_len = 0;
356 scene, view_layer, CTX_wm_view3d(C));
357 for (Object *obedit : objects) {
359
360 if (em->bm->totfacesel == 0) {
361 continue;
362 }
363
364 if (use_exact) {
365 has_isect = BM_mesh_boolean(
366 em->bm, em->looptris, test_fn, nullptr, 2, use_self, true, false, boolean_operation);
367 }
368 else {
369 has_isect = BM_mesh_intersect(em->bm,
370 em->looptris,
371 test_fn,
372 nullptr,
373 false,
374 false,
375 true,
376 true,
377 false,
378 true,
379 boolean_operation,
380 eps);
381 }
382
383 edbm_intersect_select(em, static_cast<Mesh *>(obedit->data), has_isect);
384
385 if (!has_isect) {
386 isect_len++;
387 }
388 }
389
390 if (isect_len == objects.size()) {
391 BKE_report(op->reports, RPT_WARNING, "No intersections found");
392 }
393 return OPERATOR_FINISHED;
394}
395
397{
398 uiLayout *layout = op->layout;
399 uiLayout *row;
400
401 bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT;
402
403 uiLayoutSetPropSep(layout, true);
404 uiLayoutSetPropDecorate(layout, false);
405
406 row = uiLayoutRow(layout, false);
407 uiItemR(row, op->ptr, "operation", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
408 uiItemS(layout);
409
410 row = uiLayoutRow(layout, false);
411 uiItemR(row, op->ptr, "solver", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
412 uiItemS(layout);
413
414 uiItemR(layout, op->ptr, "use_swap", UI_ITEM_NONE, nullptr, ICON_NONE);
415 uiItemR(layout, op->ptr, "use_self", UI_ITEM_NONE, nullptr, ICON_NONE);
416 if (!use_exact) {
417 uiItemR(layout, op->ptr, "threshold", UI_ITEM_NONE, nullptr, ICON_NONE);
418 }
419}
420
422{
423 static const EnumPropertyItem isect_boolean_operation_items[] = {
424 {BMESH_ISECT_BOOLEAN_ISECT, "INTERSECT", 0, "Intersect", ""},
425 {BMESH_ISECT_BOOLEAN_UNION, "UNION", 0, "Union", ""},
426 {BMESH_ISECT_BOOLEAN_DIFFERENCE, "DIFFERENCE", 0, "Difference", ""},
427 {0, nullptr, 0, nullptr, nullptr},
428 };
429
430 static const EnumPropertyItem isect_boolean_solver_items[] = {
431 {ISECT_SOLVER_FAST, "FAST", 0, "Fast", "Faster solver, some limitations"},
432 {ISECT_SOLVER_EXACT, "EXACT", 0, "Exact", "Exact solver, slower, handles more cases"},
433 {0, nullptr, 0, nullptr, nullptr},
434 };
435
436 /* identifiers */
437 ot->name = "Intersect (Boolean)";
438 ot->description = "Cut solid geometry from selected to unselected";
439 ot->idname = "MESH_OT_intersect_boolean";
440
441 /* api callbacks */
445
446 /* props */
448 "operation",
449 isect_boolean_operation_items,
451 "Boolean Operation",
452 "Which boolean operation to apply");
454 "use_swap",
455 false,
456 "Swap",
457 "Use with difference intersection to swap which side is kept");
459 ot->srna, "use_self", false, "Self Intersection", "Do self-union or self-intersection");
461 ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge Threshold", "", 0.0, 0.001);
463 "solver",
464 isect_boolean_solver_items,
466 "Solver",
467 "Which Boolean solver to use");
468
469 /* flags */
471}
472
475/* -------------------------------------------------------------------- */
480 BMFace *f,
481 const char hflag, /* reusable memory buffer */
482 BLI_Buffer *edge_net_temp_buf)
483{
484 const int f_index = BM_elem_index_get(f);
485
486 BMLoop *l_iter;
487 BMLoop *l_first;
488 BMVert *v;
489
490 /* likely this will stay very small
491 * all verts pushed into this stack _must_ have their previous edges set! */
492 BLI_SMALLSTACK_DECLARE(vert_stack, BMVert *);
493 BLI_SMALLSTACK_DECLARE(vert_stack_next, BMVert *);
494
495 BLI_assert(edge_net_temp_buf->count == 0);
496
497 /* collect all edges */
498 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
499 do {
500 BMIter iter;
501 BMEdge *e;
502
503 BM_ITER_ELEM (e, &iter, l_iter->v, BM_EDGES_OF_VERT) {
504 if (BM_elem_flag_test(e, hflag) && (BM_elem_index_get(e) == f_index)) {
505 v = BM_edge_other_vert(e, l_iter->v);
506 v->e = e;
507
508 BLI_SMALLSTACK_PUSH(vert_stack, v);
509 BLI_buffer_append(edge_net_temp_buf, BMEdge *, e);
510 }
511 }
512 } while ((l_iter = l_iter->next) != l_first);
513
514 /* now assign all */
515 /* pop free values into the next stack */
516 while ((v = static_cast<BMVert *>(BLI_SMALLSTACK_POP_EX(vert_stack, vert_stack_next)))) {
517 BMIter eiter;
518 BMEdge *e_next;
519
520 BM_ITER_ELEM (e_next, &eiter, v, BM_EDGES_OF_VERT) {
521 if (BM_elem_flag_test(e_next, hflag) && (BM_elem_index_get(e_next) == -1)) {
522 BMVert *v_next;
523 v_next = BM_edge_other_vert(e_next, v);
524 BM_elem_index_set(e_next, f_index);
525 BLI_SMALLSTACK_PUSH(vert_stack_next, v_next);
526 BLI_buffer_append(edge_net_temp_buf, BMEdge *, e_next);
527 }
528 }
529
530 if (BLI_SMALLSTACK_IS_EMPTY(vert_stack)) {
531 BLI_SMALLSTACK_SWAP(vert_stack, vert_stack_next);
532 }
533 }
534
535 Vector<BMFace *> face_arr;
537 bm, f, static_cast<BMEdge **>(edge_net_temp_buf->data), edge_net_temp_buf->count, &face_arr);
538
539 BLI_buffer_clear(edge_net_temp_buf);
540
541 for (BMFace *face : face_arr) {
542 BM_face_select_set(bm, face, true);
543 BM_elem_flag_disable(face, hflag);
544 }
545}
546
551static bool bm_vert_in_faces_radial(BMVert *v, BMEdge *e_radial, BMFace *f_ignore)
552{
553 BLI_assert(BM_vert_in_face(v, f_ignore) == false);
554 if (e_radial->l) {
555 BMLoop *l_iter = e_radial->l;
556 do {
557 if (l_iter->f != f_ignore) {
558 if (BM_vert_in_face(v, l_iter->f)) {
559 return true;
560 }
561 }
562 } while ((l_iter = l_iter->radial_next) != e_radial->l);
563 }
564 return false;
565}
566
567#ifdef USE_NET_ISLAND_CONNECT
568
569struct LinkBase {
570 LinkNode *list;
572};
573
575 BMFace *f_key,
576 BMEdge *e_val,
578{
579 void **ls_base_p;
580 LinkBase *ls_base;
581 LinkNode *ls;
582
583 if (!BLI_ghash_ensure_p(gh, f_key, &ls_base_p)) {
584 ls_base = static_cast<LinkBase *>(*ls_base_p = BLI_memarena_alloc(mem_arena,
585 sizeof(*ls_base)));
586 ls_base->list = nullptr;
587 ls_base->list_len = 0;
588 }
589 else {
590 ls_base = static_cast<LinkBase *>(*ls_base_p);
591 }
592
593 ls = static_cast<LinkNode *>(BLI_memarena_alloc(mem_arena, sizeof(*ls)));
594 ls->next = ls_base->list;
595 ls->link = e_val;
596 ls_base->list = ls;
597 ls_base->list_len += 1;
598}
599
600static int bm_edge_sort_length_cb(const void *e_a_v, const void *e_b_v)
601{
602 const float val_a = -BM_edge_calc_length_squared(*((BMEdge **)e_a_v));
603 const float val_b = -BM_edge_calc_length_squared(*((BMEdge **)e_b_v));
604
605 if (val_a > val_b) {
606 return 1;
607 }
608 if (val_a < val_b) {
609 return -1;
610 }
611 return 0;
612}
613
615 BMesh *bm, BMFace *f, LinkNode *e_link, const int e_link_len, MemArena *mem_arena_edgenet)
616{
617 BMEdge **edge_arr = static_cast<BMEdge **>(
618 BLI_memarena_alloc(mem_arena_edgenet, sizeof(*edge_arr) * e_link_len));
619 int edge_arr_len = 0;
620
621 while (e_link) {
622 edge_arr[edge_arr_len++] = static_cast<BMEdge *>(e_link->link);
623 e_link = e_link->next;
624 }
625
626 {
627 uint edge_arr_holes_len;
628 BMEdge **edge_arr_holes;
630 f,
631 edge_arr,
632 e_link_len,
633 true,
634 mem_arena_edgenet,
635 &edge_arr_holes,
636 &edge_arr_holes_len))
637 {
638 edge_arr_len = edge_arr_holes_len;
639 edge_arr = edge_arr_holes; /* owned by the arena */
640 }
641 }
642
643 BM_face_split_edgenet(bm, f, edge_arr, edge_arr_len, nullptr);
644
645 for (int i = e_link_len; i < edge_arr_len; i++) {
646 BM_edge_select_set(bm, edge_arr[i], true);
647 }
648
649 if (e_link_len != edge_arr_len) {
650 /* connecting partial islands can add redundant edges
651 * sort before removal to give deterministic outcome */
652 qsort(edge_arr, edge_arr_len - e_link_len, sizeof(*edge_arr), bm_edge_sort_length_cb);
653 for (int i = e_link_len; i < edge_arr_len; i++) {
654 BMFace *f_pair[2];
655 if (BM_edge_face_pair(edge_arr[i], &f_pair[0], &f_pair[1])) {
656 if (BM_face_share_vert_count(f_pair[0], f_pair[1]) == 2) {
657 BMFace *f_new = BM_faces_join(bm, f_pair, 2, true);
658 if (f_new) {
659 BM_face_select_set(bm, f_new, true);
660 }
661 }
662 }
663 }
664 }
665}
666
686 BMFace *f_a,
687 BMVert *v_pivot,
688 BMFace **ftable,
689 const int ftable_len,
690 float r_v_pivot_co[3],
691 float *r_v_pivot_fac)
692{
693 const int f_a_index = BM_elem_index_get(e_a);
694 bool found_other_self = false;
695 int found_other_face = 0;
696 BLI_SMALLSTACK_DECLARE(face_stack, BMFace *);
697
698 /* loop over surrounding edges to check if we're part of a chain or a delimiter vertex */
699 BMEdge *e_b = v_pivot->e;
700 do {
701 if (e_b != e_a) {
702 const int f_b_index = BM_elem_index_get(e_b);
703 if (f_b_index == f_a_index) {
704 /* not an endpoint */
705 found_other_self = true;
706 }
707 else if (f_b_index != -1) {
708 BLI_assert(f_b_index < ftable_len);
709 UNUSED_VARS_NDEBUG(ftable_len);
710
711 /* 'v_pivot' spans 2+ faces,
712 * tag to ensure we pick an edge that includes this face */
713 BMFace *f_b = ftable[f_b_index];
716 BLI_SMALLSTACK_PUSH(face_stack, f_b);
717 found_other_face++;
718 }
719 }
720 }
721 } while ((e_b = BM_DISK_EDGE_NEXT(e_b, v_pivot)) != v_pivot->e);
722
723 BMEdge *e_split = nullptr;
724
725 /* if we have no others or the other edge is outside this face,
726 * we're an endpoint to connect to a boundary */
727 if ((found_other_self == false) || found_other_face) {
728
729 BMLoop *l_iter, *l_first;
730 l_iter = l_first = BM_FACE_FIRST_LOOP(f_a);
731 float dist_best_sq = FLT_MAX;
732
733 do {
734 float v_pivot_co_test[3];
735 float v_pivot_fac = line_point_factor_v3(v_pivot->co, l_iter->e->v1->co, l_iter->e->v2->co);
736 CLAMP(v_pivot_fac, 0.0f, 1.0f);
737 interp_v3_v3v3(v_pivot_co_test, l_iter->e->v1->co, l_iter->e->v2->co, v_pivot_fac);
738
739 float dist_test_sq = len_squared_v3v3(v_pivot_co_test, v_pivot->co);
740 if ((dist_test_sq < dist_best_sq) || (e_split == nullptr)) {
741 bool ok = true;
742
743 if (UNLIKELY(BM_edge_exists(v_pivot, l_iter->e->v1) ||
744 BM_edge_exists(v_pivot, l_iter->e->v2)))
745 {
746 /* very unlikely but will cause complications splicing the verts together,
747 * so just skip this case */
748 ok = false;
749 }
750 else if (found_other_face) {
751 /* double check that _all_ the faces used by v_pivot's edges are attached
752 * to this edge otherwise don't attempt the split since it will give
753 * non-deterministic results */
754 BMLoop *l_radial_iter = l_iter->radial_next;
755 int other_face_shared = 0;
756 if (l_radial_iter != l_iter) {
757 do {
758 if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_INTERNAL_TAG)) {
759 other_face_shared++;
760 }
761 } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter);
762 }
763 if (other_face_shared != found_other_face) {
764 ok = false;
765 }
766 }
767
768 if (ok) {
769 e_split = l_iter->e;
770 dist_best_sq = dist_test_sq;
771 copy_v3_v3(r_v_pivot_co, v_pivot_co_test);
772 *r_v_pivot_fac = v_pivot_fac;
773 }
774 }
775 } while ((l_iter = l_iter->next) != l_first);
776 }
777
778 {
779 /* reset the flag, for future use */
780 BMFace *f;
781 while ((f = static_cast<BMFace *>(BLI_SMALLSTACK_POP(face_stack)))) {
783 }
784 }
785
786 return e_split;
787}
788
789#endif /* USE_NET_ISLAND_CONNECT */
790
792{
793 const char hflag = BM_ELEM_TAG;
794
795 BMEdge *e;
796 BMIter iter;
797
798 BLI_SMALLSTACK_DECLARE(loop_stack, BMLoop *);
799
800 const Scene *scene = CTX_data_scene(C);
801 ViewLayer *view_layer = CTX_data_view_layer(C);
803 scene, view_layer, CTX_wm_view3d(C));
804 for (Object *obedit : objects) {
806 BMesh *bm = em->bm;
807
808 if ((bm->totedgesel == 0) || (bm->totfacesel == 0)) {
809 continue;
810 }
811
812 {
813 BMVert *v;
814 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
815 BM_elem_flag_disable(v, hflag);
816 }
817 }
818
819 /* edge index is set to -1 then used to associate them with faces */
820 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
822 BM_elem_flag_enable(e, hflag);
823
824 BM_elem_flag_enable(e->v1, hflag);
825 BM_elem_flag_enable(e->v2, hflag);
826 }
827 else {
828 BM_elem_flag_disable(e, hflag);
829 }
830 BM_elem_index_set(e, -1); /* set_dirty */
831 }
833
834 {
835 BMFace *f;
836 int i;
837 BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
839 BM_elem_flag_enable(f, hflag);
840 }
841 else {
842 BM_elem_flag_disable(f, hflag);
843 }
845 BM_elem_index_set(f, i); /* set_ok */
846 }
847 }
848 bm->elem_index_dirty &= ~BM_FACE;
849
850 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
851 if (BM_elem_flag_test(e, hflag)) {
852 BMIter viter;
853 BMVert *v;
854 BM_ITER_ELEM (v, &viter, e, BM_VERTS_OF_EDGE) {
855 BMIter liter;
856 BMLoop *l;
857
858 uint loop_stack_len;
859 BMLoop *l_best = nullptr;
860
862 loop_stack_len = 0;
863
864 BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
865 if (BM_elem_flag_test(l->f, hflag)) {
866 BLI_SMALLSTACK_PUSH(loop_stack, l);
867 loop_stack_len++;
868 }
869 }
870
871 if (loop_stack_len == 0) {
872 /* pass */
873 }
874 else if (loop_stack_len == 1) {
875 l_best = static_cast<BMLoop *>(BLI_SMALLSTACK_POP(loop_stack));
876 }
877 else {
878 /* complicated case, match the edge with a face-loop */
879
880 BMVert *v_other = BM_edge_other_vert(e, v);
881 float e_dir[3];
882
883 /* we want closest to zero */
884 float dot_best = FLT_MAX;
885
886 sub_v3_v3v3(e_dir, v_other->co, v->co);
887 normalize_v3(e_dir);
888
889 while ((l = static_cast<BMLoop *>(BLI_SMALLSTACK_POP(loop_stack)))) {
890 float dot_test;
891
892 /* Check dot first to save on expensive angle-comparison.
893 * ideal case is 90d difference == 0.0 dot */
894 dot_test = fabsf(dot_v3v3(e_dir, l->f->no));
895 if (dot_test < dot_best) {
896
897 /* check we're in the correct corner
898 * (works with convex loops too) */
900 l->prev->v->co, l->v->co, v_other->co, l->f->no) <
902 l->prev->v->co, l->v->co, l->next->v->co, l->f->no))
903 {
904 dot_best = dot_test;
905 l_best = l;
906 }
907 }
908 }
909 }
910
911 if (l_best) {
912 BM_elem_index_set(e, BM_elem_index_get(l_best->f)); /* set_dirty */
913 }
914 }
915 }
916 }
917
918 {
919 BMFace *f;
920 BLI_buffer_declare_static(BMEdge **, edge_net_temp_buf, 0, 128);
921
922 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
923 if (BM_elem_flag_test(f, hflag)) {
924 bm_face_split_by_edges(bm, f, hflag, &edge_net_temp_buf);
925 }
926 }
927 BLI_buffer_free(&edge_net_temp_buf);
928 }
929
930#ifdef USE_NET_ISLAND_CONNECT
931 /* before overwriting edge index values, collect edges left untouched */
932 BLI_Stack *edges_loose = BLI_stack_new(sizeof(BMEdge *), __func__);
933 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
935 BLI_stack_push(edges_loose, &e);
936 }
937 }
938#endif
939
941 params.calc_looptris = true;
942 params.calc_normals = true;
943 params.is_destructive = true;
944 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
945
946#ifdef USE_NET_ISLAND_CONNECT
947 /* we may have remaining isolated regions remaining,
948 * these will need to have connecting edges created */
949 if (!BLI_stack_is_empty(edges_loose)) {
950 GHash *face_edge_map = BLI_ghash_ptr_new(__func__);
951
953
955
956 {
957 BMBVHTree *bmbvh = BKE_bmbvh_new(bm, em->looptris, BMBVH_RESPECT_SELECT, nullptr, false);
958
959 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
960 BM_elem_index_set(e, -1); /* set_dirty */
961 }
962
963 while (!BLI_stack_is_empty(edges_loose)) {
964 BLI_stack_pop(edges_loose, &e);
965 float e_center[3];
966 mid_v3_v3v3(e_center, e->v1->co, e->v2->co);
967
968 BMFace *f = BKE_bmbvh_find_face_closest(bmbvh, e_center, FLT_MAX);
969 if (f) {
970 ghash_insert_face_edge_link(face_edge_map, f, e, mem_arena);
971 BM_elem_index_set(e, BM_elem_index_get(f)); /* set_dirty */
972 }
973 }
974
975 BKE_bmbvh_free(bmbvh);
976 }
977
979
981
982 /* detect edges chains that span faces
983 * and splice vertices into the closest edges */
984 {
985 GHashIterator gh_iter;
986
987 GHASH_ITER (gh_iter, face_edge_map) {
988 BMFace *f = static_cast<BMFace *>(BLI_ghashIterator_getKey(&gh_iter));
989 LinkBase *e_ls_base = static_cast<LinkBase *>(BLI_ghashIterator_getValue(&gh_iter));
990 LinkNode *e_link = e_ls_base->list;
991
992 do {
993 e = static_cast<BMEdge *>(e_link->link);
994
995 for (int j = 0; j < 2; j++) {
996 BMVert *v_pivot = (&e->v1)[j];
997 /* checking that \a v_pivot isn't in the face prevents attempting
998 * to splice the same vertex into an edge from multiple faces */
999 if (!BM_vert_in_face(v_pivot, f)) {
1000 float v_pivot_co[3];
1001 float v_pivot_fac;
1003 e, f, v_pivot, bm->ftable, bm->totface, v_pivot_co, &v_pivot_fac);
1004
1005 if (e_split) {
1006 /* for degenerate cases this vertex may be in one
1007 * of this edges radial faces */
1008 if (!bm_vert_in_faces_radial(v_pivot, e_split, f)) {
1009 BMEdge *e_new;
1010 BMVert *v_new = BM_edge_split(bm, e_split, e_split->v1, &e_new, v_pivot_fac);
1011 if (v_new) {
1012 /* we _know_ these don't share an edge */
1013 BM_vert_splice(bm, v_pivot, v_new);
1014 BM_elem_index_set(e_new, BM_elem_index_get(e_split));
1015 }
1016 }
1017 }
1018 }
1019 }
1020
1021 } while ((e_link = e_link->next));
1022 }
1023 }
1024
1025 {
1026 MemArena *mem_arena_edgenet = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
1027
1028 GHashIterator gh_iter;
1029
1030 GHASH_ITER (gh_iter, face_edge_map) {
1031 BMFace *f = static_cast<BMFace *>(BLI_ghashIterator_getKey(&gh_iter));
1032 LinkBase *e_ls_base = static_cast<LinkBase *>(BLI_ghashIterator_getValue(&gh_iter));
1033
1035 bm, f, e_ls_base->list, e_ls_base->list_len, mem_arena_edgenet);
1036
1037 BLI_memarena_clear(mem_arena_edgenet);
1038 }
1039
1040 BLI_memarena_free(mem_arena_edgenet);
1041 }
1042
1044
1045 BLI_ghash_free(face_edge_map, nullptr, nullptr);
1046
1048 params.calc_looptris = true;
1049 params.calc_normals = true;
1050 params.is_destructive = true;
1051 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
1052 }
1053
1054 BLI_stack_free(edges_loose);
1055#endif /* USE_NET_ISLAND_CONNECT */
1056 }
1057 return OPERATOR_FINISHED;
1058}
1059
1061{
1062 /* identifiers */
1063 ot->name = "Weld Edges into Faces";
1064 ot->description = "Weld loose edges into faces (splitting them into new faces)";
1065 ot->idname = "MESH_OT_face_split_by_edges";
1066
1067 /* api callbacks */
1070
1071 /* flags */
1073}
1074
Scene * CTX_data_scene(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
struct BMFace * BKE_bmbvh_find_face_closest(const BMBVHTree *tree, const float co[3], float dist_max)
BMBVHTree * BKE_bmbvh_new(struct BMesh *bm, blender::Span< std::array< BMLoop *, 3 > > looptris, int flag, const blender::float3 *cos_cage, bool cos_cage_free)
@ BMBVH_RESPECT_SELECT
void BKE_bmbvh_free(BMBVHTree *tree)
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_buffer_append(buffer_, type_, val_)
Definition BLI_buffer.h:52
#define BLI_buffer_declare_static(type_, name_, flag_, static_count_)
Definition BLI_buffer.h:27
#define BLI_buffer_clear(buffer_)
Definition BLI_buffer.h:56
#define BLI_buffer_free(name_)
Definition BLI_buffer.h:96
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.h:299
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_ptr_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.c:860
bool BLI_ghash_ensure_p(GHash *gh, void *key, void ***r_val) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:752
float line_point_factor_v3(const float p[3], const float l1[3], const float l2[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])
float angle_signed_on_axis_v3v3v3_v3(const float v1[3], const float v2[3], const float v3[3], const float axis[3]) ATTR_WARN_UNUSED_RESULT
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
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float normalize_v3(float n[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)
void BLI_stack_pop(BLI_Stack *stack, void *dst) ATTR_NONNULL()
Definition stack.c:137
void BLI_stack_push(BLI_Stack *stack, const void *src) ATTR_NONNULL()
Definition stack.c:131
bool BLI_stack_is_empty(const BLI_Stack *stack) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition stack.c:249
void BLI_stack_free(BLI_Stack *stack) ATTR_NONNULL()
Definition stack.c:96
#define BLI_stack_new(esize, descr)
unsigned int uint
#define CLAMP(a, b, c)
#define UNUSED_VARS_NDEBUG(...)
#define UNLIKELY(x)
Object is a sort of wrapper for general info.
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
void EDBM_update(Mesh *mesh, const EDBMUpdate_Params *params)
void EDBM_selectmode_flush(BMEditMesh *em)
bool ED_operator_editmesh(bContext *C)
Read Guarded memory(de)allocation.
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
void uiItemS(uiLayout *layout)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_ITEM_R_EXPAND
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
bool BM_mesh_boolean_knife(BMesh *, blender::Span< std::array< BMLoop *, 3 > > looptris, int(*test_fn)(BMFace *, void *), void *, const int, const bool, const bool, const bool, const bool)
bool BM_mesh_boolean(BMesh *, blender::Span< std::array< BMLoop *, 3 > > looptris, int(*test_fn)(BMFace *, void *), void *, const int, const bool, const bool, const bool, const int)
#define BM_elem_cb_check_hflag_enabled_simple(type, hflag_p)
#define BM_DISK_EDGE_NEXT(e, v)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
@ BM_ELEM_INTERNAL_TAG
@ BM_ELEM_TAG
#define BM_FACE_FIRST_LOOP(p)
BMFace * BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del)
Join Connected Faces.
bool BM_vert_splice(BMesh *bm, BMVert *v_dst, BMVert *v_src)
Splice Vert.
#define BM_elem_index_get(ele)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_index_set(ele, index)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
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)
@ BMESH_ISECT_BOOLEAN_DIFFERENCE
@ BMESH_ISECT_BOOLEAN_UNION
@ BMESH_ISECT_BOOLEAN_ISECT
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar)
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_VERTS_OF_EDGE
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_VERT
@ BM_EDGES_OF_VERT
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_face_select_set(BMesh *bm, BMFace *f, const bool select)
Select Face.
void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select)
Select Edge.
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.
#define BM_FACE
#define BM_EDGE
#define BM_VERT
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)
int BM_face_share_vert_count(BMFace *f_a, BMFace *f_b)
float BM_edge_calc_length_squared(const BMEdge *e)
bool BM_edge_face_pair(BMEdge *e, BMFace **r_fa, BMFace **r_fb)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
bool BM_vert_in_face(BMVert *v, BMFace *f)
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_wire(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
void BM_mesh_separate_faces(BMesh *bm, BMFaceFilterFunc filter_fn, void *user_data)
#define fabsf(x)
void MESH_OT_intersect(wmOperatorType *ot)
static bool bm_vert_in_faces_radial(BMVert *v, BMEdge *e_radial, BMFace *f_ignore)
void MESH_OT_intersect_boolean(wmOperatorType *ot)
static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op)
static void bm_face_split_by_edges(BMesh *bm, BMFace *f, const char hflag, BLI_Buffer *edge_net_temp_buf)
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)
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)
static int bm_face_isect_self(BMFace *f, void *)
static void edbm_intersect_ui(bContext *, wmOperator *op)
@ ISECT_SEL_UNSEL
static void ghash_insert_face_edge_link(GHash *gh, BMFace *f_key, BMEdge *e_val, MemArena *mem_arena)
static void edbm_intersect_select(BMEditMesh *em, Mesh *mesh, bool do_select)
void MESH_OT_face_split_by_edges(wmOperatorType *ot)
static int edbm_face_split_by_edges_exec(bContext *C, wmOperator *)
static int bm_face_isect_pair_swap(BMFace *f, void *)
static void edbm_intersect_boolean_ui(bContext *, wmOperator *op)
static int edbm_intersect_exec(bContext *C, wmOperator *op)
@ ISECT_SOLVER_EXACT
@ ISECT_SOLVER_FAST
@ ISECT_SEPARATE_ALL
@ ISECT_SEPARATE_NONE
@ ISECT_SEPARATE_CUT
static int bm_edge_sort_length_cb(const void *e_a_v, const void *e_b_v)
static int bm_face_isect_pair(BMFace *f, void *)
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
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
static MemArena * mem_arena
Definition makesdna.cc:62
const btScalar eps
Definition poly34.cpp:11
float RNA_float_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_float_distance(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
#define FLT_MAX
Definition stdcycles.h:14
void * data
Definition BLI_buffer.h:16
size_t count
Definition BLI_buffer.h:18
BMVert * v1
BMVert * v2
struct BMLoop * l
blender::Array< std::array< BMLoop *, 3 > > looptris
float no[3]
struct BMVert * v
struct BMEdge * e
struct BMLoop * radial_next
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
float co[3]
struct BMEdge * e
int totfacesel
char elem_index_dirty
short selectmode
int totedgesel
BMFace ** ftable
int totface
LinkNode * list
void * link
struct LinkNode * next
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
void(* ui)(bContext *C, wmOperator *op)
Definition WM_types.hh:1053
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct uiLayout * layout
struct PointerRNA * ptr
wmOperatorType * ot
Definition wm_files.cc:4125