Blender V5.0
editmesh_tools.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2004 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cstddef>
10#include <fmt/format.h>
11
12#include "MEM_guardedalloc.h"
13
14#include "DNA_key_types.h"
15#include "DNA_material_types.h"
16#include "DNA_mesh_types.h"
17#include "DNA_meshdata_types.h"
18#include "DNA_modifier_types.h"
19#include "DNA_object_types.h"
20#include "DNA_scene_types.h"
21
22#include "BLI_array.hh"
23#include "BLI_bitmap.h"
24#include "BLI_heap_simple.h"
25#include "BLI_linklist.h"
26#include "BLI_linklist_stack.h"
27#include "BLI_listbase.h"
28#include "BLI_math_bits.h"
29#include "BLI_math_geom.h"
30#include "BLI_math_matrix.h"
31#include "BLI_math_rotation.h"
32#include "BLI_math_vector.h"
33#include "BLI_rand.h"
34#include "BLI_sort_utils.h"
35
36#include "BKE_attribute.h"
37#include "BKE_context.hh"
38#include "BKE_customdata.hh"
39#include "BKE_deform.hh"
40#include "BKE_editmesh.hh"
41#include "BKE_key.hh"
42#include "BKE_layer.hh"
43#include "BKE_lib_id.hh"
44#include "BKE_material.hh"
45#include "BKE_mesh.hh"
46#include "BKE_mesh_types.hh"
47#include "BKE_object.hh"
48#include "BKE_object_types.hh"
49#include "BKE_report.hh"
50
51#include "DEG_depsgraph.hh"
53
54#include "BLT_translation.hh"
55
56#include "RNA_access.hh"
57#include "RNA_define.hh"
58#include "RNA_enum_types.hh"
59#include "RNA_prototypes.hh"
60
61#include "WM_api.hh"
62#include "WM_types.hh"
63
64#include "ED_mesh.hh"
65#include "ED_object.hh"
66#include "ED_outliner.hh"
67#include "ED_screen.hh"
68#include "ED_select_utils.hh"
69#include "ED_transform.hh"
70#include "ED_uvedit.hh"
71#include "ED_view3d.hh"
72
73#include "UI_interface.hh"
75#include "UI_resources.hh"
76
77#include "mesh_intern.hh" /* own include */
78
79#include "bmesh_tools.hh"
80
81using blender::Vector;
82
83#define USE_FACE_CREATE_SEL_EXTEND
84
85/* -------------------------------------------------------------------- */
88
90{
91 const int cuts = RNA_int_get(op->ptr, "number_cuts");
92 const float smooth = RNA_float_get(op->ptr, "smoothness");
93 const float fractal = RNA_float_get(op->ptr, "fractal") / 2.5f;
94 const float along_normal = RNA_float_get(op->ptr, "fractal_along_normal");
95 const bool use_quad_tri = !RNA_boolean_get(op->ptr, "ngon");
96
97 if (use_quad_tri && RNA_enum_get(op->ptr, "quadcorner") == SUBD_CORNER_STRAIGHT_CUT) {
98 RNA_enum_set(op->ptr, "quadcorner", SUBD_CORNER_INNERVERT);
99 }
100 const int quad_corner_type = RNA_enum_get(op->ptr, "quadcorner");
101 const int seed = RNA_int_get(op->ptr, "seed");
102
103 const Scene *scene = CTX_data_scene(C);
104 ViewLayer *view_layer = CTX_data_view_layer(C);
106 scene, view_layer, CTX_wm_view3d(C));
107
108 for (Object *obedit : objects) {
110
111 if (!(em->bm->totedgesel || em->bm->totfacesel)) {
112 continue;
113 }
114
117 smooth,
119 false,
120 fractal,
121 along_normal,
122 cuts,
124 quad_corner_type,
125 use_quad_tri,
126 true,
127 false,
128 seed);
129
131 params.calc_looptris = true;
132 params.calc_normals = false;
133 params.is_destructive = true;
134 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
135 }
136
137 return OPERATOR_FINISHED;
138}
139
140/* NOTE: these values must match delete_mesh() event values. */
142 {SUBD_CORNER_INNERVERT, "INNERVERT", 0, "Inner Vert", ""},
143 {SUBD_CORNER_PATH, "PATH", 0, "Path", ""},
144 {SUBD_CORNER_STRAIGHT_CUT, "STRAIGHT_CUT", 0, "Straight Cut", ""},
145 {SUBD_CORNER_FAN, "FAN", 0, "Fan", ""},
146 {0, nullptr, 0, nullptr, nullptr},
147};
148
150{
151 PropertyRNA *prop;
152
153 /* identifiers */
154 ot->name = "Subdivide";
155 ot->description = "Subdivide selected edges";
156 ot->idname = "MESH_OT_subdivide";
157
158 /* API callbacks. */
159 ot->exec = edbm_subdivide_exec;
160 ot->poll = ED_operator_editmesh;
161
162 /* flags */
164
165 /* properties */
166 prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 100, "Number of Cuts", "", 1, 10);
167 /* avoid re-using last var because it can cause
168 * _very_ high poly meshes and annoy users (or worse crash) */
170
172 ot->srna, "smoothness", 0.0f, 0.0f, 1e3f, "Smoothness", "Smoothness factor", 0.0f, 1.0f);
173
175
176 RNA_def_boolean(ot->srna,
177 "ngon",
178 true,
179 "Create N-Gons",
180 "When disabled, newly created faces are limited to 3 and 4 sided faces");
182 ot->srna,
183 "quadcorner",
186 "Quad Corner Type",
187 "How to subdivide quad corners (anything other than Straight Cut will prevent n-gons)");
188
189 RNA_def_float(ot->srna,
190 "fractal",
191 0.0f,
192 0.0f,
193 1e6f,
194 "Fractal",
195 "Fractal randomness factor",
196 0.0f,
197 1000.0f);
198 RNA_def_float(ot->srna,
199 "fractal_along_normal",
200 0.0f,
201 0.0f,
202 1.0f,
203 "Along Normal",
204 "Apply fractal displacement along normal only",
205 0.0f,
206 1.0f);
207 RNA_def_int(ot->srna,
208 "seed",
209 0,
210 0,
211 INT_MAX,
212 "Random Seed",
213 "Seed for the random number generator",
214 0,
215 255);
216}
217
219
220/* -------------------------------------------------------------------- */
226
235
237 const int cuts_min,
238 const int cuts_default)
239{
240 /* NOTE: these values must match delete_mesh() event values. */
241 static const EnumPropertyItem prop_subd_edgering_types[] = {
242 {SUBD_RING_INTERP_LINEAR, "LINEAR", 0, "Linear", ""},
243 {SUBD_RING_INTERP_PATH, "PATH", 0, "Blend Path", ""},
244 {SUBD_RING_INTERP_SURF, "SURFACE", 0, "Blend Surface", ""},
245 {0, nullptr, 0, nullptr, nullptr},
246 };
247
248 PropertyRNA *prop;
249
250 prop = RNA_def_int(
251 ot->srna, "number_cuts", cuts_default, 0, 1000, "Number of Cuts", "", cuts_min, 64);
253
254 RNA_def_enum(ot->srna,
255 "interpolation",
256 prop_subd_edgering_types,
258 "Interpolation",
259 "Interpolation method");
260
262 ot->srna, "smoothness", 1.0f, 0.0f, 1e3f, "Smoothness", "Smoothness factor", 0.0f, 2.0f);
263
264 /* profile-shape */
265 RNA_def_float(ot->srna,
266 "profile_shape_factor",
267 0.0f,
268 -1e3f,
269 1e3f,
270 "Profile Factor",
271 "How much intermediary new edges are shrunk/expanded",
272 -2.0f,
273 2.0f);
274
275 prop = RNA_def_property(ot->srna, "profile_shape", PROP_ENUM, PROP_NONE);
278 RNA_def_property_ui_text(prop, "Profile Shape", "Shape of the profile");
280 BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */
281}
282
284{
285 op_props->interp_mode = RNA_enum_get(op->ptr, "interpolation");
286 op_props->cuts = RNA_int_get(op->ptr, "number_cuts");
287 op_props->smooth = RNA_float_get(op->ptr, "smoothness");
288
289 op_props->profile_shape = RNA_enum_get(op->ptr, "profile_shape");
290 op_props->profile_shape_factor = RNA_float_get(op->ptr, "profile_shape_factor");
291}
292
294{
295 const Scene *scene = CTX_data_scene(C);
296 ViewLayer *view_layer = CTX_data_view_layer(C);
298 scene, view_layer, CTX_wm_view3d(C));
299 EdgeRingOpSubdProps op_props;
300
302
303 for (Object *obedit : objects) {
305
306 if (em->bm->totedgesel == 0) {
307 continue;
308 }
309
310 if (!EDBM_op_callf(em,
311 op,
312 "subdivide_edgering edges=%he interp_mode=%i cuts=%i smooth=%f "
313 "profile_shape=%i profile_shape_factor=%f",
315 op_props.interp_mode,
316 op_props.cuts,
317 op_props.smooth,
318 op_props.profile_shape,
319 op_props.profile_shape_factor))
320 {
321 continue;
322 }
323
325 params.calc_looptris = true;
326 params.calc_normals = false;
327 params.is_destructive = true;
328 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
329 }
330
331 return OPERATOR_FINISHED;
332}
333
335{
336 /* identifiers */
337 ot->name = "Subdivide Edge-Ring";
338 ot->description = "Subdivide perpendicular edges to the selected edge-ring";
339 ot->idname = "MESH_OT_subdivide_edgering";
340
341 /* API callbacks. */
343 ot->poll = ED_operator_editmesh;
344
345 /* flags */
347
348 /* properties */
350}
351
353
354/* -------------------------------------------------------------------- */
357
359{
360 const int iterations = RNA_int_get(op->ptr, "iterations");
361 const Scene *scene = CTX_data_scene(C);
362 ViewLayer *view_layer = CTX_data_view_layer(C);
364 scene, view_layer, CTX_wm_view3d(C));
365 for (Object *obedit : objects) {
367
368 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
369 continue;
370 }
371
372 BMOperator bmop;
373 EDBM_op_init(em, &bmop, op, "unsubdivide verts=%hv iterations=%i", BM_ELEM_SELECT, iterations);
374
375 BMO_op_exec(em->bm, &bmop);
376
377 if (!EDBM_op_finish(em, &bmop, op, true)) {
378 continue;
379 }
380
381 if ((em->selectmode & SCE_SELECT_VERTEX) == 0) {
382 EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX); /* need to flush vert->face first */
383 }
385
387 params.calc_looptris = true;
388 params.calc_normals = false;
389 params.is_destructive = true;
390 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
391 }
392
393 return OPERATOR_FINISHED;
394}
395
397{
398 /* identifiers */
399 ot->name = "Un-Subdivide";
400 ot->description = "Un-subdivide selected edges and faces";
401 ot->idname = "MESH_OT_unsubdivide";
402
403 /* API callbacks. */
405 ot->poll = ED_operator_editmesh;
406
407 /* flags */
409
410 /* props */
412 ot->srna, "iterations", 2, 1, 1000, "Iterations", "Number of times to un-subdivide", 1, 100);
413}
414
416
417/* -------------------------------------------------------------------- */
420
421/* NOTE: these values must match delete_mesh() event values. */
422enum {
428};
429
431 const int totelem_old[3],
432 const int totelem_new[3])
433{
434 BKE_reportf(reports,
435 RPT_INFO,
436 "Removed: %d vertices, %d edges, %d faces",
437 totelem_old[0] - totelem_new[0],
438 totelem_old[1] - totelem_new[1],
439 totelem_old[2] - totelem_new[2]);
440}
441
443{
444 const Scene *scene = CTX_data_scene(C);
445 ViewLayer *view_layer = CTX_data_view_layer(C);
446
448 scene, view_layer, CTX_wm_view3d(C));
449 bool changed_multi = false;
450
451 for (Object *obedit : objects) {
453 const int type = RNA_enum_get(op->ptr, "type");
454
455 switch (type) {
456 case MESH_DELETE_VERT: /* Erase Vertices */
457 if (em->bm->totvertsel == 0) {
458 continue;
459 }
461 if (!EDBM_op_callf(em, op, "delete geom=%hv context=%i", BM_ELEM_SELECT, DEL_VERTS)) {
462 continue;
463 }
464 break;
465 case MESH_DELETE_EDGE: /* Erase Edges */
466 if (em->bm->totedgesel == 0) {
467 continue;
468 }
470 if (!EDBM_op_callf(em, op, "delete geom=%he context=%i", BM_ELEM_SELECT, DEL_EDGES)) {
471 continue;
472 }
473 break;
474 case MESH_DELETE_FACE: /* Erase Faces */
475 if (em->bm->totfacesel == 0) {
476 continue;
477 }
479 if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_FACES)) {
480 continue;
481 }
482 break;
483 case MESH_DELETE_EDGE_FACE: /* Edges and Faces */
484 if ((em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
485 continue;
486 }
488 if (!EDBM_op_callf(em, op, "delete geom=%hef context=%i", BM_ELEM_SELECT, DEL_EDGESFACES))
489 {
490 continue;
491 }
492 break;
493 case MESH_DELETE_ONLY_FACE: /* Only faces. */
494 if (em->bm->totfacesel == 0) {
495 continue;
496 }
498 if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_ONLYFACES)) {
499 continue;
500 }
501 break;
502 default:
503 BLI_assert(0);
504 break;
505 }
506
507 changed_multi = true;
508
510
512
514 params.calc_looptris = true;
515 params.calc_normals = false;
516 params.is_destructive = true;
517 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
518
519 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
520 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
521 }
522
523 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
524}
525
527{
528 static const EnumPropertyItem prop_mesh_delete_types[] = {
529 {MESH_DELETE_VERT, "VERT", 0, "Vertices", ""},
530 {MESH_DELETE_EDGE, "EDGE", 0, "Edges", ""},
531 {MESH_DELETE_FACE, "FACE", 0, "Faces", ""},
532 {MESH_DELETE_EDGE_FACE, "EDGE_FACE", 0, "Only Edges & Faces", ""},
533 {MESH_DELETE_ONLY_FACE, "ONLY_FACE", 0, "Only Faces", ""},
534 {0, nullptr, 0, nullptr, nullptr},
535 };
536
537 /* identifiers */
538 ot->name = "Delete";
539 ot->description = "Delete selected vertices, edges or faces";
540 ot->idname = "MESH_OT_delete";
541
542 /* API callbacks. */
543 ot->invoke = WM_menu_invoke;
544 ot->exec = edbm_delete_exec;
545
546 ot->poll = ED_operator_editmesh;
547
548 /* flags */
550
551 /* props */
552 ot->prop = RNA_def_enum(ot->srna,
553 "type",
554 prop_mesh_delete_types,
556 "Type",
557 "Method used for deleting mesh data");
559}
560
562
563/* -------------------------------------------------------------------- */
566
568{
569 BMLoop *l_iter, *l_first;
570
571 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
572 do {
573 if (!BM_edge_is_boundary(l_iter->e)) {
574 return false;
575 }
576 } while ((l_iter = l_iter->next) != l_first);
577
578 return true;
579}
580
582{
583 const Scene *scene = CTX_data_scene(C);
584 ViewLayer *view_layer = CTX_data_view_layer(C);
585 int totelem_old_sel[3];
586 int totelem_old[3];
587
589 scene, view_layer, CTX_wm_view3d(C));
590
591 EDBM_mesh_stats_multi(objects, totelem_old, totelem_old_sel);
592
593 const bool use_verts = (RNA_boolean_get(op->ptr, "use_verts") && totelem_old_sel[0]);
594 const bool use_edges = (RNA_boolean_get(op->ptr, "use_edges") && totelem_old_sel[1]);
595 const bool use_faces = (RNA_boolean_get(op->ptr, "use_faces") && totelem_old_sel[2]);
596
597 for (Object *obedit : objects) {
598
600 BMesh *bm = em->bm;
601 BMIter iter;
602
604
605 if (use_faces) {
606 BMFace *f;
607
608 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
611 }
612 }
613
615 }
616
617 if (use_edges) {
618 BMEdge *e;
619
620 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
623 }
624 }
625
627 }
628
629 if (use_verts) {
630 BMVert *v;
631
632 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
634 BM_elem_flag_set(v, BM_ELEM_TAG, (v->e == nullptr));
635 }
636 }
637
639 }
640
642
644 params.calc_looptris = true;
645 params.calc_normals = false;
646 params.is_destructive = true;
647 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
648 }
649
650 int totelem_new[3];
651 EDBM_mesh_stats_multi(objects, totelem_new, nullptr);
652
653 edbm_report_delete_info(op->reports, totelem_old, totelem_new);
654
655 return OPERATOR_FINISHED;
656}
657
659{
660 /* identifiers */
661 ot->name = "Delete Loose";
662 ot->description = "Delete loose vertices, edges or faces";
663 ot->idname = "MESH_OT_delete_loose";
664
665 /* API callbacks. */
667
668 ot->poll = ED_operator_editmesh;
669
670 /* flags */
672
673 /* props */
674 RNA_def_boolean(ot->srna, "use_verts", true, "Vertices", "Remove loose vertices");
675 RNA_def_boolean(ot->srna, "use_edges", true, "Edges", "Remove loose edges");
676 RNA_def_boolean(ot->srna, "use_faces", false, "Faces", "Remove loose faces");
677}
678
680
681/* -------------------------------------------------------------------- */
684
686{
687 const Scene *scene = CTX_data_scene(C);
688 ViewLayer *view_layer = CTX_data_view_layer(C);
690 scene, view_layer, CTX_wm_view3d(C));
691 for (Object *obedit : objects) {
693
694 if (em->bm->totedgesel == 0) {
695 continue;
696 }
697
698 if (!EDBM_op_callf(em, op, "collapse edges=%he uvs=%b", BM_ELEM_SELECT, true)) {
699 continue;
700 }
701
703 params.calc_looptris = true;
704 params.calc_normals = false;
705 params.is_destructive = true;
706 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
707 }
708
709 return OPERATOR_FINISHED;
710}
711
713{
714 /* identifiers */
715 ot->name = "Collapse Edges & Faces";
716 ot->description =
717 "Collapse isolated edge and face regions, merging data such as UVs and color attributes. "
718 "This can collapse edge-rings as well as regions of connected faces into vertices";
719 ot->idname = "MESH_OT_edge_collapse";
720
721 /* API callbacks. */
723 ot->poll = ED_operator_editmesh;
724
725 /* flags */
727}
728
730
731/* -------------------------------------------------------------------- */
734
736{
737 BMEdge *e;
738 BMIter iter;
739
740 uint vote_on_smooth[2] = {0, 0};
741
742 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
743 if (BM_elem_flag_test(e, BM_ELEM_SELECT) && e->l) {
744 vote_on_smooth[BM_elem_flag_test_bool(e->l->f, BM_ELEM_SMOOTH)]++;
745 }
746 }
747
748 return (vote_on_smooth[0] < vote_on_smooth[1]);
749}
750
751#ifdef USE_FACE_CREATE_SEL_EXTEND
757 BMVert *v, BMEdge *e_used, BMEdge **e_arr, const int e_arr_len, bool (*func)(const BMEdge *))
758{
759 BMIter iter;
760 BMEdge *e_iter;
761 int i = 0;
762 BM_ITER_ELEM (e_iter, &iter, v, BM_EDGES_OF_VERT) {
763 if (BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN) == false) {
764 if ((e_used == nullptr) || (e_used != e_iter)) {
765 if (func(e_iter)) {
766 e_arr[i++] = e_iter;
767 if (i >= e_arr_len) {
768 break;
769 }
770 }
771 }
772 }
773 }
774 return i;
775}
776
778{
779 BMIter iter;
780 bool found = false;
781
782 if (bm->totvertsel == 1 && bm->totedgesel == 0 && bm->totfacesel == 0) {
783 /* first look for 2 boundary edges */
784 BMVert *v;
785
786 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
788 found = true;
789 break;
790 }
791 }
792
793 if (found) {
794 BMEdge *ed_pair[3];
795 if (((edbm_add_edge_face_exec__vert_edge_lookup(v, nullptr, ed_pair, 3, BM_edge_is_wire) ==
796 2) &&
797 (BM_edge_share_face_check(ed_pair[0], ed_pair[1]) == false)) ||
798
800 v, nullptr, ed_pair, 3, BM_edge_is_boundary) == 2) &&
801 (BM_edge_share_face_check(ed_pair[0], ed_pair[1]) == false)))
802 {
803 BMEdge *e_other = BM_edge_exists(BM_edge_other_vert(ed_pair[0], v),
804 BM_edge_other_vert(ed_pair[1], v));
805 BM_edge_select_set(bm, ed_pair[0], true);
806 BM_edge_select_set(bm, ed_pair[1], true);
807 if (e_other) {
808 BM_edge_select_set(bm, e_other, true);
809 }
810 return (BMElem *)v;
811 }
812 }
813 }
814 else if (bm->totvertsel == 2 && bm->totedgesel == 1 && bm->totfacesel == 0) {
815 /* first look for 2 boundary edges */
816 BMEdge *e;
817
818 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
820 found = true;
821 break;
822 }
823 }
824 if (found) {
825 BMEdge *ed_pair_v1[2];
826 BMEdge *ed_pair_v2[2];
827 if (((edbm_add_edge_face_exec__vert_edge_lookup(e->v1, e, ed_pair_v1, 2, BM_edge_is_wire) ==
828 1) &&
830 1) &&
831 (BM_edge_share_face_check(e, ed_pair_v1[0]) == false) &&
832 (BM_edge_share_face_check(e, ed_pair_v2[0]) == false)) ||
833
834# if 1 /* better support mixed cases #37203. */
836 1) &&
838 e->v2, e, ed_pair_v2, 2, BM_edge_is_boundary) == 1) &&
839 (BM_edge_share_face_check(e, ed_pair_v1[0]) == false) &&
840 (BM_edge_share_face_check(e, ed_pair_v2[0]) == false)) ||
841
843 e->v1, e, ed_pair_v1, 2, BM_edge_is_boundary) == 1) &&
845 1) &&
846 (BM_edge_share_face_check(e, ed_pair_v1[0]) == false) &&
847 (BM_edge_share_face_check(e, ed_pair_v2[0]) == false)) ||
848# endif
849
851 e->v1, e, ed_pair_v1, 2, BM_edge_is_boundary) == 1) &&
853 e->v2, e, ed_pair_v2, 2, BM_edge_is_boundary) == 1) &&
854 (BM_edge_share_face_check(e, ed_pair_v1[0]) == false) &&
855 (BM_edge_share_face_check(e, ed_pair_v2[0]) == false)))
856 {
857 BMVert *v1_other = BM_edge_other_vert(ed_pair_v1[0], e->v1);
858 BMVert *v2_other = BM_edge_other_vert(ed_pair_v2[0], e->v2);
859 BMEdge *e_other = (v1_other != v2_other) ? BM_edge_exists(v1_other, v2_other) : nullptr;
860 BM_edge_select_set(bm, ed_pair_v1[0], true);
861 BM_edge_select_set(bm, ed_pair_v2[0], true);
862 if (e_other) {
863 BM_edge_select_set(bm, e_other, true);
864 }
865 return (BMElem *)e;
866 }
867 }
868 }
869
870 return nullptr;
871}
873{
874 /* Now we need to find the edge that isn't connected to this element. */
876
877 /* Notes on hidden geometry:
878 * - Un-hide the face since its possible hidden was copied when copying
879 * surrounding face attributes.
880 * - Un-hide before adding to select history
881 * since we may extend into an existing, hidden vert/edge.
882 */
883
885 BM_face_select_set(bm, f, false);
886
887 if (ele_desel->head.htype == BM_VERT) {
888 BMLoop *l = BM_face_vert_share_loop(f, (BMVert *)ele_desel);
889 BLI_assert(f->len == 3);
890 BM_vert_select_set(bm, (BMVert *)ele_desel, false);
891 BM_edge_select_set(bm, l->next->e, true);
892 BM_select_history_store(bm, l->next->e);
893 }
894 else {
895 BMLoop *l = BM_face_edge_share_loop(f, (BMEdge *)ele_desel);
896 BLI_assert(ELEM(f->len, 4, 3));
897
898 BM_edge_select_set(bm, (BMEdge *)ele_desel, false);
899 if (f->len == 4) {
900 BMEdge *e_active = l->next->next->e;
902 BM_edge_select_set(bm, e_active, true);
903 BM_select_history_store(bm, e_active);
904 }
905 else {
906 BMVert *v_active = l->next->next->v;
908 BM_vert_select_set(bm, v_active, true);
909 BM_select_history_store(bm, v_active);
910 }
911 }
912}
913#endif /* USE_FACE_CREATE_SEL_EXTEND */
914
916{
917 /* When this is used to dissolve we could avoid this, but checking isn't too slow. */
918 bool changed_multi = false;
919 const Scene *scene = CTX_data_scene(C);
920 ViewLayer *view_layer = CTX_data_view_layer(C);
922 scene, view_layer, CTX_wm_view3d(C));
923 for (Object *obedit : objects) {
925
926 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
927 continue;
928 }
929
930 bool use_smooth = edbm_add_edge_face__smooth_get(em->bm);
931 int totedge_orig = em->bm->totedge;
932 int totface_orig = em->bm->totface;
933
934 BMOperator bmop;
935#ifdef USE_FACE_CREATE_SEL_EXTEND
936 BMElem *ele_desel;
937 BMFace *ele_desel_face;
938
939 /* be extra clever, figure out if a partial selection should be extended so we can create
940 * geometry with single vert or single edge selection. */
942#endif
943 if (!EDBM_op_init(em,
944 &bmop,
945 op,
946 "contextual_create geom=%hfev mat_nr=%i use_smooth=%b",
948 em->mat_nr,
949 use_smooth))
950 {
951 continue;
952 }
953
954 BMO_op_exec(em->bm, &bmop);
955
956 /* cancel if nothing was done */
957 if ((totedge_orig == em->bm->totedge) && (totface_orig == em->bm->totface)) {
958 EDBM_op_finish(em, &bmop, op, true);
959 continue;
960 }
961#ifdef USE_FACE_CREATE_SEL_EXTEND
962 /* normally we would want to leave the new geometry selected,
963 * but being able to press F many times to add geometry is too useful! */
964 if (ele_desel && (BMO_slot_buffer_len(bmop.slots_out, "faces.out") == 1) &&
965 (ele_desel_face = static_cast<BMFace *>(
966 BMO_slot_buffer_get_first(bmop.slots_out, "faces.out"))))
967 {
968 edbm_add_edge_face_exec__tricky_finalize_sel(em->bm, ele_desel, ele_desel_face);
969 }
970 else
971#endif
972 {
973 /* Newly created faces may include existing hidden edges,
974 * copying face data from surrounding, may have copied hidden face flag too.
975 *
976 * Important that faces use flushing since 'edges.out'
977 * won't include hidden edges that already existed.
978 */
980 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_HIDDEN, true);
982 em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_HIDDEN, false);
983
985 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
987 em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true);
988 }
989
990 if (!EDBM_op_finish(em, &bmop, op, true)) {
991 continue;
992 }
993
995 params.calc_looptris = true;
996 params.calc_normals = false;
997 params.is_destructive = true;
998 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
999 changed_multi = true;
1000 }
1001
1002 if (!changed_multi) {
1003 return OPERATOR_CANCELLED;
1004 }
1005
1006 return OPERATOR_FINISHED;
1007}
1008
1010{
1011 /* identifiers */
1012 ot->name = "Make Edge/Face";
1013 ot->description = "Add an edge or face to selected";
1014 ot->idname = "MESH_OT_edge_face_add";
1015
1016 /* API callbacks. */
1018 ot->poll = ED_operator_editmesh;
1019
1020 /* flags */
1021 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1022}
1023
1025
1026/* -------------------------------------------------------------------- */
1029
1031{
1032 Scene *scene = CTX_data_scene(C);
1033 ViewLayer *view_layer = CTX_data_view_layer(C);
1034 BMEdge *eed;
1035 BMIter iter;
1036 const bool clear = RNA_boolean_get(op->ptr, "clear");
1037
1039 scene, view_layer, CTX_wm_view3d(C));
1040 for (Object *obedit : objects) {
1042 BMesh *bm = em->bm;
1043
1044 if (bm->totedgesel == 0) {
1045 continue;
1046 }
1047
1048 if (clear) {
1049 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
1051 continue;
1052 }
1053
1055 }
1056 }
1057 else {
1058 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
1060 continue;
1061 }
1063 }
1064 }
1065 }
1066
1067 ED_uvedit_live_unwrap(scene, objects);
1068
1069 for (Object *obedit : objects) {
1071 params.calc_looptris = true;
1072 params.calc_normals = false;
1073 params.is_destructive = false;
1074 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
1075 }
1076
1077 return OPERATOR_FINISHED;
1078}
1079
1081{
1082 PropertyRNA *prop;
1083
1084 /* identifiers */
1085 ot->name = "Mark Seam";
1086 ot->idname = "MESH_OT_mark_seam";
1087 ot->description = "(Un)mark selected edges as a seam";
1088
1089 /* API callbacks. */
1090 ot->exec = edbm_mark_seam_exec;
1091 ot->poll = ED_operator_editmesh;
1092
1093 /* flags */
1094 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1095
1096 prop = RNA_def_boolean(ot->srna, "clear", false, "Clear", "");
1098
1100}
1101
1103
1104/* -------------------------------------------------------------------- */
1107
1109{
1110 BMEdge *eed;
1111 BMIter iter;
1112 const bool clear = RNA_boolean_get(op->ptr, "clear");
1113 const bool use_verts = RNA_boolean_get(op->ptr, "use_verts");
1114 const Scene *scene = CTX_data_scene(C);
1115 ViewLayer *view_layer = CTX_data_view_layer(C);
1116
1118 scene, view_layer, CTX_wm_view3d(C));
1119 for (Object *obedit : objects) {
1121 BMesh *bm = em->bm;
1122
1123 if ((use_verts && bm->totvertsel == 0) || (!use_verts && bm->totedgesel == 0)) {
1124 continue;
1125 }
1126
1127 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
1128 if (use_verts) {
1129 if (!(BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) ||
1131 {
1132 continue;
1133 }
1134 }
1135 else if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
1136 continue;
1137 }
1138
1140 }
1141
1143 params.calc_looptris = true;
1144 params.calc_normals = false;
1145 params.is_destructive = false;
1146 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
1147 }
1148
1149 return OPERATOR_FINISHED;
1150}
1151
1153{
1154 PropertyRNA *prop;
1155
1156 /* identifiers */
1157 ot->name = "Mark Sharp";
1158 ot->idname = "MESH_OT_mark_sharp";
1159 ot->description = "(Un)mark selected edges as sharp";
1160
1161 /* API callbacks. */
1162 ot->exec = edbm_mark_sharp_exec;
1163 ot->poll = ED_operator_editmesh;
1164
1165 /* flags */
1166 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1167
1168 prop = RNA_def_boolean(ot->srna, "clear", false, "Clear", "");
1170 prop = RNA_def_boolean(
1171 ot->srna,
1172 "use_verts",
1173 false,
1174 "Vertices",
1175 "Consider vertices instead of edges to select which edges to (un)tag as sharp");
1177}
1178
1180
1181/* -------------------------------------------------------------------- */
1184
1186{
1187 BMesh *bm = em->bm;
1188 BMOperator bmop;
1189 const int verts_len = bm->totvertsel;
1190 bool is_pair = (verts_len == 2);
1191 int len = 0;
1192 bool check_degenerate = true;
1193
1194 bool checks_succeded = true;
1195
1196 /* sanity check */
1197 if (verts_len < 2) {
1198 return false;
1199 }
1200
1201 BMVert **verts = MEM_malloc_arrayN<BMVert *>(verts_len, __func__);
1202 {
1203 BMIter iter;
1204 BMVert *v;
1205 int i = 0;
1206
1207 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
1209 verts[i++] = v;
1210 }
1211 }
1212
1215 {
1216 check_degenerate = false;
1217 is_pair = false;
1218 }
1219 }
1220
1221 if (is_pair) {
1222 if (!EDBM_op_init(em,
1223 &bmop,
1224 op,
1225 "connect_vert_pair verts=%eb verts_exclude=%hv faces_exclude=%hf",
1226 verts,
1227 verts_len,
1230 {
1231 checks_succeded = false;
1232 }
1233 }
1234 else {
1235 if (!EDBM_op_init(em,
1236 &bmop,
1237 op,
1238 "connect_verts verts=%eb faces_exclude=%hf check_degenerate=%b",
1239 verts,
1240 verts_len,
1242 check_degenerate))
1243 {
1244 checks_succeded = false;
1245 }
1246 }
1247 if (checks_succeded) {
1248 BMBackup em_backup = EDBM_redo_state_store(em);
1249
1251
1252 BMO_op_exec(bm, &bmop);
1253 const bool failure = BMO_error_occurred_at_level(bm, BMO_ERROR_FATAL);
1254 len = BMO_slot_get(bmop.slots_out, "edges.out")->len;
1255
1256 if (len && is_pair) {
1257 /* new verts have been added, we have to select the edges, not just flush */
1259 em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true);
1260 }
1261
1262 bool em_backup_free = true;
1263 if (!EDBM_op_finish(em, &bmop, op, false)) {
1264 len = 0;
1265 }
1266 else if (failure) {
1267 len = 0;
1268 EDBM_redo_state_restore_and_free(&em_backup, em, true);
1269 em_backup_free = false;
1270 }
1271 else {
1272 /* so newly created edges get the selection state from the vertex */
1274
1276
1278 params.calc_looptris = true;
1279 params.calc_normals = false;
1280 params.is_destructive = true;
1281 EDBM_update(mesh, &params);
1282 }
1283
1284 if (em_backup_free) {
1285 EDBM_redo_state_free(&em_backup);
1286 }
1287 }
1289
1290 return len;
1291}
1292
1294{
1295 const Scene *scene = CTX_data_scene(C);
1296 ViewLayer *view_layer = CTX_data_view_layer(C);
1297 uint failed_objects_len = 0;
1299 scene, view_layer, CTX_wm_view3d(C));
1300
1301 for (Object *obedit : objects) {
1303
1304 if (!edbm_connect_vert_pair(em, static_cast<Mesh *>(obedit->data), op)) {
1305 failed_objects_len++;
1306 }
1307 }
1308 return failed_objects_len == objects.size() ? OPERATOR_CANCELLED : OPERATOR_FINISHED;
1309}
1310
1312{
1313 /* identifiers */
1314 ot->name = "Vertex Connect";
1315 ot->idname = "MESH_OT_vert_connect";
1316 ot->description = "Connect selected vertices of faces, splitting the face";
1317
1318 /* API callbacks. */
1319 ot->exec = edbm_vert_connect_exec;
1320 ot->poll = ED_operator_editmesh;
1321
1322 /* flags */
1323 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1324}
1325
1327
1328/* -------------------------------------------------------------------- */
1331
1336{
1337 BMEditSelection *ele_a = static_cast<BMEditSelection *>(bm->selected.first);
1338 BMEditSelection *ele_b = static_cast<BMEditSelection *>(bm->selected.last);
1339 if ((ele_a->htype == BM_VERT) && (ele_b->htype == BM_VERT)) {
1341 1) &&
1343 1))
1344 {
1345 return true;
1346 }
1347 }
1348
1349 return false;
1350}
1351
1352static bool bm_vert_connect_pair(BMesh *bm, BMVert *v_a, BMVert *v_b)
1353{
1354 BMOperator bmop;
1355 BMVert **verts;
1356 const int totedge_orig = bm->totedge;
1357
1358 BMO_op_init(bm, &bmop, BMO_FLAG_DEFAULTS, "connect_vert_pair");
1359
1360 verts = static_cast<BMVert **>(BMO_slot_buffer_alloc(&bmop, bmop.slots_in, "verts", 2));
1361 verts[0] = v_a;
1362 verts[1] = v_b;
1363
1366
1367 BMO_op_exec(bm, &bmop);
1369 BMO_op_finish(bm, &bmop);
1370 return (bm->totedge != totedge_orig);
1371}
1372
1374{
1375 /* Logic is as follows:
1376 *
1377 * - If there are any isolated/wire verts - connect as edges.
1378 * - Otherwise connect faces.
1379 * - If all edges have been created already, closed the loop.
1380 */
1381 if (BLI_listbase_count_at_most(&bm->selected, 2) == 2 && (bm->totvertsel > 2)) {
1382 BMEditSelection *ese;
1383 int tot = 0;
1384 bool changed = false;
1385 bool has_wire = false;
1386 // bool all_verts;
1387
1388 /* ensure all verts have history */
1389 for (ese = static_cast<BMEditSelection *>(bm->selected.first); ese; ese = ese->next, tot++) {
1390 BMVert *v;
1391 if (ese->htype != BM_VERT) {
1392 break;
1393 }
1394 v = (BMVert *)ese->ele;
1395 if ((has_wire == false) && ((v->e == nullptr) || BM_vert_is_wire(v))) {
1396 has_wire = true;
1397 }
1398 }
1399 // all_verts = (ese == nullptr);
1400
1401 if (has_wire == false) {
1402 /* all verts have faces , connect verts via faces! */
1403 if (tot == bm->totvertsel) {
1404 BMEditSelection *ese_last;
1405 ese_last = static_cast<BMEditSelection *>(bm->selected.first);
1406 ese = ese_last->next;
1407
1408 do {
1409
1410 if (BM_edge_exists((BMVert *)ese_last->ele, (BMVert *)ese->ele)) {
1411 /* pass, edge exists (and will be selected) */
1412 }
1413 else {
1414 changed |= bm_vert_connect_pair(bm, (BMVert *)ese_last->ele, (BMVert *)ese->ele);
1415 }
1416 } while ((void)(ese_last = ese), (ese = ese->next));
1417
1418 if (changed) {
1419 return true;
1420 }
1421 }
1422
1423 if (changed == false) {
1424 /* existing loops: close the selection */
1426 changed |= bm_vert_connect_pair(bm,
1427 (BMVert *)((BMEditSelection *)bm->selected.first)->ele,
1428 (BMVert *)((BMEditSelection *)bm->selected.last)->ele);
1429
1430 if (changed) {
1431 return true;
1432 }
1433 }
1434 }
1435 }
1436
1437 else {
1438 /* no faces, simply connect the verts by edges */
1439 BMEditSelection *ese_prev;
1440 ese_prev = static_cast<BMEditSelection *>(bm->selected.first);
1441 ese = ese_prev->next;
1442
1443 do {
1444 if (BM_edge_exists((BMVert *)ese_prev->ele, (BMVert *)ese->ele)) {
1445 /* pass, edge exists (and will be selected) */
1446 }
1447 else {
1448 BMEdge *e;
1449 e = BM_edge_create(
1450 bm, (BMVert *)ese_prev->ele, (BMVert *)ese->ele, nullptr, eBMCreateFlag(0));
1451 BM_edge_select_set(bm, e, true);
1452 changed = true;
1453 }
1454 } while ((void)(ese_prev = ese), (ese = ese->next));
1455
1456 if (changed == false) {
1457 /* existing loops: close the selection */
1459 BMEdge *e;
1460 ese_prev = static_cast<BMEditSelection *>(bm->selected.first);
1461 ese = static_cast<BMEditSelection *>(bm->selected.last);
1462 e = BM_edge_create(
1463 bm, (BMVert *)ese_prev->ele, (BMVert *)ese->ele, nullptr, eBMCreateFlag(0));
1464 BM_edge_select_set(bm, e, true);
1465 }
1466 }
1467
1468 return true;
1469 }
1470 }
1471
1472 return false;
1473}
1474
1480{
1481 ListBase selected_orig = {nullptr, nullptr};
1482 int edges_len = 0;
1483 bool side = false;
1484
1485 /* first check all edges are OK */
1486 LISTBASE_FOREACH (BMEditSelection *, ese, &bm->selected) {
1487 if (ese->htype == BM_EDGE) {
1488 edges_len += 1;
1489 }
1490 else {
1491 return false;
1492 }
1493 }
1494 /* if this is a mixed selection, bail out! */
1495 if (bm->totedgesel != edges_len) {
1496 return false;
1497 }
1498
1499 std::swap(bm->selected, selected_orig);
1500
1501 /* convert edge selection into 2 ordered loops (where the first edge ends up in the middle) */
1502 LISTBASE_FOREACH (BMEditSelection *, ese, &selected_orig) {
1503 BMEdge *e_curr = (BMEdge *)ese->ele;
1504 BMEdge *e_prev = ese->prev ? (BMEdge *)ese->prev->ele : nullptr;
1505 BMLoop *l_curr;
1506 BMLoop *l_prev;
1507 BMVert *v;
1508
1509 if (e_prev) {
1510 BMFace *f = BM_edge_pair_share_face_by_len(e_curr, e_prev, &l_curr, &l_prev, true);
1511 if (f) {
1512 if ((e_curr->v1 != l_curr->v) == (e_prev->v1 != l_prev->v)) {
1513 side = !side;
1514 }
1515 }
1516 else if (is_quad_flip_v3(e_curr->v1->co, e_curr->v2->co, e_prev->v2->co, e_prev->v1->co)) {
1517 side = !side;
1518 }
1519 }
1520
1521 v = (&e_curr->v1)[side];
1522 if (!bm->selected.last || (BMVert *)((BMEditSelection *)bm->selected.last)->ele != v) {
1524 }
1525
1526 v = (&e_curr->v1)[!side];
1527 if (!bm->selected.first || (BMVert *)((BMEditSelection *)bm->selected.first)->ele != v) {
1529 }
1530
1531 e_prev = e_curr;
1532 }
1533
1534 *r_selected = bm->selected;
1535 bm->selected = selected_orig;
1536
1537 return true;
1538}
1539
1541{
1542 const Scene *scene = CTX_data_scene(C);
1543 ViewLayer *view_layer = CTX_data_view_layer(C);
1544 uint failed_selection_order_len = 0;
1545 uint failed_connect_len = 0;
1546 bool has_select_history_mixed = false;
1547 bool has_select_history_face = false;
1549 scene, view_layer, CTX_wm_view3d(C));
1550
1551 for (Object *obedit : objects) {
1553 BMesh *bm = em->bm;
1554 const bool is_pair = (em->bm->totvertsel == 2);
1555 ListBase selected_orig = {nullptr, nullptr};
1556
1557 if (bm->totvertsel == 0) {
1558 continue;
1559 }
1560
1561 /* when there is only 2 vertices, we can ignore selection order */
1562 if (is_pair) {
1563 if (!edbm_connect_vert_pair(em, static_cast<Mesh *>(obedit->data), op)) {
1564 failed_connect_len++;
1565 }
1566 continue;
1567 }
1568
1569 /* Skip mixed selections since path handling only supports uniform types, see #147150. */
1570 const char htype_selected = BM_select_history_htype_all(bm);
1571 if (count_bits_i(htype_selected) > 1) {
1572 has_select_history_mixed = true;
1573 failed_selection_order_len++;
1574 continue;
1575 }
1576 /* Faces are not supported, this check is only done to show a more useful error. */
1577 if (htype_selected & BM_FACE) {
1578 has_select_history_face = true;
1579 failed_selection_order_len++;
1580 continue;
1581 }
1582
1583 if (bm->selected.first) {
1584 BMEditSelection *ese = static_cast<BMEditSelection *>(bm->selected.first);
1585 if (ese->htype == BM_EDGE) {
1587 std::swap(bm->selected, selected_orig);
1588 }
1589 }
1590 }
1591
1593
1596
1598
1600 params.calc_looptris = true;
1601 params.calc_normals = false;
1602 params.is_destructive = true;
1603 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
1604 }
1605 else {
1606 failed_selection_order_len++;
1607 }
1608
1609 if (!BLI_listbase_is_empty(&selected_orig)) {
1611 bm->selected = selected_orig;
1612 }
1613 }
1614
1615 if (failed_selection_order_len == objects.size()) {
1616 if (has_select_history_mixed) {
1617 BKE_report(op->reports, RPT_ERROR, "Could not connect mixed selection types");
1618 }
1619 else if (has_select_history_face) {
1620 BKE_report(op->reports, RPT_ERROR, "Could not connect a face selection");
1621 }
1622 else {
1623 BKE_report(op->reports, RPT_ERROR, "Invalid selection order");
1624 }
1625 return OPERATOR_CANCELLED;
1626 }
1627 if (failed_connect_len == objects.size()) {
1628 BKE_report(op->reports, RPT_ERROR, "Could not connect vertices");
1629 return OPERATOR_CANCELLED;
1630 }
1631
1632 return OPERATOR_FINISHED;
1633}
1634
1636{
1637 /* identifiers */
1638 ot->name = "Vertex Connect Path";
1639 ot->idname = "MESH_OT_vert_connect_path";
1640 ot->description = "Connect vertices by their selection order, creating edges, splitting faces";
1641
1642 /* API callbacks. */
1644 ot->poll = ED_operator_editmesh;
1645
1646 /* flags */
1647 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1648}
1649
1651
1652/* -------------------------------------------------------------------- */
1655
1657{
1658 const Scene *scene = CTX_data_scene(C);
1659 ViewLayer *view_layer = CTX_data_view_layer(C);
1661 scene, view_layer, CTX_wm_view3d(C));
1662 for (Object *obedit : objects) {
1664
1665 if (em->bm->totfacesel == 0) {
1666 continue;
1667 }
1668
1670 em, op, "faces.out", true, "connect_verts_concave faces=%hf", BM_ELEM_SELECT))
1671 {
1672 continue;
1673 }
1675 params.calc_looptris = true;
1676 params.calc_normals = false;
1677 params.is_destructive = true;
1678 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
1679 }
1680
1681 return OPERATOR_FINISHED;
1682}
1683
1685{
1686 /* identifiers */
1687 ot->name = "Split Concave Faces";
1688 ot->idname = "MESH_OT_vert_connect_concave";
1689 ot->description = "Make all faces convex";
1690
1691 /* API callbacks. */
1693 ot->poll = ED_operator_editmesh;
1694
1695 /* flags */
1696 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1697}
1698
1700
1701/* -------------------------------------------------------------------- */
1704
1706{
1707 const Scene *scene = CTX_data_scene(C);
1708 ViewLayer *view_layer = CTX_data_view_layer(C);
1709 const float angle_limit = RNA_float_get(op->ptr, "angle_limit");
1711 scene, view_layer, CTX_wm_view3d(C));
1712
1713 for (Object *obedit : objects) {
1715
1716 if (em->bm->totfacesel == 0) {
1717 continue;
1718 }
1719
1721 op,
1722 "faces.out",
1723 true,
1724 "connect_verts_nonplanar faces=%hf angle_limit=%f",
1726 angle_limit))
1727 {
1728 continue;
1729 }
1730
1732 params.calc_looptris = true;
1733 params.calc_normals = false;
1734 params.is_destructive = true;
1735 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
1736 }
1737
1738 return OPERATOR_FINISHED;
1739}
1740
1742{
1743 PropertyRNA *prop;
1744
1745 /* identifiers */
1746 ot->name = "Split Non-Planar Faces";
1747 ot->idname = "MESH_OT_vert_connect_nonplanar";
1748 ot->description = "Split non-planar faces that exceed the angle threshold";
1749
1750 /* API callbacks. */
1752 ot->poll = ED_operator_editmesh;
1753
1754 /* flags */
1755 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1756
1757 /* props */
1758 prop = RNA_def_float_rotation(ot->srna,
1759 "angle_limit",
1760 0,
1761 nullptr,
1762 0.0f,
1763 DEG2RADF(180.0f),
1764 "Max Angle",
1765 "Angle limit",
1766 0.0f,
1767 DEG2RADF(180.0f));
1769}
1770
1772
1773/* -------------------------------------------------------------------- */
1776
1778{
1779 const Scene *scene = CTX_data_scene(C);
1780 ViewLayer *view_layer = CTX_data_view_layer(C);
1782 scene, view_layer, CTX_wm_view3d(C));
1783
1784 const int repeat = RNA_int_get(op->ptr, "repeat");
1785 const float fac = RNA_float_get(op->ptr, "factor");
1786
1787 int totobjects = 0;
1788
1789 for (Object *obedit : objects) {
1791
1792 if (em->bm->totfacesel == 0) {
1793 continue;
1794 }
1795
1797 continue;
1798 }
1799
1800 totobjects++;
1801
1802 if (!EDBM_op_callf(
1803 em, op, "planar_faces faces=%hf iterations=%i factor=%f", BM_ELEM_SELECT, repeat, fac))
1804 {
1805 continue;
1806 }
1807
1809 params.calc_looptris = true;
1810 params.calc_normals = true;
1811 params.is_destructive = true;
1812 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
1813 }
1814
1815 return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1816}
1817
1819{
1820 /* identifiers */
1821 ot->name = "Make Planar Faces";
1822 ot->idname = "MESH_OT_face_make_planar";
1823 ot->description = "Flatten selected faces";
1824
1825 /* API callbacks. */
1827 ot->poll = ED_operator_editmesh;
1828
1829 /* flags */
1830 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1831
1832 /* props */
1833 RNA_def_float(ot->srna, "factor", 1.0f, -10.0f, 10.0f, "Factor", "", 0.0f, 1.0f);
1834 RNA_def_int(ot->srna, "repeat", 1, 1, 10000, "Iterations", "", 1, 200);
1835}
1836
1838
1839/* -------------------------------------------------------------------- */
1842
1844{
1845 BMesh *bm = em->bm;
1846 if (bm->totedgesel == 0) {
1847 return false;
1848 }
1849
1851
1853 em, op, "edges.out", false, "split_edges edges=%he", BM_ELEM_SELECT))
1854 {
1855 return false;
1856 }
1857
1859
1862 params.calc_looptris = true;
1863 params.calc_normals = false;
1864 params.is_destructive = true;
1865 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
1866
1867 return true;
1868}
1869
1871{
1872 BMesh *bm = em->bm;
1873
1874 /* Note that tracking vertices through the 'split_edges' operator is complicated.
1875 * Instead, tag loops for selection. */
1876 if (bm->totvertsel == 0) {
1877 return false;
1878 }
1879
1881
1882 /* Flush from vertices to edges. */
1883 BMIter iter;
1884 BMEdge *eed;
1885 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
1887 if (eed->l != nullptr) {
1890 {
1892 }
1893 /* Store selection in loop tags. */
1894 BMLoop *l_iter = eed->l;
1895 do {
1897 } while ((l_iter = l_iter->radial_next) != eed->l);
1898 }
1899 }
1900
1901 if (!EDBM_op_callf(em,
1902 op,
1903 "split_edges edges=%he verts=%hv use_verts=%b",
1906 true))
1907 {
1908 return false;
1909 }
1910
1911 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
1912 if (eed->l != nullptr) {
1913 BMLoop *l_iter = eed->l;
1914 do {
1915 if (BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
1916 BM_vert_select_set(em->bm, l_iter->v, true);
1917 }
1918 } while ((l_iter = l_iter->radial_next) != eed->l);
1919 }
1920 else {
1921 /* Split out wire. */
1922 for (int i = 0; i < 2; i++) {
1923 BMVert *v = *(&eed->v1 + i);
1925 if (eed != BM_DISK_EDGE_NEXT(eed, v)) {
1926 BM_vert_separate(bm, v, &eed, 1, true, nullptr, nullptr);
1927 }
1928 }
1929 }
1930 }
1931 }
1932
1934
1937 params.calc_looptris = true;
1938 params.calc_normals = false;
1939 params.is_destructive = true;
1940 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
1941
1942 return true;
1943}
1944
1946{
1947 const int type = RNA_enum_get(op->ptr, "type");
1948
1949 const Scene *scene = CTX_data_scene(C);
1950 ViewLayer *view_layer = CTX_data_view_layer(C);
1952 scene, view_layer, CTX_wm_view3d(C));
1953 for (Object *obedit : objects) {
1955
1956 switch (type) {
1957 case BM_VERT:
1958 if (!edbm_edge_split_selected_verts(op, obedit, em)) {
1959 continue;
1960 }
1961 break;
1962 case BM_EDGE:
1963 if (!edbm_edge_split_selected_edges(op, obedit, em)) {
1964 continue;
1965 }
1966 break;
1967 default:
1968 BLI_assert(0);
1969 }
1970 }
1971
1972 return OPERATOR_FINISHED;
1973}
1974
1976{
1977 /* identifiers */
1978 ot->name = "Edge Split";
1979 ot->idname = "MESH_OT_edge_split";
1980 ot->description = "Split selected edges so that each neighbor face gets its own copy";
1981
1982 /* API callbacks. */
1983 ot->exec = edbm_edge_split_exec;
1984 ot->poll = ED_operator_editmesh;
1985
1986 /* flags */
1987 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1988
1989 /* properties */
1990 static const EnumPropertyItem split_type_items[] = {
1991 {BM_EDGE, "EDGE", 0, "Faces by Edges", "Split faces along selected edges"},
1992 {BM_VERT,
1993 "VERT",
1994 0,
1995 "Faces & Edges by Vertices",
1996 "Split faces and edges connected to selected vertices"},
1997 {0, nullptr, 0, nullptr, nullptr},
1998 };
1999
2000 ot->prop = RNA_def_enum(
2001 ot->srna, "type", split_type_items, BM_EDGE, "Type", "Method to use for splitting");
2002}
2003
2005
2006/* -------------------------------------------------------------------- */
2009
2011{
2012 const Scene *scene = CTX_data_scene(C);
2013 ViewLayer *view_layer = CTX_data_view_layer(C);
2015 scene, view_layer, CTX_wm_view3d(C));
2016 bool changed = false;
2017
2018 for (Object *obedit : objects) {
2020 if (em->bm->totvertsel == 0) {
2021 continue;
2022 }
2023
2024 BMOperator bmop;
2025 BMesh *bm = em->bm;
2026 changed = true;
2027
2028 EDBM_op_init(em,
2029 &bmop,
2030 op,
2031 "duplicate geom=%hvef use_select_history=%b use_edge_flip_from_face=%b",
2033 true,
2034 true);
2035
2036 BMO_op_exec(bm, &bmop);
2037
2038 /* de-select all would clear otherwise */
2040
2042
2044 bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
2045
2046 /* Rebuild edit-selection. */
2048
2049 if (!EDBM_op_finish(em, &bmop, op, true)) {
2050 continue;
2051 }
2053 params.calc_looptris = true;
2054 params.calc_normals = false;
2055 params.is_destructive = true;
2056 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2057 }
2058
2059 return (changed) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
2060}
2061
2063 wmOperator *op,
2064 const wmEvent * /*event*/)
2065{
2066 WM_cursor_wait(true);
2067 const wmOperatorStatus retval = edbm_duplicate_exec(C, op);
2068 WM_cursor_wait(false);
2069
2070 return retval;
2071}
2072
2074{
2075 /* identifiers */
2076 ot->name = "Duplicate";
2077 ot->description = "Duplicate selected vertices, edges or faces";
2078 ot->idname = "MESH_OT_duplicate";
2079
2080 /* API callbacks. */
2081 ot->invoke = edbm_duplicate_invoke;
2082 ot->exec = edbm_duplicate_exec;
2083
2084 ot->poll = ED_operator_editmesh;
2085
2086 /* to give to transform */
2087 PropertyRNA *prop = RNA_def_int(ot->srna,
2088 "mode",
2090 0,
2091 INT_MAX,
2092 "Mode",
2093 "",
2094 0,
2095 INT_MAX);
2097}
2098
2100{
2101 BMLoopNorEditDataArray *lnors_ed_arr = nullptr;
2102 if (CustomData_has_layer_named(&bm->ldata, CD_PROP_INT16_2D, "custom_normal")) {
2103 /* The mesh has custom normal data, update these too.
2104 * Otherwise they will be left in a mangled state.
2105 */
2108 bm,
2109 true,
2110 /* Force #BM_FACE because the loop below operates on all selected faces. */
2111 BM_FACE);
2112 }
2113
2114 return lnors_ed_arr;
2115}
2116
2118{
2119 if (!lnors_ed_arr) {
2120 return false;
2121 }
2122
2123 if (lnors_ed_arr->totloop == 0) {
2124 /* No loops normals to flip, exit early! */
2125 return false;
2126 }
2127
2128 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
2130
2131 /* We need to recreate the custom normal array because the clnors_data will
2132 * be mangled because we swapped the loops around when we flipped the faces. */
2134 bm,
2135 true,
2136 /* Force #BM_FACE because the loop below operates on all selected faces. */
2137 BM_FACE);
2138
2139 {
2140 /* We need to recalculate all loop normals in the affected area. Even the ones that are not
2141 * going to be flipped because the clnors data is mangled. */
2142
2143 BMLoopNorEditData *lnor_ed_new_full = lnors_ed_arr_new_full->lnor_editdata;
2144 for (int i = 0; i < lnors_ed_arr_new_full->totloop; i++, lnor_ed_new_full++) {
2145
2146 BMLoopNorEditData *lnor_ed =
2147 lnors_ed_arr->lidx_to_lnor_editdata[lnor_ed_new_full->loop_index];
2148
2149 BLI_assert(lnor_ed != nullptr);
2150
2152 bm->lnor_spacearr->lspacearr[lnor_ed_new_full->loop_index],
2153 lnor_ed->nloc,
2154 lnor_ed_new_full->clnors_data);
2155 }
2156 }
2157
2158 BMFace *f;
2159 BMLoop *l, *l_start;
2160 BMIter iter_f;
2161 BM_ITER_MESH (f, &iter_f, bm, BM_FACES_OF_MESH) {
2162 /* Flip all the custom loop normals on the selected faces. */
2164 continue;
2165 }
2166
2167 /* Because the winding has changed, we need to go the reverse way around the face to get the
2168 * correct placement of the normals. However we need to derive the old loop index to get the
2169 * correct data. Note that the first loop index is the same though. So the loop starts and ends
2170 * in the same place as before the flip.
2171 */
2172
2173 l_start = l = BM_FACE_FIRST_LOOP(f);
2174 int old_index = BM_elem_index_get(l);
2175 do {
2176 int loop_index = BM_elem_index_get(l);
2177
2178 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lidx_to_lnor_editdata[old_index];
2179 BMLoopNorEditData *lnor_ed_new = lnors_ed_arr_new_full->lidx_to_lnor_editdata[loop_index];
2180 BLI_assert(lnor_ed != nullptr && lnor_ed_new != nullptr);
2181
2182 negate_v3(lnor_ed->nloc);
2183
2185 bm->lnor_spacearr->lspacearr[loop_index], lnor_ed->nloc, lnor_ed_new->clnors_data);
2186
2187 old_index++;
2188 l = l->prev;
2189 } while (l != l_start);
2190 }
2191 BM_loop_normal_editdata_array_free(lnors_ed_arr_new_full);
2192 return true;
2193}
2194
2196
2197/* -------------------------------------------------------------------- */
2200
2202{
2203 if (!CustomData_has_layer_named(&em->bm->ldata, CD_PROP_INT16_2D, "custom_normal")) {
2204 return;
2205 }
2206
2207 /* The mesh has custom normal data, flip them. */
2208 BMesh *bm = em->bm;
2209
2212 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
2213
2214 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
2215 negate_v3(lnor_ed->nloc);
2216
2218 bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->nloc, lnor_ed->clnors_data);
2219 }
2222 params.calc_looptris = true;
2223 params.calc_normals = false;
2224 params.is_destructive = false;
2225 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2226}
2227
2229{
2230 if (EDBM_op_callf(em, op, "flip_quad_tessellation faces=%hf", BM_ELEM_SELECT)) {
2232 params.calc_looptris = true;
2233 params.calc_normals = false;
2234 params.is_destructive = false;
2235 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2236 }
2237}
2238
2240{
2241
2242 bool has_flipped_faces = false;
2243
2244 /* See if we have any custom normals to flip. */
2246
2247 if (EDBM_op_callf(em, op, "reverse_faces faces=%hf flip_multires=%b", BM_ELEM_SELECT, true)) {
2248 has_flipped_faces = true;
2249 }
2250
2251 if (flip_custom_normals(em->bm, lnors_ed_arr) || has_flipped_faces) {
2253 params.calc_looptris = true;
2254 params.calc_normals = false;
2255 params.is_destructive = false;
2256 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2257 }
2258
2259 if (lnors_ed_arr != nullptr) {
2261 }
2262}
2263
2265{
2266 const Scene *scene = CTX_data_scene(C);
2267 ViewLayer *view_layer = CTX_data_view_layer(C);
2269 scene, view_layer, CTX_wm_view3d(C));
2270
2271 for (Object *obedit : objects) {
2273 if (em->bm->totfacesel == 0) {
2274 continue;
2275 }
2276 edbm_flip_quad_tessellation(op, obedit, em);
2277 }
2278
2279 return OPERATOR_FINISHED;
2280}
2281
2283{
2284 const bool only_clnors = RNA_boolean_get(op->ptr, "only_clnors");
2285
2286 const Scene *scene = CTX_data_scene(C);
2287 ViewLayer *view_layer = CTX_data_view_layer(C);
2289 scene, view_layer, CTX_wm_view3d(C));
2290
2291 for (Object *obedit : objects) {
2293
2294 if (only_clnors) {
2295 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
2296 continue;
2297 }
2299 }
2300 else {
2301 if (em->bm->totfacesel == 0) {
2302 continue;
2303 }
2304 edbm_flip_normals_face_winding(op, obedit, em);
2305 }
2306 }
2307
2308 return OPERATOR_FINISHED;
2309}
2310
2312{
2313 /* identifiers */
2314 ot->name = "Flip Normals";
2315 ot->description = "Flip the direction of selected faces' normals (and of their vertices)";
2316 ot->idname = "MESH_OT_flip_normals";
2317
2318 /* API callbacks. */
2319 ot->exec = edbm_flip_normals_exec;
2320 ot->poll = ED_operator_editmesh;
2321
2322 /* flags */
2323 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2324
2325 RNA_def_boolean(ot->srna,
2326 "only_clnors",
2327 false,
2328 "Custom Normals Only",
2329 "Only flip the custom loop normals of the selected elements");
2330}
2331
2333
2334/* -------------------------------------------------------------------- */
2337
2342{
2343 BMEdge *eed;
2344 BMIter iter;
2345 const bool use_ccw = RNA_boolean_get(op->ptr, "use_ccw");
2346
2347 int tot_failed_all = 0;
2348 bool no_selected_edges = true, invalid_selected_edges = true;
2349
2350 const Scene *scene = CTX_data_scene(C);
2351 ViewLayer *view_layer = CTX_data_view_layer(C);
2353 scene, view_layer, CTX_wm_view3d(C));
2354 for (Object *obedit : objects) {
2356 int tot = 0;
2357
2358 if (em->bm->totedgesel == 0) {
2359 continue;
2360 }
2361 no_selected_edges = false;
2362
2363 /* first see if we have two adjacent faces */
2364 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
2367 BMFace *fa, *fb;
2368 if (BM_edge_face_pair(eed, &fa, &fb)) {
2369 /* if both faces are selected we rotate between them,
2370 * otherwise - rotate between 2 unselected - but not mixed */
2373 tot++;
2374 }
2375 }
2376 }
2377 }
2378
2379 /* OK, we don't have two adjacent faces, but we do have two selected ones.
2380 * that's an error condition. */
2381 if (tot == 0) {
2382 continue;
2383 }
2384 invalid_selected_edges = false;
2385
2386 BMOperator bmop;
2387 EDBM_op_init(em, &bmop, op, "rotate_edges edges=%he use_ccw=%b", BM_ELEM_TAG, use_ccw);
2388
2389 /* avoids leaving old verts selected which can be a problem running multiple times,
2390 * since this means the edges become selected around the face
2391 * which then attempt to rotate */
2393
2394 BMO_op_exec(em->bm, &bmop);
2395 /* edges may rotate into hidden vertices, if this does _not_ run we get an illogical state */
2397 em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_HIDDEN, true);
2399 em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true);
2400
2401 const int tot_rotate = BMO_slot_buffer_len(bmop.slots_out, "edges.out");
2402 const int tot_failed = tot - tot_rotate;
2403
2404 tot_failed_all += tot_failed;
2405
2406 if (tot_failed != 0) {
2407 /* If some edges fail to rotate, we need to re-select them,
2408 * otherwise we can end up with invalid selection
2409 * (unselected edge between 2 selected faces). */
2411 }
2412
2414
2415 if (!EDBM_op_finish(em, &bmop, op, true)) {
2416 continue;
2417 }
2418
2420 params.calc_looptris = true;
2421 params.calc_normals = false;
2422 params.is_destructive = true;
2423 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2424 }
2425
2426 if (no_selected_edges) {
2427 BKE_report(
2428 op->reports, RPT_ERROR, "Select edges or face pairs for edge loops to rotate about");
2429 return OPERATOR_CANCELLED;
2430 }
2431
2432 /* Ok, we don't have two adjacent faces, but we do have two selected ones.
2433 * that's an error condition. */
2434 if (invalid_selected_edges) {
2435 BKE_report(op->reports, RPT_ERROR, "Could not find any selected edges that can be rotated");
2436 return OPERATOR_CANCELLED;
2437 }
2438
2439 if (tot_failed_all != 0) {
2440 BKE_reportf(op->reports, RPT_WARNING, "Unable to rotate %d edge(s)", tot_failed_all);
2441 }
2442
2443 return OPERATOR_FINISHED;
2444}
2445
2447{
2448 /* identifiers */
2449 ot->name = "Rotate Selected Edge";
2450 ot->description = "Rotate selected edge or adjoining faces";
2451 ot->idname = "MESH_OT_edge_rotate";
2452
2453 /* API callbacks. */
2455 ot->poll = ED_operator_editmesh;
2456
2457 /* flags */
2458 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2459
2460 /* props */
2461 RNA_def_boolean(ot->srna, "use_ccw", false, "Counter Clockwise", "");
2462}
2463
2465
2466/* -------------------------------------------------------------------- */
2469
2471{
2472 const bool unselected = RNA_boolean_get(op->ptr, "unselected");
2473 const Scene *scene = CTX_data_scene(C);
2474 ViewLayer *view_layer = CTX_data_view_layer(C);
2475 bool changed = false;
2476
2478 scene, view_layer, CTX_wm_view3d(C));
2479 for (Object *obedit : objects) {
2481 BMesh *bm = em->bm;
2482
2483 if (unselected) {
2484 if (em->selectmode & SCE_SELECT_VERTEX) {
2485 if (bm->totvertsel == bm->totvert) {
2486 continue;
2487 }
2488 }
2489 else if (em->selectmode & SCE_SELECT_EDGE) {
2490 if (bm->totedgesel == bm->totedge) {
2491 continue;
2492 }
2493 }
2494 else if (em->selectmode & SCE_SELECT_FACE) {
2495 if (bm->totfacesel == bm->totface) {
2496 continue;
2497 }
2498 }
2499 }
2500 else {
2501 if (bm->totvertsel == 0) {
2502 continue;
2503 }
2504 }
2505
2506 /* Disable as this causes the following problem:
2507 * Selecting an area of interest (on one side of the mesh), "Invert" then "Hide"
2508 * will hide the area of interest too. */
2509#if 0
2510 /* Only if symmetry is enabled. */
2512#endif
2513
2514 if (EDBM_mesh_hide(em, unselected)) {
2516 params.calc_looptris = true;
2517 params.calc_normals = false;
2518 params.is_destructive = false;
2519 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2520 changed = true;
2521 }
2522 }
2523
2524 if (!changed) {
2525 return OPERATOR_CANCELLED;
2526 }
2527
2528 return OPERATOR_FINISHED;
2529}
2530
2532{
2533 /* identifiers */
2534 ot->name = "Hide Selected";
2535 ot->idname = "MESH_OT_hide";
2536 ot->description = "Hide (un)selected vertices, edges or faces";
2537
2538 /* API callbacks. */
2539 ot->exec = edbm_hide_exec;
2540 ot->poll = ED_operator_editmesh;
2541
2542 /* flags */
2543 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2544
2545 /* props */
2547 ot->srna, "unselected", false, "Unselected", "Hide unselected rather than selected");
2548}
2549
2551
2552/* -------------------------------------------------------------------- */
2555
2557{
2558 const bool select = RNA_boolean_get(op->ptr, "select");
2559 const Scene *scene = CTX_data_scene(C);
2560 ViewLayer *view_layer = CTX_data_view_layer(C);
2561
2563 scene, view_layer, CTX_wm_view3d(C));
2564 for (Object *obedit : objects) {
2566
2567 if (EDBM_mesh_reveal(em, select)) {
2569 params.calc_looptris = true;
2570 params.calc_normals = false;
2571 params.is_destructive = false;
2572 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2573 }
2574 }
2575
2576 return OPERATOR_FINISHED;
2577}
2578
2580{
2581 /* identifiers */
2582 ot->name = "Reveal Hidden";
2583 ot->idname = "MESH_OT_reveal";
2584 ot->description = "Reveal all hidden vertices, edges and faces";
2585
2586 /* API callbacks. */
2587 ot->exec = edbm_reveal_exec;
2588 ot->poll = ED_operator_editmesh;
2589
2590 /* flags */
2591 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2592
2593 RNA_def_boolean(ot->srna, "select", true, "Select", "");
2594}
2595
2597
2598/* -------------------------------------------------------------------- */
2601
2603{
2604 const Scene *scene = CTX_data_scene(C);
2605 ViewLayer *view_layer = CTX_data_view_layer(C);
2606 const bool inside = RNA_boolean_get(op->ptr, "inside");
2607
2609 scene, view_layer, CTX_wm_view3d(C));
2610 for (Object *obedit : objects) {
2612
2613 if (em->bm->totfacesel == 0) {
2614 continue;
2615 }
2616
2617 BMLoopNorEditDataArray *lnors_ed_arr = nullptr;
2618
2619 if (inside) {
2620 /* Save custom normal data for later so we can flip them correctly. */
2621 lnors_ed_arr = flip_custom_normals_init_data(em->bm);
2622 }
2623
2624 if (!EDBM_op_callf(em, op, "recalc_face_normals faces=%hf", BM_ELEM_SELECT)) {
2625 continue;
2626 }
2627
2628 if (inside) {
2629 EDBM_op_callf(em, op, "reverse_faces faces=%hf flip_multires=%b", BM_ELEM_SELECT, true);
2630 flip_custom_normals(em->bm, lnors_ed_arr);
2631 if (lnors_ed_arr != nullptr) {
2633 }
2634 }
2635
2637 params.calc_looptris = true;
2638 params.calc_normals = false;
2639 params.is_destructive = false;
2640 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2641 }
2642
2643 return OPERATOR_FINISHED;
2644}
2645
2647{
2648 /* identifiers */
2649 ot->name = "Recalculate Normals";
2650 ot->description = "Make face and vertex normals point either outside or inside the mesh";
2651 ot->idname = "MESH_OT_normals_make_consistent";
2652
2653 /* API callbacks. */
2655 ot->poll = ED_operator_editmesh;
2656
2657 /* flags */
2658 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2659
2660 RNA_def_boolean(ot->srna, "inside", false, "Inside", "");
2661}
2662
2664
2665/* -------------------------------------------------------------------- */
2668
2670{
2671 const float fac = RNA_float_get(op->ptr, "factor");
2672
2673 const bool xaxis = RNA_boolean_get(op->ptr, "xaxis");
2674 const bool yaxis = RNA_boolean_get(op->ptr, "yaxis");
2675 const bool zaxis = RNA_boolean_get(op->ptr, "zaxis");
2676 int repeat = RNA_int_get(op->ptr, "repeat");
2677
2678 if (!repeat) {
2679 repeat = 1;
2680 }
2681
2682 const Scene *scene = CTX_data_scene(C);
2683 ViewLayer *view_layer = CTX_data_view_layer(C);
2684 int tot_selected = 0, tot_locked = 0;
2686 scene, view_layer, CTX_wm_view3d(C));
2687 for (Object *obedit : objects) {
2688 Mesh *mesh = static_cast<Mesh *>(obedit->data);
2690 bool mirrx = false, mirry = false, mirrz = false;
2691 float clip_dist = 0.0f;
2692 const bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
2693
2694 if (em->bm->totvertsel == 0) {
2695 continue;
2696 }
2697
2699 tot_locked++;
2700 continue;
2701 }
2702
2703 tot_selected++;
2704
2705 /* mirror before smooth */
2706 if (((Mesh *)obedit->data)->symmetry & ME_SYMMETRY_X) {
2707 EDBM_verts_mirror_cache_begin(em, 0, false, true, false, use_topology);
2708 }
2709
2710 /* if there is a mirror modifier with clipping, flag the verts that
2711 * are within tolerance of the plane(s) of reflection
2712 */
2713 LISTBASE_FOREACH (ModifierData *, md, &obedit->modifiers) {
2714 if (md->type == eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) {
2716
2717 if (mmd->flag & MOD_MIR_CLIPPING) {
2718 if (mmd->flag & MOD_MIR_AXIS_X) {
2719 mirrx = true;
2720 }
2721 if (mmd->flag & MOD_MIR_AXIS_Y) {
2722 mirry = true;
2723 }
2724 if (mmd->flag & MOD_MIR_AXIS_Z) {
2725 mirrz = true;
2726 }
2727
2728 clip_dist = mmd->tolerance;
2729 }
2730 }
2731 }
2732
2733 for (int i = 0; i < repeat; i++) {
2734 if (!EDBM_op_callf(
2735 em,
2736 op,
2737 "smooth_vert verts=%hv factor=%f mirror_clip_x=%b mirror_clip_y=%b mirror_clip_z=%b "
2738 "clip_dist=%f use_axis_x=%b use_axis_y=%b use_axis_z=%b",
2740 fac,
2741 mirrx,
2742 mirry,
2743 mirrz,
2744 clip_dist,
2745 xaxis,
2746 yaxis,
2747 zaxis))
2748 {
2749 continue;
2750 }
2751 }
2752
2753 /* NOTE: redundant calculation could be avoided if the EDBM API could skip calculation. */
2754 bool calc_normals = false;
2755
2756 /* apply mirror */
2757 if (((Mesh *)obedit->data)->symmetry & ME_SYMMETRY_X) {
2760 calc_normals = true;
2761 }
2762
2764 params.calc_looptris = true;
2765 params.calc_normals = calc_normals;
2766 params.is_destructive = false;
2767 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2768 }
2769
2770 if (tot_selected == 0 && !tot_locked) {
2771 BKE_report(op->reports, RPT_WARNING, "No selected vertex");
2772 }
2773
2774 return tot_selected ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
2775}
2776
2778{
2779 /* identifiers */
2780 ot->name = "Smooth Vertices";
2781 ot->description = "Flatten angles of selected vertices";
2782 ot->idname = "MESH_OT_vertices_smooth";
2783
2784 /* API callbacks. */
2786 ot->poll = ED_operator_editmesh;
2787
2788 /* flags */
2789 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2790
2791 ot->prop = RNA_def_float_factor(
2792 ot->srna, "factor", 0.0f, -10.0f, 10.0f, "Smoothing", "Smoothing factor", 0.0f, 1.0f);
2794 ot->srna, "repeat", 1, 1, 1000, "Repeat", "Number of times to smooth the mesh", 1, 100);
2795
2797
2798 RNA_def_boolean(ot->srna, "xaxis", true, "X-Axis", "Smooth along the X axis");
2799 RNA_def_boolean(ot->srna, "yaxis", true, "Y-Axis", "Smooth along the Y axis");
2800 RNA_def_boolean(ot->srna, "zaxis", true, "Z-Axis", "Smooth along the Z axis");
2801
2802 /* Set generic modal callbacks. */
2804}
2805
2807
2808/* -------------------------------------------------------------------- */
2811
2813{
2814 int tot_selected = 0, tot_locked = 0;
2815 const Scene *scene = CTX_data_scene(C);
2816 ViewLayer *view_layer = CTX_data_view_layer(C);
2817
2818 const float lambda_factor = RNA_float_get(op->ptr, "lambda_factor");
2819 const float lambda_border = RNA_float_get(op->ptr, "lambda_border");
2820 const bool usex = RNA_boolean_get(op->ptr, "use_x");
2821 const bool usey = RNA_boolean_get(op->ptr, "use_y");
2822 const bool usez = RNA_boolean_get(op->ptr, "use_z");
2823 const bool preserve_volume = RNA_boolean_get(op->ptr, "preserve_volume");
2824 int repeat = RNA_int_get(op->ptr, "repeat");
2825
2826 if (!repeat) {
2827 repeat = 1;
2828 }
2829
2831 scene, view_layer, CTX_wm_view3d(C));
2832 for (Object *obedit : objects) {
2834 Mesh *mesh = static_cast<Mesh *>(obedit->data);
2835 bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
2836
2837 if (em->bm->totvertsel == 0) {
2838 continue;
2839 }
2840
2842 tot_locked++;
2843 continue;
2844 }
2845
2846 tot_selected++;
2847
2848 /* Mirror before smooth. */
2849 if (((Mesh *)obedit->data)->symmetry & ME_SYMMETRY_X) {
2850 EDBM_verts_mirror_cache_begin(em, 0, false, true, false, use_topology);
2851 }
2852
2853 bool failed_repeat_loop = false;
2854 for (int i = 0; i < repeat; i++) {
2855 if (!EDBM_op_callf(em,
2856 op,
2857 "smooth_laplacian_vert verts=%hv lambda_factor=%f lambda_border=%f "
2858 "use_x=%b use_y=%b use_z=%b preserve_volume=%b",
2860 lambda_factor,
2861 lambda_border,
2862 usex,
2863 usey,
2864 usez,
2865 preserve_volume))
2866 {
2867 failed_repeat_loop = true;
2868 break;
2869 }
2870 }
2871 if (failed_repeat_loop) {
2872 continue;
2873 }
2874
2875 /* NOTE: redundant calculation could be avoided if the EDBM API could skip calculation. */
2876 bool calc_normals = false;
2877
2878 /* Apply mirror. */
2879 if (((Mesh *)obedit->data)->symmetry & ME_SYMMETRY_X) {
2882 calc_normals = true;
2883 }
2884
2886 params.calc_looptris = true;
2887 params.calc_normals = calc_normals;
2888 params.is_destructive = false;
2889 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2890 }
2891
2892 if (tot_selected == 0 && !tot_locked) {
2893 BKE_report(op->reports, RPT_WARNING, "No selected vertex");
2894 }
2895
2896 return tot_selected ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
2897}
2898
2900{
2901 /* identifiers */
2902 ot->name = "Laplacian Smooth Vertices";
2903 ot->description = "Laplacian smooth of selected vertices";
2904 ot->idname = "MESH_OT_vertices_smooth_laplacian";
2905
2906 /* API callbacks. */
2908 ot->poll = ED_operator_editmesh;
2909
2910 /* flags */
2911 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2912
2914 ot->srna, "repeat", 1, 1, 1000, "Number of iterations to smooth the mesh", "", 1, 200);
2916 ot->srna, "lambda_factor", 1.0f, 1e-7f, 1000.0f, "Lambda factor", "", 1e-7f, 1000.0f);
2917 RNA_def_float(ot->srna,
2918 "lambda_border",
2919 5e-5f,
2920 1e-7f,
2921 1000.0f,
2922 "Lambda factor in border",
2923 "",
2924 1e-7f,
2925 1000.0f);
2926
2928
2929 RNA_def_boolean(ot->srna, "use_x", true, "Smooth X Axis", "Smooth object along X axis");
2930 RNA_def_boolean(ot->srna, "use_y", true, "Smooth Y Axis", "Smooth object along Y axis");
2931 RNA_def_boolean(ot->srna, "use_z", true, "Smooth Z Axis", "Smooth object along Z axis");
2932 RNA_def_boolean(ot->srna,
2933 "preserve_volume",
2934 true,
2935 "Preserve Volume",
2936 "Apply volume preservation after smooth");
2937}
2938
2940
2941/* -------------------------------------------------------------------- */
2944
2945static void mesh_set_smooth_faces(BMEditMesh *em, short smooth)
2946{
2947 BMIter iter;
2948 BMFace *efa;
2949
2950 if (em == nullptr) {
2951 return;
2952 }
2953
2954 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2956 BM_elem_flag_set(efa, BM_ELEM_SMOOTH, smooth);
2957 }
2958 }
2959}
2960
2962{
2963 const Scene *scene = CTX_data_scene(C);
2964 ViewLayer *view_layer = CTX_data_view_layer(C);
2966 scene, view_layer, CTX_wm_view3d(C));
2967 for (Object *obedit : objects) {
2969
2970 if (em->bm->totfacesel == 0) {
2971 continue;
2972 }
2973
2974 mesh_set_smooth_faces(em, 1);
2976 params.calc_looptris = false;
2977 params.calc_normals = false;
2978 params.is_destructive = false;
2979 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2980 }
2981
2982 return OPERATOR_FINISHED;
2983}
2984
2986{
2987 /* identifiers */
2988 ot->name = "Shade Smooth";
2989 ot->description = "Display faces smooth (using vertex normals)";
2990 ot->idname = "MESH_OT_faces_shade_smooth";
2991
2992 /* API callbacks. */
2994 ot->poll = ED_operator_editmesh;
2995
2996 /* flags */
2997 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2998}
2999
3001
3002/* -------------------------------------------------------------------- */
3005
3007{
3008 const Scene *scene = CTX_data_scene(C);
3009 ViewLayer *view_layer = CTX_data_view_layer(C);
3011 scene, view_layer, CTX_wm_view3d(C));
3012 for (Object *obedit : objects) {
3014
3015 if (em->bm->totfacesel == 0) {
3016 continue;
3017 }
3018
3019 mesh_set_smooth_faces(em, 0);
3021 params.calc_looptris = false;
3022 params.calc_normals = false;
3023 params.is_destructive = false;
3024 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
3025 }
3026
3027 return OPERATOR_FINISHED;
3028}
3029
3031{
3032 /* identifiers */
3033 ot->name = "Shade Flat";
3034 ot->description = "Display faces flat";
3035 ot->idname = "MESH_OT_faces_shade_flat";
3036
3037 /* API callbacks. */
3039 ot->poll = ED_operator_editmesh;
3040
3041 /* flags */
3042 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3043}
3044
3046
3047/* -------------------------------------------------------------------- */
3050
3052{
3053 /* get the direction from RNA */
3054 const bool use_ccw = RNA_boolean_get(op->ptr, "use_ccw");
3055
3056 const Scene *scene = CTX_data_scene(C);
3057 ViewLayer *view_layer = CTX_data_view_layer(C);
3059 scene, view_layer, CTX_wm_view3d(C));
3060 for (Object *obedit : objects) {
3062
3063 if (em->bm->totfacesel == 0) {
3064 continue;
3065 }
3066
3067 BMOperator bmop;
3068
3069 EDBM_op_init(em, &bmop, op, "rotate_uvs faces=%hf use_ccw=%b", BM_ELEM_SELECT, use_ccw);
3070
3071 BMO_op_exec(em->bm, &bmop);
3072
3073 if (!EDBM_op_finish(em, &bmop, op, true)) {
3074 continue;
3075 }
3076
3078 params.calc_looptris = false;
3079 params.calc_normals = false;
3080 params.is_destructive = false;
3081 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
3082 }
3083
3084 return OPERATOR_FINISHED;
3085}
3086
3088{
3089 const Scene *scene = CTX_data_scene(C);
3090 ViewLayer *view_layer = CTX_data_view_layer(C);
3092 scene, view_layer, CTX_wm_view3d(C));
3093 for (Object *obedit : objects) {
3095
3096 if (em->bm->totfacesel == 0) {
3097 continue;
3098 }
3099
3100 BMOperator bmop;
3101
3102 EDBM_op_init(em, &bmop, op, "reverse_uvs faces=%hf", BM_ELEM_SELECT);
3103
3104 BMO_op_exec(em->bm, &bmop);
3105
3106 if (!EDBM_op_finish(em, &bmop, op, true)) {
3107 continue;
3108 }
3110 params.calc_looptris = false;
3111 params.calc_normals = false;
3112 params.is_destructive = false;
3113 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
3114 }
3115
3116 return OPERATOR_FINISHED;
3117}
3118
3120{
3121 /* get the direction from RNA */
3122 const bool use_ccw = RNA_boolean_get(op->ptr, "use_ccw");
3123
3124 const Scene *scene = CTX_data_scene(C);
3125 ViewLayer *view_layer = CTX_data_view_layer(C);
3127 scene, view_layer, CTX_wm_view3d(C));
3128
3129 for (uint ob_index = 0; ob_index < objects.size(); ob_index++) {
3130 Object *ob = objects[ob_index];
3132 if (em->bm->totfacesel == 0) {
3133 continue;
3134 }
3135
3136 BMOperator bmop;
3137
3142 if (!layer) {
3143 continue;
3144 }
3145
3146 int color_index = BKE_attribute_to_index(
3148 EDBM_op_init(em,
3149 &bmop,
3150 op,
3151 "rotate_colors faces=%hf use_ccw=%b color_index=%i",
3153 use_ccw,
3154 color_index);
3155
3156 BMO_op_exec(em->bm, &bmop);
3157
3158 if (!EDBM_op_finish(em, &bmop, op, true)) {
3159 continue;
3160 }
3161
3162 /* dependencies graph and notification stuff */
3164 params.calc_looptris = false;
3165 params.calc_normals = false;
3166 params.is_destructive = false;
3167 EDBM_update(static_cast<Mesh *>(ob->data), &params);
3168 }
3169
3170 return OPERATOR_FINISHED;
3171}
3172
3174{
3175 const Scene *scene = CTX_data_scene(C);
3176 ViewLayer *view_layer = CTX_data_view_layer(C);
3178 scene, view_layer, CTX_wm_view3d(C));
3179
3180 for (Object *obedit : objects) {
3182
3183 if (em->bm->totfacesel == 0) {
3184 continue;
3185 }
3186
3187 Mesh *mesh = BKE_object_get_original_mesh(obedit);
3191 if (!layer) {
3192 continue;
3193 }
3194
3195 BMOperator bmop;
3196
3197 int color_index = BKE_attribute_to_index(
3200 em, &bmop, op, "reverse_colors faces=%hf color_index=%i", BM_ELEM_SELECT, color_index);
3201
3202 BMO_op_exec(em->bm, &bmop);
3203
3204 if (!EDBM_op_finish(em, &bmop, op, true)) {
3205 continue;
3206 }
3207
3209 params.calc_looptris = false;
3210 params.calc_normals = false;
3211 params.is_destructive = false;
3212 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
3213 }
3214
3215 return OPERATOR_FINISHED;
3216}
3217
3219{
3220 /* identifiers */
3221 ot->name = "Rotate UVs";
3222 ot->idname = "MESH_OT_uvs_rotate";
3223 ot->description = "Rotate UV coordinates inside faces";
3224
3225 /* API callbacks. */
3226 ot->exec = edbm_rotate_uvs_exec;
3227 ot->poll = ED_operator_editmesh;
3228
3229 /* flags */
3230 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3231
3232 /* props */
3233 RNA_def_boolean(ot->srna, "use_ccw", false, "Counter Clockwise", "");
3234}
3235
3237{
3238 /* identifiers */
3239 ot->name = "Reverse UVs";
3240 ot->idname = "MESH_OT_uvs_reverse";
3241 ot->description = "Flip direction of UV coordinates inside faces";
3242
3243 /* API callbacks. */
3244 ot->exec = edbm_reverse_uvs_exec;
3245 ot->poll = ED_operator_editmesh;
3246
3247 /* flags */
3248 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3249
3250 /* props */
3251 // RNA_def_enum(ot->srna, "axis", axis_items, DIRECTION_CW, "Axis", "Axis to mirror UVs around");
3252}
3253
3255{
3256 /* identifiers */
3257 ot->name = "Rotate Colors";
3258 ot->idname = "MESH_OT_colors_rotate";
3259 ot->description = "Rotate face corner color attribute inside faces";
3260
3261 /* API callbacks. */
3263 ot->poll = ED_operator_editmesh;
3264
3265 /* flags */
3266 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3267
3268 /* props */
3269 RNA_def_boolean(ot->srna, "use_ccw", false, "Counter Clockwise", "");
3270}
3271
3273{
3274 /* identifiers */
3275 ot->name = "Reverse Colors";
3276 ot->idname = "MESH_OT_colors_reverse";
3277 ot->description = "Flip direction of face corner color attribute inside faces";
3278
3279 /* API callbacks. */
3281 ot->poll = ED_operator_editmesh;
3282
3283 /* flags */
3284 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3285
3286 /* props */
3287#if 0
3288 RNA_def_enum(ot->srna, "axis", axis_items, DIRECTION_CW, "Axis", "Axis to mirror colors around");
3289#endif
3290}
3291
3293
3294/* -------------------------------------------------------------------- */
3297
3298enum {
3304};
3305
3307 const bool use_first,
3308 const bool use_uvmerge,
3309 wmOperator *wmop)
3310{
3311 BMVert *mergevert;
3312 BMEditSelection *ese;
3313
3314 /* operator could be called directly from shortcut or python,
3315 * so do extra check for data here
3316 */
3317
3318 /* While #merge_type_itemf does a sanity check, this operation runs on all edit-mode objects.
3319 * Some of them may not have the expected selection state. */
3320 if (use_first == false) {
3321 if (!em->bm->selected.last || ((BMEditSelection *)em->bm->selected.last)->htype != BM_VERT) {
3322 return false;
3323 }
3324
3325 ese = static_cast<BMEditSelection *>(em->bm->selected.last);
3326 mergevert = (BMVert *)ese->ele;
3327 }
3328 else {
3329 if (!em->bm->selected.first || ((BMEditSelection *)em->bm->selected.first)->htype != BM_VERT) {
3330 return false;
3331 }
3332
3333 ese = static_cast<BMEditSelection *>(em->bm->selected.first);
3334 mergevert = (BMVert *)ese->ele;
3335 }
3336
3337 if (!BM_elem_flag_test(mergevert, BM_ELEM_SELECT)) {
3338 return false;
3339 }
3340
3341 if (use_uvmerge) {
3342 if (!EDBM_op_callf(
3343 em, wmop, "pointmerge_facedata verts=%hv vert_snap=%e", BM_ELEM_SELECT, mergevert))
3344 {
3345 return false;
3346 }
3347 }
3348
3349 if (!EDBM_op_callf(em, wmop, "pointmerge verts=%hv merge_co=%v", BM_ELEM_SELECT, mergevert->co))
3350 {
3351 return false;
3352 }
3353
3354 return true;
3355}
3356
3357static bool merge_target(BMEditMesh *em,
3358 Scene *scene,
3359 Object *ob,
3360 const bool use_cursor,
3361 const bool use_uvmerge,
3362 wmOperator *wmop)
3363{
3364 BMIter iter;
3365 BMVert *v;
3366 float co[3], cent[3] = {0.0f, 0.0f, 0.0f};
3367 const float *vco = nullptr;
3368
3369 if (use_cursor) {
3370 vco = scene->cursor.location;
3371 copy_v3_v3(co, vco);
3372 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
3373 mul_m4_v3(ob->world_to_object().ptr(), co);
3374 }
3375 else {
3376 float fac;
3377 int i = 0;
3378 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
3380 continue;
3381 }
3382 add_v3_v3(cent, v->co);
3383 i++;
3384 }
3385
3386 if (!i) {
3387 return false;
3388 }
3389
3390 fac = 1.0f / float(i);
3391 mul_v3_fl(cent, fac);
3392 copy_v3_v3(co, cent);
3393 vco = co;
3394 }
3395
3396 if (!vco) {
3397 return false;
3398 }
3399
3400 if (use_uvmerge) {
3401 if (!EDBM_op_callf(em, wmop, "average_vert_facedata verts=%hv", BM_ELEM_SELECT)) {
3402 return false;
3403 }
3404 }
3405
3406 if (!EDBM_op_callf(em, wmop, "pointmerge verts=%hv merge_co=%v", BM_ELEM_SELECT, co)) {
3407 return false;
3408 }
3409
3410 return true;
3411}
3412
3414{
3415 Scene *scene = CTX_data_scene(C);
3416 ViewLayer *view_layer = CTX_data_view_layer(C);
3418 scene, view_layer, CTX_wm_view3d(C));
3419 const int type = RNA_enum_get(op->ptr, "type");
3420 const bool uvs = RNA_boolean_get(op->ptr, "uvs");
3421
3422 for (Object *obedit : objects) {
3424
3425 if (em->bm->totvertsel == 0) {
3426 continue;
3427 }
3428
3430
3431 bool ok = false;
3432 switch (type) {
3433 case MESH_MERGE_CENTER:
3434 ok = merge_target(em, scene, obedit, false, uvs, op);
3435 break;
3436 case MESH_MERGE_CURSOR:
3437 ok = merge_target(em, scene, obedit, true, uvs, op);
3438 break;
3439 case MESH_MERGE_LAST:
3440 ok = merge_firstlast(em, false, uvs, op);
3441 break;
3442 case MESH_MERGE_FIRST:
3443 ok = merge_firstlast(em, true, uvs, op);
3444 break;
3446 ok = EDBM_op_callf(em, op, "collapse edges=%he uvs=%b", BM_ELEM_SELECT, uvs);
3447 break;
3448 default:
3449 BLI_assert(0);
3450 break;
3451 }
3452
3453 if (!ok) {
3454 continue;
3455 }
3456
3458
3460 params.calc_looptris = true;
3461 params.calc_normals = false;
3462 params.is_destructive = true;
3463 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
3464
3465 /* once collapsed, we can't have edge/face selection */
3466 if ((em->selectmode & SCE_SELECT_VERTEX) == 0) {
3468 }
3469 /* Only active object supported, see comment below. */
3471 break;
3472 }
3473 }
3474
3475 return OPERATOR_FINISHED;
3476}
3477
3479 {MESH_MERGE_CENTER, "CENTER", 0, "At Center", ""},
3480 {MESH_MERGE_CURSOR, "CURSOR", 0, "At Cursor", ""},
3481 {MESH_MERGE_COLLAPSE, "COLLAPSE", 0, "Collapse", ""},
3482 {MESH_MERGE_FIRST, "FIRST", 0, "At First", ""},
3483 {MESH_MERGE_LAST, "LAST", 0, "At Last", ""},
3484 {0, nullptr, 0, nullptr, nullptr},
3485};
3486
3488 PointerRNA * /*ptr*/,
3489 PropertyRNA * /*prop*/,
3490 bool *r_free)
3491{
3492 if (!C) { /* needed for docs */
3493 return merge_type_items;
3494 }
3495
3496 Object *obedit = CTX_data_edit_object(C);
3497 if (obedit && obedit->type == OB_MESH) {
3498 EnumPropertyItem *item = nullptr;
3499 int totitem = 0;
3501
3502 /* Keep these first so that their automatic shortcuts don't change. */
3506
3507 /* Only active object supported:
3508 * In practice it doesn't make sense to run this operation on non-active meshes
3509 * since selecting will activate - we could have a separate code-path for these but it's a
3510 * hassle for now just apply to the active (first) object. */
3511 if (em->selectmode & SCE_SELECT_VERTEX) {
3512 if (em->bm->selected.first && em->bm->selected.last &&
3513 ((BMEditSelection *)em->bm->selected.first)->htype == BM_VERT &&
3514 ((BMEditSelection *)em->bm->selected.last)->htype == BM_VERT)
3515 {
3518 }
3519 else if (em->bm->selected.first &&
3520 ((BMEditSelection *)em->bm->selected.first)->htype == BM_VERT)
3521 {
3523 }
3524 else if (em->bm->selected.last &&
3525 ((BMEditSelection *)em->bm->selected.last)->htype == BM_VERT)
3526 {
3528 }
3529 }
3530
3531 RNA_enum_item_end(&item, &totitem);
3532
3533 *r_free = true;
3534
3535 return item;
3536 }
3537
3538 /* Get all items e.g. when creating keymap item. */
3539 return merge_type_items;
3540}
3541
3543{
3544 /* identifiers */
3545 ot->name = "Merge";
3546 ot->description = "Merge selected vertices";
3547 ot->idname = "MESH_OT_merge";
3548
3549 /* API callbacks. */
3550 ot->exec = edbm_merge_exec;
3551 ot->invoke = WM_menu_invoke;
3552 ot->poll = ED_operator_editmesh;
3553
3554 /* flags */
3555 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3556
3557 /* properties */
3558 ot->prop = RNA_def_enum(
3559 ot->srna, "type", merge_type_items, MESH_MERGE_CENTER, "Type", "Merge method to use");
3561
3563
3564 RNA_def_boolean(ot->srna, "uvs", false, "UVs", "Move UVs according to merge");
3565}
3566
3568
3569/* -------------------------------------------------------------------- */
3572
3574{
3575 const float threshold = RNA_float_get(op->ptr, "threshold");
3576 const bool use_unselected = RNA_boolean_get(op->ptr, "use_unselected");
3577 const bool use_sharp_edge_from_normals = RNA_boolean_get(op->ptr, "use_sharp_edge_from_normals");
3578
3579 int count_multi = 0;
3580
3581 const Scene *scene = CTX_data_scene(C);
3582 ViewLayer *view_layer = CTX_data_view_layer(C);
3584 scene, view_layer, CTX_wm_view3d(C));
3585
3586 for (Object *obedit : objects) {
3588
3589 /* Selection used as target with 'use_unselected'. */
3590 if (em->bm->totvertsel == 0) {
3591 continue;
3592 }
3593
3594 BMOperator bmop;
3595 const int totvert_orig = em->bm->totvert;
3596
3597 /* avoid losing selection state (select -> tags) */
3598 char htype_select;
3599 if (em->selectmode & SCE_SELECT_VERTEX) {
3600 htype_select = BM_VERT;
3601 }
3602 else if (em->selectmode & SCE_SELECT_EDGE) {
3603 htype_select = BM_EDGE;
3604 }
3605 else {
3606 htype_select = BM_FACE;
3607 }
3608
3610
3611 /* store selection as tags */
3612 BM_mesh_elem_hflag_enable_test(em->bm, htype_select, BM_ELEM_TAG, true, true, BM_ELEM_SELECT);
3613
3614 if (use_unselected) {
3615 EDBM_automerge(obedit, false, BM_ELEM_SELECT, threshold);
3616 }
3617 else {
3618 EDBM_op_init(em, &bmop, op, "find_doubles verts=%hv dist=%f", BM_ELEM_SELECT, threshold);
3619
3620 BMO_op_exec(em->bm, &bmop);
3621
3622 if (!EDBM_op_callf(em,
3623 op,
3624 "weld_verts targetmap=%S use_centroid=%b",
3625 &bmop,
3626 "targetmap.out",
3627 RNA_boolean_get(op->ptr, "use_centroid")))
3628 {
3629 BMO_op_finish(em->bm, &bmop);
3630 continue;
3631 }
3632
3633 if (!EDBM_op_finish(em, &bmop, op, true)) {
3634 continue;
3635 }
3636 }
3637
3638 const int count = (totvert_orig - em->bm->totvert);
3639
3640 /* restore selection from tags */
3641 BM_mesh_elem_hflag_enable_test(em->bm, htype_select, BM_ELEM_SELECT, true, true, BM_ELEM_TAG);
3643
3644 BM_custom_loop_normals_from_vector_layer(em->bm, use_sharp_edge_from_normals);
3645
3646 if (count) {
3647 count_multi += count;
3649 params.calc_looptris = true;
3650 params.calc_normals = false;
3651 params.is_destructive = true;
3652 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
3653 }
3654 }
3655
3656 BKE_reportf(op->reports,
3657 RPT_INFO,
3658 count_multi == 1 ? RPT_("Removed %d vertex") : RPT_("Removed %d vertices"),
3659 count_multi);
3660
3661 return OPERATOR_FINISHED;
3662}
3663
3665{
3666 /* identifiers */
3667 ot->name = "Merge by Distance";
3668 ot->description = "Merge vertices based on their proximity";
3669 ot->idname = "MESH_OT_remove_doubles";
3670
3671 /* API callbacks. */
3673 ot->poll = ED_operator_editmesh;
3674
3675 /* flags */
3676 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3677
3679 "threshold",
3680 1e-4f,
3681 1e-6f,
3682 50.0f,
3683 "Merge Distance",
3684 "Maximum distance between elements to merge",
3685 1e-5f,
3686 10.0f);
3687 RNA_def_boolean(ot->srna,
3688 "use_centroid",
3689 true,
3690 "Centroid Merge",
3691 "Move vertices to the centroid of the duplicate cluster, "
3692 "otherwise the vertex closest to the centroid is used.");
3693
3694 RNA_def_boolean(ot->srna,
3695 "use_unselected",
3696 false,
3697 "Unselected",
3698 "Merge selected to other unselected vertices");
3699
3700 RNA_def_boolean(ot->srna,
3701 "use_sharp_edge_from_normals",
3702 false,
3703 "Sharp Edges",
3704 "Calculate sharp edges using custom normal data (when available)");
3705}
3706
3708
3709/* -------------------------------------------------------------------- */
3712
3713/* BMESH_TODO this should be properly encapsulated in a bmop. but later. */
3714static bool shape_propagate(BMEditMesh *em, bool use_symmetry)
3715{
3716 BMIter iter;
3717 BMVert *eve = nullptr;
3718 float *co;
3719 int totshape = CustomData_number_of_layers(&em->bm->vdata, CD_SHAPEKEY);
3720
3722 return false;
3723 }
3724
3725 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
3727 BMVert *mirr = use_symmetry ? EDBM_verts_mirror_get(em, eve) : nullptr;
3728
3729 if (!mirr || !BM_elem_flag_test(mirr, BM_ELEM_SELECT) ||
3731 {
3732 continue;
3733 }
3734 }
3735
3736 for (int i = 0; i < totshape; i++) {
3737 co = static_cast<float *>(
3739 copy_v3_v3(co, eve->co);
3740 }
3741 }
3742 return true;
3743}
3744
3746{
3747 const Scene *scene = CTX_data_scene(C);
3748 ViewLayer *view_layer = CTX_data_view_layer(C);
3749 int tot_shapekeys = 0;
3750 int tot_selected_verts_objects = 0;
3751 int tot_locked = 0;
3752
3754 scene, view_layer, CTX_wm_view3d(C));
3755 for (Object *obedit : objects) {
3756 Mesh *mesh = static_cast<Mesh *>(obedit->data);
3757 BMEditMesh *em = mesh->runtime->edit_mesh.get();
3758
3759 if (em->bm->totvertsel == 0) {
3760 continue;
3761 }
3762
3763 /* Check for locked shape keys. */
3765 tot_locked++;
3766 continue;
3767 }
3768
3769 tot_selected_verts_objects++;
3770
3771 const bool use_symmetry = (mesh->symmetry & ME_SYMMETRY_X) != 0;
3772
3773 if (use_symmetry) {
3774 const bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
3775
3776 EDBM_verts_mirror_cache_begin(em, 0, false, false, false, use_topology);
3777 }
3778
3779 if (shape_propagate(em, use_symmetry)) {
3780 tot_shapekeys++;
3781 }
3782
3783 if (use_symmetry) {
3785 }
3786
3788 params.calc_looptris = false;
3789 params.calc_normals = false;
3790 params.is_destructive = false;
3791 EDBM_update(mesh, &params);
3792 }
3793
3794 if (tot_selected_verts_objects == 0) {
3795 if (!tot_locked) {
3796 BKE_report(op->reports, RPT_ERROR, "No selected vertex");
3797 }
3798 return OPERATOR_CANCELLED;
3799 }
3800 if (tot_shapekeys == 0) {
3801 BKE_report(op->reports, RPT_ERROR, "Mesh(es) do not have shape keys");
3802 return OPERATOR_CANCELLED;
3803 }
3804
3805 return OPERATOR_FINISHED;
3806}
3807
3809{
3810 /* identifiers */
3811 ot->name = "Shape Propagate";
3812 ot->description = "Apply selected vertex locations to all other shape keys";
3813 ot->idname = "MESH_OT_shape_propagate_to_all";
3814
3815 /* API callbacks. */
3817 ot->poll = ED_operator_editmesh;
3818
3819 /* flags */
3820 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3821}
3822
3824
3825/* -------------------------------------------------------------------- */
3828
3829/* BMESH_TODO this should be properly encapsulated in a bmop. but later. */
3831{
3832 Object *obedit_ref = CTX_data_edit_object(C);
3833 Mesh *me_ref = static_cast<Mesh *>(obedit_ref->data);
3834 Key *key_ref = me_ref->key;
3835 KeyBlock *kb_ref = nullptr;
3836 BMEditMesh *em_ref = me_ref->runtime->edit_mesh.get();
3837 BMVert *eve;
3838 BMIter iter;
3839 const Scene *scene = CTX_data_scene(C);
3840 ViewLayer *view_layer = CTX_data_view_layer(C);
3841 float co[3], *sco;
3842 int totshape_ref = 0;
3843
3844 const float blend = RNA_float_get(op->ptr, "blend");
3845 int shape_ref = RNA_enum_get(op->ptr, "shape");
3846 const bool use_add = RNA_boolean_get(op->ptr, "add");
3847
3848 /* Sanity check. */
3849 totshape_ref = CustomData_number_of_layers(&em_ref->bm->vdata, CD_SHAPEKEY);
3850
3851 if (totshape_ref == 0 || shape_ref < 0) {
3852 BKE_report(op->reports, RPT_ERROR, "Active mesh does not have shape keys");
3853 return OPERATOR_CANCELLED;
3854 }
3855 if (shape_ref >= totshape_ref) {
3856 /* This case occurs if operator was used before on object with more keys than current one. */
3857 shape_ref = 0; /* default to basis */
3858 }
3859
3860 /* Get shape key - needed for finding reference shape (for add mode only). */
3861 if (key_ref) {
3862 kb_ref = static_cast<KeyBlock *>(BLI_findlink(&key_ref->block, shape_ref));
3863 }
3864
3865 int tot_selected_verts_objects = 0, tot_locked = 0;
3867 scene, view_layer, CTX_wm_view3d(C));
3868 for (Object *obedit : objects) {
3869 Mesh *mesh = static_cast<Mesh *>(obedit->data);
3870 Key *key = mesh->key;
3871 KeyBlock *kb = nullptr;
3872 BMEditMesh *em = mesh->runtime->edit_mesh.get();
3873 int shape;
3874
3875 if (em->bm->totvertsel == 0) {
3876 continue;
3877 }
3878
3880 tot_locked++;
3881 continue;
3882 }
3883
3884 tot_selected_verts_objects++;
3885
3886 if (!key) {
3887 continue;
3888 }
3889 kb = BKE_keyblock_find_name(key, kb_ref->name);
3890 shape = BLI_findindex(&key->block, kb);
3891
3892 if (kb) {
3893 const bool use_symmetry = (mesh->symmetry & ME_SYMMETRY_X) != 0;
3894
3895 if (use_symmetry) {
3896 const bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
3897
3898 EDBM_verts_mirror_cache_begin(em, 0, false, true, false, use_topology);
3899 }
3900
3901 /* Perform blending on selected vertices. */
3902 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
3904 continue;
3905 }
3906
3907 /* Get coordinates of shapekey we're blending from. */
3908 sco = static_cast<float *>(
3909 CustomData_bmesh_get_n(&em->bm->vdata, eve->head.data, CD_SHAPEKEY, shape));
3910 copy_v3_v3(co, sco);
3911
3912 if (use_add) {
3913 /* In add mode, we add relative shape key offset. */
3914 const float *rco = static_cast<const float *>(
3916 sub_v3_v3v3(co, co, rco);
3917
3918 madd_v3_v3fl(eve->co, co, blend);
3919 }
3920 else {
3921 /* In blend mode, we interpolate to the shape key. */
3922 interp_v3_v3v3(eve->co, eve->co, co, blend);
3923 }
3924 }
3925
3926 if (use_symmetry) {
3929 }
3930
3932 params.calc_looptris = true;
3933 params.calc_normals = true;
3934 params.is_destructive = false;
3935 EDBM_update(mesh, &params);
3936 }
3937 }
3938
3939 if (tot_selected_verts_objects == 0 && !tot_locked) {
3940 BKE_report(op->reports, RPT_ERROR, "No selected vertex");
3941 }
3942
3943 return tot_selected_verts_objects ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
3944}
3945
3947 PointerRNA * /*ptr*/,
3948 PropertyRNA * /*prop*/,
3949 bool *r_free)
3950{
3951 Object *obedit = CTX_data_edit_object(C);
3952 BMEditMesh *em;
3953 EnumPropertyItem *item = nullptr;
3954 int totitem = 0;
3955
3956 if ((obedit && obedit->type == OB_MESH) && (em = BKE_editmesh_from_object(obedit)) &&
3958 {
3959 EnumPropertyItem tmp = {0, "", 0, "", ""};
3960 int a;
3961
3962 for (a = 0; a < em->bm->vdata.totlayer; a++) {
3963 if (em->bm->vdata.layers[a].type != CD_SHAPEKEY) {
3964 continue;
3965 }
3966
3967 tmp.value = totitem;
3968 tmp.identifier = em->bm->vdata.layers[a].name;
3969 tmp.name = em->bm->vdata.layers[a].name;
3970 /* RNA_enum_item_add sets totitem itself! */
3971 RNA_enum_item_add(&item, &totitem, &tmp);
3972 }
3973 }
3974
3975 RNA_enum_item_end(&item, &totitem);
3976 *r_free = true;
3977
3978 return item;
3979}
3980
3982{
3983 uiLayout *layout = op->layout;
3984 Object *obedit = CTX_data_edit_object(C);
3985 Mesh *mesh = static_cast<Mesh *>(obedit->data);
3986
3987 PointerRNA ptr_key = RNA_id_pointer_create((ID *)mesh->key);
3988
3989 layout->use_property_split_set(true);
3990 layout->use_property_decorate_set(false);
3991
3992 layout->prop_search(op->ptr, "shape", &ptr_key, "key_blocks", std::nullopt, ICON_SHAPEKEY_DATA);
3993 layout->prop(op->ptr, "blend", UI_ITEM_NONE, std::nullopt, ICON_NONE);
3994 layout->prop(op->ptr, "add", UI_ITEM_NONE, std::nullopt, ICON_NONE);
3995}
3996
3998{
3999 PropertyRNA *prop;
4000
4001 /* identifiers */
4002 ot->name = "Blend from Shape";
4003 ot->description = "Blend in shape from a shape key";
4004 ot->idname = "MESH_OT_blend_from_shape";
4005
4006 /* API callbacks. */
4008 /* disable because search popup closes too easily */
4009 // ot->invoke = WM_operator_props_popup_call;
4011 ot->poll = ED_operator_editmesh;
4012
4013 /* flags */
4014 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4015
4016 /* properties */
4017 prop = RNA_def_enum(
4018 ot->srna, "shape", rna_enum_dummy_NULL_items, 0, "Shape", "Shape key to use for blending");
4021 RNA_def_float(ot->srna, "blend", 1.0f, -1e3f, 1e3f, "Blend", "Blending factor", -2.0f, 2.0f);
4022 RNA_def_boolean(ot->srna, "add", true, "Add", "Add rather than blend between shapes");
4023}
4024
4026
4027/* -------------------------------------------------------------------- */
4030
4032{
4033 const float thickness = RNA_float_get(op->ptr, "thickness");
4034
4035 const Scene *scene = CTX_data_scene(C);
4036 ViewLayer *view_layer = CTX_data_view_layer(C);
4038 scene, view_layer, CTX_wm_view3d(C));
4039 for (Object *obedit : objects) {
4041 BMesh *bm = em->bm;
4042
4043 if (em->bm->totfacesel == 0) {
4044 continue;
4045 }
4046
4047 BMOperator bmop;
4048
4049 if (!EDBM_op_init(em, &bmop, op, "solidify geom=%hf thickness=%f", BM_ELEM_SELECT, thickness))
4050 {
4051 continue;
4052 }
4053
4054 /* deselect only the faces in the region to be solidified (leave wire
4055 * edges and loose verts selected, as there will be no corresponding
4056 * geometry selected below) */
4058
4059 /* run the solidify operator */
4060 BMO_op_exec(bm, &bmop);
4061
4062 /* select the newly generated faces */
4064
4065 /* No need to flush the selection, any selection history is no longer valid. */
4067
4068 if (!EDBM_op_finish(em, &bmop, op, true)) {
4069 continue;
4070 }
4071
4073 params.calc_looptris = true;
4074 params.calc_normals = false;
4075 params.is_destructive = true;
4076 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
4077 }
4078
4079 return OPERATOR_FINISHED;
4080}
4081
4083{
4084 PropertyRNA *prop;
4085 /* identifiers */
4086 ot->name = "Solidify";
4087 ot->description = "Create a solid skin by extruding, compensating for sharp angles";
4088 ot->idname = "MESH_OT_solidify";
4089
4090 /* API callbacks. */
4091 ot->exec = edbm_solidify_exec;
4092 ot->poll = ED_operator_editmesh;
4093
4094 /* flags */
4095 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4096
4098 ot->srna, "thickness", 0.01f, -1e4f, 1e4f, "Thickness", "", -10.0f, 10.0f);
4099 RNA_def_property_ui_range(prop, -10.0, 10.0, 0.1, 4);
4100}
4101
4103
4104/* -------------------------------------------------------------------- */
4107
4108enum {
4112};
4113
4116 Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
4117{
4118 Object *obedit = base_old->object;
4119 BMeshCreateParams create_params{};
4120 create_params.use_toolflags = true;
4121 BMesh *bm_new = BM_mesh_create(&bm_mesh_allocsize_default, &create_params);
4122 BM_mesh_elem_toolflags_ensure(bm_new); /* Needed for 'duplicate' BMO. */
4123
4125
4126 /* Take into account user preferences for duplicating actions. */
4127 const eDupli_ID_Flags dupflag = eDupli_ID_Flags(USER_DUP_MESH | (U.dupflag & USER_DUP_ACT));
4128 Base *base_new = blender::ed::object::add_duplicate(bmain, scene, view_layer, base_old, dupflag);
4129
4130 /* normally would call directly after but in this case delay recalc */
4131 // DAG_relations_tag_update(bmain);
4132
4133 /* new in 2.5 */
4135 base_new->object,
4138 false);
4139
4141
4142 BMO_op_callf(bm_old,
4144 "duplicate geom=%hvef dest=%p",
4146 bm_new);
4147 BMO_op_callf(bm_old,
4149 "delete geom=%hvef context=%i",
4151 DEL_FACES);
4152
4153 /* deselect loose data - this used to get deleted,
4154 * we could de-select edges and verts only, but this turns out to be less complicated
4155 * since de-selecting all skips selection flushing logic */
4157
4158 BM_mesh_normals_update(bm_new);
4159
4160 BMeshToMeshParams to_mesh_params{};
4161 BM_mesh_bm_to_me(bmain, bm_new, static_cast<Mesh *>(base_new->object->data), &to_mesh_params);
4162
4163 BM_mesh_free(bm_new);
4164 ((Mesh *)base_new->object->data)->runtime->edit_mesh = nullptr;
4165
4166 return base_new;
4167}
4168
4170 Scene *scene,
4171 ViewLayer *view_layer,
4172 Base *base_old,
4173 BMesh *bm_old,
4174 BMVert **verts,
4175 uint verts_len,
4176 BMEdge **edges,
4177 uint edges_len,
4178 BMFace **faces,
4179 uint faces_len)
4180{
4181 BMAllocTemplate bm_new_allocsize{};
4182 bm_new_allocsize.totvert = verts_len;
4183 bm_new_allocsize.totedge = edges_len;
4184 bm_new_allocsize.totloop = faces_len * 3;
4185 bm_new_allocsize.totface = faces_len;
4186
4187 const bool use_custom_normals = (bm_old->lnor_spacearr != nullptr);
4188
4189 Object *obedit = base_old->object;
4190
4191 BMeshCreateParams create_params{};
4192 BMesh *bm_new = BM_mesh_create(&bm_new_allocsize, &create_params);
4193
4194 if (use_custom_normals) {
4195 /* Needed so the temporary normal layer is copied too. */
4196 BM_mesh_copy_init_customdata_all_layers(bm_new, bm_old, BM_ALL, &bm_new_allocsize);
4197 }
4198 else {
4199 BM_mesh_copy_init_customdata(bm_new, bm_old, &bm_new_allocsize);
4200 }
4201
4202 /* Take into account user preferences for duplicating actions. */
4203 const eDupli_ID_Flags dupflag = eDupli_ID_Flags(USER_DUP_MESH | (U.dupflag & USER_DUP_ACT));
4204 Base *base_new = blender::ed::object::add_duplicate(bmain, scene, view_layer, base_old, dupflag);
4205
4206 /* normally would call directly after but in this case delay recalc */
4207 // DAG_relations_tag_update(bmain);
4208
4209 /* new in 2.5 */
4211 base_new->object,
4214 false);
4215
4217
4218 BM_mesh_copy_arrays(bm_old, bm_new, verts, verts_len, edges, edges_len, faces, faces_len);
4219
4220 if (use_custom_normals) {
4222 }
4223
4224 for (uint i = 0; i < verts_len; i++) {
4225 BM_vert_kill(bm_old, verts[i]);
4226 }
4227 BMeshToMeshParams to_mesh_params{};
4228 BM_mesh_bm_to_me(bmain, bm_new, static_cast<Mesh *>(base_new->object->data), &to_mesh_params);
4229
4230 BM_mesh_free(bm_new);
4231 ((Mesh *)base_new->object->data)->runtime->edit_mesh = nullptr;
4232
4233 return base_new;
4234}
4235
4237 Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
4238{
4239 /* we may have tags from previous operators */
4241
4242 /* sel -> tag */
4244 bm_old, BM_FACE | BM_EDGE | BM_VERT, BM_ELEM_TAG, true, false, BM_ELEM_SELECT);
4245
4246 return (mesh_separate_tagged(bmain, scene, view_layer, base_old, bm_old) != nullptr);
4247}
4248
4255static void mesh_separate_material_assign_mat_nr(Main *bmain, Object *ob, const short mat_nr)
4256{
4257 ID *obdata = static_cast<ID *>(ob->data);
4258
4259 const short *totcolp = BKE_id_material_len_p(obdata);
4260 Material ***matarar = BKE_id_material_array_p(obdata);
4261
4262 if ((totcolp && matarar) == 0) {
4263 BLI_assert(0);
4264 return;
4265 }
4266
4267 if (*totcolp) {
4268 Material *ma_ob;
4269 Material *ma_obdata;
4270 char matbit;
4271
4272 if (mat_nr < ob->totcol) {
4273 ma_ob = ob->mat[mat_nr];
4274 matbit = ob->matbits[mat_nr];
4275 }
4276 else {
4277 ma_ob = nullptr;
4278 matbit = 0;
4279 }
4280
4281 if (mat_nr < *totcolp) {
4282 ma_obdata = (*matarar)[mat_nr];
4283 }
4284 else {
4285 ma_obdata = nullptr;
4286 }
4287
4288 BKE_id_material_clear(bmain, obdata);
4289 BKE_id_material_resize(bmain, obdata, 1, true);
4291
4292 ob->mat[0] = ma_ob;
4293 id_us_plus((ID *)ma_ob);
4294 ob->matbits[0] = matbit;
4295 (*matarar)[0] = ma_obdata;
4296 id_us_plus((ID *)ma_obdata);
4297 }
4298 else {
4299 BKE_id_material_clear(bmain, obdata);
4300 }
4301}
4302
4304 Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
4305{
4306 BMFace *f_cmp, *f;
4307 BMIter iter;
4308 bool result = false;
4309
4310 while ((f_cmp = static_cast<BMFace *>(BM_iter_at_index(bm_old, BM_FACES_OF_MESH, nullptr, 0)))) {
4311 Base *base_new;
4312 const short mat_nr = f_cmp->mat_nr;
4313 int tot = 0;
4314
4316
4317 BM_ITER_MESH (f, &iter, bm_old, BM_FACES_OF_MESH) {
4318 if (f->mat_nr == mat_nr) {
4319 BMLoop *l_iter;
4320 BMLoop *l_first;
4321
4323 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
4324 do {
4327 } while ((l_iter = l_iter->next) != l_first);
4328
4329 tot++;
4330 }
4331 }
4332
4333 /* leave the current object with some materials */
4334 if (tot == bm_old->totface) {
4335 mesh_separate_material_assign_mat_nr(bmain, base_old->object, mat_nr);
4336
4337 /* since we're in editmode, must set faces here */
4338 BM_ITER_MESH (f, &iter, bm_old, BM_FACES_OF_MESH) {
4339 f->mat_nr = 0;
4340 }
4341 break;
4342 }
4343
4344 /* Move selection into a separate object */
4345 base_new = mesh_separate_tagged(bmain, scene, view_layer, base_old, bm_old);
4346 if (base_new) {
4347 mesh_separate_material_assign_mat_nr(bmain, base_new->object, mat_nr);
4348 }
4349
4350 result |= (base_new != nullptr);
4351 }
4352
4353 return result;
4354}
4355
4357 Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
4358{
4359 /* Without this, we duplicate the object mode mesh for each loose part.
4360 * This can get very slow especially for large meshes with many parts
4361 * which would duplicate the mesh on entering edit-mode. */
4362 const bool clear_object_data = true;
4363
4364 bool result = false;
4365
4366 blender::Array<BMVert *> vert_groups(bm_old->totvert);
4367 blender::Array<BMEdge *> edge_groups(bm_old->totedge);
4368 blender::Array<BMFace *> face_groups(bm_old->totface);
4369
4370 int (*groups)[3] = nullptr;
4371 int groups_len = BM_mesh_calc_edge_groups_as_arrays(
4372 bm_old, vert_groups.data(), edge_groups.data(), face_groups.data(), &groups);
4373 if (groups_len <= 1) {
4374 MEM_SAFE_FREE(groups);
4375 return false;
4376 }
4377
4378 if (clear_object_data) {
4379 ED_mesh_geometry_clear(static_cast<Mesh *>(base_old->object->data));
4380 }
4381
4383
4384 /* Separate out all groups except the first. */
4385 uint group_ofs[3] = {uint(groups[0][0]), uint(groups[0][1]), uint(groups[0][2])};
4386 for (int i = 1; i < groups_len; i++) {
4387 Base *base_new = mesh_separate_arrays(bmain,
4388 scene,
4389 view_layer,
4390 base_old,
4391 bm_old,
4392 vert_groups.data() + group_ofs[0],
4393 groups[i][0],
4394 edge_groups.data() + group_ofs[1],
4395 groups[i][1],
4396 face_groups.data() + group_ofs[2],
4397 groups[i][2]);
4398 result |= (base_new != nullptr);
4399
4400 group_ofs[0] += groups[i][0];
4401 group_ofs[1] += groups[i][1];
4402 group_ofs[2] += groups[i][2];
4403 }
4404
4405 Mesh *me_old = static_cast<Mesh *>(base_old->object->data);
4407
4408 if (clear_object_data) {
4409 BMeshToMeshParams to_mesh_params{};
4410 to_mesh_params.update_shapekey_indices = true;
4411 BM_mesh_bm_to_me(nullptr, bm_old, me_old, &to_mesh_params);
4412 }
4413
4414 MEM_freeN(groups);
4415 return result;
4416}
4417
4419{
4420 Main *bmain = CTX_data_main(C);
4421 Scene *scene = CTX_data_scene(C);
4422 ViewLayer *view_layer = CTX_data_view_layer(C);
4423 const int type = RNA_enum_get(op->ptr, "type");
4424 bool changed_multi = false;
4425
4426 if (ED_operator_editmesh(C)) {
4427 uint empty_selection_len = 0;
4429 scene, view_layer, CTX_wm_view3d(C));
4430 for (const int base_index : bases.index_range()) {
4431 Base *base = bases[base_index];
4433
4434 if (type == 0) {
4435 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
4436 /* when all objects has no selection */
4437 if (++empty_selection_len == bases.size()) {
4438 BKE_report(op->reports, RPT_ERROR, "Nothing selected");
4439 }
4440 continue;
4441 }
4442 }
4443
4444 /* editmode separate */
4445 bool changed = false;
4446 switch (type) {
4448 changed = mesh_separate_selected(bmain, scene, view_layer, base, em->bm);
4449 break;
4451 changed = mesh_separate_material(bmain, scene, view_layer, base, em->bm);
4452 break;
4454 changed = mesh_separate_loose(bmain, scene, view_layer, base, em->bm);
4455 break;
4456 default:
4457 BLI_assert(0);
4458 break;
4459 }
4460
4461 if (changed) {
4463 params.calc_looptris = true;
4464 params.calc_normals = false;
4465 params.is_destructive = true;
4466 EDBM_update(static_cast<Mesh *>(base->object->data), &params);
4467 }
4468 changed_multi |= changed;
4469 }
4470 }
4471 else {
4472 if (type == MESH_SEPARATE_SELECTED) {
4473 BKE_report(op->reports, RPT_ERROR, "Selection not supported in object mode");
4474 return OPERATOR_CANCELLED;
4475 }
4476
4477 /* object mode separate */
4478 CTX_DATA_BEGIN (C, Base *, base_iter, selected_editable_bases) {
4479 Object *ob = base_iter->object;
4480 if (ob->type != OB_MESH) {
4481 continue;
4482 }
4483 Mesh *mesh = static_cast<Mesh *>(ob->data);
4484 if (!BKE_id_is_editable(bmain, &mesh->id)) {
4485 continue;
4486 }
4487
4488 BMeshCreateParams create_params{};
4489 create_params.use_toolflags = true;
4490 BMesh *bm_old = BM_mesh_create(&bm_mesh_allocsize_default, &create_params);
4491
4492 BMeshFromMeshParams from_mesh_params{};
4493 BM_mesh_bm_from_me(bm_old, mesh, &from_mesh_params);
4494
4495 bool changed = false;
4496 switch (type) {
4498 changed = mesh_separate_material(bmain, scene, view_layer, base_iter, bm_old);
4499 break;
4501 changed = mesh_separate_loose(bmain, scene, view_layer, base_iter, bm_old);
4502 break;
4503 default:
4504 BLI_assert(0);
4505 break;
4506 }
4507
4508 if (changed) {
4509 BMeshToMeshParams to_mesh_params{};
4510 to_mesh_params.calc_object_remap = true;
4511 BM_mesh_bm_to_me(bmain, bm_old, mesh, &to_mesh_params);
4512
4515 }
4516
4517 BM_mesh_free(bm_old);
4518
4519 changed_multi |= changed;
4520 }
4522 }
4523
4524 if (changed_multi) {
4525 /* delay depsgraph recalc until all objects are duplicated */
4529
4530 return OPERATOR_FINISHED;
4531 }
4532
4533 return OPERATOR_CANCELLED;
4534}
4535
4537{
4538 static const EnumPropertyItem prop_separate_types[] = {
4539 {MESH_SEPARATE_SELECTED, "SELECTED", 0, "Selection", ""},
4540 {MESH_SEPARATE_MATERIAL, "MATERIAL", 0, "By Material", ""},
4541 {MESH_SEPARATE_LOOSE, "LOOSE", 0, "By Loose Parts", ""},
4542 {0, nullptr, 0, nullptr, nullptr},
4543 };
4544
4545 /* identifiers */
4546 ot->name = "Separate";
4547 ot->description = "Separate selected geometry into a new mesh";
4548 ot->idname = "MESH_OT_separate";
4549
4550 /* API callbacks. */
4551 ot->invoke = WM_menu_invoke;
4552 ot->exec = edbm_separate_exec;
4553 ot->poll = ED_operator_scene_editable; /* object and editmode */
4554
4555 /* flags */
4556 ot->flag = OPTYPE_UNDO;
4557
4558 ot->prop = RNA_def_enum(
4559 ot->srna, "type", prop_separate_types, MESH_SEPARATE_SELECTED, "Type", "");
4560}
4561
4563
4564/* -------------------------------------------------------------------- */
4567
4569{
4570 const bool use_beauty = RNA_boolean_get(op->ptr, "use_beauty");
4571
4572 bool has_selected_edges = false, has_faces_filled = false;
4573
4574 const Scene *scene = CTX_data_scene(C);
4575 ViewLayer *view_layer = CTX_data_view_layer(C);
4577 scene, view_layer, CTX_wm_view3d(C));
4578 for (Object *obedit : objects) {
4580
4581 const int totface_orig = em->bm->totface;
4582
4583 if (em->bm->totedgesel == 0) {
4584 continue;
4585 }
4586 has_selected_edges = true;
4587
4588 BMOperator bmop;
4589 if (!EDBM_op_init(
4590 em, &bmop, op, "triangle_fill edges=%he use_beauty=%b", BM_ELEM_SELECT, use_beauty))
4591 {
4592 continue;
4593 }
4594
4595 BMO_op_exec(em->bm, &bmop);
4596
4597 /* cancel if nothing was done */
4598 if (totface_orig == em->bm->totface) {
4599 EDBM_op_finish(em, &bmop, op, true);
4600 continue;
4601 }
4602 has_faces_filled = true;
4603
4604 /* select new geometry */
4606 em->bm, bmop.slots_out, "geom.out", BM_FACE | BM_EDGE, BM_ELEM_SELECT, true);
4607
4608 if (!EDBM_op_finish(em, &bmop, op, true)) {
4609 continue;
4610 }
4611
4613 params.calc_looptris = true;
4614 params.calc_normals = false;
4615 params.is_destructive = true;
4616 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
4617 }
4618
4619 if (!has_selected_edges) {
4620 BKE_report(op->reports, RPT_ERROR, "No edges selected");
4621 return OPERATOR_CANCELLED;
4622 }
4623
4624 if (!has_faces_filled) {
4625 BKE_report(op->reports, RPT_WARNING, "No faces filled");
4626 return OPERATOR_CANCELLED;
4627 }
4628
4629 return OPERATOR_FINISHED;
4630}
4631
4633{
4634 /* identifiers */
4635 ot->name = "Fill";
4636 ot->idname = "MESH_OT_fill";
4637 ot->description = "Fill a selected edge loop with faces";
4638 ot->translation_context = BLT_I18NCONTEXT_ID_MESH;
4639
4640 /* API callbacks. */
4641 ot->exec = edbm_fill_exec;
4642 ot->poll = ED_operator_editmesh;
4643
4644 /* flags */
4645 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4646
4647 RNA_def_boolean(ot->srna, "use_beauty", true, "Beauty", "Use best triangulation division");
4648}
4649
4651
4652/* -------------------------------------------------------------------- */
4655
4656static bool bm_edge_test_fill_grid_cb(BMEdge *e, void * /*bm_v*/)
4657{
4659}
4660
4662{
4663 BMIter iter;
4664 BMEdge *e_iter;
4665 BMVert *v_pair[2];
4666 int i = 0;
4667 BM_ITER_ELEM (e_iter, &iter, v, BM_EDGES_OF_VERT) {
4668 if (BM_elem_flag_test(e_iter, BM_ELEM_TAG)) {
4669 v_pair[i++] = BM_edge_other_vert(e_iter, v);
4670 }
4671 }
4672 BLI_assert(i == 2);
4673
4674 return fabsf(float(M_PI) - angle_v3v3v3(v_pair[0]->co, v->co, v_pair[1]->co));
4675}
4676
4680static bool edbm_fill_grid_prepare(BMesh *bm, int offset, int *span_p, const bool span_calc)
4681{
4682 /* angle differences below this value are considered 'even'
4683 * in that they shouldn't be used to calculate corners used for the 'span' */
4684 const float eps_even = 1e-3f;
4685 BMEdge *e;
4686 BMIter iter;
4687 int count;
4688 int span = *span_p;
4689
4690 ListBase eloops = {nullptr};
4691 BMEdgeLoopStore *el_store;
4692 // LinkData *el_store;
4693
4695 el_store = static_cast<BMEdgeLoopStore *>(eloops.first);
4696
4697 if (count != 1) {
4698 /* Let the operator use the selection flags,
4699 * most likely failing with an error in this case. */
4700 BM_mesh_edgeloops_free(&eloops);
4701 return false;
4702 }
4703
4704 /* Only tag edges that are part of a loop. */
4705 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
4707 }
4708 const int verts_len = BM_edgeloop_length_get(el_store);
4709 const int edges_len = verts_len - (BM_edgeloop_is_closed(el_store) ? 0 : 1);
4710 BMEdge **edges = MEM_malloc_arrayN<BMEdge *>(edges_len, __func__);
4711 BM_edgeloop_edges_get(el_store, edges);
4712 for (int i = 0; i < edges_len; i++) {
4714 }
4715
4716 if (span_calc) {
4717 span = verts_len / 4;
4718 }
4719 else {
4720 span = min_ii(span, (verts_len / 2) - 1);
4721 }
4722 offset = mod_i(offset, verts_len);
4723
4724 if ((count == 1) && ((verts_len & 1) == 0) && (verts_len == edges_len)) {
4725
4726 /* be clever! detect 2 edge loops from one closed edge loop */
4729 LinkData *v_act_link;
4730 int i;
4731
4732 if (v_act && (v_act_link = static_cast<LinkData *>(
4733 BLI_findptr(verts, v_act, offsetof(LinkData, data)))))
4734 {
4735 /* pass */
4736 }
4737 else {
4738 /* find the vertex with the best angle (a corner vertex) */
4739 LinkData *v_link_best = nullptr;
4740 float angle_best = -1.0f;
4741 LISTBASE_FOREACH (LinkData *, v_link, verts) {
4742 const float angle = edbm_fill_grid_vert_tag_angle(static_cast<BMVert *>(v_link->data));
4743 if ((angle > angle_best) || (v_link_best == nullptr)) {
4744 angle_best = angle;
4745 v_link_best = v_link;
4746 }
4747 }
4748
4749 v_act_link = v_link_best;
4750 v_act = static_cast<BMVert *>(v_act_link->data);
4751 }
4752
4753 /* set this vertex first */
4754 BLI_listbase_rotate_first(verts, v_act_link);
4755
4756 if (offset != 0) {
4757 v_act_link = static_cast<LinkData *>(BLI_findlink(verts, offset));
4758 v_act = static_cast<BMVert *>(v_act_link->data);
4759 BLI_listbase_rotate_first(verts, v_act_link);
4760 }
4761
4762 /* Run again to update the edge order from the rotated vertex list. */
4763 BM_edgeloop_edges_get(el_store, edges);
4764
4765 if (span_calc) {
4766 /* calculate the span by finding the next corner in 'verts'
4767 * we don't know what defines a corner exactly so find the 4 verts
4768 * in the loop with the greatest angle.
4769 * Tag them and use the first tagged vertex to calculate the span.
4770 *
4771 * NOTE: we may have already checked 'edbm_fill_grid_vert_tag_angle()' on each
4772 * vert, but advantage of de-duplicating is minimal. */
4773 SortPtrByFloat *ele_sort = MEM_malloc_arrayN<SortPtrByFloat>(verts_len, __func__);
4774 LinkData *v_link;
4775 for (v_link = static_cast<LinkData *>(verts->first), i = 0; v_link;
4776 v_link = v_link->next, i++)
4777 {
4778 BMVert *v = static_cast<BMVert *>(v_link->data);
4779 const float angle = edbm_fill_grid_vert_tag_angle(v);
4780 ele_sort[i].sort_value = angle;
4781 ele_sort[i].data = v_link;
4782
4783 /* Do not allow the best corner or the diagonally opposite corner to be detected. */
4784 if (ELEM(i, 0, verts_len / 2)) {
4785 ele_sort[i].sort_value = 0;
4786 }
4787 }
4788
4789 qsort(ele_sort, verts_len, sizeof(*ele_sort), BLI_sortutil_cmp_float_reverse);
4790
4791 /* Check that we have at least 3 corners.
4792 * The excluded corners are the last and second from last elements (both reset to 0).
4793 * The best remaining corner is `ele_sort[0]`
4794 * if the angle on the best remaining corner is roughly the same as the third-last,
4795 * then we can't calculate 3+ corners - fallback to the even span. */
4796 if ((ele_sort[0].sort_value - ele_sort[verts_len - 3].sort_value) > eps_even) {
4797 span = BLI_findindex(verts, ele_sort[0].data);
4798 }
4799 MEM_freeN(ele_sort);
4800 }
4801 /* end span calc */
4802 int start = 0;
4803
4804 /* The algorithm needs to iterate the shorter distance, between the best and second best vert.
4805 * If the second best vert is near the beginning of the loop, it starts at 0 and walks forward.
4806 * If, instead, the second best vert is near the end of the loop, then it starts at the second
4807 * best vertex and walks to the end of the loop. */
4808 if (span > verts_len / 2) {
4809 span = (verts_len)-span;
4810 start = (verts_len / 2) - span;
4811 }
4812
4813 /* un-flag 'rails' */
4814 for (i = start; i < start + span; i++) {
4816 BM_elem_flag_disable(edges[(verts_len / 2) + i], BM_ELEM_TAG);
4817 }
4818 }
4819 /* else let the bmesh-operator handle it */
4820
4821 BM_mesh_edgeloops_free(&eloops);
4822 MEM_freeN(edges);
4823
4824 *span_p = span;
4825
4826 return true;
4827}
4828
4833
4847{
4848 FillGridSplitJoin *split_join = MEM_callocN<FillGridSplitJoin>(__func__);
4849
4850 /* Split the selection into an island. */
4851 BMOperator split_op;
4852 BMO_op_init(em->bm, &split_op, 0, "split");
4854 em->bm, &split_op, split_op.slots_in, "geom", BM_FACE | BM_EDGE | BM_VERT, BM_ELEM_SELECT);
4855 BMO_op_exec(em->bm, &split_op);
4856
4857 /* Setup the weld op that will undo the split.
4858 * Switch the selection to the corresponding edges on the island instead of the edges around the
4859 * hole, so fill_grid will interpolate using the face and loop data from the island.
4860 * Also create a new map for the weld, which maps pairs of verts instead of pairs of edges. */
4861 BMO_op_init(em->bm, &split_join->weld_op, 0, "weld_verts");
4862 BMOpSlot *weld_target_map = BMO_slot_get(split_join->weld_op.slots_in, "targetmap");
4863 BMOIter siter;
4864 BMEdge *e;
4865 BMO_ITER (e, &siter, split_op.slots_out, "boundary_map.out", 0) {
4866 BMEdge *e_dst = static_cast<BMEdge *>(BMO_iter_map_value_ptr(&siter));
4867
4868 BLI_assert(e_dst);
4869
4870 /* For edges, flip the selection from the edge of the hole to the edge of the island. */
4872
4873 /* When these match, the source edge has been deleted. */
4874 if (e != e_dst) {
4876
4877 /* For verts, flip the selection from the edge of the hole to the edge of the island.
4878 * Also add it to the weld map. But check selection first. Don't try to add the same vert to
4879 * the map more than once. If the selection was changed false, it's already been processed.
4880 */
4884 BMO_slot_map_elem_insert(&split_join->weld_op, weld_target_map, e->v1, e_dst->v1);
4885 }
4889 BMO_slot_map_elem_insert(&split_join->weld_op, weld_target_map, e->v2, e_dst->v2);
4890 }
4891 }
4892 }
4893
4894 /* Store the island for removal once it has been replaced by new fill_grid geometry. */
4895 BMO_op_init(em->bm, &split_join->delete_op, 0, "delete");
4896 BMO_slot_int_set(split_join->delete_op.slots_in, "context", DEL_FACES);
4898 em->bm, split_op.slots_out, "geom.out", BM_FACE, BM_ELEM_SELECT, false);
4900 &split_join->delete_op,
4901 split_join->delete_op.slots_in,
4902 "geom",
4903 BM_FACE,
4905
4906 /* Clean up the split operator. */
4907 BMO_op_finish(em->bm, &split_op);
4908
4909 return split_join;
4910}
4911
4921 wmOperator *op,
4922 FillGridSplitJoin *split_join,
4923 bool changed)
4924{
4925
4926 /* If fill_grid worked, delete the replaced faces. Otherwise, restore original selection. */
4927 if (changed) {
4928 BMO_op_exec(em->bm, &split_join->delete_op);
4929 }
4930 else {
4932 em->bm, split_join->delete_op.slots_in, "geom", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
4933 }
4934 BMO_op_finish(em->bm, &split_join->delete_op);
4935
4936 /* If fill_grid created geometry from faces after those faces had been split
4937 * from the rest of the mesh, the geometry it generated will be inward-facing.
4938 * (using the fill_grid on an island instead of a hole is 'inside out'.) Fix it.
4939 * This is the same as #edbm_flip_normals_face_winding without the #EDBM_update
4940 * since that will happen later. */
4941 if (changed) {
4943 EDBM_op_callf(em, op, "reverse_faces faces=%hf flip_multires=%b", BM_ELEM_SELECT, true);
4944 if (lnors_ed_arr) {
4945 flip_custom_normals(em->bm, lnors_ed_arr);
4947 }
4948 }
4949
4950 /* Put the mesh back together. */
4951 BMO_op_exec(em->bm, &split_join->weld_op);
4952 BMO_op_finish(em->bm, &split_join->weld_op);
4953
4954 MEM_freeN(split_join);
4955}
4956
4958{
4959 const bool use_interp_simple = RNA_boolean_get(op->ptr, "use_interp_simple");
4960
4961 const Scene *scene = CTX_data_scene(C);
4962 ViewLayer *view_layer = CTX_data_view_layer(C);
4964 scene, view_layer, CTX_wm_view3d(C));
4965 for (uint ob_index = 0; ob_index < objects.size(); ob_index++) {
4966
4967 Object *obedit = objects[ob_index];
4969 if (em->bm->totedgesel == 0) {
4970 continue;
4971 }
4972
4973 bool use_prepare = true;
4974 const bool use_smooth = edbm_add_edge_face__smooth_get(em->bm);
4975
4976 FillGridSplitJoin *split_join = nullptr;
4977 if (em->bm->totfacesel != 0) {
4978 split_join = edbm_fill_grid_split_join_init(em);
4979 }
4980
4981 const int totedge_orig = em->bm->totedge;
4982 const int totface_orig = em->bm->totface;
4983
4984 if (use_prepare) {
4985 /* use when we have a single loop selected */
4986 PropertyRNA *prop_span = RNA_struct_find_property(op->ptr, "span");
4987 PropertyRNA *prop_offset = RNA_struct_find_property(op->ptr, "offset");
4988 bool calc_span;
4989
4990 int span;
4991 int offset;
4992
4993 /* Only reuse on redo because these settings need to match the current selection.
4994 * We never want to use them on other geometry, repeat last for eg, see: #60777. */
4995 if (((op->flag & OP_IS_INVOKE) || (op->flag & OP_IS_REPEAT_LAST) == 0) &&
4996 RNA_property_is_set(op->ptr, prop_span))
4997 {
4998 span = RNA_property_int_get(op->ptr, prop_span);
4999 calc_span = false;
5000 }
5001 else {
5002 /* Will be overwritten if possible. */
5003 span = 0;
5004 calc_span = true;
5005 }
5006
5007 offset = RNA_property_int_get(op->ptr, prop_offset);
5008
5009 /* in simple cases, move selection for tags, but also support more advanced cases */
5010 use_prepare = edbm_fill_grid_prepare(em->bm, offset, &span, calc_span);
5011
5012 RNA_property_int_set(op->ptr, prop_span, span);
5013 }
5014 /* end tricky prepare code */
5015
5016 bool changed = EDBM_op_call_and_selectf(
5017 em,
5018 op,
5019 "faces.out",
5020 true,
5021 "grid_fill edges=%he mat_nr=%i use_smooth=%b use_interp_simple=%b",
5022 use_prepare ? BM_ELEM_TAG : BM_ELEM_SELECT,
5023 em->mat_nr,
5024 use_smooth,
5025 use_interp_simple);
5026
5027 /* Check that the results match the return value. */
5028 const bool has_geometry_changed = totedge_orig != em->bm->totedge ||
5029 totface_orig != em->bm->totface;
5030 BLI_assert(changed == has_geometry_changed);
5031 UNUSED_VARS_NDEBUG(has_geometry_changed);
5032
5033 /* If a split/join in progress, finish it. */
5034 if (split_join) {
5035 edbm_fill_grid_split_join_finish(em, op, split_join, changed);
5036 }
5037
5038 /* Update the object. */
5039 if (changed || split_join) {
5041 params.calc_looptris = true;
5042 params.calc_normals = false;
5043 params.is_destructive = true;
5044 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5045 }
5046 else {
5047 /* NOTE: Even if there were no mesh changes, #EDBM_op_finish() changed the BMesh pointer
5048 * inside of edit mesh, so need to tell evaluated objects to sync new BMesh pointer to their
5049 * edit mesh structures. */
5050 DEG_id_tag_update(&obedit->id, 0);
5051 }
5052 }
5053
5054 return OPERATOR_FINISHED;
5055}
5056
5058{
5059 PropertyRNA *prop;
5060
5061 /* identifiers */
5062 ot->name = "Grid Fill";
5063 ot->description = "Fill grid from two loops";
5064 ot->idname = "MESH_OT_fill_grid";
5065
5066 /* API callbacks. */
5067 ot->exec = edbm_fill_grid_exec;
5068 ot->poll = ED_operator_editmesh;
5069
5070 /* flags */
5071 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5072
5073 /* properties */
5074 prop = RNA_def_int(ot->srna, "span", 1, 1, 1000, "Span", "Number of grid columns", 1, 100);
5076 prop = RNA_def_int(ot->srna,
5077 "offset",
5078 0,
5079 -1000,
5080 1000,
5081 "Offset",
5082 "Vertex that is the corner of the grid",
5083 -100,
5084 100);
5086 RNA_def_boolean(ot->srna,
5087 "use_interp_simple",
5088 false,
5089 "Simple Blending",
5090 "Use simple interpolation of grid vertices");
5091}
5092
5094
5095/* -------------------------------------------------------------------- */
5098
5100{
5101 const int sides = RNA_int_get(op->ptr, "sides");
5102
5103 const Scene *scene = CTX_data_scene(C);
5104 ViewLayer *view_layer = CTX_data_view_layer(C);
5106 scene, view_layer, CTX_wm_view3d(C));
5107
5108 for (Object *obedit : objects) {
5110
5111 if (em->bm->totedgesel == 0) {
5112 continue;
5113 }
5114
5116 em, op, "faces.out", true, "holes_fill edges=%he sides=%i", BM_ELEM_SELECT, sides))
5117 {
5118 continue;
5119 }
5120
5122 params.calc_looptris = true;
5123 params.calc_normals = false;
5124 params.is_destructive = true;
5125 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5126 }
5127
5128 return OPERATOR_FINISHED;
5129}
5130
5132{
5133 /* identifiers */
5134 ot->name = "Fill Holes";
5135 ot->idname = "MESH_OT_fill_holes";
5136 ot->description = "Fill in holes (boundary edge loops)";
5137
5138 /* API callbacks. */
5139 ot->exec = edbm_fill_holes_exec;
5140 ot->poll = ED_operator_editmesh;
5141
5142 /* flags */
5143 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5144
5145 RNA_def_int(ot->srna,
5146 "sides",
5147 4,
5148 0,
5149 1000,
5150 "Sides",
5151 "Number of sides in hole required to fill (zero fills all holes)",
5152 0,
5153 100);
5154}
5155
5157
5158/* -------------------------------------------------------------------- */
5161
5163{
5164 const Scene *scene = CTX_data_scene(C);
5165 ViewLayer *view_layer = CTX_data_view_layer(C);
5167 scene, view_layer, CTX_wm_view3d(C));
5168
5169 const float angle_max = M_PI;
5170 const float angle_limit = RNA_float_get(op->ptr, "angle_limit");
5171 char hflag;
5172
5173 for (Object *obedit : objects) {
5175
5176 if (em->bm->totfacesel == 0) {
5177 continue;
5178 }
5179
5180 if (angle_limit >= angle_max) {
5181 hflag = BM_ELEM_SELECT;
5182 }
5183 else {
5184 BMIter iter;
5185 BMEdge *e;
5186
5187 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
5191 BM_edge_calc_face_angle_ex(e, angle_max) < angle_limit));
5192 }
5193 hflag = BM_ELEM_TAG;
5194 }
5195
5197 em, op, "geom.out", true, "beautify_fill faces=%hf edges=%he", BM_ELEM_SELECT, hflag))
5198 {
5199 continue;
5200 }
5201
5203 params.calc_looptris = true;
5204 params.calc_normals = false;
5205 params.is_destructive = true;
5206 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5207 }
5208
5209 return OPERATOR_FINISHED;
5210}
5211
5213{
5214 PropertyRNA *prop;
5215
5216 /* identifiers */
5217 ot->name = "Beautify Faces";
5218 ot->idname = "MESH_OT_beautify_fill";
5219 ot->description = "Rearrange some faces to try to get less degenerated geometry";
5220
5221 /* API callbacks. */
5223 ot->poll = ED_operator_editmesh;
5224
5225 /* flags */
5226 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5227
5228 /* props */
5229 prop = RNA_def_float_rotation(ot->srna,
5230 "angle_limit",
5231 0,
5232 nullptr,
5233 0.0f,
5234 DEG2RADF(180.0f),
5235 "Max Angle",
5236 "Angle limit",
5237 0.0f,
5238 DEG2RADF(180.0f));
5240}
5241
5243
5244/* -------------------------------------------------------------------- */
5247
5249{
5250 const float offset = RNA_float_get(op->ptr, "offset");
5251 const bool use_relative_offset = RNA_boolean_get(op->ptr, "use_relative_offset");
5252 const int center_mode = RNA_enum_get(op->ptr, "center_mode");
5253
5254 const Scene *scene = CTX_data_scene(C);
5255 ViewLayer *view_layer = CTX_data_view_layer(C);
5257 scene, view_layer, CTX_wm_view3d(C));
5258 for (Object *obedit : objects) {
5260
5261 if (em->bm->totfacesel == 0) {
5262 continue;
5263 }
5264
5265 BMOperator bmop;
5266 EDBM_op_init(em,
5267 &bmop,
5268 op,
5269 "poke faces=%hf offset=%f use_relative_offset=%b center_mode=%i",
5271 offset,
5272 use_relative_offset,
5273 center_mode);
5274 BMO_op_exec(em->bm, &bmop);
5275
5277
5279 em->bm, bmop.slots_out, "verts.out", BM_VERT, BM_ELEM_SELECT, true);
5281 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
5282
5283 if (!EDBM_op_finish(em, &bmop, op, true)) {
5284 continue;
5285 }
5286
5288 params.calc_looptris = true;
5289 params.calc_normals = true;
5290 params.is_destructive = true;
5291 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5292 }
5293
5294 return OPERATOR_FINISHED;
5295}
5296
5298{
5299 static const EnumPropertyItem poke_center_modes[] = {
5301 "MEDIAN_WEIGHTED",
5302 0,
5303 "Weighted Median",
5304 "Weighted median face center"},
5305 {BMOP_POKE_MEDIAN, "MEDIAN", 0, "Median", "Median face center"},
5306 {BMOP_POKE_BOUNDS, "BOUNDS", 0, "Bounds", "Face bounds center"},
5307 {0, nullptr, 0, nullptr, nullptr},
5308 };
5309
5310 /* identifiers */
5311 ot->name = "Poke Faces";
5312 ot->idname = "MESH_OT_poke";
5313 ot->description = "Split a face into a fan";
5314
5315 /* API callbacks. */
5316 ot->exec = edbm_poke_face_exec;
5317 ot->poll = ED_operator_editmesh;
5318
5319 /* flags */
5320 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5321
5323 ot->srna, "offset", 0.0f, -1e3f, 1e3f, "Poke Offset", "Poke Offset", -1.0f, 1.0f);
5324 RNA_def_boolean(ot->srna,
5325 "use_relative_offset",
5326 false,
5327 "Offset Relative",
5328 "Scale the offset by surrounding geometry");
5329 RNA_def_enum(ot->srna,
5330 "center_mode",
5331 poke_center_modes,
5333 "Poke Center",
5334 "Poke face center calculation");
5335}
5336
5338
5339/* -------------------------------------------------------------------- */
5342
5344{
5345 const int quad_method = RNA_enum_get(op->ptr, "quad_method");
5346 const int ngon_method = RNA_enum_get(op->ptr, "ngon_method");
5347 const Scene *scene = CTX_data_scene(C);
5348 ViewLayer *view_layer = CTX_data_view_layer(C);
5349
5351 scene, view_layer, CTX_wm_view3d(C));
5352 for (Object *obedit : objects) {
5354
5355 if (em->bm->totfacesel == 0) {
5356 continue;
5357 }
5358
5359 BMOperator bmop;
5360 BMOIter oiter;
5361 BMFace *f;
5362
5364
5365 EDBM_op_init(em,
5366 &bmop,
5367 op,
5368 "triangulate faces=%hf quad_method=%i ngon_method=%i",
5370 quad_method,
5371 ngon_method);
5372 BMO_op_exec(em->bm, &bmop);
5373
5374 /* select the output */
5376 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
5377
5378 /* remove the doubles */
5379 BMO_ITER (f, &oiter, bmop.slots_out, "face_map_double.out", BM_FACE) {
5380 BM_face_kill(em->bm, f);
5381 }
5382
5384
5385 if (!EDBM_op_finish(em, &bmop, op, true)) {
5386 continue;
5387 }
5388
5390
5392 params.calc_looptris = true;
5393 params.calc_normals = false;
5394 params.is_destructive = true;
5395 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5396 }
5397
5398 return OPERATOR_FINISHED;
5399}
5400
5402{
5403 /* identifiers */
5404 ot->name = "Triangulate Faces";
5405 ot->idname = "MESH_OT_quads_convert_to_tris";
5406 ot->description = "Triangulate selected faces";
5407
5408 /* API callbacks. */
5410 ot->poll = ED_operator_editmesh;
5411
5412 /* flags */
5413 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5414
5415 RNA_def_enum(ot->srna,
5416 "quad_method",
5419 "Quad Method",
5420 "Method for splitting the quads into triangles");
5421 RNA_def_enum(ot->srna,
5422 "ngon_method",
5425 "N-gon Method",
5426 "Method for splitting the n-gons into triangles");
5427}
5428
5430
5431/* -------------------------------------------------------------------- */
5434
5435#if 0 /* See comments at top of bmo_join_triangles.cc */
5436# define USE_JOIN_TRIANGLE_TESTING_API
5437#endif
5438
5440{
5441 const Scene *scene = CTX_data_scene(C);
5442 ViewLayer *view_layer = CTX_data_view_layer(C);
5443
5445 scene, view_layer, CTX_wm_view3d(C));
5446
5447 const bool do_seam = RNA_boolean_get(op->ptr, "seam");
5448 const bool do_sharp = RNA_boolean_get(op->ptr, "sharp");
5449 const bool do_uvs = RNA_boolean_get(op->ptr, "uvs");
5450 const bool do_vcols = RNA_boolean_get(op->ptr, "vcols");
5451 const bool do_materials = RNA_boolean_get(op->ptr, "materials");
5452
5453#ifdef USE_JOIN_TRIANGLE_TESTING_API
5454 int merge_limit = RNA_int_get(op->ptr, "merge_limit");
5455 int neighbor_debug = RNA_int_get(op->ptr, "neighbor_debug");
5456#endif
5457
5458 const float topology_influence = RNA_float_get(op->ptr, "topology_influence");
5459 const bool deselect_joined = RNA_boolean_get(op->ptr, "deselect_joined");
5460
5461 float angle_face_threshold, angle_shape_threshold;
5462 bool is_face_pair;
5463 {
5464 int totelem_sel[3];
5465 EDBM_mesh_stats_multi(objects, nullptr, totelem_sel);
5466 is_face_pair = (totelem_sel[2] == 2);
5467 }
5468
5469 /* When joining exactly 2 faces, no limit.
5470 * this is useful for one off joins while editing. */
5471 {
5472 PropertyRNA *prop;
5473 prop = RNA_struct_find_property(op->ptr, "face_threshold");
5474 if (is_face_pair && (RNA_property_is_set(op->ptr, prop) == false)) {
5475 angle_face_threshold = DEG2RADF(180.0f);
5476 }
5477 else {
5478 angle_face_threshold = RNA_property_float_get(op->ptr, prop);
5479 }
5480
5481 prop = RNA_struct_find_property(op->ptr, "shape_threshold");
5482 if (is_face_pair && (RNA_property_is_set(op->ptr, prop) == false)) {
5483 angle_shape_threshold = DEG2RADF(180.0f);
5484 }
5485 else {
5486 angle_shape_threshold = RNA_property_float_get(op->ptr, prop);
5487 }
5488 }
5489
5490 for (Object *obedit : objects) {
5492
5493 if (em->bm->totfacesel == 0) {
5494 continue;
5495 }
5496
5498
5499 bool extend_selection = (deselect_joined == false);
5500
5501#ifdef USE_JOIN_TRIANGLE_TESTING_API
5502 if (merge_limit != -1) {
5503 extend_selection = false;
5504 }
5505#endif
5506
5508 em,
5509 op,
5510 "faces.out",
5511 extend_selection,
5512 "join_triangles faces=%hf angle_face_threshold=%f angle_shape_threshold=%f "
5513 "cmp_seam=%b cmp_sharp=%b cmp_uvs=%b cmp_vcols=%b cmp_materials=%b "
5514#ifdef USE_JOIN_TRIANGLE_TESTING_API
5515 "merge_limit=%i neighbor_debug=%i "
5516#endif
5517 "topology_influence=%f deselect_joined=%b",
5519 angle_face_threshold,
5520 angle_shape_threshold,
5521 do_seam,
5522 do_sharp,
5523 do_uvs,
5524 do_vcols,
5525 do_materials,
5526#ifdef USE_JOIN_TRIANGLE_TESTING_API
5527 merge_limit + 1,
5528 neighbor_debug,
5529#endif
5530 topology_influence,
5531 deselect_joined))
5532 {
5533 continue;
5534 }
5535
5536 if (deselect_joined) {
5537 /* When de-selecting faces outside of face mode:
5538 * failing to flush would leave an invalid selection. */
5541 }
5542 }
5544
5546
5548 params.calc_looptris = true;
5549 params.calc_normals = false;
5550 params.is_destructive = true;
5551 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5552 }
5553
5554 return OPERATOR_FINISHED;
5555}
5556
5558{
5559 PropertyRNA *prop;
5560
5561#ifdef USE_JOIN_TRIANGLE_TESTING_API
5562 prop = RNA_def_int(ot->srna,
5563 "merge_limit",
5564 0,
5565 -1,
5566 INT_MAX,
5567 "Merge Limit",
5568 "Maximum number of merges",
5569 -1,
5570 INT_MAX);
5572
5573 prop = RNA_def_int(ot->srna,
5574 "neighbor_debug",
5575 0,
5576 0,
5577 INT_MAX,
5578 "Neighbor Debug",
5579 "Neighbor to highlight",
5580 0,
5581 INT_MAX);
5583#endif
5584
5585 prop = RNA_def_float_rotation(ot->srna,
5586 "face_threshold",
5587 0,
5588 nullptr,
5589 0.0f,
5590 DEG2RADF(180.0f),
5591 "Max Face Angle",
5592 "Face angle limit",
5593 0.0f,
5594 DEG2RADF(180.0f));
5596
5597 prop = RNA_def_float_rotation(ot->srna,
5598 "shape_threshold",
5599 0,
5600 nullptr,
5601 0.0f,
5602 DEG2RADF(180.0f),
5603 "Max Shape Angle",
5604 "Shape angle limit",
5605 0.0f,
5606 DEG2RADF(180.0f));
5608
5609 prop = RNA_def_float_factor(ot->srna,
5610 "topology_influence",
5611 0.0f,
5612 0.0f,
5613 2.0f,
5614 "Topology Influence",
5615 "How much to prioritize regular grids of quads as well as "
5616 "quads that touch existing quads",
5617 0.0f,
5618 2.0f);
5619
5620 RNA_def_boolean(ot->srna, "uvs", false, "Compare UVs", "");
5621 RNA_def_boolean(ot->srna, "vcols", false, "Compare Color Attributes", "");
5622 RNA_def_boolean(ot->srna, "seam", false, "Compare Seam", "");
5623 RNA_def_boolean(ot->srna, "sharp", false, "Compare Sharp", "");
5624 RNA_def_boolean(ot->srna, "materials", false, "Compare Materials", "");
5625
5626 RNA_def_boolean(ot->srna,
5627 "deselect_joined",
5628 false,
5629 "Deselect Joined",
5630 "Only select remaining triangles that were not merged");
5631}
5632
5634{
5635 /* identifiers */
5636 ot->name = "Triangles to Quads";
5637 ot->idname = "MESH_OT_tris_convert_to_quads";
5638 ot->description = "Merge triangles into four sided polygons where possible";
5639
5640 /* API callbacks. */
5642 ot->poll = ED_operator_editmesh;
5643
5644 /* flags */
5645 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5646
5648}
5649
5651
5652/* -------------------------------------------------------------------- */
5660
5662{
5663 const float ratio = RNA_float_get(op->ptr, "ratio");
5664 bool use_vertex_group = RNA_boolean_get(op->ptr, "use_vertex_group");
5665 const float vertex_group_factor = RNA_float_get(op->ptr, "vertex_group_factor");
5666 const bool invert_vertex_group = RNA_boolean_get(op->ptr, "invert_vertex_group");
5667 const bool use_symmetry = RNA_boolean_get(op->ptr, "use_symmetry");
5668 const float symmetry_eps = 0.00002f;
5669 const int symmetry_axis = use_symmetry ? RNA_enum_get(op->ptr, "symmetry_axis") : -1;
5670
5671 /* nop */
5672 if (ratio == 1.0f) {
5673 return OPERATOR_FINISHED;
5674 }
5675
5676 const Scene *scene = CTX_data_scene(C);
5677 ViewLayer *view_layer = CTX_data_view_layer(C);
5679 scene, view_layer, CTX_wm_view3d(C));
5680
5681 for (Object *obedit : objects) {
5683 BMesh *bm = em->bm;
5684 if (bm->totedgesel == 0) {
5685 continue;
5686 }
5687
5688 float *vweights = MEM_malloc_arrayN<float>(bm->totvert, __func__);
5689 {
5690 const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
5691 const int defbase_act = BKE_object_defgroup_active_index_get(obedit) - 1;
5692
5693 if (use_vertex_group && (cd_dvert_offset == -1)) {
5694 BKE_report(op->reports, RPT_WARNING, "No active vertex group");
5695 use_vertex_group = false;
5696 }
5697
5698 BMIter iter;
5699 BMVert *v;
5700 int i;
5702 float weight = 0.0f;
5704 if (use_vertex_group) {
5705 const MDeformVert *dv = static_cast<const MDeformVert *>(
5706 BM_ELEM_CD_GET_VOID_P(v, cd_dvert_offset));
5707 weight = BKE_defvert_find_weight(dv, defbase_act);
5708 if (invert_vertex_group) {
5709 weight = 1.0f - weight;
5710 }
5711 }
5712 else {
5713 weight = 1.0f;
5714 }
5715 }
5716
5717 vweights[i] = weight;
5718 BM_elem_index_set(v, i); /* set_inline */
5719 }
5720 bm->elem_index_dirty &= ~BM_VERT;
5721 }
5722
5723 float ratio_adjust;
5724
5725 if ((bm->totface == bm->totfacesel) || (ratio == 0.0f)) {
5726 ratio_adjust = ratio;
5727 }
5728 else {
5736
5737 int totface_basis = 0;
5738 int totface_adjacent = 0;
5739 BMIter iter;
5740 BMFace *f;
5741 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
5742 /* count faces during decimation, ngons are triangulated */
5743 const int f_len = f->len > 4 ? (f->len - 2) : 1;
5744 totface_basis += f_len;
5745
5746 BMLoop *l_iter, *l_first;
5747 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
5748 do {
5749 if (vweights[BM_elem_index_get(l_iter->v)] != 0.0f) {
5750 totface_adjacent += f_len;
5751 break;
5752 }
5753 } while ((l_iter = l_iter->next) != l_first);
5754 }
5755
5756 ratio_adjust = ratio;
5757 ratio_adjust = 1.0f - ratio_adjust;
5758 ratio_adjust *= float(totface_adjacent) / float(totface_basis);
5759 ratio_adjust = 1.0f - ratio_adjust;
5760 }
5761
5763 em->bm, ratio_adjust, vweights, vertex_group_factor, false, symmetry_axis, symmetry_eps);
5764
5765 MEM_freeN(vweights);
5766
5767 {
5768 short selectmode = em->selectmode;
5769 if ((selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) == 0) {
5770 /* ensure we flush edges -> faces */
5771 selectmode |= SCE_SELECT_EDGE;
5772 }
5773 EDBM_selectmode_flush_ex(em, selectmode);
5775 }
5777 params.calc_looptris = true;
5778 params.calc_normals = true;
5779 params.is_destructive = true;
5780 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5781 }
5782
5783 return OPERATOR_FINISHED;
5784}
5785
5786static bool edbm_decimate_check(bContext * /*C*/, wmOperator * /*op*/)
5787{
5788 return true;
5789}
5790
5791static void edbm_decimate_ui(bContext * /*C*/, wmOperator *op)
5792{
5793 uiLayout *layout = op->layout, *row, *col, *sub;
5794
5795 layout->use_property_split_set(true);
5796
5797 layout->prop(op->ptr, "ratio", UI_ITEM_NONE, std::nullopt, ICON_NONE);
5798
5799 layout->prop(op->ptr, "use_vertex_group", UI_ITEM_NONE, std::nullopt, ICON_NONE);
5800 col = &layout->column(false);
5801 col->active_set(RNA_boolean_get(op->ptr, "use_vertex_group"));
5802 col->prop(op->ptr, "vertex_group_factor", UI_ITEM_NONE, std::nullopt, ICON_NONE);
5803 col->prop(op->ptr, "invert_vertex_group", UI_ITEM_NONE, std::nullopt, ICON_NONE);
5804
5805 row = &layout->row(true, IFACE_("Symmetry"));
5806 row->prop(op->ptr, "use_symmetry", UI_ITEM_NONE, "", ICON_NONE);
5807 sub = &row->row(true);
5808 sub->active_set(RNA_boolean_get(op->ptr, "use_symmetry"));
5809 sub->prop(op->ptr, "symmetry_axis", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
5810}
5811
5813{
5814 /* identifiers */
5815 ot->name = "Decimate Geometry";
5816 ot->idname = "MESH_OT_decimate";
5817 ot->description = "Simplify geometry by collapsing edges";
5818
5819 /* API callbacks. */
5820 ot->exec = edbm_decimate_exec;
5821 ot->check = edbm_decimate_check;
5822 ot->ui = edbm_decimate_ui;
5823 ot->poll = ED_operator_editmesh;
5824
5825 /* flags */
5826 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5827
5828 /* NOTE: keep in sync with 'rna_def_modifier_decimate'. */
5829 RNA_def_float(ot->srna, "ratio", 1.0f, 0.0f, 1.0f, "Ratio", "", 0.0f, 1.0f);
5830
5831 RNA_def_boolean(ot->srna,
5832 "use_vertex_group",
5833 false,
5834 "Vertex Group",
5835 "Use active vertex group as an influence");
5836 RNA_def_float(ot->srna,
5837 "vertex_group_factor",
5838 1.0f,
5839 0.0f,
5840 1000.0f,
5841 "Weight",
5842 "Vertex group strength",
5843 0.0f,
5844 10.0f);
5846 ot->srna, "invert_vertex_group", false, "Invert", "Invert vertex group influence");
5847
5848 RNA_def_boolean(ot->srna, "use_symmetry", false, "Symmetry", "Maintain symmetry on an axis");
5849
5850 RNA_def_enum(ot->srna, "symmetry_axis", rna_enum_axis_xyz_items, 1, "Axis", "Axis of symmetry");
5851}
5852
5854
5855/* -------------------------------------------------------------------- */
5858
5860{
5861 PropertyRNA *prop;
5862
5863 prop = RNA_def_boolean(ot->srna,
5864 "use_verts",
5865 value,
5866 "Dissolve Vertices",
5867 "Dissolve remaining vertices which connect to only two edges");
5868
5869 if (flag) {
5871 }
5872}
5874{
5875 RNA_def_boolean(ot->srna,
5876 "use_face_split",
5877 false,
5878 "Face Split",
5879 "Split off face corners to maintain surrounding geometry");
5880}
5882{
5883 RNA_def_boolean(ot->srna,
5884 "use_boundary_tear",
5885 false,
5886 "Tear Boundary",
5887 "Split off face corners instead of merging faces");
5888}
5890{
5892 ot->srna,
5893 "angle_threshold",
5894 0,
5895 nullptr,
5896 0.0f,
5897 DEG2RADF(180.0f),
5898 "Angle Threshold",
5899 "Remaining vertices which separate edge pairs are preserved if their edge angle exceeds "
5900 "this threshold.",
5901 0.0f,
5902 DEG2RADF(180.0f));
5904 if (flag) {
5906 }
5907}
5908
5910{
5911 const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split");
5912 const bool use_boundary_tear = RNA_boolean_get(op->ptr, "use_boundary_tear");
5913
5914 const Scene *scene = CTX_data_scene(C);
5915 ViewLayer *view_layer = CTX_data_view_layer(C);
5917 scene, view_layer, CTX_wm_view3d(C));
5918
5919 for (Object *obedit : objects) {
5921
5922 if (em->bm->totvertsel == 0) {
5923 continue;
5924 }
5925
5927
5928 if (!EDBM_op_callf(em,
5929 op,
5930 "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b",
5932 use_face_split,
5933 use_boundary_tear))
5934 {
5935 continue;
5936 }
5937
5939
5941 params.calc_looptris = true;
5942 params.calc_normals = false;
5943 params.is_destructive = true;
5944 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5945 }
5946
5947 return OPERATOR_FINISHED;
5948}
5949
5951{
5952 /* identifiers */
5953 ot->name = "Dissolve Vertices";
5954 ot->description = "Dissolve vertices, merge edges and faces";
5955 ot->idname = "MESH_OT_dissolve_verts";
5956
5957 /* API callbacks. */
5959 ot->poll = ED_operator_editmesh;
5960
5961 /* flags */
5962 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5963
5966}
5967
5969
5970/* -------------------------------------------------------------------- */
5973
5975{
5976 const bool use_verts = RNA_boolean_get(op->ptr, "use_verts");
5977 const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split");
5978 const float angle_threshold = RNA_float_get(op->ptr, "angle_threshold");
5979
5980 const Scene *scene = CTX_data_scene(C);
5981 ViewLayer *view_layer = CTX_data_view_layer(C);
5983 scene, view_layer, CTX_wm_view3d(C));
5984 for (Object *obedit : objects) {
5986
5987 if (em->bm->totedgesel == 0) {
5988 continue;
5989 }
5990
5992
5993 if (!EDBM_op_callf(
5994 em,
5995 op,
5996 "dissolve_edges edges=%he use_verts=%b use_face_split=%b angle_threshold=%f",
5998 use_verts,
5999 use_face_split,
6000 angle_threshold))
6001 {
6002 continue;
6003 }
6004
6006
6008 params.calc_looptris = true;
6009 params.calc_normals = false;
6010 params.is_destructive = true;
6011 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
6012 }
6013
6014 return OPERATOR_FINISHED;
6015}
6016
6018{
6019 /* identifiers */
6020 ot->name = "Dissolve Edges";
6021 ot->description = "Dissolve edges, merging faces";
6022 ot->idname = "MESH_OT_dissolve_edges";
6023
6024 /* API callbacks. */
6026 ot->poll = ED_operator_editmesh;
6027
6028 /* flags */
6029 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6030
6034}
6035
6037
6038/* -------------------------------------------------------------------- */
6041
6043{
6044 const bool use_verts = RNA_boolean_get(op->ptr, "use_verts");
6045 const Scene *scene = CTX_data_scene(C);
6046 ViewLayer *view_layer = CTX_data_view_layer(C);
6048 scene, view_layer, CTX_wm_view3d(C));
6049 for (Object *obedit : objects) {
6051
6052 if (em->bm->totfacesel == 0) {
6053 continue;
6054 }
6055
6057
6059 op,
6060 "region.out",
6061 true,
6062 "dissolve_faces faces=%hf use_verts=%b",
6064 use_verts))
6065 {
6066 continue;
6067 }
6068
6070
6072 params.calc_looptris = true;
6073 params.calc_normals = false;
6074 params.is_destructive = true;
6075 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
6076 }
6077
6078 return OPERATOR_FINISHED;
6079}
6080
6082{
6083 /* identifiers */
6084 ot->name = "Dissolve Faces";
6085 ot->description = "Dissolve faces";
6086 ot->idname = "MESH_OT_dissolve_faces";
6087
6088 /* API callbacks. */
6090 ot->poll = ED_operator_editmesh;
6091
6092 /* flags */
6093 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6094
6096}
6097
6099
6100/* -------------------------------------------------------------------- */
6103
6105{
6106 Object *obedit = CTX_data_edit_object(C);
6108 PropertyRNA *prop;
6109
6110 prop = RNA_struct_find_property(op->ptr, "use_verts");
6111 if (!RNA_property_is_set(op->ptr, prop)) {
6112 /* always enable in edge-mode */
6113 if ((em->selectmode & SCE_SELECT_FACE) == 0) {
6114 RNA_property_boolean_set(op->ptr, prop, true);
6115 }
6116 }
6117
6118 if (em->selectmode & SCE_SELECT_VERTEX) {
6119 return edbm_dissolve_verts_exec(C, op);
6120 }
6121 if (em->selectmode & SCE_SELECT_EDGE) {
6122 return edbm_dissolve_edges_exec(C, op);
6123 }
6124 return edbm_dissolve_faces_exec(C, op);
6125}
6126
6127static bool dissolve_mode_poll_property(const bContext *C, wmOperator *op, const PropertyRNA *prop)
6128{
6129 UNUSED_VARS(op);
6130
6131 const char *prop_id = RNA_property_identifier(prop);
6132
6133 Object *obedit = CTX_data_edit_object(C);
6134 const BMEditMesh *em = BKE_editmesh_from_object(obedit);
6135 bool is_edge_select_mode = false;
6136
6137 if (em->selectmode & SCE_SELECT_VERTEX) {
6138 /* Pass. */
6139 }
6140 if (em->selectmode & SCE_SELECT_EDGE) {
6141 is_edge_select_mode = true;
6142 }
6143
6144 if (!is_edge_select_mode) {
6145 /* Angle Threshold is only used in edge select mode. */
6146 if (STREQ(prop_id, "angle_threshold")) {
6147 return false;
6148 }
6149 }
6150 return true;
6151}
6152
6154{
6155 /* identifiers */
6156 ot->name = "Dissolve Selection";
6157 ot->description = "Dissolve geometry based on the selection mode";
6158 ot->idname = "MESH_OT_dissolve_mode";
6159
6160 /* API callbacks. */
6162 ot->poll = ED_operator_editmesh;
6163 ot->poll_property = dissolve_mode_poll_property;
6164
6165 /* flags */
6166 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6167
6172}
6173
6175
6176/* -------------------------------------------------------------------- */
6179
6181{
6182 const float angle_limit = RNA_float_get(op->ptr, "angle_limit");
6183 const bool use_dissolve_boundaries = RNA_boolean_get(op->ptr, "use_dissolve_boundaries");
6184 const int delimit = RNA_enum_get(op->ptr, "delimit");
6185 char dissolve_flag;
6186
6187 const Scene *scene = CTX_data_scene(C);
6188 ViewLayer *view_layer = CTX_data_view_layer(C);
6190 scene, view_layer, CTX_wm_view3d(C));
6191 for (Object *obedit : objects) {
6193 BMesh *bm = em->bm;
6194
6195 if ((bm->totvertsel == 0) && (bm->totedgesel == 0) && (bm->totfacesel == 0)) {
6196 continue;
6197 }
6198
6200
6201 if (em->selectmode == SCE_SELECT_FACE) {
6202 /* flush selection to tags and untag edges/verts with partially selected faces */
6203 BMIter iter;
6204 BMIter liter;
6205
6206 BMElem *ele;
6207 BMFace *f;
6208 BMLoop *l;
6209
6210 BM_ITER_MESH (ele, &iter, bm, BM_VERTS_OF_MESH) {
6212 }
6213 BM_ITER_MESH (ele, &iter, bm, BM_EDGES_OF_MESH) {
6215 }
6216
6217 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
6219 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
6222 }
6223 }
6224 }
6225
6226 dissolve_flag = BM_ELEM_TAG;
6227 }
6228 else {
6229 dissolve_flag = BM_ELEM_SELECT;
6230 }
6231
6233 em,
6234 op,
6235 "region.out",
6236 true,
6237 "dissolve_limit edges=%he verts=%hv angle_limit=%f use_dissolve_boundaries=%b delimit=%i",
6238 dissolve_flag,
6239 dissolve_flag,
6240 angle_limit,
6241 use_dissolve_boundaries,
6242 delimit);
6243
6245
6247 params.calc_looptris = true;
6248 params.calc_normals = false;
6249 params.is_destructive = true;
6250 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
6251 }
6252
6253 return OPERATOR_FINISHED;
6254}
6255
6257{
6258 PropertyRNA *prop;
6259
6260 /* identifiers */
6261 ot->name = "Limited Dissolve";
6262 ot->idname = "MESH_OT_dissolve_limited";
6263 ot->description =
6264 "Dissolve selected edges and vertices, limited by the angle of surrounding geometry";
6265
6266 /* API callbacks. */
6268 ot->poll = ED_operator_editmesh;
6269
6270 /* flags */
6271 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6272
6273 prop = RNA_def_float_rotation(ot->srna,
6274 "angle_limit",
6275 0,
6276 nullptr,
6277 0.0f,
6278 DEG2RADF(180.0f),
6279 "Max Angle",
6280 "Angle limit",
6281 0.0f,
6282 DEG2RADF(180.0f));
6284 RNA_def_boolean(ot->srna,
6285 "use_dissolve_boundaries",
6286 false,
6287 "All Boundaries",
6288 "Dissolve all vertices in between face boundaries");
6289 RNA_def_enum_flag(ot->srna,
6290 "delimit",
6293 "Delimit",
6294 "Delimit dissolve operation");
6295}
6296
6298
6299/* -------------------------------------------------------------------- */
6302
6304{
6305 const Scene *scene = CTX_data_scene(C);
6306 ViewLayer *view_layer = CTX_data_view_layer(C);
6307 int totelem_old[3] = {0, 0, 0};
6308 int totelem_new[3] = {0, 0, 0};
6309
6311 scene, view_layer, CTX_wm_view3d(C));
6312
6313 for (Object *obedit : objects) {
6315 BMesh *bm = em->bm;
6316 totelem_old[0] += bm->totvert;
6317 totelem_old[1] += bm->totedge;
6318 totelem_old[2] += bm->totface;
6319 } /* objects */
6320
6321 const float thresh = RNA_float_get(op->ptr, "threshold");
6322
6323 for (Object *obedit : objects) {
6325 BMesh *bm = em->bm;
6326
6327 if (!EDBM_op_callf(em, op, "dissolve_degenerate edges=%he dist=%f", BM_ELEM_SELECT, thresh)) {
6328 continue;
6329 }
6330
6331 /* tricky to maintain correct selection here, so just flush up from verts */
6333
6335 params.calc_looptris = true;
6336 params.calc_normals = false;
6337 params.is_destructive = true;
6338 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
6339
6340 totelem_new[0] += bm->totvert;
6341 totelem_new[1] += bm->totedge;
6342 totelem_new[2] += bm->totface;
6343 }
6344
6345 edbm_report_delete_info(op->reports, totelem_old, totelem_new);
6346
6347 return OPERATOR_FINISHED;
6348}
6349
6351{
6352 /* identifiers */
6353 ot->name = "Degenerate Dissolve";
6354 ot->idname = "MESH_OT_dissolve_degenerate";
6355 ot->description = "Dissolve zero area faces and zero length edges";
6356
6357 /* API callbacks. */
6359 ot->poll = ED_operator_editmesh;
6360
6361 /* flags */
6362 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6363
6365 "threshold",
6366 1e-4f,
6367 1e-6f,
6368 50.0f,
6369 "Merge Distance",
6370 "Maximum distance between elements to merge",
6371 1e-5f,
6372 10.0f);
6373}
6374
6376
6377/* -------------------------------------------------------------------- */
6380
6381/* internally uses dissolve */
6383{
6384 const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split");
6385 const Scene *scene = CTX_data_scene(C);
6386 ViewLayer *view_layer = CTX_data_view_layer(C);
6387
6389 scene, view_layer, CTX_wm_view3d(C));
6390 for (Object *obedit : objects) {
6392
6393 if (em->bm->totedgesel == 0) {
6394 continue;
6395 }
6396
6397 /* deal with selection */
6398 {
6399 BMEdge *e;
6400 BMIter iter;
6401
6403
6404 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
6405 if (BM_elem_flag_test(e, BM_ELEM_SELECT) && e->l) {
6406 BMLoop *l_iter = e->l;
6407 do {
6409 } while ((l_iter = l_iter->radial_next) != e->l);
6410 }
6411 }
6412 }
6413
6414 if (!EDBM_op_callf(
6415 em,
6416 op,
6417 "dissolve_edges edges=%he use_verts=%b use_face_split=%b angle_threshold=%f",
6419 true,
6420 use_face_split,
6421 M_PI))
6422 {
6423 continue;
6424 }
6425
6427
6430
6432 params.calc_looptris = true;
6433 params.calc_normals = false;
6434 params.is_destructive = true;
6435 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
6436 }
6437
6438 return OPERATOR_FINISHED;
6439}
6440
6442{
6443 /* identifiers */
6444 ot->name = "Delete Edge Loop";
6445 ot->description = "Delete an edge loop by merging the faces on each side";
6446 ot->idname = "MESH_OT_delete_edgeloop";
6447
6448 /* API callbacks. */
6450 ot->poll = ED_operator_editmesh;
6451
6452 /* flags */
6453 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6454
6455 RNA_def_boolean(ot->srna,
6456 "use_face_split",
6457 true,
6458 "Face Split",
6459 "Split off face corners to maintain surrounding geometry");
6460}
6461
6463
6464/* -------------------------------------------------------------------- */
6467
6469{
6470 const Scene *scene = CTX_data_scene(C);
6471 ViewLayer *view_layer = CTX_data_view_layer(C);
6473 scene, view_layer, CTX_wm_view3d(C));
6474 for (Object *obedit : objects) {
6476 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
6477 continue;
6478 }
6480
6481 BMOperator bmop;
6482 EDBM_op_init(em, &bmop, op, "split geom=%hvef use_only_faces=%b", BM_ELEM_SELECT, false);
6483 BMO_op_exec(em->bm, &bmop);
6486 em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
6487
6489
6490 if (!EDBM_op_finish(em, &bmop, op, true)) {
6491 continue;
6492 }
6493
6494 /* Geometry has changed, need to recalculate normals and tessellation. */
6496 params.calc_looptris = true;
6497 params.calc_normals = true;
6498 params.is_destructive = true;
6499 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
6500 }
6501
6502 return OPERATOR_FINISHED;
6503}
6504
6506{
6507 /* identifiers */
6508 ot->name = "Split";
6509 ot->idname = "MESH_OT_split";
6510 ot->description = "Split off selected geometry from connected unselected geometry";
6511
6512 /* API callbacks. */
6513 ot->exec = edbm_split_exec;
6514 ot->poll = ED_operator_editmesh;
6515
6516 /* flags */
6517 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6518}
6519
6521
6522/* -------------------------------------------------------------------- */
6528
6529enum {
6545};
6546
6549 float srt;
6552};
6553
6554static int bmelemsort_comp(const void *v1, const void *v2)
6555{
6556 const BMElemSort *x1 = static_cast<const BMElemSort *>(v1);
6557 const BMElemSort *x2 = static_cast<const BMElemSort *>(v2);
6558
6559 return (x1->srt > x2->srt) - (x1->srt < x2->srt);
6560}
6561
6562/* Reorders vertices/edges/faces using a given methods. Loops are not supported. */
6564 Scene *scene,
6565 Object *ob,
6566 RegionView3D *rv3d,
6567 const int types,
6568 const int flag,
6569 const int action,
6570 const int reverse,
6571 const uint seed)
6572{
6574
6575 BMVert *ve;
6576 BMEdge *ed;
6577 BMFace *fa;
6578 BMIter iter;
6579
6580 /* In all five elements below, 0 = vertices, 1 = edges, 2 = faces. */
6581 /* Just to mark protected elements. */
6582 char *pblock[3] = {nullptr, nullptr, nullptr}, *pb;
6583 BMElemSort *sblock[3] = {nullptr, nullptr, nullptr}, *sb;
6584 uint *map[3] = {nullptr, nullptr, nullptr}, *mp;
6585 int totelem[3] = {0, 0, 0};
6586 int affected[3] = {0, 0, 0};
6587 int i, j;
6588
6589 if (!(types && flag && action)) {
6590 return;
6591 }
6592
6593 if (types & BM_VERT) {
6594 totelem[0] = em->bm->totvert;
6595 }
6596 if (types & BM_EDGE) {
6597 totelem[1] = em->bm->totedge;
6598 }
6599 if (types & BM_FACE) {
6600 totelem[2] = em->bm->totface;
6601 }
6602
6603 if (ELEM(action, SRT_VIEW_ZAXIS, SRT_VIEW_XAXIS)) {
6604 float mat[4][4];
6605 float fact = reverse ? -1.0 : 1.0;
6606 int coidx = (action == SRT_VIEW_ZAXIS) ? 2 : 0;
6607
6608 /* Apply the view matrix to the object matrix. */
6609 mul_m4_m4m4(mat, rv3d->viewmat, ob->object_to_world().ptr());
6610
6611 if (totelem[0]) {
6612 pb = pblock[0] = MEM_calloc_arrayN<char>(totelem[0], __func__);
6613 sb = sblock[0] = MEM_calloc_arrayN<BMElemSort>(totelem[0], __func__);
6614
6615 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6616 if (BM_elem_flag_test(ve, flag)) {
6617 float co[3];
6618 mul_v3_m4v3(co, mat, ve->co);
6619
6620 pb[i] = false;
6621 sb[affected[0]].org_idx = i;
6622 sb[affected[0]++].srt = co[coidx] * fact;
6623 }
6624 else {
6625 pb[i] = true;
6626 }
6627 }
6628 }
6629
6630 if (totelem[1]) {
6631 pb = pblock[1] = MEM_calloc_arrayN<char>(totelem[1], __func__);
6632 sb = sblock[1] = MEM_calloc_arrayN<BMElemSort>(totelem[1], __func__);
6633
6634 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6635 if (BM_elem_flag_test(ed, flag)) {
6636 float co[3];
6637 mid_v3_v3v3(co, ed->v1->co, ed->v2->co);
6638 mul_m4_v3(mat, co);
6639
6640 pb[i] = false;
6641 sb[affected[1]].org_idx = i;
6642 sb[affected[1]++].srt = co[coidx] * fact;
6643 }
6644 else {
6645 pb[i] = true;
6646 }
6647 }
6648 }
6649
6650 if (totelem[2]) {
6651 pb = pblock[2] = MEM_calloc_arrayN<char>(totelem[2], __func__);
6652 sb = sblock[2] = MEM_calloc_arrayN<BMElemSort>(totelem[2], __func__);
6653
6654 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6655 if (BM_elem_flag_test(fa, flag)) {
6656 float co[3];
6658 mul_m4_v3(mat, co);
6659
6660 pb[i] = false;
6661 sb[affected[2]].org_idx = i;
6662 sb[affected[2]++].srt = co[coidx] * fact;
6663 }
6664 else {
6665 pb[i] = true;
6666 }
6667 }
6668 }
6669 }
6670
6671 else if (action == SRT_CURSOR_DISTANCE) {
6672 float cur[3];
6673 float mat[4][4];
6674 float fact = reverse ? -1.0 : 1.0;
6675
6676 copy_v3_v3(cur, scene->cursor.location);
6677
6678 invert_m4_m4(mat, ob->object_to_world().ptr());
6679 mul_m4_v3(mat, cur);
6680
6681 if (totelem[0]) {
6682 pb = pblock[0] = MEM_calloc_arrayN<char>(totelem[0], __func__);
6683 sb = sblock[0] = MEM_calloc_arrayN<BMElemSort>(totelem[0], __func__);
6684
6685 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6686 if (BM_elem_flag_test(ve, flag)) {
6687 pb[i] = false;
6688 sb[affected[0]].org_idx = i;
6689 sb[affected[0]++].srt = len_squared_v3v3(cur, ve->co) * fact;
6690 }
6691 else {
6692 pb[i] = true;
6693 }
6694 }
6695 }
6696
6697 if (totelem[1]) {
6698 pb = pblock[1] = MEM_calloc_arrayN<char>(totelem[1], __func__);
6699 sb = sblock[1] = MEM_calloc_arrayN<BMElemSort>(totelem[1], __func__);
6700
6701 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6702 if (BM_elem_flag_test(ed, flag)) {
6703 float co[3];
6704 mid_v3_v3v3(co, ed->v1->co, ed->v2->co);
6705
6706 pb[i] = false;
6707 sb[affected[1]].org_idx = i;
6708 sb[affected[1]++].srt = len_squared_v3v3(cur, co) * fact;
6709 }
6710 else {
6711 pb[i] = true;
6712 }
6713 }
6714 }
6715
6716 if (totelem[2]) {
6717 pb = pblock[2] = MEM_calloc_arrayN<char>(totelem[2], __func__);
6718 sb = sblock[2] = MEM_calloc_arrayN<BMElemSort>(totelem[2], __func__);
6719
6720 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6721 if (BM_elem_flag_test(fa, flag)) {
6722 float co[3];
6724
6725 pb[i] = false;
6726 sb[affected[2]].org_idx = i;
6727 sb[affected[2]++].srt = len_squared_v3v3(cur, co) * fact;
6728 }
6729 else {
6730 pb[i] = true;
6731 }
6732 }
6733 }
6734 }
6735
6736 /* Faces only! */
6737 else if (action == SRT_MATERIAL && totelem[2]) {
6738 pb = pblock[2] = MEM_calloc_arrayN<char>(totelem[2], __func__);
6739 sb = sblock[2] = MEM_calloc_arrayN<BMElemSort>(totelem[2], __func__);
6740
6741 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6742 if (BM_elem_flag_test(fa, flag)) {
6743 /* Reverse materials' order, not order of faces inside each mat! */
6744 /* NOTE: cannot use totcol, as mat_nr may sometimes be greater... */
6745 float srt = reverse ? float(MAXMAT - fa->mat_nr) : float(fa->mat_nr);
6746 pb[i] = false;
6747 sb[affected[2]].org_idx = i;
6748 /* Multiplying with totface and adding i ensures us
6749 * we keep current order for all faces of same mat. */
6750 sb[affected[2]++].srt = srt * float(totelem[2]) + float(i);
6751 // printf("e: %d; srt: %f; final: %f\n", i, srt, srt * float(totface) + float(i));
6752 }
6753 else {
6754 pb[i] = true;
6755 }
6756 }
6757 }
6758
6759 else if (action == SRT_SELECTED) {
6760 uint *tbuf[3] = {nullptr, nullptr, nullptr}, *tb;
6761
6762 if (totelem[0]) {
6763 tb = tbuf[0] = MEM_calloc_arrayN<uint>(totelem[0], __func__);
6764 mp = map[0] = MEM_calloc_arrayN<uint>(totelem[0], __func__);
6765
6766 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6767 if (BM_elem_flag_test(ve, flag)) {
6768 mp[affected[0]++] = i;
6769 }
6770 else {
6771 *tb = i;
6772 tb++;
6773 }
6774 }
6775 }
6776
6777 if (totelem[1]) {
6778 tb = tbuf[1] = MEM_calloc_arrayN<uint>(totelem[1], __func__);
6779 mp = map[1] = MEM_calloc_arrayN<uint>(totelem[1], __func__);
6780
6781 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6782 if (BM_elem_flag_test(ed, flag)) {
6783 mp[affected[1]++] = i;
6784 }
6785 else {
6786 *tb = i;
6787 tb++;
6788 }
6789 }
6790 }
6791
6792 if (totelem[2]) {
6793 tb = tbuf[2] = MEM_calloc_arrayN<uint>(totelem[2], __func__);
6794 mp = map[2] = MEM_calloc_arrayN<uint>(totelem[2], __func__);
6795
6796 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6797 if (BM_elem_flag_test(fa, flag)) {
6798 mp[affected[2]++] = i;
6799 }
6800 else {
6801 *tb = i;
6802 tb++;
6803 }
6804 }
6805 }
6806
6807 for (j = 3; j--;) {
6808 int tot = totelem[j];
6809 int aff = affected[j];
6810 tb = tbuf[j];
6811 mp = map[j];
6812 if (!(tb && mp)) {
6813 continue;
6814 }
6815 if (ELEM(aff, 0, tot)) {
6816 MEM_freeN(tb);
6817 MEM_freeN(mp);
6818 map[j] = nullptr;
6819 continue;
6820 }
6821 if (reverse) {
6822 memcpy(tb + (tot - aff), mp, aff * sizeof(int));
6823 }
6824 else {
6825 memcpy(mp + aff, tb, (tot - aff) * sizeof(int));
6826 tb = mp;
6827 mp = map[j] = tbuf[j];
6828 tbuf[j] = tb;
6829 }
6830
6831 /* Reverse mapping, we want an org2new one! */
6832 for (i = tot, tb = tbuf[j] + tot - 1; i--; tb--) {
6833 mp[*tb] = i;
6834 }
6835 MEM_freeN(tbuf[j]);
6836 }
6837 }
6838
6839 else if (action == SRT_RANDOMIZE) {
6840 if (totelem[0]) {
6841 /* Re-init random generator for each element type, to get consistent random when
6842 * enabling/disabling an element type. */
6844 pb = pblock[0] = MEM_calloc_arrayN<char>(totelem[0], __func__);
6845 sb = sblock[0] = MEM_calloc_arrayN<BMElemSort>(totelem[0], __func__);
6846
6847 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6848 if (BM_elem_flag_test(ve, flag)) {
6849 pb[i] = false;
6850 sb[affected[0]].org_idx = i;
6851 sb[affected[0]++].srt = BLI_rng_get_float(rng);
6852 }
6853 else {
6854 pb[i] = true;
6855 }
6856 }
6857
6858 BLI_rng_free(rng);
6859 }
6860
6861 if (totelem[1]) {
6863 pb = pblock[1] = MEM_calloc_arrayN<char>(totelem[1], __func__);
6864 sb = sblock[1] = MEM_calloc_arrayN<BMElemSort>(totelem[1], __func__);
6865
6866 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6867 if (BM_elem_flag_test(ed, flag)) {
6868 pb[i] = false;
6869 sb[affected[1]].org_idx = i;
6870 sb[affected[1]++].srt = BLI_rng_get_float(rng);
6871 }
6872 else {
6873 pb[i] = true;
6874 }
6875 }
6876
6877 BLI_rng_free(rng);
6878 }
6879
6880 if (totelem[2]) {
6882 pb = pblock[2] = MEM_calloc_arrayN<char>(totelem[2], __func__);
6883 sb = sblock[2] = MEM_calloc_arrayN<BMElemSort>(totelem[2], __func__);
6884
6885 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6886 if (BM_elem_flag_test(fa, flag)) {
6887 pb[i] = false;
6888 sb[affected[2]].org_idx = i;
6889 sb[affected[2]++].srt = BLI_rng_get_float(rng);
6890 }
6891 else {
6892 pb[i] = true;
6893 }
6894 }
6895
6896 BLI_rng_free(rng);
6897 }
6898 }
6899
6900 else if (action == SRT_REVERSE) {
6901 if (totelem[0]) {
6902 pb = pblock[0] = MEM_calloc_arrayN<char>(totelem[0], __func__);
6903 sb = sblock[0] = MEM_calloc_arrayN<BMElemSort>(totelem[0], __func__);
6904
6905 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6906 if (BM_elem_flag_test(ve, flag)) {
6907 pb[i] = false;
6908 sb[affected[0]].org_idx = i;
6909 sb[affected[0]++].srt = float(-i);
6910 }
6911 else {
6912 pb[i] = true;
6913 }
6914 }
6915 }
6916
6917 if (totelem[1]) {
6918 pb = pblock[1] = MEM_calloc_arrayN<char>(totelem[1], __func__);
6919 sb = sblock[1] = MEM_calloc_arrayN<BMElemSort>(totelem[1], __func__);
6920
6921 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6922 if (BM_elem_flag_test(ed, flag)) {
6923 pb[i] = false;
6924 sb[affected[1]].org_idx = i;
6925 sb[affected[1]++].srt = float(-i);
6926 }
6927 else {
6928 pb[i] = true;
6929 }
6930 }
6931 }
6932
6933 if (totelem[2]) {
6934 pb = pblock[2] = MEM_calloc_arrayN<char>(totelem[2], __func__);
6935 sb = sblock[2] = MEM_calloc_arrayN<BMElemSort>(totelem[2], __func__);
6936
6937 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6938 if (BM_elem_flag_test(fa, flag)) {
6939 pb[i] = false;
6940 sb[affected[2]].org_idx = i;
6941 sb[affected[2]++].srt = float(-i);
6942 }
6943 else {
6944 pb[i] = true;
6945 }
6946 }
6947 }
6948 }
6949
6950 // printf("%d vertices: %d to be affected...\n", totelem[0], affected[0]);
6951 // printf("%d edges: %d to be affected...\n", totelem[1], affected[1]);
6952 // printf("%d faces: %d to be affected...\n", totelem[2], affected[2]);
6953 if (affected[0] == 0 && affected[1] == 0 && affected[2] == 0) {
6954 for (j = 3; j--;) {
6955 if (pblock[j]) {
6956 MEM_freeN(pblock[j]);
6957 }
6958 if (sblock[j]) {
6959 MEM_freeN(sblock[j]);
6960 }
6961 if (map[j]) {
6962 MEM_freeN(map[j]);
6963 }
6964 }
6965 return;
6966 }
6967
6968 /* Sort affected elements, and populate mapping arrays, if needed. */
6969 for (j = 3; j--;) {
6970 pb = pblock[j];
6971 sb = sblock[j];
6972 if (pb && sb && !map[j]) {
6973 const char *p_blk;
6974 BMElemSort *s_blk;
6975 int tot = totelem[j];
6976 int aff = affected[j];
6977
6978 qsort(sb, aff, sizeof(BMElemSort), bmelemsort_comp);
6979
6980 mp = map[j] = MEM_malloc_arrayN<uint>(tot, __func__);
6981 p_blk = pb + tot - 1;
6982 s_blk = sb + aff - 1;
6983 for (i = tot; i--; p_blk--) {
6984 if (*p_blk) { /* Protected! */
6985 mp[i] = i;
6986 }
6987 else {
6988 mp[s_blk->org_idx] = i;
6989 s_blk--;
6990 }
6991 }
6992 }
6993 if (pb) {
6994 MEM_freeN(pb);
6995 }
6996 if (sb) {
6997 MEM_freeN(sb);
6998 }
6999 }
7000
7001 BM_mesh_remap(em->bm, map[0], map[1], map[2]);
7002
7004 params.calc_looptris = (totelem[2] != 0);
7005 params.calc_normals = false;
7006 params.is_destructive = true;
7007 EDBM_update(static_cast<Mesh *>(ob->data), &params);
7008
7009 DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_GEOMETRY);
7011
7012 for (j = 3; j--;) {
7013 if (map[j]) {
7014 MEM_freeN(map[j]);
7015 }
7016 }
7017}
7018
7020{
7021 Scene *scene = CTX_data_scene(C);
7022 ViewLayer *view_layer = CTX_data_view_layer(C);
7023 Object *ob_active = CTX_data_edit_object(C);
7024
7025 /* may be nullptr */
7027
7028 const int action = RNA_enum_get(op->ptr, "type");
7029 PropertyRNA *prop_elem_types = RNA_struct_find_property(op->ptr, "elements");
7030 const bool use_reverse = RNA_boolean_get(op->ptr, "reverse");
7031 uint seed = RNA_int_get(op->ptr, "seed");
7032 int elem_types = 0;
7033
7034 if (ELEM(action, SRT_VIEW_ZAXIS, SRT_VIEW_XAXIS)) {
7035 if (rv3d == nullptr) {
7036 BKE_report(op->reports, RPT_ERROR, "View not found, cannot sort by view axis");
7037 return OPERATOR_CANCELLED;
7038 }
7039 }
7040
7041 /* If no elem_types set, use current selection mode to set it! */
7042 if (RNA_property_is_set(op->ptr, prop_elem_types)) {
7043 elem_types = RNA_property_enum_get(op->ptr, prop_elem_types);
7044 }
7045 else {
7046 BMEditMesh *em = BKE_editmesh_from_object(ob_active);
7047 if (em->selectmode & SCE_SELECT_VERTEX) {
7048 elem_types |= BM_VERT;
7049 }
7050 if (em->selectmode & SCE_SELECT_EDGE) {
7051 elem_types |= BM_EDGE;
7052 }
7053 if (em->selectmode & SCE_SELECT_FACE) {
7054 elem_types |= BM_FACE;
7055 }
7056 RNA_enum_set(op->ptr, "elements", elem_types);
7057 }
7058
7060 scene, view_layer, CTX_wm_view3d(C));
7061
7062 for (uint ob_index = 0; ob_index < objects.size(); ob_index++) {
7063 Object *ob = objects[ob_index];
7065 BMesh *bm = em->bm;
7066
7067 if (!((elem_types & BM_VERT && bm->totvertsel > 0) ||
7068 (elem_types & BM_EDGE && bm->totedgesel > 0) ||
7069 (elem_types & BM_FACE && bm->totfacesel > 0)))
7070 {
7071 continue;
7072 }
7073
7074 int seed_iter = seed;
7075
7076 /* This gives a consistent result regardless of object order */
7077 if (ob_index) {
7078 seed_iter += BLI_ghashutil_strhash_p(ob->id.name);
7079 }
7080
7082 C, scene, ob, rv3d, elem_types, BM_ELEM_SELECT, action, use_reverse, seed_iter);
7083 }
7084 return OPERATOR_FINISHED;
7085}
7086
7088 wmOperator *op,
7089 const PropertyRNA *prop)
7090{
7091 const char *prop_id = RNA_property_identifier(prop);
7092 const int action = RNA_enum_get(op->ptr, "type");
7093
7094 /* Only show seed for randomize action! */
7095 if (STREQ(prop_id, "seed")) {
7096 if (action == SRT_RANDOMIZE) {
7097 return true;
7098 }
7099 return false;
7100 }
7101
7102 /* Hide seed for reverse and randomize actions! */
7103 if (STREQ(prop_id, "reverse")) {
7104 if (ELEM(action, SRT_RANDOMIZE, SRT_REVERSE)) {
7105 return false;
7106 }
7107 return true;
7108 }
7109
7110 return true;
7111}
7112
7114{
7115 static const EnumPropertyItem type_items[] = {
7117 "VIEW_ZAXIS",
7118 0,
7119 "View Z Axis",
7120 "Sort selected elements from farthest to nearest one in current view"},
7122 "VIEW_XAXIS",
7123 0,
7124 "View X Axis",
7125 "Sort selected elements from left to right one in current view"},
7127 "CURSOR_DISTANCE",
7128 0,
7129 "Cursor Distance",
7130 "Sort selected elements from nearest to farthest from 3D cursor"},
7131 {SRT_MATERIAL,
7132 "MATERIAL",
7133 0,
7134 "Material",
7135 "Sort selected faces from smallest to greatest material index"},
7136 {SRT_SELECTED,
7137 "SELECTED",
7138 0,
7139 "Selected",
7140 "Move all selected elements in first places, preserving their relative order.\n"
7141 "Warning: This will affect unselected elements' indices as well"},
7142 {SRT_RANDOMIZE, "RANDOMIZE", 0, "Randomize", "Randomize order of selected elements"},
7143 {SRT_REVERSE, "REVERSE", 0, "Reverse", "Reverse current order of selected elements"},
7144 {0, nullptr, 0, nullptr, nullptr},
7145 };
7146
7147 static const EnumPropertyItem elem_items[] = {
7148 {BM_VERT, "VERT", 0, "Vertices", ""},
7149 {BM_EDGE, "EDGE", 0, "Edges", ""},
7150 {BM_FACE, "FACE", 0, "Faces", ""},
7151 {0, nullptr, 0, nullptr, nullptr},
7152 };
7153
7154 /* identifiers */
7155 ot->name = "Sort Mesh Elements";
7156 ot->description =
7157 "The order of selected vertices/edges/faces is modified, based on a given method";
7158 ot->idname = "MESH_OT_sort_elements";
7159
7160 /* API callbacks. */
7161 ot->invoke = WM_menu_invoke;
7163 ot->poll = ED_operator_editmesh;
7164 ot->poll_property = edbm_sort_elements_poll_property;
7165
7166 /* flags */
7167 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
7168
7169 /* properties */
7170 ot->prop = RNA_def_enum(ot->srna,
7171 "type",
7172 type_items,
7174 "Type",
7175 "Type of reordering operation to apply");
7176 RNA_def_enum_flag(ot->srna,
7177 "elements",
7178 elem_items,
7179 BM_VERT,
7180 "Elements",
7181 "Which elements to affect (vertices, edges and/or faces)");
7182 RNA_def_boolean(ot->srna, "reverse", false, "Reverse", "Reverse the sorting effect");
7183 RNA_def_int(ot->srna, "seed", 0, 0, INT_MAX, "Seed", "Seed for random-based operations", 0, 255);
7184}
7185
7187
7188/* -------------------------------------------------------------------- */
7191
7192enum {
7196};
7197
7199{
7200 /* tags boundary edges from a face selection */
7201 BMIter iter;
7202 BMFace *f;
7203 BMEdge *e;
7204 int totface_del = 0;
7205
7207
7208 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
7212 }
7213 else {
7214 BMIter fiter;
7215 bool is_all_sel = true;
7216 /* check if its only used by selected faces */
7217 BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) {
7219 /* Tag face for removal. */
7220 if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
7222 totface_del++;
7223 }
7224 }
7225 else {
7226 is_all_sel = false;
7227 }
7228 }
7229
7230 if (is_all_sel == false) {
7232 }
7233 }
7234 }
7235 }
7236
7237 return totface_del;
7238}
7239
7241 BMEditMesh *em,
7242 Mesh *mesh,
7243 const bool use_pairs,
7244 const bool use_cyclic,
7245 const bool use_merge,
7246 const float merge_factor,
7247 const int twist_offset)
7248{
7249 BMOperator bmop;
7250 char edge_hflag;
7251 int totface_del = 0;
7252 BMFace **totface_del_arr = nullptr;
7253 const bool use_faces = (em->bm->totfacesel != 0);
7254 bool changed = false;
7255
7256 if (use_faces) {
7257 /* NOTE: When all faces are selected, all faces will be deleted with no edge-loops remaining.
7258 * In this case bridge will fail with a waning and delete all faces.
7259 * Ideally it's possible to detect cases when deleting faces leaves remaining edge-loops.
7260 * While this can be done in trivial cases - by checking the number of selected faces matches
7261 * the number of faces, that won't work for more involved cases involving hidden faces
7262 * and wire edges. One option could be to copy & restore the edit-mesh however
7263 * this is quite an expensive operation - to properly handle clearly invalid input.
7264 * Accept this limitation, the user must undo to restore the previous state, see: #123405. */
7265
7266 BMIter iter;
7267 BMFace *f;
7268 int i;
7269
7270 totface_del = edbm_bridge_tag_boundary_edges(em->bm);
7271 totface_del_arr = static_cast<BMFace **>(
7272 MEM_mallocN(sizeof(*totface_del_arr) * totface_del, __func__));
7273
7274 i = 0;
7275 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
7277 totface_del_arr[i++] = f;
7278 }
7279 }
7280 edge_hflag = BM_ELEM_TAG;
7281 }
7282 else {
7283 edge_hflag = BM_ELEM_SELECT;
7284 }
7285
7286 EDBM_op_init(em,
7287 &bmop,
7288 op,
7289 "bridge_loops edges=%he use_pairs=%b use_cyclic=%b use_merge=%b merge_factor=%f "
7290 "twist_offset=%i",
7291 edge_hflag,
7292 use_pairs,
7293 use_cyclic,
7294 use_merge,
7295 merge_factor,
7296 twist_offset);
7297
7298 if (use_faces && totface_del) {
7299 int i;
7301 for (i = 0; i < totface_del; i++) {
7302 BM_elem_flag_enable(totface_del_arr[i], BM_ELEM_TAG);
7303 }
7304 BMO_op_callf(em->bm,
7306 "delete geom=%hf context=%i",
7309 changed = true;
7310 }
7311
7312 BMO_op_exec(em->bm, &bmop);
7313
7315 /* when merge is used the edges are joined and remain selected */
7316 if (use_merge == false) {
7319 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
7320
7321 changed = true;
7322 }
7323
7324 if (use_merge == false) {
7325 EdgeRingOpSubdProps op_props;
7326 mesh_operator_edgering_props_get(op, &op_props);
7327
7328 if (op_props.cuts) {
7329 BMOperator bmop_subd;
7330 /* we only need face normals updated */
7332
7333 BMO_op_initf(em->bm,
7334 &bmop_subd,
7335 0,
7336 "subdivide_edgering edges=%S interp_mode=%i cuts=%i smooth=%f "
7337 "profile_shape=%i profile_shape_factor=%f",
7338 &bmop,
7339 "edges.out",
7340 op_props.interp_mode,
7341 op_props.cuts,
7342 op_props.smooth,
7343 op_props.profile_shape,
7344 op_props.profile_shape_factor);
7345 BMO_op_exec(em->bm, &bmop_subd);
7347 em->bm, bmop_subd.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
7348 BMO_op_finish(em->bm, &bmop_subd);
7349
7350 changed = true;
7351 }
7352 }
7353 }
7354
7355 if (totface_del_arr) {
7356 MEM_freeN(totface_del_arr);
7357 }
7358
7359 if (EDBM_op_finish(em, &bmop, op, true)) {
7360 changed = true;
7361 }
7362
7363 if (changed) {
7365 params.calc_looptris = true;
7366 params.calc_normals = false;
7367 params.is_destructive = true;
7368 EDBM_update(mesh, &params);
7369 }
7370
7371 /* Always return finished so the user can select different options. */
7372 return OPERATOR_FINISHED;
7373}
7374
7376{
7377 const int type = RNA_enum_get(op->ptr, "type");
7378 const bool use_pairs = (type == MESH_BRIDGELOOP_PAIRS);
7379 const bool use_cyclic = (type == MESH_BRIDGELOOP_CLOSED);
7380 const bool use_merge = RNA_boolean_get(op->ptr, "use_merge");
7381 const float merge_factor = RNA_float_get(op->ptr, "merge_factor");
7382 const int twist_offset = RNA_int_get(op->ptr, "twist_offset");
7383 const Scene *scene = CTX_data_scene(C);
7384 ViewLayer *view_layer = CTX_data_view_layer(C);
7385
7387 scene, view_layer, CTX_wm_view3d(C));
7388 for (Object *obedit : objects) {
7390
7391 if (em->bm->totvertsel == 0) {
7392 continue;
7393 }
7394
7396 em,
7397 static_cast<Mesh *>(obedit->data),
7398 use_pairs,
7399 use_cyclic,
7400 use_merge,
7401 merge_factor,
7402 twist_offset);
7403 }
7404 return OPERATOR_FINISHED;
7405}
7406
7408{
7409 static const EnumPropertyItem type_items[] = {
7410 {MESH_BRIDGELOOP_SINGLE, "SINGLE", 0, "Open Loop", ""},
7411 {MESH_BRIDGELOOP_CLOSED, "CLOSED", 0, "Closed Loop", ""},
7412 {MESH_BRIDGELOOP_PAIRS, "PAIRS", 0, "Loop Pairs", ""},
7413 {0, nullptr, 0, nullptr, nullptr},
7414 };
7415
7416 /* identifiers */
7417 ot->name = "Bridge Edge Loops";
7418 ot->description = "Create a bridge of faces between two or more selected edge loops";
7419 ot->idname = "MESH_OT_bridge_edge_loops";
7420
7421 /* API callbacks. */
7423 ot->poll = ED_operator_editmesh;
7424
7425 /* flags */
7426 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
7427
7428 ot->prop = RNA_def_enum(ot->srna,
7429 "type",
7430 type_items,
7432 "Connect Loops",
7433 "Method of bridging multiple loops");
7434
7435 RNA_def_boolean(ot->srna, "use_merge", false, "Merge", "Merge rather than creating faces");
7436 RNA_def_float(ot->srna, "merge_factor", 0.5f, 0.0f, 1.0f, "Merge Factor", "", 0.0f, 1.0f);
7437 RNA_def_int(ot->srna,
7438 "twist_offset",
7439 0,
7440 -1000,
7441 1000,
7442 "Twist",
7443 "Twist offset for closed loops",
7444 -1000,
7445 1000);
7446
7448}
7449
7451
7452/* -------------------------------------------------------------------- */
7455
7457{
7458 const bool use_boundary = RNA_boolean_get(op->ptr, "use_boundary");
7459 const bool use_even_offset = RNA_boolean_get(op->ptr, "use_even_offset");
7460 const bool use_replace = RNA_boolean_get(op->ptr, "use_replace");
7461 const bool use_relative_offset = RNA_boolean_get(op->ptr, "use_relative_offset");
7462 const bool use_crease = RNA_boolean_get(op->ptr, "use_crease");
7463 const float crease_weight = RNA_float_get(op->ptr, "crease_weight");
7464 const float thickness = RNA_float_get(op->ptr, "thickness");
7465 const float offset = RNA_float_get(op->ptr, "offset");
7466
7467 const Scene *scene = CTX_data_scene(C);
7468 ViewLayer *view_layer = CTX_data_view_layer(C);
7470 scene, view_layer, CTX_wm_view3d(C));
7471 for (Object *obedit : objects) {
7473
7474 if (em->bm->totfacesel == 0) {
7475 continue;
7476 }
7477
7478 BMOperator bmop;
7479
7480 EDBM_op_init(em,
7481 &bmop,
7482 op,
7483 "wireframe faces=%hf use_replace=%b use_boundary=%b use_even_offset=%b "
7484 "use_relative_offset=%b "
7485 "use_crease=%b crease_weight=%f thickness=%f offset=%f",
7487 use_replace,
7488 use_boundary,
7489 use_even_offset,
7490 use_relative_offset,
7491 use_crease,
7492 crease_weight,
7493 thickness,
7494 offset);
7495
7496 BMO_op_exec(em->bm, &bmop);
7497
7500 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
7501
7502 if (!EDBM_op_finish(em, &bmop, op, true)) {
7503 continue;
7504 }
7505
7507 params.calc_looptris = true;
7508 params.calc_normals = false;
7509 params.is_destructive = true;
7510 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7511 }
7512
7513 return OPERATOR_FINISHED;
7514}
7515
7517{
7518 PropertyRNA *prop;
7519
7520 /* identifiers */
7521 ot->name = "Wireframe";
7522 ot->idname = "MESH_OT_wireframe";
7523 ot->description = "Create a solid wireframe from faces";
7524
7525 /* API callbacks. */
7526 ot->exec = edbm_wireframe_exec;
7527 ot->poll = ED_operator_editmesh;
7528
7529 /* flags */
7530 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
7531
7532 /* properties */
7533 RNA_def_boolean(ot->srna, "use_boundary", true, "Boundary", "Inset face boundaries");
7534 RNA_def_boolean(ot->srna,
7535 "use_even_offset",
7536 true,
7537 "Offset Even",
7538 "Scale the offset to give more even thickness");
7539 RNA_def_boolean(ot->srna,
7540 "use_relative_offset",
7541 false,
7542 "Offset Relative",
7543 "Scale the offset by surrounding geometry");
7544 RNA_def_boolean(ot->srna, "use_replace", true, "Replace", "Remove original faces");
7546 ot->srna, "thickness", 0.01f, 0.0f, 1e4f, "Thickness", "", 0.0f, 10.0f);
7547 /* use 1 rather than 10 for max else dragging the button moves too far */
7548 RNA_def_property_ui_range(prop, 0.0, 1.0, 0.01, 4);
7549 RNA_def_float_distance(ot->srna, "offset", 0.01f, 0.0f, 1e4f, "Offset", "", 0.0f, 10.0f);
7550 RNA_def_boolean(ot->srna,
7551 "use_crease",
7552 false,
7553 "Crease",
7554 "Crease hub edges for an improved subdivision surface");
7555 prop = RNA_def_float(
7556 ot->srna, "crease_weight", 0.01f, 0.0f, 1e3f, "Crease Weight", "", 0.0f, 1.0f);
7557 RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 2);
7558}
7559
7561
7562/* -------------------------------------------------------------------- */
7565
7567{
7568 const bool use_cap_endpoint = RNA_boolean_get(op->ptr, "use_cap_endpoint");
7569 bool changed_multi = false;
7570 Scene *scene = CTX_data_scene(C);
7571 ViewLayer *view_layer = CTX_data_view_layer(C);
7573 scene, view_layer, CTX_wm_view3d(C));
7574 for (Base *base : bases) {
7575 Object *obedit = base->object;
7577
7578 if (em->bm->totedgesel == 0) {
7579 continue;
7580 }
7581
7582 BMOperator bmop;
7583 EDBM_op_init(em,
7584 &bmop,
7585 op,
7586 "offset_edgeloops edges=%he use_cap_endpoint=%b",
7588 use_cap_endpoint);
7589
7590 BMO_op_exec(em->bm, &bmop);
7591
7593
7595 em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true);
7596
7597 if (EDBM_op_finish(em, &bmop, op, true)) {
7599 params.calc_looptris = true;
7600 params.calc_normals = false;
7601 params.is_destructive = true;
7602 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7603 changed_multi = true;
7604 }
7605 }
7606
7607 if (changed_multi) {
7613 if (scene->toolsettings->selectmode == SCE_SELECT_FACE) {
7615 }
7616 }
7617
7618 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
7619}
7620
7622{
7623 /* identifiers */
7624 ot->name = "Offset Edge Loop";
7625 ot->idname = "MESH_OT_offset_edge_loops";
7626 ot->description = "Create offset edge loop from the current selection";
7627
7628 /* API callbacks. */
7630 ot->poll = ED_operator_editmesh;
7631
7632 /* Keep internal, since this is only meant to be accessed via
7633 * `MESH_OT_offset_edge_loops_slide`. */
7634
7635 /* flags */
7637
7639 ot->srna, "use_cap_endpoint", false, "Cap Endpoint", "Extend loop around end-points");
7640}
7641
7643
7644/* -------------------------------------------------------------------- */
7647
7648#ifdef WITH_BULLET
7649static wmOperatorStatus edbm_convex_hull_exec(bContext *C, wmOperator *op)
7650{
7651 const bool use_existing_faces = RNA_boolean_get(op->ptr, "use_existing_faces");
7652 const bool delete_unused = RNA_boolean_get(op->ptr, "delete_unused");
7653 const bool make_holes = RNA_boolean_get(op->ptr, "make_holes");
7654 const bool join_triangles = RNA_boolean_get(op->ptr, "join_triangles");
7655
7656 float angle_face_threshold = RNA_float_get(op->ptr, "face_threshold");
7657 float angle_shape_threshold = RNA_float_get(op->ptr, "shape_threshold");
7658
7659 const Scene *scene = CTX_data_scene(C);
7660 ViewLayer *view_layer = CTX_data_view_layer(C);
7662 scene, view_layer, CTX_wm_view3d(C));
7663 for (Object *obedit : objects) {
7665
7666 if (em->bm->totvertsel == 0) {
7667 continue;
7668 }
7669
7670 BMOperator bmop;
7671
7672 EDBM_op_init(em,
7673 &bmop,
7674 op,
7675 "convex_hull input=%hvef "
7676 "use_existing_faces=%b",
7678 use_existing_faces);
7679 BMO_op_exec(em->bm, &bmop);
7680
7681 /* Hull fails if input is coplanar */
7683 EDBM_op_finish(em, &bmop, op, true);
7684 continue;
7685 }
7686
7688 em->bm, bmop.slots_out, "geom.out", BM_FACE, BM_ELEM_SELECT, true);
7689
7690 /* Delete unused vertices, edges, and faces */
7691 if (delete_unused) {
7692 if (!EDBM_op_callf(
7693 em, op, "delete geom=%S context=%i", &bmop, "geom_unused.out", DEL_ONLYTAGGED))
7694 {
7695 EDBM_op_finish(em, &bmop, op, true);
7696 continue;
7697 }
7698 }
7699
7700 /* Delete hole edges/faces */
7701 if (make_holes) {
7702 if (!EDBM_op_callf(
7703 em, op, "delete geom=%S context=%i", &bmop, "geom_holes.out", DEL_ONLYTAGGED))
7704 {
7705 EDBM_op_finish(em, &bmop, op, true);
7706 continue;
7707 }
7708 }
7709
7710 /* Merge adjacent triangles */
7711 if (join_triangles) {
7713 op,
7714 "faces.out",
7715 true,
7716 "join_triangles faces=%S "
7717 "angle_face_threshold=%f angle_shape_threshold=%f",
7718 &bmop,
7719 "geom.out",
7720 angle_face_threshold,
7721 angle_shape_threshold))
7722 {
7723 EDBM_op_finish(em, &bmop, op, true);
7724 continue;
7725 }
7726 }
7727
7728 if (!EDBM_op_finish(em, &bmop, op, true)) {
7729 continue;
7730 }
7731
7733 params.calc_looptris = true;
7734 params.calc_normals = false;
7735 params.is_destructive = true;
7736 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7737
7740 }
7741
7742 return OPERATOR_FINISHED;
7743}
7744
7746{
7747 /* identifiers */
7748 ot->name = "Convex Hull";
7749 ot->description = "Enclose selected vertices in a convex polyhedron";
7750 ot->idname = "MESH_OT_convex_hull";
7751
7752 /* API callbacks. */
7753 ot->exec = edbm_convex_hull_exec;
7754 ot->poll = ED_operator_editmesh;
7755
7756 /* flags */
7757 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
7758
7759 /* props */
7760 RNA_def_boolean(ot->srna,
7761 "delete_unused",
7762 true,
7763 "Delete Unused",
7764 "Delete selected elements that are not used by the hull");
7765
7766 RNA_def_boolean(ot->srna,
7767 "use_existing_faces",
7768 true,
7769 "Use Existing Faces",
7770 "Skip hull triangles that are covered by a pre-existing face");
7771
7772 RNA_def_boolean(ot->srna,
7773 "make_holes",
7774 false,
7775 "Make Holes",
7776 "Delete selected faces that are used by the hull");
7777
7779 ot->srna, "join_triangles", true, "Join Triangles", "Merge adjacent triangles into quads");
7780
7782}
7783#endif /* WITH_BULLET */
7784
7786
7787/* -------------------------------------------------------------------- */
7790
7792{
7793 const float thresh = RNA_float_get(op->ptr, "threshold");
7794 const Scene *scene = CTX_data_scene(C);
7795 ViewLayer *view_layer = CTX_data_view_layer(C);
7797 scene, view_layer, CTX_wm_view3d(C));
7798
7799 for (Object *obedit : objects) {
7801
7802 if (em->bm->totvertsel == 0) {
7803 continue;
7804 }
7805
7806 BMOperator bmop;
7807 EDBM_op_init(em,
7808 &bmop,
7809 op,
7810 "symmetrize input=%hvef direction=%i dist=%f",
7812 RNA_enum_get(op->ptr, "direction"),
7813 thresh);
7814 BMO_op_exec(em->bm, &bmop);
7815
7817
7819 em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
7820
7821 if (!EDBM_op_finish(em, &bmop, op, true)) {
7822 continue;
7823 }
7824
7825 bool calc_normals = false;
7826 if (scene->toolsettings->automerge & AUTO_MERGE) {
7827 calc_normals = EDBM_automerge_connected(
7828 obedit, false, BM_ELEM_SELECT, scene->toolsettings->doublimit);
7829 }
7830
7832 params.calc_looptris = true;
7833 params.calc_normals = calc_normals;
7834 params.is_destructive = true;
7835 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7836
7839 }
7840
7841 return OPERATOR_FINISHED;
7842}
7843
7845{
7846 /* identifiers */
7847 ot->name = "Symmetrize";
7848 ot->description = "Enforce symmetry (both form and topological) across an axis";
7849 ot->idname = "MESH_OT_symmetrize";
7850
7851 /* API callbacks. */
7852 ot->exec = mesh_symmetrize_exec;
7853 ot->poll = ED_operator_editmesh;
7854
7855 /* flags */
7856 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
7857
7858 ot->prop = RNA_def_enum(ot->srna,
7859 "direction",
7862 "Direction",
7863 "Which sides to copy from and to");
7864 RNA_def_float(ot->srna,
7865 "threshold",
7866 1e-4f,
7867 0.0f,
7868 10.0f,
7869 "Threshold",
7870 "Limit for snap middle vertices to the axis center",
7871 1e-5f,
7872 0.1f);
7873}
7874
7876
7877/* -------------------------------------------------------------------- */
7880
7882{
7883 const float eps = 0.00001f;
7884 const float eps_sq = eps * eps;
7885 const bool use_topology = false;
7886
7887 const float thresh = RNA_float_get(op->ptr, "threshold");
7888 const float fac = RNA_float_get(op->ptr, "factor");
7889 const bool use_center = RNA_boolean_get(op->ptr, "use_center");
7890 const int axis_dir = RNA_enum_get(op->ptr, "direction");
7891
7892 /* Vertices stats (total over all selected objects). */
7893 int totvertfound = 0, totvertmirr = 0, totvertfail = 0, totobjects = 0;
7894
7895 /* Axis. */
7896 int axis = axis_dir % 3;
7897 bool axis_sign = axis != axis_dir;
7898
7899 const Scene *scene = CTX_data_scene(C);
7900 ViewLayer *view_layer = CTX_data_view_layer(C);
7902 scene, view_layer, CTX_wm_view3d(C));
7903
7904 for (Object *obedit : objects) {
7906 BMesh *bm = em->bm;
7907
7908 if (em->bm->totvertsel == 0) {
7909 continue;
7910 }
7911
7913 continue;
7914 }
7915
7916 totobjects++;
7917
7918 /* Only allocate memory after checking whether to skip object. */
7919 int *index = MEM_malloc_arrayN<int>(bm->totvert, __func__);
7920
7921 /* Vertex iter. */
7922 BMIter iter;
7923 BMVert *v;
7924 int i;
7925
7926 EDBM_verts_mirror_cache_begin_ex(em, axis, true, true, false, use_topology, thresh, index);
7927
7929
7931
7933 if ((BM_elem_flag_test(v, BM_ELEM_SELECT) != false) &&
7934 (BM_elem_flag_test(v, BM_ELEM_TAG) == false))
7935 {
7936 int i_mirr = index[i];
7937 if (i_mirr != -1) {
7938
7939 BMVert *v_mirr = BM_vert_at_index(bm, index[i]);
7940
7941 if (v != v_mirr) {
7942 float co[3], co_mirr[3];
7943
7944 if ((v->co[axis] > v_mirr->co[axis]) == axis_sign) {
7945 std::swap(v, v_mirr);
7946 }
7947
7948 copy_v3_v3(co_mirr, v_mirr->co);
7949 co_mirr[axis] *= -1.0f;
7950
7951 if (len_squared_v3v3(v->co, co_mirr) > eps_sq) {
7952 totvertmirr++;
7953 }
7954
7955 interp_v3_v3v3(co, v->co, co_mirr, fac);
7956
7957 copy_v3_v3(v->co, co);
7958
7959 co[axis] *= -1.0f;
7960 copy_v3_v3(v_mirr->co, co);
7961
7964 totvertfound++;
7965 }
7966 else {
7967 if (use_center) {
7968
7969 if (fabsf(v->co[axis]) > eps) {
7970 totvertmirr++;
7971 }
7972
7973 v->co[axis] = 0.0f;
7974 }
7976 totvertfound++;
7977 }
7978 }
7979 else {
7980 totvertfail++;
7981 }
7982 }
7983 }
7985 params.calc_looptris = false;
7986 params.calc_normals = false;
7987 params.is_destructive = false;
7988 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7989
7990 /* No need to end cache, just free the array. */
7991 MEM_freeN(index);
7992 }
7993
7994 if (totvertfail) {
7995 BKE_reportf(op->reports,
7997 "%d already symmetrical, %d pairs mirrored, %d failed",
7998 totvertfound - totvertmirr,
7999 totvertmirr,
8000 totvertfail);
8001 }
8002 else if (totobjects) {
8003 BKE_reportf(op->reports,
8004 RPT_INFO,
8005 "%d already symmetrical, %d pairs mirrored",
8006 totvertfound - totvertmirr,
8007 totvertmirr);
8008 }
8009
8010 return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
8011}
8012
8014{
8015 /* identifiers */
8016 ot->name = "Snap to Symmetry";
8017 ot->description = "Snap vertex pairs to their mirrored locations";
8018 ot->idname = "MESH_OT_symmetry_snap";
8019
8020 /* API callbacks. */
8022 ot->poll = ED_operator_editmesh;
8023
8024 /* flags */
8025 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
8026
8027 ot->prop = RNA_def_enum(ot->srna,
8028 "direction",
8031 "Direction",
8032 "Which sides to copy from and to");
8034 "threshold",
8035 0.05f,
8036 0.0f,
8037 10.0f,
8038 "Threshold",
8039 "Distance within which matching vertices are searched",
8040 1e-4f,
8041 1.0f);
8042 RNA_def_float(ot->srna,
8043 "factor",
8044 0.5f,
8045 0.0f,
8046 1.0f,
8047 "Factor",
8048 "Mix factor of the locations of the vertices",
8049 0.0f,
8050 1.0f);
8052 ot->srna, "use_center", true, "Center", "Snap middle vertices to the axis center");
8053}
8054
8056
8057#if defined(WITH_FREESTYLE)
8058
8059/* -------------------------------------------------------------------- */
8062
8063static wmOperatorStatus edbm_mark_freestyle_edge_exec(bContext *C, wmOperator *op)
8064{
8065 BMEdge *eed;
8066 BMIter iter;
8067 const bool clear = RNA_boolean_get(op->ptr, "clear");
8068 const Scene *scene = CTX_data_scene(C);
8069 ViewLayer *view_layer = CTX_data_view_layer(C);
8070
8072 scene, view_layer, CTX_wm_view3d(C));
8073 for (Object *obedit : objects) {
8075
8076 if (em == nullptr) {
8077 continue;
8078 }
8079
8080 BMesh *bm = em->bm;
8081
8082 if (bm->totedgesel == 0) {
8083 continue;
8084 }
8085
8086 BM_data_layer_ensure_named(bm, &em->bm->edata, CD_PROP_BOOL, "freestyle_edge");
8087 const int offset = CustomData_get_offset_named(&em->bm->edata, CD_PROP_BOOL, "freestyle_edge");
8088 if (offset == -1) {
8089 continue;
8090 }
8091
8092 if (clear) {
8093 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
8095 BM_ELEM_CD_SET_BOOL(eed, offset, false);
8096 }
8097 }
8098 }
8099 else {
8100 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
8102 BM_ELEM_CD_SET_BOOL(eed, offset, true);
8103 }
8104 }
8105 }
8106
8107 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
8108 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
8109 }
8110
8111 return OPERATOR_FINISHED;
8112}
8113
8114void MESH_OT_mark_freestyle_edge(wmOperatorType *ot)
8115{
8116 PropertyRNA *prop;
8117
8118 /* identifiers */
8119 ot->name = "Mark Freestyle Edge";
8120 ot->description = "(Un)mark selected edges as Freestyle feature edges";
8121 ot->idname = "MESH_OT_mark_freestyle_edge";
8122
8123 /* API callbacks. */
8124 ot->exec = edbm_mark_freestyle_edge_exec;
8125 ot->poll = ED_operator_editmesh;
8126
8127 /* flags */
8128 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
8129
8130 prop = RNA_def_boolean(ot->srna, "clear", false, "Clear", "");
8132}
8133
8135
8136/* -------------------------------------------------------------------- */
8139
8140static wmOperatorStatus edbm_mark_freestyle_face_exec(bContext *C, wmOperator *op)
8141{
8142 BMFace *efa;
8143 BMIter iter;
8144 const bool clear = RNA_boolean_get(op->ptr, "clear");
8145 const Scene *scene = CTX_data_scene(C);
8146 ViewLayer *view_layer = CTX_data_view_layer(C);
8147
8149 scene, view_layer, CTX_wm_view3d(C));
8150 for (Object *obedit : objects) {
8152
8153 if (em == nullptr) {
8154 continue;
8155 }
8156
8157 if (em->bm->totfacesel == 0) {
8158 continue;
8159 }
8160
8161 BM_data_layer_ensure_named(em->bm, &em->bm->pdata, CD_PROP_BOOL, "freestyle_face");
8162 const int offset = CustomData_get_offset_named(&em->bm->pdata, CD_PROP_BOOL, "freestyle_face");
8163 if (offset == -1) {
8164 continue;
8165 }
8166
8167 if (clear) {
8168 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
8170 BM_ELEM_CD_SET_BOOL(efa, offset, false);
8171 }
8172 }
8173 }
8174 else {
8175 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
8177 BM_ELEM_CD_SET_BOOL(efa, offset, true);
8178 }
8179 }
8180 }
8181
8182 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
8183 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
8184 }
8185
8186 return OPERATOR_FINISHED;
8187}
8188
8189void MESH_OT_mark_freestyle_face(wmOperatorType *ot)
8190{
8191 PropertyRNA *prop;
8192
8193 /* identifiers */
8194 ot->name = "Mark Freestyle Face";
8195 ot->description = "(Un)mark selected faces for exclusion from Freestyle feature edge detection";
8196 ot->idname = "MESH_OT_mark_freestyle_face";
8197
8198 /* API callbacks. */
8199 ot->exec = edbm_mark_freestyle_face_exec;
8200 ot->poll = ED_operator_editmesh;
8201
8202 /* flags */
8203 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
8204
8205 prop = RNA_def_boolean(ot->srna, "clear", false, "Clear", "");
8207}
8208
8210
8211#endif /* WITH_FREESTYLE */
8212
8213/* -------------------------------------------------------------------- */
8216
8217/* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
8218/* NOTE: We could add more here, like e.g. a switch between local or global coordinates of target,
8219 * use number-input to type in explicit vector values. */
8220enum {
8221 /* Generic commands. */
8224
8225 /* Point To operator. */
8230
8236};
8237
8239{
8240 static const EnumPropertyItem modal_items[] = {
8241 {EDBM_CLNOR_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
8242 {EDBM_CLNOR_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
8243
8244 /* Point To operator. */
8245 {EDBM_CLNOR_MODAL_POINTTO_RESET, "RESET", 0, "Reset", "Reset normals to initial ones"},
8247 "INVERT",
8248 0,
8249 "Invert",
8250 "Toggle inversion of affected normals"},
8252 "SPHERIZE",
8253 0,
8254 "Spherize",
8255 "Interpolate between new and original normals"},
8256 {EDBM_CLNOR_MODAL_POINTTO_ALIGN, "ALIGN", 0, "Align", "Make all affected normals parallel"},
8257
8259 "USE_MOUSE",
8260 0,
8261 "Use Mouse",
8262 "Follow mouse cursor position"},
8264 "USE_PIVOT",
8265 0,
8266 "Use Pivot",
8267 "Use current rotation/scaling pivot point coordinates"},
8269 "USE_OBJECT",
8270 0,
8271 "Use Object",
8272 "Use current edited object's location"},
8274 "SET_USE_3DCURSOR",
8275 0,
8276 "Set and Use 3D Cursor",
8277 "Set new 3D cursor position and use it"},
8279 "SET_USE_SELECTED",
8280 0,
8281 "Select and Use Mesh Item",
8282 "Select new active mesh element and use its location"},
8283 {0, nullptr, 0, nullptr, nullptr},
8284 };
8285 static const char *keymap_name = "Custom Normals Modal Map";
8286
8287 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, keymap_name);
8288
8289 /* We only need to add map once */
8290 if (keymap && keymap->modal_items) {
8291 return nullptr;
8292 }
8293
8294 keymap = WM_modalkeymap_ensure(keyconf, keymap_name, modal_items);
8295
8296 WM_modalkeymap_assign(keymap, "MESH_OT_point_normals");
8297
8298 return keymap;
8299}
8300
8301#define CLNORS_VALID_VEC_LEN (1e-4f)
8302
8304
8305/* -------------------------------------------------------------------- */
8308
8309enum {
8312};
8313
8316 "COORDINATES",
8317 0,
8318 "Coordinates",
8319 "Use static coordinates (defined by various means)"},
8320 {EDBM_CLNOR_POINTTO_MODE_MOUSE, "MOUSE", 0, "Mouse", "Follow mouse cursor"},
8321 {0, nullptr, 0, nullptr, nullptr},
8322};
8323
8324/* Initialize loop normal data */
8326{
8327 Object *obedit = CTX_data_edit_object(C);
8329 BMesh *bm = em->bm;
8330
8333
8334 op->customdata = lnors_ed_arr;
8335
8336 return (lnors_ed_arr->totloop != 0);
8337}
8338
8340{
8341 if (op->customdata != nullptr) {
8342 return true;
8343 }
8344 return point_normals_init(C, op);
8345}
8346
8348{
8349 if (op->customdata != nullptr) {
8350 BMLoopNorEditDataArray *lnors_ed_arr = static_cast<BMLoopNorEditDataArray *>(op->customdata);
8352 op->customdata = nullptr;
8353 }
8354}
8355
8357{
8359 ED_workspace_status_text(C, nullptr);
8360}
8361
8363{
8365
8366 status.opmodal(IFACE_("Confirm"), op->type, EDBM_CLNOR_MODAL_CONFIRM);
8367 status.opmodal(IFACE_("Cancel"), op->type, EDBM_CLNOR_MODAL_CANCEL);
8368 status.opmodal(IFACE_("Reset"), op->type, EDBM_CLNOR_MODAL_POINTTO_RESET);
8369
8370 status.opmodal(IFACE_("Invert"),
8371 op->type,
8373 RNA_boolean_get(op->ptr, "invert"));
8374 status.opmodal(IFACE_("Spherize"),
8375 op->type,
8377 RNA_boolean_get(op->ptr, "spherize"));
8378 status.opmodal(IFACE_("Align"),
8379 op->type,
8381 RNA_boolean_get(op->ptr, "align"));
8382
8383 status.opmodal(IFACE_("Use mouse"),
8384 op->type,
8387
8388 status.opmodal(IFACE_("Use Pivot"), op->type, EDBM_CLNOR_MODAL_POINTTO_USE_PIVOT);
8389 status.opmodal(IFACE_("Use Object"), op->type, EDBM_CLNOR_MODAL_POINTTO_USE_OBJECT);
8390 status.opmodal(
8391 IFACE_("Set and use 3D cursor"), op->type, EDBM_CLNOR_MODAL_POINTTO_SET_USE_3DCURSOR);
8392 status.opmodal(
8393 IFACE_("Select and use mesh item"), op->type, EDBM_CLNOR_MODAL_POINTTO_SET_USE_SELECTED);
8394}
8395
8396/* TODO: move that to generic function in BMesh? */
8397static void bmesh_selected_verts_center_calc(BMesh *bm, float *r_center)
8398{
8399 BMVert *v;
8400 BMIter viter;
8401 int i = 0;
8402
8403 zero_v3(r_center);
8404 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
8406 add_v3_v3(r_center, v->co);
8407 i++;
8408 }
8409 }
8410 mul_v3_fl(r_center, 1.0f / float(i));
8411}
8412
8413static void point_normals_apply(bContext *C, wmOperator *op, float target[3], const bool do_reset)
8414{
8415 Object *obedit = CTX_data_edit_object(C);
8416 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
8417 BMLoopNorEditDataArray *lnors_ed_arr = static_cast<BMLoopNorEditDataArray *>(op->customdata);
8418
8419 const bool do_invert = RNA_boolean_get(op->ptr, "invert");
8420 const bool do_spherize = RNA_boolean_get(op->ptr, "spherize");
8421 const bool do_align = RNA_boolean_get(op->ptr, "align");
8422 float center[3];
8423
8424 if (do_align && !do_reset) {
8426 }
8427
8428 sub_v3_v3(target, obedit->loc); /* Move target to local coordinates. */
8429
8430 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
8431 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
8432 if (do_reset) {
8433 copy_v3_v3(lnor_ed->nloc, lnor_ed->niloc);
8434 }
8435 else if (do_spherize) {
8436 /* Note that this is *not* real spherical interpolation.
8437 * Probably good enough in this case though? */
8438 const float strength = RNA_float_get(op->ptr, "spherize_strength");
8439 float spherized_normal[3];
8440
8441 sub_v3_v3v3(spherized_normal, target, lnor_ed->loc);
8442
8443 /* otherwise, multiplication by strength is meaningless... */
8444 normalize_v3(spherized_normal);
8445
8446 mul_v3_fl(spherized_normal, strength);
8447 mul_v3_v3fl(lnor_ed->nloc, lnor_ed->niloc, 1.0f - strength);
8448 add_v3_v3(lnor_ed->nloc, spherized_normal);
8449 }
8450 else if (do_align) {
8451 sub_v3_v3v3(lnor_ed->nloc, target, center);
8452 }
8453 else {
8454 sub_v3_v3v3(lnor_ed->nloc, target, lnor_ed->loc);
8455 }
8456
8457 if (do_invert && !do_reset) {
8458 negate_v3(lnor_ed->nloc);
8459 }
8460 if (normalize_v3(lnor_ed->nloc) >= CLNORS_VALID_VEC_LEN) {
8462 bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->nloc, lnor_ed->clnors_data);
8463 }
8464 }
8465}
8466
8468{
8469 /* As this operator passes events through, we can't be sure the user didn't exit edit-mode.
8470 * or performed some other operation. */
8471 if (!WM_operator_poll(C, op->type)) {
8473 return OPERATOR_CANCELLED;
8474 }
8475
8476 View3D *v3d = CTX_wm_view3d(C);
8477 Scene *scene = CTX_data_scene(C);
8478 Object *obedit = CTX_data_edit_object(C);
8480 BMesh *bm = em->bm;
8481
8482 float target[3];
8483
8485 int mode = RNA_enum_get(op->ptr, "mode");
8486 int new_mode = mode;
8487 bool force_mousemove = false;
8488 bool do_reset = false;
8489
8490 PropertyRNA *prop_target = RNA_struct_find_property(op->ptr, "target_location");
8491
8492 if (event->type == EVT_MODAL_MAP) {
8493 switch (event->val) {
8495 RNA_property_float_get_array(op->ptr, prop_target, target);
8497 break;
8498
8500 do_reset = true;
8502 break;
8503
8505 do_reset = true;
8507 break;
8508
8510 PropertyRNA *prop_invert = RNA_struct_find_property(op->ptr, "invert");
8512 op->ptr, prop_invert, !RNA_property_boolean_get(op->ptr, prop_invert));
8513 RNA_property_float_get_array(op->ptr, prop_target, target);
8515 break;
8516 }
8517
8519 PropertyRNA *prop_spherize = RNA_struct_find_property(op->ptr, "spherize");
8521 op->ptr, prop_spherize, !RNA_property_boolean_get(op->ptr, prop_spherize));
8522 RNA_property_float_get_array(op->ptr, prop_target, target);
8524 break;
8525 }
8526
8528 PropertyRNA *prop_align = RNA_struct_find_property(op->ptr, "align");
8530 op->ptr, prop_align, !RNA_property_boolean_get(op->ptr, prop_align));
8531 RNA_property_float_get_array(op->ptr, prop_target, target);
8533 break;
8534 }
8535
8538 /* We want to immediately update to mouse cursor position... */
8539 force_mousemove = true;
8541 break;
8542
8545 copy_v3_v3(target, obedit->loc);
8547 break;
8548
8552 copy_v3_v3(target, scene->cursor.location);
8554 break;
8555
8560 params.sel_op = SEL_OP_SET;
8561 if (EDBM_select_pick(C, event->mval, params)) {
8562 /* Point to newly selected active. */
8564
8565 add_v3_v3(target, obedit->loc);
8567 }
8568 break;
8569 }
8572 switch (scene->toolsettings->transform_pivot_point) {
8573 case V3D_AROUND_CENTER_BOUNDS: /* calculateCenterBound */
8574 {
8575 BMVert *v;
8576 BMIter viter;
8577 float min[3], max[3];
8578 int i = 0;
8579
8580 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
8582 if (i) {
8583 minmax_v3v3_v3(min, max, v->co);
8584 }
8585 else {
8586 copy_v3_v3(min, v->co);
8587 copy_v3_v3(max, v->co);
8588 }
8589 i++;
8590 }
8591 }
8592 mid_v3_v3v3(target, min, max);
8593 add_v3_v3(target, obedit->loc);
8594 break;
8595 }
8596
8599 add_v3_v3(target, obedit->loc);
8600 break;
8601 }
8602
8603 case V3D_AROUND_CURSOR:
8604 copy_v3_v3(target, scene->cursor.location);
8605 break;
8606
8607 case V3D_AROUND_ACTIVE:
8608 if (!blender::ed::object::calc_active_center_for_editmode(obedit, false, target)) {
8609 zero_v3(target);
8610 }
8611 add_v3_v3(target, obedit->loc);
8612 break;
8613
8614 default:
8615 BKE_report(op->reports, RPT_WARNING, "Does not support Individual Origins as pivot");
8616 copy_v3_v3(target, obedit->loc);
8617 }
8619 break;
8620 }
8621 default:
8622 break;
8623 }
8624 }
8625
8626 if (new_mode != mode) {
8627 mode = new_mode;
8628 RNA_enum_set(op->ptr, "mode", mode);
8629 }
8630
8631 /* Only handle mouse-move event in case we are in mouse mode. */
8632 if (event->type == MOUSEMOVE || force_mousemove) {
8633 if (mode == EDBM_CLNOR_POINTTO_MODE_MOUSE) {
8634 ARegion *region = CTX_wm_region(C);
8635 float center[3];
8636
8638
8639 ED_view3d_win_to_3d_int(v3d, region, center, event->mval, target);
8640
8642 }
8643 }
8644
8645 if (ret != OPERATOR_PASS_THROUGH) {
8647 RNA_property_float_set_array(op->ptr, prop_target, target);
8648 }
8649
8650 if (point_normals_ensure(C, op)) {
8651 point_normals_apply(C, op, target, do_reset);
8653 params.calc_looptris = true;
8654 params.calc_normals = false;
8655 params.is_destructive = false;
8656 /* Recheck booleans. */
8657 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
8658
8660 }
8661 else {
8663 }
8664 }
8665
8668 }
8669
8670 /* If we allow other tools to run, we can't be sure if they will re-allocate
8671 * the data this operator uses, see: #68159.
8672 * Free the data here, then use #point_normals_ensure to add it back on demand. */
8673 if (ret == OPERATOR_PASS_THROUGH) {
8674 /* Don't free on mouse-move, causes creation/freeing of the loop data in an inefficient way. */
8675 if (!ISMOUSE_MOTION(event->type)) {
8677 }
8678 }
8679 return ret;
8680}
8681
8683 wmOperator *op,
8684 const wmEvent * /*event*/)
8685{
8686 if (!point_normals_init(C, op)) {
8688 return OPERATOR_CANCELLED;
8689 }
8690
8692
8694
8697}
8698
8699/* TODO: make this work on multiple objects at once */
8701{
8702 Object *obedit = CTX_data_edit_object(C);
8703
8704 if (!point_normals_init(C, op)) {
8706 return OPERATOR_CANCELLED;
8707 }
8708
8709 /* Note that 'mode' is ignored in exec case,
8710 * we directly use vector stored in target_location, whatever that is. */
8711
8712 float target[3];
8713 RNA_float_get_array(op->ptr, "target_location", target);
8714
8715 point_normals_apply(C, op, target, false);
8716
8718 params.calc_looptris = true;
8719 params.calc_normals = false;
8720 params.is_destructive = false;
8721 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
8723
8724 return OPERATOR_FINISHED;
8725}
8726
8727static bool point_normals_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop, void * /*user_data*/)
8728{
8729 const char *prop_id = RNA_property_identifier(prop);
8730
8731 /* Only show strength option if spherize is enabled. */
8732 if (STREQ(prop_id, "spherize_strength")) {
8733 return RNA_boolean_get(ptr, "spherize");
8734 }
8735
8736 /* Else, show it! */
8737 return true;
8738}
8739
8741{
8742 uiLayout *layout = op->layout;
8744
8746
8747 layout->use_property_split_set(true);
8748
8749 /* Main auto-draw call */
8750 uiDefAutoButsRNA(layout,
8751 &ptr,
8753 nullptr,
8754 nullptr,
8756 false);
8757}
8758
8760{
8761 /* identifiers */
8762 ot->name = "Point Normals to Target";
8763 ot->description = "Point selected custom normals to specified Target";
8764 ot->idname = "MESH_OT_point_normals";
8765
8766 /* API callbacks. */
8768 ot->invoke = edbm_point_normals_invoke;
8770 ot->poll = ED_operator_editmesh;
8772 ot->cancel = point_normals_cancel;
8773
8774 /* flags */
8776
8777 ot->prop = RNA_def_enum(ot->srna,
8778 "mode",
8781 "Mode",
8782 "How to define coordinates to point custom normals to");
8784
8785 RNA_def_boolean(ot->srna, "invert", false, "Invert", "Invert affected normals");
8786
8787 RNA_def_boolean(ot->srna, "align", false, "Align", "Make all affected normals parallel");
8788
8790 "target_location",
8791 3,
8792 nullptr,
8793 -FLT_MAX,
8794 FLT_MAX,
8795 "Target",
8796 "Target location to which normals will point",
8797 -1000.0f,
8798 1000.0f);
8799
8801 ot->srna, "spherize", false, "Spherize", "Interpolate between original and new normals");
8802
8803 RNA_def_float(ot->srna,
8804 "spherize_strength",
8805 0.1,
8806 0.0f,
8807 1.0f,
8808 "Spherize Strength",
8809 "Ratio of spherized normal to original normal",
8810 0.0f,
8811 1.0f);
8812}
8813
8815
8816/* -------------------------------------------------------------------- */
8819
8820static void normals_merge(BMesh *bm, BMLoopNorEditDataArray *lnors_ed_arr)
8821{
8822 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
8823
8824 BLI_SMALLSTACK_DECLARE(clnors, short *);
8825
8826 BLI_assert(bm->lnor_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
8827
8829
8830 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
8832
8833 if (BM_elem_flag_test(lnor_ed->loop, BM_ELEM_TAG)) {
8834 continue;
8835 }
8836
8837 MLoopNorSpace *lnor_space = bm->lnor_spacearr->lspacearr[lnor_ed->loop_index];
8838
8839 if ((lnor_space->flags & MLNOR_SPACE_IS_SINGLE) == 0) {
8840 LinkNode *loops = lnor_space->loops;
8841 float avg_normal[3] = {0.0f, 0.0f, 0.0f};
8842 short *clnors_data;
8843
8844 for (; loops; loops = loops->next) {
8845 BMLoop *l = static_cast<BMLoop *>(loops->link);
8846 const int loop_index = BM_elem_index_get(l);
8847
8848 BMLoopNorEditData *lnor_ed_tmp = lnors_ed_arr->lidx_to_lnor_editdata[loop_index];
8849 BLI_assert(lnor_ed_tmp->loop_index == loop_index && lnor_ed_tmp->loop == l);
8850 add_v3_v3(avg_normal, lnor_ed_tmp->nloc);
8851 BLI_SMALLSTACK_PUSH(clnors, lnor_ed_tmp->clnors_data);
8853 }
8854 if (normalize_v3(avg_normal) < CLNORS_VALID_VEC_LEN) {
8855 /* If avg normal is nearly 0, set clnor to default value. */
8856 zero_v3(avg_normal);
8857 }
8858 while ((clnors_data = static_cast<short *>(BLI_SMALLSTACK_POP(clnors)))) {
8859 BKE_lnor_space_custom_normal_to_data(lnor_space, avg_normal, clnors_data);
8860 }
8861 }
8862 }
8863}
8864
8866{
8867 BMFace *f;
8868 BMLoop *l, *l_curr, *l_first;
8869 BMIter fiter;
8870
8871 BLI_assert(bm->lnor_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
8872
8874
8875 BLI_SMALLSTACK_DECLARE(loop_stack, BMLoop *);
8876
8877 const int cd_clnors_offset = CustomData_get_offset_named(
8878 &bm->ldata, CD_PROP_INT16_2D, "custom_normal");
8879 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
8881
8882 l_curr = l_first = BM_FACE_FIRST_LOOP(f);
8883 do {
8884 if (BM_elem_flag_test(l_curr->v, BM_ELEM_SELECT) &&
8885 (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) ||
8887 {
8888 if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
8889 !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG))
8890 {
8891 const int loop_index = BM_elem_index_get(l_curr);
8892 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l_curr, cd_clnors_offset));
8894 bm->lnor_spacearr->lspacearr[loop_index], f->no, clnors);
8895 }
8896 else {
8897 BMVert *v_pivot = l_curr->v;
8898 UNUSED_VARS_NDEBUG(v_pivot);
8899 BMEdge *e_next;
8900 const BMEdge *e_org = l_curr->e;
8901 BMLoop *lfan_pivot, *lfan_pivot_next;
8902
8903 lfan_pivot = l_curr;
8904 e_next = lfan_pivot->e;
8905 float avg_normal[3] = {0.0f};
8906
8907 while (true) {
8908 lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next);
8909 if (lfan_pivot_next) {
8910 BLI_assert(lfan_pivot_next->v == v_pivot);
8911 }
8912 else {
8913 e_next = (lfan_pivot->e == e_next) ? lfan_pivot->prev->e : lfan_pivot->e;
8914 }
8915
8916 BLI_SMALLSTACK_PUSH(loop_stack, lfan_pivot);
8917 add_v3_v3(avg_normal, lfan_pivot->f->no);
8918
8919 if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) {
8920 break;
8921 }
8922 lfan_pivot = lfan_pivot_next;
8923 }
8924 if (normalize_v3(avg_normal) < CLNORS_VALID_VEC_LEN) {
8925 /* If avg normal is nearly 0, set clnor to default value. */
8926 zero_v3(avg_normal);
8927 }
8928 while ((l = static_cast<BMLoop *>(BLI_SMALLSTACK_POP(loop_stack)))) {
8929 const int l_index = BM_elem_index_get(l);
8930 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
8932 bm->lnor_spacearr->lspacearr[l_index], avg_normal, clnors);
8933 }
8934 }
8935 }
8936 } while ((l_curr = l_curr->next) != l_first);
8937 }
8938}
8939
8940static wmOperatorStatus normals_split_merge(bContext *C, const bool do_merge)
8941{
8942 const Scene *scene = CTX_data_scene(C);
8943 ViewLayer *view_layer = CTX_data_view_layer(C);
8945 scene, view_layer, CTX_wm_view3d(C));
8946
8947 for (Object *obedit : objects) {
8949 BMesh *bm = em->bm;
8950 BMEdge *e;
8951 BMIter eiter;
8952
8954
8955 /* Note that we need temp lnor editing data for all loops of all affected vertices, since by
8956 * setting some faces/edges as smooth we are going to change clnors spaces... See also #65809.
8957 */
8958 BMLoopNorEditDataArray *lnors_ed_arr = do_merge ?
8960 nullptr;
8961
8962 mesh_set_smooth_faces(em, do_merge);
8963
8964 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
8966 BM_elem_flag_set(e, BM_ELEM_SMOOTH, do_merge);
8967 }
8968 }
8969
8970 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
8972
8973 if (do_merge) {
8974 normals_merge(bm, lnors_ed_arr);
8975 }
8976 else {
8978 }
8979
8980 if (lnors_ed_arr) {
8982 }
8983
8985 params.calc_looptris = true;
8986 params.calc_normals = false;
8987 params.is_destructive = false;
8988 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
8989 }
8990
8991 return OPERATOR_FINISHED;
8992}
8993
8995{
8996 return normals_split_merge(C, true);
8997}
8998
9000{
9001 /* identifiers */
9002 ot->name = "Merge Normals";
9003 ot->description = "Merge custom normals of selected vertices";
9004 ot->idname = "MESH_OT_merge_normals";
9005
9006 /* API callbacks. */
9008 ot->poll = ED_operator_editmesh;
9009
9010 /* flags */
9011 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9012}
9013
9015{
9016 return normals_split_merge(C, false);
9017}
9018
9020{
9021 /* identifiers */
9022 ot->name = "Split Normals";
9023 ot->description = "Split custom normals of selected vertices";
9024 ot->idname = "MESH_OT_split_normals";
9025
9026 /* API callbacks. */
9028 ot->poll = ED_operator_editmesh;
9029
9030 /* flags */
9031 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9032}
9033
9035
9036/* -------------------------------------------------------------------- */
9039
9040enum {
9044};
9045
9048 "CUSTOM_NORMAL",
9049 0,
9050 "Custom Normal",
9051 "Take average of vertex normals"},
9053 "FACE_AREA",
9054 0,
9055 "Face Area",
9056 "Set all vertex normals by face area"},
9058 "CORNER_ANGLE",
9059 0,
9060 "Corner Angle",
9061 "Set all vertex normals by corner angle"},
9062 {0, nullptr, 0, nullptr, nullptr},
9063};
9064
9066{
9067 const Scene *scene = CTX_data_scene(C);
9068 ViewLayer *view_layer = CTX_data_view_layer(C);
9070 scene, view_layer, CTX_wm_view3d(C));
9071 const int average_type = RNA_enum_get(op->ptr, "average_type");
9072 const float absweight = float(RNA_int_get(op->ptr, "weight"));
9073 const float threshold = RNA_float_get(op->ptr, "threshold");
9074
9075 HeapSimple *loop_weight = BLI_heapsimple_new();
9076 BLI_SMALLSTACK_DECLARE(loop_stack, BMLoop *);
9077
9078 for (uint ob_index = 0; ob_index < objects.size(); ob_index++) {
9080 BLI_assert(BLI_heapsimple_is_empty(loop_weight));
9081
9082 Object *obedit = objects[ob_index];
9084 BMesh *bm = em->bm;
9085 BMFace *f;
9086 BMLoop *l, *l_curr, *l_first;
9087 BMIter fiter;
9088
9089 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
9091
9092 const int cd_clnors_offset = CustomData_get_offset_named(
9093 &bm->ldata, CD_PROP_INT16_2D, "custom_normal");
9094
9095 float weight = absweight / 50.0f;
9096 if (absweight == 100.0f) {
9097 weight = float(SHRT_MAX);
9098 }
9099 else if (absweight == 1.0f) {
9100 weight = 1 / float(SHRT_MAX);
9101 }
9102 else if ((weight - 1) * 25 > 1) {
9103 weight = (weight - 1) * 25;
9104 }
9105
9107
9108 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
9109 l_curr = l_first = BM_FACE_FIRST_LOOP(f);
9110 do {
9111 if (BM_elem_flag_test(l_curr->v, BM_ELEM_SELECT) &&
9112 (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) ||
9114 {
9115 if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
9116 !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG))
9117 {
9118 const int loop_index = BM_elem_index_get(l_curr);
9119 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l_curr, cd_clnors_offset));
9121 bm->lnor_spacearr->lspacearr[loop_index], f->no, clnors);
9122 }
9123 else {
9124 BMVert *v_pivot = l_curr->v;
9125 UNUSED_VARS_NDEBUG(v_pivot);
9126 BMEdge *e_next;
9127 const BMEdge *e_org = l_curr->e;
9128 BMLoop *lfan_pivot, *lfan_pivot_next;
9129
9130 lfan_pivot = l_curr;
9131 e_next = lfan_pivot->e;
9132
9133 while (true) {
9134 lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next);
9135 if (lfan_pivot_next) {
9136 BLI_assert(lfan_pivot_next->v == v_pivot);
9137 }
9138 else {
9139 e_next = (lfan_pivot->e == e_next) ? lfan_pivot->prev->e : lfan_pivot->e;
9140 }
9141
9142 float val = 1.0f;
9143 if (average_type == EDBM_CLNOR_AVERAGE_FACE_AREA) {
9144 val = 1.0f / BM_face_calc_area(lfan_pivot->f);
9145 }
9146 else if (average_type == EDBM_CLNOR_AVERAGE_ANGLE) {
9147 val = 1.0f / BM_loop_calc_face_angle(lfan_pivot);
9148 }
9149
9150 BLI_heapsimple_insert(loop_weight, val, lfan_pivot);
9151
9152 if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) {
9153 break;
9154 }
9155 lfan_pivot = lfan_pivot_next;
9156 }
9157
9158 float wnor[3], avg_normal[3] = {0.0f}, count = 0;
9159 float val = BLI_heapsimple_top_value(loop_weight);
9160
9161 while (!BLI_heapsimple_is_empty(loop_weight)) {
9162 const float cur_val = BLI_heapsimple_top_value(loop_weight);
9163 if (!compare_ff(val, cur_val, threshold)) {
9164 count++;
9165 val = cur_val;
9166 }
9167 l = static_cast<BMLoop *>(BLI_heapsimple_pop_min(loop_weight));
9168 BLI_SMALLSTACK_PUSH(loop_stack, l);
9169
9170 const float n_weight = pow(weight, count);
9171
9172 if (average_type == EDBM_CLNOR_AVERAGE_LOOP) {
9173 const int l_index = BM_elem_index_get(l);
9174 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
9176 bm->lnor_spacearr->lspacearr[l_index], clnors, wnor);
9177 }
9178 else {
9179 copy_v3_v3(wnor, l->f->no);
9180 }
9181 mul_v3_fl(wnor, (1.0f / cur_val) * (1.0f / n_weight));
9182 add_v3_v3(avg_normal, wnor);
9183 }
9184
9185 if (normalize_v3(avg_normal) < CLNORS_VALID_VEC_LEN) {
9186 /* If avg normal is nearly 0, set clnor to default value. */
9187 zero_v3(avg_normal);
9188 }
9189 while ((l = static_cast<BMLoop *>(BLI_SMALLSTACK_POP(loop_stack)))) {
9190 const int l_index = BM_elem_index_get(l);
9191 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
9193 bm->lnor_spacearr->lspacearr[l_index], avg_normal, clnors);
9194 }
9195 }
9196 }
9197 } while ((l_curr = l_curr->next) != l_first);
9198 }
9199
9201 params.calc_looptris = true;
9202 params.calc_normals = false;
9203 params.is_destructive = false;
9204 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
9205 }
9206
9207 BLI_heapsimple_free(loop_weight, nullptr);
9208
9209 return OPERATOR_FINISHED;
9210}
9211
9213 PropertyRNA *prop,
9214 void * /*user_data*/)
9215{
9216 const char *prop_id = RNA_property_identifier(prop);
9217 const int average_type = RNA_enum_get(ptr, "average_type");
9218
9219 /* Only show weight/threshold options when not in loop average type. */
9220 const bool is_clor_average_loop = average_type == EDBM_CLNOR_AVERAGE_LOOP;
9221 if (STREQ(prop_id, "weight")) {
9222 return !is_clor_average_loop;
9223 }
9224 if (STREQ(prop_id, "threshold")) {
9225 return !is_clor_average_loop;
9226 }
9227
9228 /* Else, show it! */
9229 return true;
9230}
9231
9233{
9234 uiLayout *layout = op->layout;
9236
9238
9239 layout->use_property_split_set(true);
9240
9241 /* Main auto-draw call */
9242 uiDefAutoButsRNA(layout,
9243 &ptr,
9245 nullptr,
9246 nullptr,
9248 false);
9249}
9250
9252{
9253 /* identifiers */
9254 ot->name = "Average Normals";
9255 ot->description = "Average custom normals of selected vertices";
9256 ot->idname = "MESH_OT_average_normals";
9257
9258 /* API callbacks. */
9260 ot->poll = ED_operator_editmesh;
9262
9263 /* flags */
9264 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9265
9266 ot->prop = RNA_def_enum(ot->srna,
9267 "average_type",
9270 "Type",
9271 "Averaging method");
9272
9273 RNA_def_int(ot->srna, "weight", 50, 1, 100, "Weight", "Weight applied per face", 1, 100);
9274
9275 RNA_def_float(ot->srna,
9276 "threshold",
9277 0.01f,
9278 0,
9279 10,
9280 "Threshold",
9281 "Threshold value for different weights to be considered equal",
9282 0,
9283 5);
9284}
9285
9287
9288/* -------------------------------------------------------------------- */
9291
9292enum {
9298};
9299
9301 {EDBM_CLNOR_TOOLS_COPY, "COPY", 0, "Copy Normal", "Copy normal to the internal clipboard"},
9303 "PASTE",
9304 0,
9305 "Paste Normal",
9306 "Paste normal from the internal clipboard"},
9307 {EDBM_CLNOR_TOOLS_ADD, "ADD", 0, "Add Normal", "Add normal vector with selection"},
9309 "MULTIPLY",
9310 0,
9311 "Multiply Normal",
9312 "Multiply normal vector with selection"},
9314 "RESET",
9315 0,
9316 "Reset Normal",
9317 "Reset the internal clipboard and/or normal of selected element"},
9318 {0, nullptr, 0, nullptr, nullptr},
9319};
9320
9322{
9323 Scene *scene = CTX_data_scene(C);
9324 ViewLayer *view_layer = CTX_data_view_layer(C);
9326 scene, view_layer, CTX_wm_view3d(C));
9327 const int mode = RNA_enum_get(op->ptr, "mode");
9328 const bool absolute = RNA_boolean_get(op->ptr, "absolute");
9329 float *normal_vector = scene->toolsettings->normal_vector;
9330 bool done_copy = false;
9331
9332 for (Object *obedit : objects) {
9334 BMesh *bm = em->bm;
9335
9336 if (bm->totloop == 0) {
9337 continue;
9338 }
9339
9342 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
9343
9344 switch (mode) {
9346 if (bm->totfacesel == 0 && bm->totvertsel == 0) {
9348 continue;
9349 }
9350
9351 if (done_copy ||
9352 (bm->totfacesel != 1 && lnors_ed_arr->totloop != 1 && bm->totvertsel != 1))
9353 {
9354 BKE_report(op->reports,
9355 RPT_ERROR,
9356 "Can only copy one custom normal, vertex normal or face normal");
9358 continue;
9359 }
9360 if (lnors_ed_arr->totloop == 1) {
9361 copy_v3_v3(scene->toolsettings->normal_vector, lnors_ed_arr->lnor_editdata->nloc);
9362 }
9363 else if (bm->totfacesel == 1) {
9364 BMFace *f;
9365 BMIter fiter;
9366 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
9369 }
9370 }
9371 }
9372 else {
9373 /* 'Vertex' normal, i.e. common set of loop normals on the same vertex,
9374 * only if they are all the same. */
9375 bool are_same_lnors = true;
9376 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9377 if (!compare_v3v3(lnors_ed_arr->lnor_editdata->nloc, lnor_ed->nloc, 1e-4f)) {
9378 are_same_lnors = false;
9379 }
9380 }
9381 if (are_same_lnors) {
9382 copy_v3_v3(scene->toolsettings->normal_vector, lnors_ed_arr->lnor_editdata->nloc);
9383 }
9384 }
9385 done_copy = true;
9386 break;
9387
9389 if (!absolute) {
9390 if (normalize_v3(normal_vector) < CLNORS_VALID_VEC_LEN) {
9391 /* If normal is nearly 0, do nothing. */
9392 break;
9393 }
9394 }
9395 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9396 if (absolute) {
9397 float abs_normal[3];
9398 copy_v3_v3(abs_normal, lnor_ed->loc);
9399 negate_v3(abs_normal);
9400 add_v3_v3(abs_normal, normal_vector);
9401
9402 if (normalize_v3(abs_normal) < CLNORS_VALID_VEC_LEN) {
9403 /* If abs normal is nearly 0, set clnor to initial value. */
9404 copy_v3_v3(abs_normal, lnor_ed->niloc);
9405 }
9406 BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index],
9407 abs_normal,
9408 lnor_ed->clnors_data);
9409 }
9410 else {
9411 BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index],
9412 normal_vector,
9413 lnor_ed->clnors_data);
9414 }
9415 }
9416 break;
9417
9419 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9420 mul_v3_v3(lnor_ed->nloc, normal_vector);
9421
9422 if (normalize_v3(lnor_ed->nloc) < CLNORS_VALID_VEC_LEN) {
9423 /* If abs normal is nearly 0, set clnor to initial value. */
9424 copy_v3_v3(lnor_ed->nloc, lnor_ed->niloc);
9425 }
9426 BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index],
9427 lnor_ed->nloc,
9428 lnor_ed->clnors_data);
9429 }
9430 break;
9431
9433 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9434 add_v3_v3(lnor_ed->nloc, normal_vector);
9435
9436 if (normalize_v3(lnor_ed->nloc) < CLNORS_VALID_VEC_LEN) {
9437 /* If abs normal is nearly 0, set clnor to initial value. */
9438 copy_v3_v3(lnor_ed->nloc, lnor_ed->niloc);
9439 }
9440 BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index],
9441 lnor_ed->nloc,
9442 lnor_ed->clnors_data);
9443 }
9444 break;
9445
9447 zero_v3(normal_vector);
9448 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9449 BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index],
9450 normal_vector,
9451 lnor_ed->clnors_data);
9452 }
9453 break;
9454
9455 default:
9456 BLI_assert(0);
9457 break;
9458 }
9459
9461
9463 params.calc_looptris = true;
9464 params.calc_normals = false;
9465 params.is_destructive = false;
9466 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
9467 }
9468
9469 return OPERATOR_FINISHED;
9470}
9471
9472static bool normals_tools_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop, void * /*user_data*/)
9473{
9474 const char *prop_id = RNA_property_identifier(prop);
9475 const int mode = RNA_enum_get(ptr, "mode");
9476
9477 /* Only show absolute option in paste mode. */
9478 if (STREQ(prop_id, "absolute")) {
9479 return (mode == EDBM_CLNOR_TOOLS_PASTE);
9480 }
9481
9482 /* Else, show it! */
9483 return true;
9484}
9485
9487{
9488 uiLayout *layout = op->layout;
9490
9492
9493 /* Main auto-draw call */
9494 uiDefAutoButsRNA(layout,
9495 &ptr,
9497 nullptr,
9498 nullptr,
9500 false);
9501}
9502
9504{
9505 /* identifiers */
9506 ot->name = "Normals Vector Tools";
9507 ot->description = "Custom normals tools using Normal Vector of UI";
9508 ot->idname = "MESH_OT_normals_tools";
9509
9510 /* API callbacks. */
9512 ot->poll = ED_operator_editmesh;
9514
9515 /* flags */
9516 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9517
9518 ot->prop = RNA_def_enum(ot->srna,
9519 "mode",
9522 "Mode",
9523 "Mode of tools taking input from interface");
9525
9526 RNA_def_boolean(ot->srna,
9527 "absolute",
9528 false,
9529 "Absolute Coordinates",
9530 "Copy Absolute coordinates of Normal vector");
9531}
9532
9534
9535/* -------------------------------------------------------------------- */
9538
9540{
9541 const Scene *scene = CTX_data_scene(C);
9542 ViewLayer *view_layer = CTX_data_view_layer(C);
9544 scene, view_layer, CTX_wm_view3d(C));
9545
9546 for (Object *obedit : objects) {
9548 BMesh *bm = em->bm;
9549 if (bm->totfacesel == 0) {
9550 continue;
9551 }
9552
9553 BMFace *f;
9554 BMVert *v;
9555 BMEdge *e;
9556 BMLoop *l;
9557 BMIter fiter, viter, eiter, liter;
9558
9559 const bool keep_sharp = RNA_boolean_get(op->ptr, "keep_sharp");
9560
9562
9563 float (*vert_normals)[3] = static_cast<float (*)[3]>(
9564 MEM_mallocN(sizeof(*vert_normals) * bm->totvert, __func__));
9565 {
9566 int v_index;
9567 BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, v_index) {
9568 BM_vert_calc_normal_ex(v, BM_ELEM_SELECT, vert_normals[v_index]);
9569 }
9570 }
9571
9572 BLI_bitmap *loop_set = BLI_BITMAP_NEW(bm->totloop, __func__);
9573 const int cd_clnors_offset = CustomData_get_offset_named(
9574 &bm->ldata, CD_PROP_INT16_2D, "custom_normal");
9575
9576 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
9577 BM_ITER_ELEM (e, &eiter, f, BM_EDGES_OF_FACE) {
9578 if (!keep_sharp ||
9580 {
9581 BM_ITER_ELEM (v, &viter, e, BM_VERTS_OF_EDGE) {
9583 const int l_index = BM_elem_index_get(l);
9584 const int v_index = BM_elem_index_get(l->v);
9585
9586 if (!is_zero_v3(vert_normals[v_index])) {
9587 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
9589 bm->lnor_spacearr->lspacearr[l_index], vert_normals[v_index], clnors);
9590
9591 if (bm->lnor_spacearr->lspacearr[l_index]->flags & MLNOR_SPACE_IS_SINGLE) {
9592 BLI_BITMAP_ENABLE(loop_set, l_index);
9593 }
9594 else {
9595 LinkNode *loops = bm->lnor_spacearr->lspacearr[l_index]->loops;
9596 for (; loops; loops = loops->next) {
9597 BLI_BITMAP_ENABLE(loop_set, BM_elem_index_get((BMLoop *)loops->link));
9598 }
9599 }
9600 }
9601 }
9602 }
9603 }
9604 }
9605
9606 int v_index;
9607 BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, v_index) {
9608 BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
9609 if (BLI_BITMAP_TEST(loop_set, BM_elem_index_get(l))) {
9610 const int loop_index = BM_elem_index_get(l);
9611 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
9613 bm->lnor_spacearr->lspacearr[loop_index], vert_normals[v_index], clnors);
9614 }
9615 }
9616 }
9617
9618 MEM_freeN(loop_set);
9619 MEM_freeN(vert_normals);
9621 params.calc_looptris = true;
9622 params.calc_normals = false;
9623 params.is_destructive = false;
9624 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
9625 }
9626
9627 return OPERATOR_FINISHED;
9628}
9629
9631{
9632 /* identifiers */
9633 ot->name = "Set Normals from Faces";
9634 ot->description = "Set the custom normals from the selected faces ones";
9635 ot->idname = "MESH_OT_set_normals_from_faces";
9636
9637 /* API callbacks. */
9639 ot->poll = ED_operator_editmesh;
9640
9641 /* flags */
9642 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9643
9645 ot->srna, "keep_sharp", false, "Keep Sharp Edges", "Do not set sharp edges to face");
9646}
9647
9649
9650/* -------------------------------------------------------------------- */
9653
9655{
9656 const Scene *scene = CTX_data_scene(C);
9657 ViewLayer *view_layer = CTX_data_view_layer(C);
9659 scene, view_layer, CTX_wm_view3d(C));
9660
9661 for (Object *obedit : objects) {
9663 BMesh *bm = em->bm;
9664 BMFace *f;
9665 BMLoop *l;
9666 BMIter fiter, liter;
9667
9670
9671 float (*smooth_normal)[3] = static_cast<float (*)[3]>(
9672 MEM_callocN(sizeof(*smooth_normal) * lnors_ed_arr->totloop, __func__));
9673
9674 /* NOTE(@mont29): This is weird choice of operation, taking all loops of faces of current
9675 * vertex. Could lead to some rather far away loops weighting as much as very close ones
9676 * (topologically speaking), with complex polygons.
9677 * Using topological distance here (rather than geometrical one)
9678 * makes sense IMHO, but would rather go with a more consistent and flexible code,
9679 * we could even add max topological distance to take into account, and a weighting curve.
9680 * Would do that later though, think for now we can live with that choice. */
9681 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
9682 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9683 l = lnor_ed->loop;
9684 float loop_normal[3];
9685
9686 BM_ITER_ELEM (f, &fiter, l->v, BM_FACES_OF_VERT) {
9687 BMLoop *l_other;
9688 BM_ITER_ELEM (l_other, &liter, f, BM_LOOPS_OF_FACE) {
9689 const int l_index_other = BM_elem_index_get(l_other);
9690 short *clnors = static_cast<short *>(
9691 BM_ELEM_CD_GET_VOID_P(l_other, lnors_ed_arr->cd_custom_normal_offset));
9693 bm->lnor_spacearr->lspacearr[l_index_other], clnors, loop_normal);
9694 add_v3_v3(smooth_normal[i], loop_normal);
9695 }
9696 }
9697 }
9698
9699 const float factor = RNA_float_get(op->ptr, "factor");
9700
9701 lnor_ed = lnors_ed_arr->lnor_editdata;
9702 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9703 float current_normal[3];
9704
9705 if (normalize_v3(smooth_normal[i]) < CLNORS_VALID_VEC_LEN) {
9706 /* Skip in case the smooth normal is invalid. */
9707 continue;
9708 }
9709
9711 bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->clnors_data, current_normal);
9712
9713 /* NOTE: again, this is not true spherical interpolation that normals would need...
9714 * But it's probably good enough for now. */
9715 mul_v3_fl(current_normal, 1.0f - factor);
9716 mul_v3_fl(smooth_normal[i], factor);
9717 add_v3_v3(current_normal, smooth_normal[i]);
9718
9719 if (normalize_v3(current_normal) < CLNORS_VALID_VEC_LEN) {
9720 /* Skip in case the smoothed normal is invalid. */
9721 continue;
9722 }
9723
9725 bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], current_normal, lnor_ed->clnors_data);
9726 }
9727
9729 MEM_freeN(smooth_normal);
9730
9732 params.calc_looptris = true;
9733 params.calc_normals = false;
9734 params.is_destructive = false;
9735 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
9736 }
9737
9738 return OPERATOR_FINISHED;
9739}
9740
9742{
9743 /* identifiers */
9744 ot->name = "Smooth Normals Vectors";
9745 ot->description = "Smooth custom normals based on adjacent vertex normals";
9746 ot->idname = "MESH_OT_smooth_normals";
9747
9748 /* API callbacks. */
9750 ot->poll = ED_operator_editmesh;
9751
9752 /* flags */
9753 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9754
9755 RNA_def_float(ot->srna,
9756 "factor",
9757 0.5f,
9758 0.0f,
9759 1.0f,
9760 "Factor",
9761 "Specifies weight of smooth vs original normal",
9762 0.0f,
9763 1.0f);
9764}
9765
9767
9768/* -------------------------------------------------------------------- */
9771
9773{
9774 const Scene *scene = CTX_data_scene(C);
9775 ViewLayer *view_layer = CTX_data_view_layer(C);
9777 scene, view_layer, CTX_wm_view3d(C));
9778
9779 for (Object *obedit : objects) {
9781 BMesh *bm = em->bm;
9782 BMFace *f;
9783 BMIter fiter;
9784 const int face_strength = RNA_enum_get(op->ptr, "face_strength");
9785 const bool set = RNA_boolean_get(op->ptr, "set");
9786
9788
9789 const char *layer_id = MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID;
9790 int cd_prop_int_index = CustomData_get_named_layer_index(&bm->pdata, CD_PROP_INT32, layer_id);
9791 if (cd_prop_int_index == -1) {
9792 BM_data_layer_add_named(bm, &bm->pdata, CD_PROP_INT32, layer_id);
9793 cd_prop_int_index = CustomData_get_named_layer_index(&bm->pdata, CD_PROP_INT32, layer_id);
9794 }
9795 cd_prop_int_index -= CustomData_get_layer_index(&bm->pdata, CD_PROP_INT32);
9796 const int cd_prop_int_offset = CustomData_get_n_offset(
9797 &bm->pdata, CD_PROP_INT32, cd_prop_int_index);
9798
9800
9801 if (set) {
9802 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
9804 int *strength = static_cast<int *>(BM_ELEM_CD_GET_VOID_P(f, cd_prop_int_offset));
9805 *strength = face_strength;
9806 }
9807 }
9808 }
9809 else {
9810 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
9811 const int *strength = static_cast<int *>(BM_ELEM_CD_GET_VOID_P(f, cd_prop_int_offset));
9812 if (*strength == face_strength) {
9813 BM_face_select_set(bm, f, true);
9815 }
9816 else {
9817 BM_face_select_set(bm, f, false);
9818 }
9819 }
9820 }
9821
9823 params.calc_looptris = false;
9824 params.calc_normals = false;
9825 params.is_destructive = false;
9826 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
9827 }
9828
9829 return OPERATOR_FINISHED;
9830}
9831
9833 {FACE_STRENGTH_WEAK, "WEAK", 0, "Weak", ""},
9834 {FACE_STRENGTH_MEDIUM, "MEDIUM", 0, "Medium", ""},
9835 {FACE_STRENGTH_STRONG, "STRONG", 0, "Strong", ""},
9836 {0, nullptr, 0, nullptr, nullptr},
9837};
9838
9840{
9841 /* identifiers */
9842 ot->name = "Face Normals Strength";
9843 ot->description = "Set/Get strength of face (used in Weighted Normal modifier)";
9844 ot->idname = "MESH_OT_mod_weighted_strength";
9845
9846 /* API callbacks. */
9848 ot->poll = ED_operator_editmesh;
9849
9850 /* flags */
9851 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9852
9853 ot->prop = RNA_def_boolean(ot->srna, "set", false, "Set Value", "Set value of faces");
9854
9855 ot->prop = RNA_def_enum(
9856 ot->srna,
9857 "face_strength",
9860 "Face Strength",
9861 "Strength to use for assigning or selecting face influence for weighted normal modifier");
9862}
9863
9865{
9866 /* identifiers */
9867 ot->name = "Flip Quad Tessellation";
9868 ot->description = "Flips the tessellation of selected quads";
9869 ot->idname = "MESH_OT_flip_quad_tessellation";
9870
9872 ot->poll = ED_operator_editmesh;
9873
9874 /* flags */
9875 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9876}
9877
Generic geometry attributes built on CustomData.
int BKE_attribute_to_index(const AttributeOwner &owner, const CustomDataLayer *layer, AttrDomainMask domain_mask, eCustomDataMask layer_mask)
Definition attribute.cc:931
@ ATTR_DOMAIN_MASK_CORNER
const struct CustomDataLayer * BKE_attribute_search(const AttributeOwner &owner, blender::StringRef name, eCustomDataMask type, AttrDomainMask domain_mask)
Definition attribute.cc:639
#define CTX_DATA_BEGIN(C, Type, instance, member)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
#define CTX_DATA_END
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
int CustomData_get_n_offset(const CustomData *data, eCustomDataType type, int n)
void * CustomData_bmesh_get_n(const CustomData *data, void *block, eCustomDataType type, int n)
int CustomData_get_named_layer_index(const CustomData *data, eCustomDataType type, blender::StringRef name)
bool CustomData_has_layer_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
int CustomData_get_offset_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
int CustomData_get_layer_index(const CustomData *data, eCustomDataType type)
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
int CustomData_number_of_layers(const CustomData *data, eCustomDataType type)
support for deformation groups and hooks.
int BKE_object_defgroup_active_index_get(const Object *ob)
Definition deform.cc:607
float BKE_defvert_find_weight(const MDeformVert *dvert, int defgroup)
Definition deform.cc:774
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:61
void BKE_editmesh_lnorspace_update(BMEditMesh *em)
Definition editmesh.cc:220
KeyBlock * BKE_keyblock_find_name(Key *key, const char name[])
Definition key.cc:1916
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
blender::Vector< Base * > BKE_view_layer_array_from_bases_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
bool BKE_id_is_editable(const Main *bmain, const ID *id)
Definition lib_id.cc:2523
void id_us_plus(ID *id)
Definition lib_id.cc:358
General operations, lookup, etc. for materials.
void BKE_objects_materials_sync_length_all(Main *bmain, ID *id)
short * BKE_object_material_len_p(Object *ob)
void BKE_id_material_clear(Main *bmain, ID *id)
short * BKE_id_material_len_p(ID *id)
Material *** BKE_id_material_array_p(ID *id)
void BKE_id_material_resize(Main *bmain, ID *id, short totcol, bool do_id_user)
void BKE_object_material_array_assign(Main *bmain, Object *ob, Material ***matar, int totcol, bool to_object_only)
Material *** BKE_object_material_array_p(Object *ob)
void BKE_lnor_space_custom_data_to_normal(const MLoopNorSpace *lnor_space, const short clnor_data[2], float r_custom_lnor[3])
@ MLNOR_SPACE_IS_SINGLE
Definition BKE_mesh.h:278
void BKE_lnor_space_custom_normal_to_data(const MLoopNorSpace *lnor_space, const float custom_lnor[3], short r_clnor_data[2])
@ MLNOR_SPACEARR_BMLOOP_PTR
Definition BKE_mesh.h:300
General operations, lookup, etc. for blender objects.
Mesh * BKE_object_get_original_mesh(const Object *object)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_INFO
Definition BKE_report.hh:35
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_BITMAP_NEW(_num, _alloc_string)
Definition BLI_bitmap.h:37
#define BLI_BITMAP_TEST(_bitmap, _index)
Definition BLI_bitmap.h:61
#define BLI_BITMAP_ENABLE(_bitmap, _index)
Definition BLI_bitmap.h:78
unsigned int BLI_bitmap
Definition BLI_bitmap.h:13
unsigned int BLI_ghashutil_strhash_p(const void *ptr)
A min-heap / priority queue ADT.
HeapSimple * BLI_heapsimple_new(void) ATTR_WARN_UNUSED_RESULT
void BLI_heapsimple_free(HeapSimple *heap, HeapSimpleFreeFP ptrfreefp) ATTR_NONNULL(1)
float BLI_heapsimple_top_value(const HeapSimple *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void * BLI_heapsimple_pop_min(HeapSimple *heap) ATTR_NONNULL(1)
bool BLI_heapsimple_is_empty(const HeapSimple *heap) ATTR_NONNULL(1)
void BLI_heapsimple_insert(HeapSimple *heap, float value, void *ptr) ATTR_NONNULL(1)
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
void BLI_listbase_rotate_first(ListBase *lb, void *vlink) ATTR_NONNULL(1
int BLI_listbase_count_at_most(const ListBase *listbase, int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:511
void * BLI_findptr(const struct ListBase *listbase, const void *ptr, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE int min_ii(int a, int b)
MINLINE int mod_i(int i, int n)
MINLINE int compare_ff(float a, float b, float max_diff)
MINLINE int count_bits_i(unsigned int n)
#define DEG2RADF(_deg)
#define M_PI
int is_quad_flip_v3(const float v1[3], const float v2[3], const float v3[3], const float v4[3])
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void mul_m4_v3(const float M[4][4], float r[3])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
void minmax_v3v3_v3(float min[3], float max[3], const float vec[3])
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void mul_v3_v3(float r[3], const float a[3])
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
MINLINE void negate_v3(float r[3])
MINLINE bool compare_v3v3(const float v1[3], const float v2[3], float limit) ATTR_WARN_UNUSED_RESULT
MINLINE bool is_zero_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void zero_v3(float r[3])
float angle_v3v3v3(const float a[3], const float b[3], const float c[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
Random number functions.
void BLI_rng_free(struct RNG *rng) ATTR_NONNULL(1)
Definition rand.cc:53
struct RNG * BLI_rng_new_srandom(unsigned int seed)
Definition rand.cc:46
float BLI_rng_get_float(struct RNG *rng) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition rand.cc:88
int BLI_sortutil_cmp_float_reverse(const void *a_, const void *b_)
Definition sort_utils.cc:39
unsigned int uint
#define UNUSED_VARS(...)
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define STREQ(a, b)
#define RPT_(msgid)
#define BLT_I18NCONTEXT_ID_CURVE_LEGACY
#define IFACE_(msgid)
#define BLT_I18NCONTEXT_ID_MESH
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1101
@ ID_RECALC_GEOMETRY_ALL_MODES
Definition DNA_ID.h:1181
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
#define CD_MASK_COLOR_ALL
@ CD_MDEFORMVERT
@ CD_PROP_INT32
@ CD_PROP_INT16_2D
#define MAXMAT
@ ME_EDIT_MIRROR_TOPO
@ ME_SYMMETRY_X
@ MOD_TRIANGULATE_QUAD_BEAUTY
@ eModifierMode_Realtime
@ eModifierType_Mirror
#define MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID
@ MOD_MIR_AXIS_Z
@ MOD_MIR_CLIPPING
@ MOD_MIR_AXIS_X
@ MOD_MIR_AXIS_Y
@ MOD_TRIANGULATE_NGON_BEAUTY
Object is a sort of wrapper for general info.
@ OB_MESH
@ PROP_SMOOTH
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ AUTO_MERGE
eDupli_ID_Flags
@ USER_DUP_MESH
@ USER_DUP_ACT
@ V3D_AROUND_ACTIVE
@ V3D_AROUND_CENTER_BOUNDS
@ V3D_AROUND_CURSOR
@ V3D_AROUND_CENTER_MEDIAN
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
@ OP_IS_REPEAT_LAST
@ OP_IS_MODAL_GRAB_CURSOR
void EDBM_flag_disable_all(BMEditMesh *em, char hflag)
void void EDBM_redo_state_restore_and_free(BMBackup *backup, BMEditMesh *em, bool recalc_looptris) ATTR_NONNULL(1
void EDBM_select_flush_from_verts(BMEditMesh *em, bool select)
void EDBM_update(Mesh *mesh, const EDBMUpdate_Params *params)
bool EDBM_uvselect_clear(BMEditMesh *em)
BMVert * EDBM_verts_mirror_get(BMEditMesh *em, BMVert *v)
void EDBM_verts_mirror_cache_begin(BMEditMesh *em, int axis, bool use_self, bool use_select, bool respecthide, bool use_topology)
void void void EDBM_redo_state_free(BMBackup *backup) ATTR_NONNULL(1)
bool EDBM_automerge_connected(Object *obedit, bool update, char hflag, float dist)
void EDBM_mesh_normals_update(BMEditMesh *em)
bool EDBM_mesh_reveal(BMEditMesh *em, bool select)
bool EDBM_mesh_hide(BMEditMesh *em, bool swap)
void EDBM_mesh_stats_multi(blender::Span< Object * > objects, int totelem[3], int totelem_sel[3])
void ED_mesh_geometry_clear(Mesh *mesh)
void EDBM_verts_mirror_apply(BMEditMesh *em, int sel_from, int sel_to)
void EDBM_selectmode_flush(BMEditMesh *em)
BMBackup EDBM_redo_state_store(BMEditMesh *em)
bool EDBM_automerge(Object *obedit, bool update, char hflag, float dist)
void EDBM_selectmode_flush_ex(BMEditMesh *em, short selectmode)
void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em, int axis, bool use_self, bool use_select, bool respecthide, bool use_topology, float maxdist, int *r_index)
void EDBM_verts_mirror_cache_end(BMEditMesh *em)
bool EDBM_selectmode_disable_multi_ex(Scene *scene, blender::Span< Base * > bases, short selectmode_disable, short selectmode_fallback)
bool EDBM_select_pick(bContext *C, const int mval[2], const SelectPick_Params &params)
void ED_outliner_select_sync_from_object_tag(bContext *C)
bool ED_operator_scene_editable(bContext *C)
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1024
bool ED_operator_editmesh(bContext *C)
@ SEL_OP_SET
void ED_uvedit_live_unwrap(const Scene *scene, blender::Span< Object * > objects)
RegionView3D * ED_view3d_context_rv3d(bContext *C)
void ED_view3d_win_to_3d_int(const View3D *v3d, const ARegion *region, const float depth_pt[3], const int mval[2], float r_out[3])
void ED_view3d_cursor3d_update(bContext *C, const int mval[2], bool use_depth, enum eV3DCursorOrient orientation)
@ V3D_CURSOR_ORIENT_NONE
Definition ED_view3d.hh:95
void view3d_operator_needs_gpu(const bContext *C)
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
@ PROP_ENUM
Definition RNA_types.hh:166
PropertyFlag
Definition RNA_types.hh:300
@ PROP_NEVER_UNLINK
Definition RNA_types.hh:384
@ PROP_ENUM_NO_TRANSLATE
Definition RNA_types.hh:432
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
@ PROP_NONE
Definition RNA_types.hh:233
#define C
Definition RandGen.cpp:29
@ UI_BUT_LABEL_ALIGN_NONE
eAutoPropButsReturn uiDefAutoButsRNA(uiLayout *layout, PointerRNA *ptr, bool(*check_prop)(PointerRNA *ptr, PropertyRNA *prop, void *user_data), void *user_data, PropertyRNA *prop_activate_init, eButLabelAlign label_align, bool compact)
@ UI_ITEM_R_EXPAND
#define UI_ITEM_NONE
#define NC_GEOM
Definition WM_types.hh:393
#define ND_DRAW
Definition WM_types.hh:461
#define ND_DATA
Definition WM_types.hh:509
@ OPTYPE_INTERNAL
Definition WM_types.hh:202
@ OPTYPE_BLOCKING
Definition WM_types.hh:184
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_SELECT
Definition WM_types.hh:508
#define NC_OBJECT
Definition WM_types.hh:379
#define U
#define BM_elem_cb_check_hflag_disabled_simple(type, hflag_n)
@ BM_SPACEARR_DIRTY_ALL
#define BM_DISK_EDGE_NEXT(e, v)
#define BM_ALL_NOLOOP
#define BM_ALL
#define BM_FACE_FIRST_LOOP(p)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SELECT
@ BM_ELEM_SMOOTH
@ BM_ELEM_TAG
#define BM_ELEM_CD_SET_BOOL(ele, offset, f)
#define BM_ELEM_CD_GET_VOID_P(ele, offset)
void BM_mesh_copy_init_customdata(BMesh *bm_dst, BMesh *bm_src, const BMAllocTemplate *allocsize)
void BM_mesh_copy_init_customdata_all_layers(BMesh *bm_dst, BMesh *bm_src, const char htype, const BMAllocTemplate *allocsize)
void BM_vert_separate(BMesh *bm, BMVert *v, BMEdge **e_in, int e_in_len, const bool copy_select, BMVert ***r_vout, int *r_vout_len)
void BM_vert_kill(BMesh *bm, BMVert *v)
void BM_face_kill(BMesh *bm, BMFace *f)
BMEdge * BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *e_example, const eBMCreateFlag create_flag)
Main function for creating a new edge.
eBMCreateFlag
Definition bmesh_core.hh:27
void BM_mesh_decimate_collapse(BMesh *bm, float factor, float *vweights, float vweight_factor, bool do_triangulate, int symmetry_axis, float symmetry_eps)
BM_mesh_decimate.
void BM_mesh_delete_hflag_context(BMesh *bm, const char hflag, const int type)
void BM_mesh_edgeloops_free(ListBase *eloops)
int BM_mesh_edgeloops_find(BMesh *bm, ListBase *r_eloops, bool(*test_fn)(BMEdge *, void *user_data), void *user_data)
int BM_edgeloop_length_get(BMEdgeLoopStore *el_store)
bool BM_edgeloop_is_closed(BMEdgeLoopStore *el_store)
void BM_edgeloop_edges_get(BMEdgeLoopStore *el_store, BMEdge **e_arr)
ListBase * BM_edgeloop_verts_get(BMEdgeLoopStore *el_store)
@ BMO_ERROR_FATAL
@ BMO_ERROR_CANCEL
bool BMO_error_occurred_at_level(BMesh *bm, eBMOpErrorLevel level)
#define BM_elem_index_get(ele)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_flag_set(ele, hflag, val)
#define BM_elem_index_set(ele, index)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_test_bool(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const StringRef name)
void BM_data_layer_ensure_named(BMesh *bm, CustomData *data, int type, const StringRef name)
void * BM_iter_at_index(BMesh *bm, const char itype, void *data, int index)
int BM_iter_elem_count_flag(const char itype, void *data, const char hflag, const bool value)
Elem Iter Flag Count.
#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_FACES_OF_EDGE
@ BM_FACES_OF_VERT
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_VERTS_OF_EDGE
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_VERT
@ BM_EDGES_OF_VERT
@ BM_EDGES_OF_FACE
@ BM_LOOPS_OF_FACE
BMesh const char void * data
BMesh * bm
BMVert * BM_mesh_active_vert_get(BMesh *bm)
void BM_select_history_clear(BMesh *bm)
char BM_select_history_htype_all(const BMesh *bm)
void BM_face_select_set(BMesh *bm, BMFace *f, const bool select)
Select Face.
void BM_vert_select_set(BMesh *bm, BMVert *v, const bool select)
Select Vert.
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_hflag_enable_test(BMesh *bm, const char htype, const char hflag, const bool respecthide, const bool overwrite, const char hflag_test)
#define BM_select_history_store_notest(bm, ele)
#define BM_select_history_store_head_notest(bm, ele)
#define BM_select_history_store(bm, ele)
#define BM_SELECT_HISTORY_BACKUP(bm)
#define BM_SELECT_HISTORY_RESTORE(bm)
const BMAllocTemplate bm_mesh_allocsize_default
Definition bmesh_mesh.cc:30
void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const uint *face_idx)
void BM_mesh_elem_toolflags_ensure(BMesh *bm)
Definition bmesh_mesh.cc:82
void BM_mesh_free(BMesh *bm)
BMesh Free Mesh.
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
BMesh * BM_mesh_create(const BMAllocTemplate *allocsize, const BMeshCreateParams *params)
BMesh Make Mesh.
BLI_INLINE BMVert * BM_vert_at_index(BMesh *bm, const int index)
void BM_mesh_bm_from_me(BMesh *bm, const Mesh *mesh, const BMeshFromMeshParams *params)
void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *mesh, const BMeshToMeshParams *params)
void BM_mesh_copy_arrays(BMesh *bm_src, BMesh *bm_dst, BMVert **verts_src, uint verts_src_len, BMEdge **edges_src, uint edges_src_len, BMFace **faces_src, uint faces_src_len)
void BM_lnorspace_update(BMesh *bm)
void BM_normals_loops_edges_tag(BMesh *bm, const bool do_edges)
BMLoopNorEditDataArray * BM_loop_normal_editdata_array_init_with_htype(BMesh *bm, const bool do_all_loops_of_vert, const char htype_override)
bool BM_custom_loop_normals_to_vector_layer(BMesh *bm)
void BM_mesh_normals_update(BMesh *bm)
BMLoopNorEditDataArray * BM_loop_normal_editdata_array_init(BMesh *bm, const bool do_all_loops_of_vert)
void BM_loop_normal_editdata_array_free(BMLoopNorEditDataArray *lnors_ed_arr)
bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr)
void BM_custom_loop_normals_from_vector_layer(BMesh *bm, bool add_sharp_edges)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
void * BMO_slot_buffer_alloc(BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, int len)
@ DEL_ONLYTAGGED
@ DEL_FACES_KEEP_BOUNDARY
@ DEL_EDGESFACES
@ DEL_ONLYFACES
@ BMO_SYMMETRIZE_NEGATIVE_X
void BMO_slot_buffer_hflag_enable(BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, char hflag, bool do_flush)
BMO_FLAG_BUFFER.
void * BMO_iter_map_value_ptr(BMOIter *iter)
void BMO_slot_buffer_hflag_disable(BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, char hflag, bool do_flush)
BMO_FLAG_BUFFER.
void BMO_slot_buffer_from_enabled_hflag(BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, char hflag)
BMOpSlot * BMO_slot_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *identifier)
BMESH OPSTACK GET SLOT.
void BMO_op_exec(BMesh *bm, BMOperator *op)
BMESH OPSTACK EXEC OP.
void * BMO_slot_buffer_get_first(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
void BMO_slot_int_set(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, int i)
void BMO_op_init(BMesh *bm, BMOperator *op, int flag, const char *opname)
BMESH OPSTACK INIT OP.
#define BMO_ITER(ele, iter, slot_args, slot_name, restrict_flag)
bool BMO_op_initf(BMesh *bm, BMOperator *op, int flag, const char *fmt,...)
void BMO_op_finish(BMesh *bm, BMOperator *op)
BMESH OPSTACK FINISH OP.
int BMO_slot_buffer_len(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
bool BMO_op_callf(BMesh *bm, int flag, const char *fmt,...)
#define BMO_FLAG_DEFAULTS
@ BMO_FLAG_RESPECT_HIDE
@ BMO_DELIM_NORMAL
BLI_INLINE void BMO_slot_map_elem_insert(BMOperator *op, BMOpSlot *slot, const void *element, void *val)
@ SUBD_CORNER_FAN
@ SUBD_CORNER_STRAIGHT_CUT
@ SUBD_CORNER_PATH
@ SUBD_CORNER_INNERVERT
void BM_mesh_esubdivide(BMesh *bm, char edge_hflag, float smooth, short smooth_falloff, bool use_smooth_even, float fractal, float along_normal, int numcuts, int seltype, int cornertype, short use_single_edge, short use_grid_fill, short use_only_quads, int seed)
@ FACE_STRENGTH_STRONG
@ FACE_STRENGTH_WEAK
@ FACE_STRENGTH_MEDIUM
@ SUBDIV_SELECT_ORIG
@ SUBD_RING_INTERP_SURF
@ SUBD_RING_INTERP_PATH
@ SUBD_RING_INTERP_LINEAR
@ BMOP_POKE_MEDIAN_WEIGHTED
@ BMOP_POKE_BOUNDS
@ BMOP_POKE_MEDIAN
@ SUBD_FALLOFF_LIN
void BM_vert_normal_update(BMVert *v)
bool BM_vert_calc_normal_ex(const BMVert *v, const char hflag, float r_no[3])
float BM_face_calc_area(const BMFace *f)
void BM_face_calc_center_median(const BMFace *f, float r_cent[3])
int BM_mesh_calc_edge_groups_as_arrays(BMesh *bm, BMVert **verts, BMEdge **edges, BMFace **faces, int(**r_groups)[3])
bool BM_vert_is_wire(const BMVert *v)
BMFace * BM_edge_pair_share_face_by_len(BMEdge *e_a, BMEdge *e_b, BMLoop **r_l_a, BMLoop **r_l_b, const bool allow_adjacent)
BMLoop * BM_vert_step_fan_loop(BMLoop *l, BMEdge **e_step)
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_edge_share_face_check(BMEdge *e1, BMEdge *e2)
float BM_edge_calc_face_angle_ex(const BMEdge *e, const float fallback)
BMESH EDGE/FACE ANGLE.
float BM_loop_calc_face_angle(const BMLoop *l)
BMLoop * BM_face_edge_share_loop(BMFace *f, BMEdge *e)
Return the Loop Shared by Face and Edge.
BMLoop * BM_face_vert_share_loop(BMFace *f, BMVert *v)
Return the Loop Shared by Face and Vertex.
bool BM_vert_pair_share_face_check_cb(BMVert *v_a, BMVert *v_b, bool(*test_fn)(BMFace *, void *user_data), void *user_data)
BLI_INLINE bool BM_edge_is_boundary(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
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 BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
btMatrix3x3 absolute() const
Return the matrix with all values non negative.
Definition btVector3.h:364
static unsigned long seed
Definition btSoftBody.h:39
static AttributeOwner from_id(ID *id)
Definition attribute.cc:44
const T * data() const
Definition BLI_array.hh:312
int64_t size() const
IndexRange index_range() const
nullptr float
#define offsetof(t, d)
static bool UNUSED_FUNCTION EDBM_select_mirrored_extend_all(Object *obedit, BMEditMesh *em)
void MESH_OT_poke(wmOperatorType *ot)
static wmOperatorStatus edbm_flip_normals_exec(bContext *C, wmOperator *op)
static void edbm_dissolve_prop__use_angle_threshold(wmOperatorType *ot, int flag)
static int edbm_bridge_edge_loops_for_single_editmesh(wmOperator *op, BMEditMesh *em, Mesh *mesh, const bool use_pairs, const bool use_cyclic, const bool use_merge, const float merge_factor, const int twist_offset)
static wmOperatorStatus edbm_vert_connect_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_add_edge_face_exec(bContext *C, wmOperator *op)
void MESH_OT_wireframe(wmOperatorType *ot)
void MESH_OT_hide(wmOperatorType *ot)
void MESH_OT_shape_propagate_to_all(wmOperatorType *ot)
static void edbm_normals_tools_ui(bContext *C, wmOperator *op)
static void mesh_operator_edgering_props_get(wmOperator *op, EdgeRingOpSubdProps *op_props)
static wmOperatorStatus normals_split_merge(bContext *C, const bool do_merge)
static wmOperatorStatus edbm_rotate_uvs_exec(bContext *C, wmOperator *op)
void MESH_OT_uvs_reverse(wmOperatorType *ot)
static bool normals_tools_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop, void *)
static wmOperatorStatus edbm_separate_exec(bContext *C, wmOperator *op)
void MESH_OT_mod_weighted_strength(wmOperatorType *ot)
static bool edbm_sort_elements_poll_property(const bContext *, wmOperator *op, const PropertyRNA *prop)
static wmOperatorStatus edbm_merge_exec(bContext *C, wmOperator *op)
static void mesh_operator_edgering_props(wmOperatorType *ot, const int cuts_min, const int cuts_default)
static bool flip_custom_normals(BMesh *bm, BMLoopNorEditDataArray *lnors_ed_arr)
static wmOperatorStatus edbm_split_exec(bContext *C, wmOperator *op)
@ EDBM_CLNOR_POINTTO_MODE_MOUSE
@ EDBM_CLNOR_POINTTO_MODE_COORDINATES
static void edbm_dissolve_prop__use_face_split(wmOperatorType *ot)
static wmOperatorStatus edbm_reverse_colors_exec(bContext *C, wmOperator *op)
static void point_normals_update_statusbar(bContext *C, wmOperator *op)
@ MESH_MERGE_LAST
@ MESH_MERGE_CENTER
@ MESH_MERGE_CURSOR
@ MESH_MERGE_FIRST
@ MESH_MERGE_COLLAPSE
static EnumPropertyItem average_method_items[]
static bool dissolve_mode_poll_property(const bContext *C, wmOperator *op, const PropertyRNA *prop)
static wmOperatorStatus edbm_unsubdivide_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_decimate_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_delete_loose_exec(bContext *C, wmOperator *op)
static void edbm_blend_from_shape_ui(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_set_normals_from_faces_exec(bContext *C, wmOperator *op)
static bool bm_vert_connect_pair(BMesh *bm, BMVert *v_a, BMVert *v_b)
static wmOperatorStatus mesh_symmetry_snap_exec(bContext *C, wmOperator *op)
static bool point_normals_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop, void *)
static wmOperatorStatus edbm_point_normals_exec(bContext *C, wmOperator *op)
void MESH_OT_faces_shade_flat(wmOperatorType *ot)
static wmOperatorStatus edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op)
static BMElem * edbm_add_edge_face_exec__tricky_extend_sel(BMesh *bm)
void MESH_OT_edge_rotate(wmOperatorType *ot)
static wmOperatorStatus edbm_dissolve_limited_exec(bContext *C, wmOperator *op)
static bool edbm_connect_vert_pair(BMEditMesh *em, Mesh *mesh, wmOperator *op)
static void mesh_separate_material_assign_mat_nr(Main *bmain, Object *ob, const short mat_nr)
void MESH_OT_set_normals_from_faces(wmOperatorType *ot)
void MESH_OT_symmetrize(wmOperatorType *ot)
static void normals_merge(BMesh *bm, BMLoopNorEditDataArray *lnors_ed_arr)
static const EnumPropertyItem prop_mesh_cornervert_types[]
void MESH_OT_edge_split(wmOperatorType *ot)
static wmOperatorStatus edbm_dissolve_degenerate_exec(bContext *C, wmOperator *op)
static bool bm_vert_connect_select_history(BMesh *bm)
static bool merge_firstlast(BMEditMesh *em, const bool use_first, const bool use_uvmerge, wmOperator *wmop)
void MESH_OT_delete(wmOperatorType *ot)
static wmOperatorStatus edbm_mod_weighted_strength_exec(bContext *C, wmOperator *op)
@ MESH_BRIDGELOOP_PAIRS
@ MESH_BRIDGELOOP_SINGLE
@ MESH_BRIDGELOOP_CLOSED
static int edbm_bridge_tag_boundary_edges(BMesh *bm)
static wmOperatorStatus edbm_subdivide_exec(bContext *C, wmOperator *op)
static void edbm_average_normals_ui(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_reverse_uvs_exec(bContext *C, wmOperator *op)
void MESH_OT_beautify_fill(wmOperatorType *ot)
static bool mesh_separate_loose(Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
static void bmesh_selected_verts_center_calc(BMesh *bm, float *r_center)
static wmOperatorStatus edbm_solidify_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_wireframe_exec(bContext *C, wmOperator *op)
void MESH_OT_colors_rotate(wmOperatorType *ot)
void MESH_OT_quads_convert_to_tris(wmOperatorType *ot)
static wmOperatorStatus edbm_duplicate_exec(bContext *C, wmOperator *op)
static void edbm_flip_quad_tessellation(wmOperator *op, Object *obedit, BMEditMesh *em)
static bool bm_face_is_loose(BMFace *f)
static wmOperatorStatus edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op)
void MESH_OT_vert_connect(wmOperatorType *ot)
static const EnumPropertyItem * merge_type_itemf(bContext *C, PointerRNA *, PropertyRNA *, bool *r_free)
static wmOperatorStatus edbm_rotate_colors_exec(bContext *C, wmOperator *op)
static bool edbm_edge_split_selected_verts(wmOperator *op, Object *obedit, BMEditMesh *em)
static EnumPropertyItem clnors_pointto_mode_items[]
void MESH_OT_vertices_smooth(wmOperatorType *ot)
static int bmelemsort_comp(const void *v1, const void *v2)
static void edbm_dissolve_prop__use_verts(wmOperatorType *ot, bool value, int flag)
void MESH_OT_face_make_planar(wmOperatorType *ot)
static void point_normals_free(wmOperator *op)
void MESH_OT_dissolve_edges(wmOperatorType *ot)
void MESH_OT_uvs_rotate(wmOperatorType *ot)
static int edbm_add_edge_face_exec__vert_edge_lookup(BMVert *v, BMEdge *e_used, BMEdge **e_arr, const int e_arr_len, bool(*func)(const BMEdge *))
static void point_normals_apply(bContext *C, wmOperator *op, float target[3], const bool do_reset)
static wmOperatorStatus edbm_beautify_fill_exec(bContext *C, wmOperator *op)
void MESH_OT_delete_loose(wmOperatorType *ot)
void MESH_OT_split_normals(wmOperatorType *ot)
@ MESH_DELETE_EDGE_FACE
@ MESH_DELETE_EDGE
@ MESH_DELETE_FACE
@ MESH_DELETE_VERT
@ MESH_DELETE_ONLY_FACE
static void edbm_point_normals_ui(bContext *C, wmOperator *op)
void MESH_OT_fill(wmOperatorType *ot)
static void point_normals_cancel(bContext *C, wmOperator *op)
void MESH_OT_mark_seam(wmOperatorType *ot)
@ EDBM_CLNOR_TOOLS_RESET
@ EDBM_CLNOR_TOOLS_COPY
@ EDBM_CLNOR_TOOLS_ADD
@ EDBM_CLNOR_TOOLS_MULTIPLY
@ EDBM_CLNOR_TOOLS_PASTE
static bool bm_vert_connect_select_history_edge_to_vert_path(BMesh *bm, ListBase *r_selected)
void MESH_OT_remove_doubles(wmOperatorType *ot)
static wmOperatorStatus edbm_vert_connect_path_exec(bContext *C, wmOperator *op)
void MESH_OT_offset_edge_loops(wmOperatorType *ot)
static wmOperatorStatus edbm_normals_make_consistent_exec(bContext *C, wmOperator *op)
static BMLoopNorEditDataArray * flip_custom_normals_init_data(BMesh *bm)
static wmOperatorStatus edbm_faces_shade_flat_exec(bContext *C, wmOperator *)
static wmOperatorStatus edbm_fill_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_dissolve_edges_exec(bContext *C, wmOperator *op)
@ SRT_VIEW_XAXIS
@ SRT_SELECTED
@ SRT_RANDOMIZE
@ SRT_MATERIAL
@ SRT_CURSOR_DISTANCE
@ SRT_REVERSE
@ SRT_VIEW_ZAXIS
void MESH_OT_subdivide_edgering(wmOperatorType *ot)
static wmOperatorStatus edbm_do_smooth_vertex_exec(bContext *C, wmOperator *op)
static bool mesh_separate_selected(Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
void MESH_OT_separate(wmOperatorType *ot)
static wmOperatorStatus edbm_smooth_normals_exec(bContext *C, wmOperator *op)
void MESH_OT_dissolve_limited(wmOperatorType *ot)
static wmOperatorStatus edbm_subdivide_edge_ring_exec(bContext *C, wmOperator *op)
void MESH_OT_dissolve_degenerate(wmOperatorType *ot)
void MESH_OT_blend_from_shape(wmOperatorType *ot)
void MESH_OT_fill_grid(wmOperatorType *ot)
void MESH_OT_vert_connect_nonplanar(wmOperatorType *ot)
static const EnumPropertyItem * shape_itemf(bContext *C, PointerRNA *, PropertyRNA *, bool *r_free)
static bool merge_target(BMEditMesh *em, Scene *scene, Object *ob, const bool use_cursor, const bool use_uvmerge, wmOperator *wmop)
void MESH_OT_flip_quad_tessellation(wmOperatorType *ot)
void MESH_OT_decimate(wmOperatorType *ot)
void MESH_OT_point_normals(wmOperatorType *ot)
static wmOperatorStatus edbm_merge_normals_exec(bContext *C, wmOperator *)
static wmOperatorStatus edbm_dissolve_mode_exec(bContext *C, wmOperator *op)
static void edbm_flip_normals_custom_loop_normals(Object *obedit, BMEditMesh *em)
@ EDBM_CLNOR_AVERAGE_LOOP
@ EDBM_CLNOR_AVERAGE_FACE_AREA
@ EDBM_CLNOR_AVERAGE_ANGLE
static bool edbm_add_edge_face__smooth_get(BMesh *bm)
static bool average_normals_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop, void *)
static void edbm_add_edge_face_exec__tricky_finalize_sel(BMesh *bm, BMElem *ele_desel, BMFace *f)
static wmOperatorStatus edbm_blend_from_shape_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_vert_connect_nonplaner_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_edge_rotate_selected_exec(bContext *C, wmOperator *op)
static FillGridSplitJoin * edbm_fill_grid_split_join_init(BMEditMesh *em)
void MESH_OT_dissolve_mode(wmOperatorType *ot)
static void mesh_set_smooth_faces(BMEditMesh *em, short smooth)
void MESH_OT_bridge_edge_loops(wmOperatorType *ot)
static wmOperatorStatus edbm_fill_holes_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_hide_exec(bContext *C, wmOperator *op)
static bool bm_vert_is_select_history_open(BMesh *bm)
static Base * mesh_separate_tagged(Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
void MESH_OT_subdivide(wmOperatorType *ot)
void MESH_OT_unsubdivide(wmOperatorType *ot)
@ MESH_SEPARATE_LOOSE
@ MESH_SEPARATE_MATERIAL
@ MESH_SEPARATE_SELECTED
void MESH_OT_duplicate(wmOperatorType *ot)
static bool shape_propagate(BMEditMesh *em, bool use_symmetry)
wmKeyMap * point_normals_modal_keymap(wmKeyConfig *keyconf)
static wmOperatorStatus edbm_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *)
void MESH_OT_normals_make_consistent(wmOperatorType *ot)
static wmOperatorStatus edbm_quads_convert_to_tris_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_sort_elements_exec(bContext *C, wmOperator *op)
void MESH_OT_tris_convert_to_quads(wmOperatorType *ot)
static const EnumPropertyItem prop_mesh_face_strength_types[]
static wmOperatorStatus edbm_poke_face_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_offset_edgeloop_exec(bContext *C, wmOperator *op)
void MESH_OT_flip_normals(wmOperatorType *ot)
static wmOperatorStatus edbm_reveal_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_normals_tools_exec(bContext *C, wmOperator *op)
static void edbm_report_delete_info(ReportList *reports, const int totelem_old[3], const int totelem_new[3])
static bool edbm_fill_grid_prepare(BMesh *bm, int offset, int *span_p, const bool span_calc)
static void join_triangle_props(wmOperatorType *ot)
void MESH_OT_mark_sharp(wmOperatorType *ot)
static wmOperatorStatus edbm_delete_exec(bContext *C, wmOperator *op)
void MESH_OT_smooth_normals(wmOperatorType *ot)
void MESH_OT_vert_connect_path(wmOperatorType *ot)
void MESH_OT_merge_normals(wmOperatorType *ot)
static wmOperatorStatus edbm_vert_connect_concave_exec(bContext *C, wmOperator *op)
static bool point_normals_ensure(bContext *C, wmOperator *op)
void MESH_OT_reveal(wmOperatorType *ot)
static wmOperatorStatus edbm_bridge_edge_loops_exec(bContext *C, wmOperator *op)
void MESH_OT_delete_edgeloop(wmOperatorType *ot)
static void sort_bmelem_flag(bContext *C, Scene *scene, Object *ob, RegionView3D *rv3d, const int types, const int flag, const int action, const int reverse, const uint seed)
static void edbm_dissolve_prop__use_boundary_tear(wmOperatorType *ot)
void MESH_OT_merge(wmOperatorType *ot)
void MESH_OT_sort_elements(wmOperatorType *ot)
void MESH_OT_fill_holes(wmOperatorType *ot)
#define CLNORS_VALID_VEC_LEN
static wmOperatorStatus edbm_delete_edgeloop_exec(bContext *C, wmOperator *op)
void MESH_OT_solidify(wmOperatorType *ot)
static wmOperatorStatus edbm_mark_sharp_exec(bContext *C, wmOperator *op)
static EnumPropertyItem normal_vector_tool_items[]
static bool mesh_separate_material(Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
void MESH_OT_vert_connect_concave(wmOperatorType *ot)
static void edbm_decimate_ui(bContext *, wmOperator *op)
static wmOperatorStatus edbm_shape_propagate_to_all_exec(bContext *C, wmOperator *op)
static bool point_normals_init(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_average_normals_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_face_make_planar_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_flip_quad_tessellation_exec(bContext *C, wmOperator *op)
static bool edbm_edge_split_selected_edges(wmOperator *op, Object *obedit, BMEditMesh *em)
void MESH_OT_symmetry_snap(wmOperatorType *ot)
void MESH_OT_normals_tools(wmOperatorType *ot)
static wmOperatorStatus edbm_point_normals_invoke(bContext *C, wmOperator *op, const wmEvent *)
static wmOperatorStatus edbm_mark_seam_exec(bContext *C, wmOperator *op)
void MESH_OT_average_normals(wmOperatorType *ot)
static void edbm_flip_normals_face_winding(wmOperator *op, Object *obedit, BMEditMesh *em)
void MESH_OT_faces_shade_smooth(wmOperatorType *ot)
void MESH_OT_colors_reverse(wmOperatorType *ot)
static wmOperatorStatus edbm_collapse_edge_exec(bContext *C, wmOperator *op)
static const EnumPropertyItem merge_type_items[]
void MESH_OT_dissolve_faces(wmOperatorType *ot)
static void normals_split(BMesh *bm)
static wmOperatorStatus edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus edbm_remove_doubles_exec(bContext *C, wmOperator *op)
@ EDBM_CLNOR_MODAL_POINTTO_RESET
@ EDBM_CLNOR_MODAL_CANCEL
@ EDBM_CLNOR_MODAL_POINTTO_INVERT
@ EDBM_CLNOR_MODAL_CONFIRM
@ EDBM_CLNOR_MODAL_POINTTO_ALIGN
@ EDBM_CLNOR_MODAL_POINTTO_USE_OBJECT
@ EDBM_CLNOR_MODAL_POINTTO_SET_USE_3DCURSOR
@ EDBM_CLNOR_MODAL_POINTTO_USE_MOUSE
@ EDBM_CLNOR_MODAL_POINTTO_SET_USE_SELECTED
@ EDBM_CLNOR_MODAL_POINTTO_USE_PIVOT
@ EDBM_CLNOR_MODAL_POINTTO_SPHERIZE
static wmOperatorStatus edbm_split_normals_exec(bContext *C, wmOperator *)
static void edbm_fill_grid_split_join_finish(BMEditMesh *em, wmOperator *op, FillGridSplitJoin *split_join, bool changed)
static bool bm_edge_test_fill_grid_cb(BMEdge *e, void *)
static wmOperatorStatus edbm_edge_split_exec(bContext *C, wmOperator *op)
void MESH_OT_edge_face_add(wmOperatorType *ot)
void MESH_OT_edge_collapse(wmOperatorType *ot)
static wmOperatorStatus edbm_dissolve_verts_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_dissolve_faces_exec(bContext *C, wmOperator *op)
void MESH_OT_vertices_smooth_laplacian(wmOperatorType *ot)
static bool edbm_decimate_check(bContext *, wmOperator *)
void MESH_OT_dissolve_verts(wmOperatorType *ot)
static wmOperatorStatus edbm_faces_shade_smooth_exec(bContext *C, wmOperator *)
static wmOperatorStatus mesh_symmetrize_exec(bContext *C, wmOperator *op)
static Base * mesh_separate_arrays(Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old, BMVert **verts, uint verts_len, BMEdge **edges, uint edges_len, BMFace **faces, uint faces_len)
static wmOperatorStatus edbm_fill_grid_exec(bContext *C, wmOperator *op)
void MESH_OT_split(wmOperatorType *ot)
static float edbm_fill_grid_vert_tag_angle(BMVert *v)
bool EDBM_op_callf(BMEditMesh *em, wmOperator *op, const char *fmt,...)
bool EDBM_op_init(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const char *fmt,...)
bool EDBM_op_call_and_selectf(BMEditMesh *em, wmOperator *op, const char *select_slot_out, const bool select_extend, const char *fmt,...)
bool EDBM_op_finish(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const bool do_report)
static float verts[][3]
uint col
#define pow
#define select(A, B, C)
BLI_INLINE float fb(float length, float L)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int count
static char ** types
Definition makesdna.cc:71
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static char faces[256]
void MESH_OT_convex_hull(wmOperatorType *ot)
static void clear(Message &msg)
Definition msgfmt.cc:213
bool shape_key_report_if_locked(const Object *obedit, ReportList *reports)
bool shape_key_report_if_any_locked(Object *ob, ReportList *reports)
void base_select(Base *base, eObjectSelect_Mode mode)
Base * add_duplicate(Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base, eDupli_ID_Flags dupflag)
bool calc_active_center_for_editmode(Object *obedit, bool select_only, float r_center[3])
const btScalar eps
Definition poly34.cpp:11
const int status
return ret
#define fabsf
float RNA_property_float_get(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_int_set(PointerRNA *ptr, PropertyRNA *prop, int value)
void RNA_property_float_get_array(PointerRNA *ptr, PropertyRNA *prop, float *values)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
int RNA_property_int_get(PointerRNA *ptr, PropertyRNA *prop)
int RNA_int_get(PointerRNA *ptr, const char *name)
void RNA_property_boolean_set(PointerRNA *ptr, PropertyRNA *prop, bool value)
float RNA_float_get(PointerRNA *ptr, const char *name)
void RNA_property_float_set_array(PointerRNA *ptr, PropertyRNA *prop, const float *values)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
int RNA_enum_get(PointerRNA *ptr, const char *name)
const char * RNA_property_identifier(const PropertyRNA *prop)
PointerRNA RNA_id_pointer_create(ID *id)
PropertyRNA * RNA_def_float_factor(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)
void RNA_def_property_float_default(PropertyRNA *prop, float value)
void RNA_def_property_enum_default(PropertyRNA *prop, int value)
void RNA_def_property_ui_text(PropertyRNA *prop, const char *name, const char *description)
PropertyRNA * RNA_def_float_rotation(StructOrFunctionRNA *cont_, const char *identifier, const int len, 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_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_float(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)
void RNA_def_property_int_default(PropertyRNA *prop, int value)
void RNA_def_property_enum_items(PropertyRNA *prop, const EnumPropertyItem *item)
PropertyRNA * RNA_def_float_vector_xyz(StructOrFunctionRNA *cont_, const char *identifier, const int len, 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_property(StructOrFunctionRNA *cont_, const char *identifier, int type, int subtype)
PropertyRNA * RNA_def_enum_flag(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
void RNA_enum_item_end(EnumPropertyItem **items, int *totitem)
void RNA_enum_item_add(EnumPropertyItem **items, int *totitem, const EnumPropertyItem *item)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_translation_context(PropertyRNA *prop, const char *context)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
void RNA_def_enum_funcs(PropertyRNA *prop, EnumPropertyItemFunc itemfunc)
void RNA_enum_items_add_value(EnumPropertyItem **items, int *totitem, const EnumPropertyItem *item, int value)
void RNA_def_property_ui_range(PropertyRNA *prop, double min, double max, double step, int precision)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
const EnumPropertyItem rna_enum_mesh_delimit_mode_items[]
Definition rna_mesh.cc:26
const EnumPropertyItem rna_enum_modifier_triangulate_ngon_method_items[]
const EnumPropertyItem rna_enum_axis_xyz_items[]
const EnumPropertyItem rna_enum_modifier_triangulate_quad_method_items[]
const EnumPropertyItem rna_enum_dummy_NULL_items[]
Definition rna_rna.cc:26
const EnumPropertyItem rna_enum_proportional_falloff_curve_only_items[]
Definition rna_scene.cc:112
const EnumPropertyItem rna_enum_symmetrize_direction_items[]
#define min(a, b)
Definition sort.cc:36
#define FLT_MAX
Definition stdcycles.h:14
BMVert * v1
BMVert * v2
struct BMLoop * l
short selectmode
struct BMEditSelection * next
BMHeader head
short mat_nr
float no[3]
void * data
BMLoopNorEditData ** lidx_to_lnor_editdata
BMLoopNorEditData * lnor_editdata
struct BMVert * v
struct BMEdge * e
struct BMLoop * radial_next
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
struct BMOpSlot slots_out[BMO_OP_MAX_SLOTS]
struct BMOpSlot slots_in[BMO_OP_MAX_SLOTS]
float co[3]
BMHeader head
int totvert
int totfacesel
struct MLoopNorSpaceArray * lnor_spacearr
CustomData vdata
int totedge
ListBase selected
CustomData edata
int totvertsel
int totedgesel
CustomData pdata
CustomData ldata
int totface
struct Object * object
CustomDataLayer * layers
const char * identifier
Definition RNA_types.hh:657
const char * name
Definition RNA_types.hh:661
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
char name[64]
short relative
ListBase block
void * data
struct LinkData * next
void * link
struct LinkNode * next
void * last
void * first
struct LinkNode * loops
Definition BKE_mesh.h:271
char symmetry
MeshRuntimeHandle * runtime
char editflag
struct Key * key
char * active_color_attribute
ObjectRuntimeHandle * runtime
struct Material ** mat
float loc[3]
char * matbits
Definition rand.cc:33
float viewmat[4][4]
struct ToolSettings * toolsettings
View3DCursor cursor
float normal_vector[3]
void use_property_decorate_set(bool is_sep)
uiLayout & column(bool align)
void prop_search(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop, PropertyRNA *item_searchpropname, std::optional< blender::StringRefNull > name, int icon, bool results_are_suggestions)
uiLayout & row(bool align)
void use_property_split_set(bool value)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
wmEventType type
Definition WM_types.hh:757
short val
Definition WM_types.hh:759
int mval[2]
Definition WM_types.hh:763
const void * modal_items
StructRNA * srna
Definition WM_types.hh:1127
struct ReportList * reports
IDProperty * properties
struct uiLayout * layout
struct wmOperatorType * type
struct PointerRNA * ptr
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
static int blend(const Tex *tex, const float texvec[3], TexResult *texres)
uint len
void WM_cursor_wait(bool val)
bool WM_operator_poll(bContext *C, wmOperatorType *ot)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
#define ISMOUSE_MOTION(event_type)
@ EVT_MODAL_MAP
@ MOUSEMOVE
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:932
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:959
void WM_operatortype_props_advanced_begin(wmOperatorType *ot)
void WM_operator_type_modal_from_exec_for_object_edit_coords(wmOperatorType *ot)
wmOperatorStatus WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
uint8_t flag
Definition wm_window.cc:145