Blender V4.5
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_geom.h"
29#include "BLI_math_matrix.h"
30#include "BLI_math_rotation.h"
31#include "BLI_math_vector.h"
32#include "BLI_rand.h"
33#include "BLI_sort_utils.h"
34
35#include "BKE_attribute.h"
36#include "BKE_context.hh"
37#include "BKE_customdata.hh"
38#include "BKE_deform.hh"
39#include "BKE_editmesh.hh"
40#include "BKE_key.hh"
41#include "BKE_layer.hh"
42#include "BKE_lib_id.hh"
43#include "BKE_material.hh"
44#include "BKE_mesh.hh"
45#include "BKE_mesh_types.hh"
46#include "BKE_object.hh"
47#include "BKE_object_types.hh"
48#include "BKE_report.hh"
49
50#include "DEG_depsgraph.hh"
52
53#include "BLT_translation.hh"
54
55#include "RNA_access.hh"
56#include "RNA_define.hh"
57#include "RNA_enum_types.hh"
58#include "RNA_prototypes.hh"
59
60#include "WM_api.hh"
61#include "WM_types.hh"
62
63#include "ED_mesh.hh"
64#include "ED_object.hh"
65#include "ED_outliner.hh"
66#include "ED_screen.hh"
67#include "ED_select_utils.hh"
68#include "ED_transform.hh"
69#include "ED_uvedit.hh"
70#include "ED_view3d.hh"
71
72#include "UI_interface.hh"
73#include "UI_resources.hh"
74
75#include "mesh_intern.hh" /* own include */
76
77#include "bmesh_tools.hh"
78
79using blender::Vector;
80
81#define USE_FACE_CREATE_SEL_EXTEND
82
83/* -------------------------------------------------------------------- */
86
88{
89 const int cuts = RNA_int_get(op->ptr, "number_cuts");
90 const float smooth = RNA_float_get(op->ptr, "smoothness");
91 const float fractal = RNA_float_get(op->ptr, "fractal") / 2.5f;
92 const float along_normal = RNA_float_get(op->ptr, "fractal_along_normal");
93 const bool use_quad_tri = !RNA_boolean_get(op->ptr, "ngon");
94
95 if (use_quad_tri && RNA_enum_get(op->ptr, "quadcorner") == SUBD_CORNER_STRAIGHT_CUT) {
96 RNA_enum_set(op->ptr, "quadcorner", SUBD_CORNER_INNERVERT);
97 }
98 const int quad_corner_type = RNA_enum_get(op->ptr, "quadcorner");
99 const int seed = RNA_int_get(op->ptr, "seed");
100
101 const Scene *scene = CTX_data_scene(C);
102 ViewLayer *view_layer = CTX_data_view_layer(C);
104 scene, view_layer, CTX_wm_view3d(C));
105
106 for (Object *obedit : objects) {
108
109 if (!(em->bm->totedgesel || em->bm->totfacesel)) {
110 continue;
111 }
112
115 smooth,
117 false,
118 fractal,
119 along_normal,
120 cuts,
122 quad_corner_type,
123 use_quad_tri,
124 true,
125 false,
126 seed);
127
129 params.calc_looptris = true;
130 params.calc_normals = false;
131 params.is_destructive = true;
132 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
133 }
134
135 return OPERATOR_FINISHED;
136}
137
138/* NOTE: these values must match delete_mesh() event values. */
140 {SUBD_CORNER_INNERVERT, "INNERVERT", 0, "Inner Vert", ""},
141 {SUBD_CORNER_PATH, "PATH", 0, "Path", ""},
142 {SUBD_CORNER_STRAIGHT_CUT, "STRAIGHT_CUT", 0, "Straight Cut", ""},
143 {SUBD_CORNER_FAN, "FAN", 0, "Fan", ""},
144 {0, nullptr, 0, nullptr, nullptr},
145};
146
148{
149 PropertyRNA *prop;
150
151 /* identifiers */
152 ot->name = "Subdivide";
153 ot->description = "Subdivide selected edges";
154 ot->idname = "MESH_OT_subdivide";
155
156 /* API callbacks. */
157 ot->exec = edbm_subdivide_exec;
158 ot->poll = ED_operator_editmesh;
159
160 /* flags */
162
163 /* properties */
164 prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 100, "Number of Cuts", "", 1, 10);
165 /* avoid re-using last var because it can cause
166 * _very_ high poly meshes and annoy users (or worse crash) */
168
170 ot->srna, "smoothness", 0.0f, 0.0f, 1e3f, "Smoothness", "Smoothness factor", 0.0f, 1.0f);
171
173
174 RNA_def_boolean(ot->srna,
175 "ngon",
176 true,
177 "Create N-Gons",
178 "When disabled, newly created faces are limited to 3 and 4 sided faces");
180 ot->srna,
181 "quadcorner",
184 "Quad Corner Type",
185 "How to subdivide quad corners (anything other than Straight Cut will prevent n-gons)");
186
187 RNA_def_float(ot->srna,
188 "fractal",
189 0.0f,
190 0.0f,
191 1e6f,
192 "Fractal",
193 "Fractal randomness factor",
194 0.0f,
195 1000.0f);
196 RNA_def_float(ot->srna,
197 "fractal_along_normal",
198 0.0f,
199 0.0f,
200 1.0f,
201 "Along Normal",
202 "Apply fractal displacement along normal only",
203 0.0f,
204 1.0f);
205 RNA_def_int(ot->srna,
206 "seed",
207 0,
208 0,
209 INT_MAX,
210 "Random Seed",
211 "Seed for the random number generator",
212 0,
213 255);
214}
215
217
218/* -------------------------------------------------------------------- */
224
233
235 const int cuts_min,
236 const int cuts_default)
237{
238 /* NOTE: these values must match delete_mesh() event values. */
239 static const EnumPropertyItem prop_subd_edgering_types[] = {
240 {SUBD_RING_INTERP_LINEAR, "LINEAR", 0, "Linear", ""},
241 {SUBD_RING_INTERP_PATH, "PATH", 0, "Blend Path", ""},
242 {SUBD_RING_INTERP_SURF, "SURFACE", 0, "Blend Surface", ""},
243 {0, nullptr, 0, nullptr, nullptr},
244 };
245
246 PropertyRNA *prop;
247
248 prop = RNA_def_int(
249 ot->srna, "number_cuts", cuts_default, 0, 1000, "Number of Cuts", "", cuts_min, 64);
251
252 RNA_def_enum(ot->srna,
253 "interpolation",
254 prop_subd_edgering_types,
256 "Interpolation",
257 "Interpolation method");
258
260 ot->srna, "smoothness", 1.0f, 0.0f, 1e3f, "Smoothness", "Smoothness factor", 0.0f, 2.0f);
261
262 /* profile-shape */
263 RNA_def_float(ot->srna,
264 "profile_shape_factor",
265 0.0f,
266 -1e3f,
267 1e3f,
268 "Profile Factor",
269 "How much intermediary new edges are shrunk/expanded",
270 -2.0f,
271 2.0f);
272
273 prop = RNA_def_property(ot->srna, "profile_shape", PROP_ENUM, PROP_NONE);
276 RNA_def_property_ui_text(prop, "Profile Shape", "Shape of the profile");
278 BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */
279}
280
282{
283 op_props->interp_mode = RNA_enum_get(op->ptr, "interpolation");
284 op_props->cuts = RNA_int_get(op->ptr, "number_cuts");
285 op_props->smooth = RNA_float_get(op->ptr, "smoothness");
286
287 op_props->profile_shape = RNA_enum_get(op->ptr, "profile_shape");
288 op_props->profile_shape_factor = RNA_float_get(op->ptr, "profile_shape_factor");
289}
290
292{
293 const Scene *scene = CTX_data_scene(C);
294 ViewLayer *view_layer = CTX_data_view_layer(C);
296 scene, view_layer, CTX_wm_view3d(C));
297 EdgeRingOpSubdProps op_props;
298
300
301 for (Object *obedit : objects) {
303
304 if (em->bm->totedgesel == 0) {
305 continue;
306 }
307
308 if (!EDBM_op_callf(em,
309 op,
310 "subdivide_edgering edges=%he interp_mode=%i cuts=%i smooth=%f "
311 "profile_shape=%i profile_shape_factor=%f",
313 op_props.interp_mode,
314 op_props.cuts,
315 op_props.smooth,
316 op_props.profile_shape,
317 op_props.profile_shape_factor))
318 {
319 continue;
320 }
321
323 params.calc_looptris = true;
324 params.calc_normals = false;
325 params.is_destructive = true;
326 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
327 }
328
329 return OPERATOR_FINISHED;
330}
331
333{
334 /* identifiers */
335 ot->name = "Subdivide Edge-Ring";
336 ot->description = "Subdivide perpendicular edges to the selected edge-ring";
337 ot->idname = "MESH_OT_subdivide_edgering";
338
339 /* API callbacks. */
341 ot->poll = ED_operator_editmesh;
342
343 /* flags */
345
346 /* properties */
348}
349
351
352/* -------------------------------------------------------------------- */
355
357{
358 const int iterations = RNA_int_get(op->ptr, "iterations");
359 const Scene *scene = CTX_data_scene(C);
360 ViewLayer *view_layer = CTX_data_view_layer(C);
362 scene, view_layer, CTX_wm_view3d(C));
363 for (Object *obedit : objects) {
365
366 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
367 continue;
368 }
369
370 BMOperator bmop;
371 EDBM_op_init(em, &bmop, op, "unsubdivide verts=%hv iterations=%i", BM_ELEM_SELECT, iterations);
372
373 BMO_op_exec(em->bm, &bmop);
374
375 if (!EDBM_op_finish(em, &bmop, op, true)) {
376 continue;
377 }
378
379 if ((em->selectmode & SCE_SELECT_VERTEX) == 0) {
380 EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX); /* need to flush vert->face first */
381 }
383
385 params.calc_looptris = true;
386 params.calc_normals = false;
387 params.is_destructive = true;
388 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
389 }
390
391 return OPERATOR_FINISHED;
392}
393
395{
396 /* identifiers */
397 ot->name = "Un-Subdivide";
398 ot->description = "Un-subdivide selected edges and faces";
399 ot->idname = "MESH_OT_unsubdivide";
400
401 /* API callbacks. */
403 ot->poll = ED_operator_editmesh;
404
405 /* flags */
407
408 /* props */
410 ot->srna, "iterations", 2, 1, 1000, "Iterations", "Number of times to un-subdivide", 1, 100);
411}
412
414
415/* -------------------------------------------------------------------- */
418
419/* NOTE: these values must match delete_mesh() event values. */
420enum {
426};
427
429 const int totelem_old[3],
430 const int totelem_new[3])
431{
433 RPT_INFO,
434 "Removed: %d vertices, %d edges, %d faces",
435 totelem_old[0] - totelem_new[0],
436 totelem_old[1] - totelem_new[1],
437 totelem_old[2] - totelem_new[2]);
438}
439
441{
442 const Scene *scene = CTX_data_scene(C);
443 ViewLayer *view_layer = CTX_data_view_layer(C);
444
446 scene, view_layer, CTX_wm_view3d(C));
447 bool changed_multi = false;
448
449 for (Object *obedit : objects) {
451 const int type = RNA_enum_get(op->ptr, "type");
452
453 switch (type) {
454 case MESH_DELETE_VERT: /* Erase Vertices */
455 if (em->bm->totvertsel == 0) {
456 continue;
457 }
459 if (!EDBM_op_callf(em, op, "delete geom=%hv context=%i", BM_ELEM_SELECT, DEL_VERTS)) {
460 continue;
461 }
462 break;
463 case MESH_DELETE_EDGE: /* Erase Edges */
464 if (em->bm->totedgesel == 0) {
465 continue;
466 }
468 if (!EDBM_op_callf(em, op, "delete geom=%he context=%i", BM_ELEM_SELECT, DEL_EDGES)) {
469 continue;
470 }
471 break;
472 case MESH_DELETE_FACE: /* Erase Faces */
473 if (em->bm->totfacesel == 0) {
474 continue;
475 }
477 if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_FACES)) {
478 continue;
479 }
480 break;
481 case MESH_DELETE_EDGE_FACE: /* Edges and Faces */
482 if ((em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
483 continue;
484 }
486 if (!EDBM_op_callf(em, op, "delete geom=%hef context=%i", BM_ELEM_SELECT, DEL_EDGESFACES))
487 {
488 continue;
489 }
490 break;
491 case MESH_DELETE_ONLY_FACE: /* Only faces. */
492 if (em->bm->totfacesel == 0) {
493 continue;
494 }
496 if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_ONLYFACES)) {
497 continue;
498 }
499 break;
500 default:
501 BLI_assert(0);
502 break;
503 }
504
505 changed_multi = true;
506
508
510
512 params.calc_looptris = true;
513 params.calc_normals = false;
514 params.is_destructive = true;
515 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
516
517 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
518 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
519 }
520
521 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
522}
523
525{
526 static const EnumPropertyItem prop_mesh_delete_types[] = {
527 {MESH_DELETE_VERT, "VERT", 0, "Vertices", ""},
528 {MESH_DELETE_EDGE, "EDGE", 0, "Edges", ""},
529 {MESH_DELETE_FACE, "FACE", 0, "Faces", ""},
530 {MESH_DELETE_EDGE_FACE, "EDGE_FACE", 0, "Only Edges & Faces", ""},
531 {MESH_DELETE_ONLY_FACE, "ONLY_FACE", 0, "Only Faces", ""},
532 {0, nullptr, 0, nullptr, nullptr},
533 };
534
535 /* identifiers */
536 ot->name = "Delete";
537 ot->description = "Delete selected vertices, edges or faces";
538 ot->idname = "MESH_OT_delete";
539
540 /* API callbacks. */
541 ot->invoke = WM_menu_invoke;
542 ot->exec = edbm_delete_exec;
543
544 ot->poll = ED_operator_editmesh;
545
546 /* flags */
548
549 /* props */
550 ot->prop = RNA_def_enum(ot->srna,
551 "type",
552 prop_mesh_delete_types,
554 "Type",
555 "Method used for deleting mesh data");
557}
558
560
561/* -------------------------------------------------------------------- */
564
566{
567 BMLoop *l_iter, *l_first;
568
569 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
570 do {
571 if (!BM_edge_is_boundary(l_iter->e)) {
572 return false;
573 }
574 } while ((l_iter = l_iter->next) != l_first);
575
576 return true;
577}
578
580{
581 const Scene *scene = CTX_data_scene(C);
582 ViewLayer *view_layer = CTX_data_view_layer(C);
583 int totelem_old_sel[3];
584 int totelem_old[3];
585
587 scene, view_layer, CTX_wm_view3d(C));
588
589 EDBM_mesh_stats_multi(objects, totelem_old, totelem_old_sel);
590
591 const bool use_verts = (RNA_boolean_get(op->ptr, "use_verts") && totelem_old_sel[0]);
592 const bool use_edges = (RNA_boolean_get(op->ptr, "use_edges") && totelem_old_sel[1]);
593 const bool use_faces = (RNA_boolean_get(op->ptr, "use_faces") && totelem_old_sel[2]);
594
595 for (Object *obedit : objects) {
596
598 BMesh *bm = em->bm;
599 BMIter iter;
600
602
603 if (use_faces) {
604 BMFace *f;
605
606 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
609 }
610 }
611
613 }
614
615 if (use_edges) {
616 BMEdge *e;
617
618 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
621 }
622 }
623
625 }
626
627 if (use_verts) {
628 BMVert *v;
629
630 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
632 BM_elem_flag_set(v, BM_ELEM_TAG, (v->e == nullptr));
633 }
634 }
635
637 }
638
640
642 params.calc_looptris = true;
643 params.calc_normals = false;
644 params.is_destructive = true;
645 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
646 }
647
648 int totelem_new[3];
649 EDBM_mesh_stats_multi(objects, totelem_new, nullptr);
650
651 edbm_report_delete_info(op->reports, totelem_old, totelem_new);
652
653 return OPERATOR_FINISHED;
654}
655
657{
658 /* identifiers */
659 ot->name = "Delete Loose";
660 ot->description = "Delete loose vertices, edges or faces";
661 ot->idname = "MESH_OT_delete_loose";
662
663 /* API callbacks. */
665
666 ot->poll = ED_operator_editmesh;
667
668 /* flags */
670
671 /* props */
672 RNA_def_boolean(ot->srna, "use_verts", true, "Vertices", "Remove loose vertices");
673 RNA_def_boolean(ot->srna, "use_edges", true, "Edges", "Remove loose edges");
674 RNA_def_boolean(ot->srna, "use_faces", false, "Faces", "Remove loose faces");
675}
676
678
679/* -------------------------------------------------------------------- */
682
684{
685 const Scene *scene = CTX_data_scene(C);
686 ViewLayer *view_layer = CTX_data_view_layer(C);
688 scene, view_layer, CTX_wm_view3d(C));
689 for (Object *obedit : objects) {
691
692 if (em->bm->totedgesel == 0) {
693 continue;
694 }
695
696 if (!EDBM_op_callf(em, op, "collapse edges=%he uvs=%b", BM_ELEM_SELECT, true)) {
697 continue;
698 }
699
701 params.calc_looptris = true;
702 params.calc_normals = false;
703 params.is_destructive = true;
704 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
705 }
706
707 return OPERATOR_FINISHED;
708}
709
711{
712 /* identifiers */
713 ot->name = "Collapse Edges & Faces";
714 ot->description =
715 "Collapse isolated edge and face regions, merging data such as UVs and color attributes. "
716 "This can collapse edge-rings as well as regions of connected faces into vertices";
717 ot->idname = "MESH_OT_edge_collapse";
718
719 /* API callbacks. */
721 ot->poll = ED_operator_editmesh;
722
723 /* flags */
725}
726
728
729/* -------------------------------------------------------------------- */
732
734{
735 BMEdge *e;
736 BMIter iter;
737
738 uint vote_on_smooth[2] = {0, 0};
739
740 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
741 if (BM_elem_flag_test(e, BM_ELEM_SELECT) && e->l) {
742 vote_on_smooth[BM_elem_flag_test_bool(e->l->f, BM_ELEM_SMOOTH)]++;
743 }
744 }
745
746 return (vote_on_smooth[0] < vote_on_smooth[1]);
747}
748
749#ifdef USE_FACE_CREATE_SEL_EXTEND
755 BMVert *v, BMEdge *e_used, BMEdge **e_arr, const int e_arr_len, bool (*func)(const BMEdge *))
756{
757 BMIter iter;
758 BMEdge *e_iter;
759 int i = 0;
760 BM_ITER_ELEM (e_iter, &iter, v, BM_EDGES_OF_VERT) {
761 if (BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN) == false) {
762 if ((e_used == nullptr) || (e_used != e_iter)) {
763 if (func(e_iter)) {
764 e_arr[i++] = e_iter;
765 if (i >= e_arr_len) {
766 break;
767 }
768 }
769 }
770 }
771 }
772 return i;
773}
774
776{
777 BMIter iter;
778 bool found = false;
779
780 if (bm->totvertsel == 1 && bm->totedgesel == 0 && bm->totfacesel == 0) {
781 /* first look for 2 boundary edges */
782 BMVert *v;
783
784 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
786 found = true;
787 break;
788 }
789 }
790
791 if (found) {
792 BMEdge *ed_pair[3];
793 if (((edbm_add_edge_face_exec__vert_edge_lookup(v, nullptr, ed_pair, 3, BM_edge_is_wire) ==
794 2) &&
795 (BM_edge_share_face_check(ed_pair[0], ed_pair[1]) == false)) ||
796
798 v, nullptr, ed_pair, 3, BM_edge_is_boundary) == 2) &&
799 (BM_edge_share_face_check(ed_pair[0], ed_pair[1]) == false)))
800 {
801 BMEdge *e_other = BM_edge_exists(BM_edge_other_vert(ed_pair[0], v),
802 BM_edge_other_vert(ed_pair[1], v));
803 BM_edge_select_set(bm, ed_pair[0], true);
804 BM_edge_select_set(bm, ed_pair[1], true);
805 if (e_other) {
806 BM_edge_select_set(bm, e_other, true);
807 }
808 return (BMElem *)v;
809 }
810 }
811 }
812 else if (bm->totvertsel == 2 && bm->totedgesel == 1 && bm->totfacesel == 0) {
813 /* first look for 2 boundary edges */
814 BMEdge *e;
815
816 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
818 found = true;
819 break;
820 }
821 }
822 if (found) {
823 BMEdge *ed_pair_v1[2];
824 BMEdge *ed_pair_v2[2];
825 if (((edbm_add_edge_face_exec__vert_edge_lookup(e->v1, e, ed_pair_v1, 2, BM_edge_is_wire) ==
826 1) &&
828 1) &&
829 (BM_edge_share_face_check(e, ed_pair_v1[0]) == false) &&
830 (BM_edge_share_face_check(e, ed_pair_v2[0]) == false)) ||
831
832# if 1 /* better support mixed cases #37203. */
834 1) &&
836 e->v2, e, ed_pair_v2, 2, BM_edge_is_boundary) == 1) &&
837 (BM_edge_share_face_check(e, ed_pair_v1[0]) == false) &&
838 (BM_edge_share_face_check(e, ed_pair_v2[0]) == false)) ||
839
841 e->v1, e, ed_pair_v1, 2, BM_edge_is_boundary) == 1) &&
843 1) &&
844 (BM_edge_share_face_check(e, ed_pair_v1[0]) == false) &&
845 (BM_edge_share_face_check(e, ed_pair_v2[0]) == false)) ||
846# endif
847
849 e->v1, e, ed_pair_v1, 2, BM_edge_is_boundary) == 1) &&
851 e->v2, e, ed_pair_v2, 2, BM_edge_is_boundary) == 1) &&
852 (BM_edge_share_face_check(e, ed_pair_v1[0]) == false) &&
853 (BM_edge_share_face_check(e, ed_pair_v2[0]) == false)))
854 {
855 BMVert *v1_other = BM_edge_other_vert(ed_pair_v1[0], e->v1);
856 BMVert *v2_other = BM_edge_other_vert(ed_pair_v2[0], e->v2);
857 BMEdge *e_other = (v1_other != v2_other) ? BM_edge_exists(v1_other, v2_other) : nullptr;
858 BM_edge_select_set(bm, ed_pair_v1[0], true);
859 BM_edge_select_set(bm, ed_pair_v2[0], true);
860 if (e_other) {
861 BM_edge_select_set(bm, e_other, true);
862 }
863 return (BMElem *)e;
864 }
865 }
866 }
867
868 return nullptr;
869}
871{
872 /* Now we need to find the edge that isn't connected to this element. */
874
875 /* Notes on hidden geometry:
876 * - Un-hide the face since its possible hidden was copied when copying
877 * surrounding face attributes.
878 * - Un-hide before adding to select history
879 * since we may extend into an existing, hidden vert/edge.
880 */
881
883 BM_face_select_set(bm, f, false);
884
885 if (ele_desel->head.htype == BM_VERT) {
886 BMLoop *l = BM_face_vert_share_loop(f, (BMVert *)ele_desel);
887 BLI_assert(f->len == 3);
888 BM_vert_select_set(bm, (BMVert *)ele_desel, false);
889 BM_edge_select_set(bm, l->next->e, true);
890 BM_select_history_store(bm, l->next->e);
891 }
892 else {
893 BMLoop *l = BM_face_edge_share_loop(f, (BMEdge *)ele_desel);
894 BLI_assert(ELEM(f->len, 4, 3));
895
896 BM_edge_select_set(bm, (BMEdge *)ele_desel, false);
897 if (f->len == 4) {
898 BMEdge *e_active = l->next->next->e;
900 BM_edge_select_set(bm, e_active, true);
901 BM_select_history_store(bm, e_active);
902 }
903 else {
904 BMVert *v_active = l->next->next->v;
906 BM_vert_select_set(bm, v_active, true);
907 BM_select_history_store(bm, v_active);
908 }
909 }
910}
911#endif /* USE_FACE_CREATE_SEL_EXTEND */
912
914{
915 /* When this is used to dissolve we could avoid this, but checking isn't too slow. */
916 bool changed_multi = false;
917 const Scene *scene = CTX_data_scene(C);
918 ViewLayer *view_layer = CTX_data_view_layer(C);
920 scene, view_layer, CTX_wm_view3d(C));
921 for (Object *obedit : objects) {
923
924 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
925 continue;
926 }
927
928 bool use_smooth = edbm_add_edge_face__smooth_get(em->bm);
929 int totedge_orig = em->bm->totedge;
930 int totface_orig = em->bm->totface;
931
932 BMOperator bmop;
933#ifdef USE_FACE_CREATE_SEL_EXTEND
934 BMElem *ele_desel;
935 BMFace *ele_desel_face;
936
937 /* be extra clever, figure out if a partial selection should be extended so we can create
938 * geometry with single vert or single edge selection. */
940#endif
941 if (!EDBM_op_init(em,
942 &bmop,
943 op,
944 "contextual_create geom=%hfev mat_nr=%i use_smooth=%b",
946 em->mat_nr,
947 use_smooth))
948 {
949 continue;
950 }
951
952 BMO_op_exec(em->bm, &bmop);
953
954 /* cancel if nothing was done */
955 if ((totedge_orig == em->bm->totedge) && (totface_orig == em->bm->totface)) {
956 EDBM_op_finish(em, &bmop, op, true);
957 continue;
958 }
959#ifdef USE_FACE_CREATE_SEL_EXTEND
960 /* normally we would want to leave the new geometry selected,
961 * but being able to press F many times to add geometry is too useful! */
962 if (ele_desel && (BMO_slot_buffer_len(bmop.slots_out, "faces.out") == 1) &&
963 (ele_desel_face = static_cast<BMFace *>(
964 BMO_slot_buffer_get_first(bmop.slots_out, "faces.out"))))
965 {
966 edbm_add_edge_face_exec__tricky_finalize_sel(em->bm, ele_desel, ele_desel_face);
967 }
968 else
969#endif
970 {
971 /* Newly created faces may include existing hidden edges,
972 * copying face data from surrounding, may have copied hidden face flag too.
973 *
974 * Important that faces use flushing since 'edges.out'
975 * won't include hidden edges that already existed.
976 */
978 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_HIDDEN, true);
980 em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_HIDDEN, false);
981
983 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
985 em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true);
986 }
987
988 if (!EDBM_op_finish(em, &bmop, op, true)) {
989 continue;
990 }
991
993 params.calc_looptris = true;
994 params.calc_normals = false;
995 params.is_destructive = true;
996 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
997 changed_multi = true;
998 }
999
1000 if (!changed_multi) {
1001 return OPERATOR_CANCELLED;
1002 }
1003
1004 return OPERATOR_FINISHED;
1005}
1006
1008{
1009 /* identifiers */
1010 ot->name = "Make Edge/Face";
1011 ot->description = "Add an edge or face to selected";
1012 ot->idname = "MESH_OT_edge_face_add";
1013
1014 /* API callbacks. */
1016 ot->poll = ED_operator_editmesh;
1017
1018 /* flags */
1019 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1020}
1021
1023
1024/* -------------------------------------------------------------------- */
1027
1029{
1030 Scene *scene = CTX_data_scene(C);
1031 ViewLayer *view_layer = CTX_data_view_layer(C);
1032 BMEdge *eed;
1033 BMIter iter;
1034 const bool clear = RNA_boolean_get(op->ptr, "clear");
1035
1037 scene, view_layer, CTX_wm_view3d(C));
1038 for (Object *obedit : objects) {
1040 BMesh *bm = em->bm;
1041
1042 if (bm->totedgesel == 0) {
1043 continue;
1044 }
1045
1046 if (clear) {
1047 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
1049 continue;
1050 }
1051
1053 }
1054 }
1055 else {
1056 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
1058 continue;
1059 }
1061 }
1062 }
1063 }
1064
1065 ED_uvedit_live_unwrap(scene, objects);
1066
1067 for (Object *obedit : objects) {
1069 params.calc_looptris = true;
1070 params.calc_normals = false;
1071 params.is_destructive = false;
1072 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
1073 }
1074
1075 return OPERATOR_FINISHED;
1076}
1077
1079{
1080 PropertyRNA *prop;
1081
1082 /* identifiers */
1083 ot->name = "Mark Seam";
1084 ot->idname = "MESH_OT_mark_seam";
1085 ot->description = "(Un)mark selected edges as a seam";
1086
1087 /* API callbacks. */
1088 ot->exec = edbm_mark_seam_exec;
1089 ot->poll = ED_operator_editmesh;
1090
1091 /* flags */
1092 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1093
1094 prop = RNA_def_boolean(ot->srna, "clear", false, "Clear", "");
1096
1098}
1099
1101
1102/* -------------------------------------------------------------------- */
1105
1107{
1108 BMEdge *eed;
1109 BMIter iter;
1110 const bool clear = RNA_boolean_get(op->ptr, "clear");
1111 const bool use_verts = RNA_boolean_get(op->ptr, "use_verts");
1112 const Scene *scene = CTX_data_scene(C);
1113 ViewLayer *view_layer = CTX_data_view_layer(C);
1114
1116 scene, view_layer, CTX_wm_view3d(C));
1117 for (Object *obedit : objects) {
1119 BMesh *bm = em->bm;
1120
1121 if ((use_verts && bm->totvertsel == 0) || (!use_verts && bm->totedgesel == 0)) {
1122 continue;
1123 }
1124
1125 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
1126 if (use_verts) {
1127 if (!(BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) ||
1129 {
1130 continue;
1131 }
1132 }
1133 else if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
1134 continue;
1135 }
1136
1138 }
1139
1141 params.calc_looptris = true;
1142 params.calc_normals = false;
1143 params.is_destructive = false;
1144 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
1145 }
1146
1147 return OPERATOR_FINISHED;
1148}
1149
1151{
1152 PropertyRNA *prop;
1153
1154 /* identifiers */
1155 ot->name = "Mark Sharp";
1156 ot->idname = "MESH_OT_mark_sharp";
1157 ot->description = "(Un)mark selected edges as sharp";
1158
1159 /* API callbacks. */
1160 ot->exec = edbm_mark_sharp_exec;
1161 ot->poll = ED_operator_editmesh;
1162
1163 /* flags */
1164 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1165
1166 prop = RNA_def_boolean(ot->srna, "clear", false, "Clear", "");
1168 prop = RNA_def_boolean(
1169 ot->srna,
1170 "use_verts",
1171 false,
1172 "Vertices",
1173 "Consider vertices instead of edges to select which edges to (un)tag as sharp");
1175}
1176
1178
1179/* -------------------------------------------------------------------- */
1182
1184{
1185 BMesh *bm = em->bm;
1186 BMOperator bmop;
1187 const int verts_len = bm->totvertsel;
1188 bool is_pair = (verts_len == 2);
1189 int len = 0;
1190 bool check_degenerate = true;
1191
1192 bool checks_succeded = true;
1193
1194 /* sanity check */
1195 if (verts_len < 2) {
1196 return false;
1197 }
1198
1199 BMVert **verts = MEM_malloc_arrayN<BMVert *>(verts_len, __func__);
1200 {
1201 BMIter iter;
1202 BMVert *v;
1203 int i = 0;
1204
1205 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
1207 verts[i++] = v;
1208 }
1209 }
1210
1213 {
1214 check_degenerate = false;
1215 is_pair = false;
1216 }
1217 }
1218
1219 if (is_pair) {
1220 if (!EDBM_op_init(em,
1221 &bmop,
1222 op,
1223 "connect_vert_pair verts=%eb verts_exclude=%hv faces_exclude=%hf",
1224 verts,
1225 verts_len,
1228 {
1229 checks_succeded = false;
1230 }
1231 }
1232 else {
1233 if (!EDBM_op_init(em,
1234 &bmop,
1235 op,
1236 "connect_verts verts=%eb faces_exclude=%hf check_degenerate=%b",
1237 verts,
1238 verts_len,
1240 check_degenerate))
1241 {
1242 checks_succeded = false;
1243 }
1244 }
1245 if (checks_succeded) {
1246 BMBackup em_backup = EDBM_redo_state_store(em);
1247
1249
1250 BMO_op_exec(bm, &bmop);
1251 const bool failure = BMO_error_occurred_at_level(bm, BMO_ERROR_FATAL);
1252 len = BMO_slot_get(bmop.slots_out, "edges.out")->len;
1253
1254 if (len && is_pair) {
1255 /* new verts have been added, we have to select the edges, not just flush */
1257 em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true);
1258 }
1259
1260 bool em_backup_free = true;
1261 if (!EDBM_op_finish(em, &bmop, op, false)) {
1262 len = 0;
1263 }
1264 else if (failure) {
1265 len = 0;
1266 EDBM_redo_state_restore_and_free(&em_backup, em, true);
1267 em_backup_free = false;
1268 }
1269 else {
1270 /* so newly created edges get the selection state from the vertex */
1272
1274
1276 params.calc_looptris = true;
1277 params.calc_normals = false;
1278 params.is_destructive = true;
1279 EDBM_update(mesh, &params);
1280 }
1281
1282 if (em_backup_free) {
1283 EDBM_redo_state_free(&em_backup);
1284 }
1285 }
1287
1288 return len;
1289}
1290
1292{
1293 const Scene *scene = CTX_data_scene(C);
1294 ViewLayer *view_layer = CTX_data_view_layer(C);
1295 uint failed_objects_len = 0;
1297 scene, view_layer, CTX_wm_view3d(C));
1298
1299 for (Object *obedit : objects) {
1301
1302 if (!edbm_connect_vert_pair(em, static_cast<Mesh *>(obedit->data), op)) {
1303 failed_objects_len++;
1304 }
1305 }
1306 return failed_objects_len == objects.size() ? OPERATOR_CANCELLED : OPERATOR_FINISHED;
1307}
1308
1310{
1311 /* identifiers */
1312 ot->name = "Vertex Connect";
1313 ot->idname = "MESH_OT_vert_connect";
1314 ot->description = "Connect selected vertices of faces, splitting the face";
1315
1316 /* API callbacks. */
1317 ot->exec = edbm_vert_connect_exec;
1318 ot->poll = ED_operator_editmesh;
1319
1320 /* flags */
1321 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1322}
1323
1325
1326/* -------------------------------------------------------------------- */
1329
1334{
1335 BMEditSelection *ele_a = static_cast<BMEditSelection *>(bm->selected.first);
1336 BMEditSelection *ele_b = static_cast<BMEditSelection *>(bm->selected.last);
1337 if ((ele_a->htype == BM_VERT) && (ele_b->htype == BM_VERT)) {
1339 1) &&
1341 1))
1342 {
1343 return true;
1344 }
1345 }
1346
1347 return false;
1348}
1349
1350static bool bm_vert_connect_pair(BMesh *bm, BMVert *v_a, BMVert *v_b)
1351{
1352 BMOperator bmop;
1353 BMVert **verts;
1354 const int totedge_orig = bm->totedge;
1355
1356 BMO_op_init(bm, &bmop, BMO_FLAG_DEFAULTS, "connect_vert_pair");
1357
1358 verts = static_cast<BMVert **>(BMO_slot_buffer_alloc(&bmop, bmop.slots_in, "verts", 2));
1359 verts[0] = v_a;
1360 verts[1] = v_b;
1361
1364
1365 BMO_op_exec(bm, &bmop);
1367 BMO_op_finish(bm, &bmop);
1368 return (bm->totedge != totedge_orig);
1369}
1370
1372{
1373 /* Logic is as follows:
1374 *
1375 * - If there are any isolated/wire verts - connect as edges.
1376 * - Otherwise connect faces.
1377 * - If all edges have been created already, closed the loop.
1378 */
1379 if (BLI_listbase_count_at_most(&bm->selected, 2) == 2 && (bm->totvertsel > 2)) {
1380 BMEditSelection *ese;
1381 int tot = 0;
1382 bool changed = false;
1383 bool has_wire = false;
1384 // bool all_verts;
1385
1386 /* ensure all verts have history */
1387 for (ese = static_cast<BMEditSelection *>(bm->selected.first); ese; ese = ese->next, tot++) {
1388 BMVert *v;
1389 if (ese->htype != BM_VERT) {
1390 break;
1391 }
1392 v = (BMVert *)ese->ele;
1393 if ((has_wire == false) && ((v->e == nullptr) || BM_vert_is_wire(v))) {
1394 has_wire = true;
1395 }
1396 }
1397 // all_verts = (ese == nullptr);
1398
1399 if (has_wire == false) {
1400 /* all verts have faces , connect verts via faces! */
1401 if (tot == bm->totvertsel) {
1402 BMEditSelection *ese_last;
1403 ese_last = static_cast<BMEditSelection *>(bm->selected.first);
1404 ese = ese_last->next;
1405
1406 do {
1407
1408 if (BM_edge_exists((BMVert *)ese_last->ele, (BMVert *)ese->ele)) {
1409 /* pass, edge exists (and will be selected) */
1410 }
1411 else {
1412 changed |= bm_vert_connect_pair(bm, (BMVert *)ese_last->ele, (BMVert *)ese->ele);
1413 }
1414 } while ((void)(ese_last = ese), (ese = ese->next));
1415
1416 if (changed) {
1417 return true;
1418 }
1419 }
1420
1421 if (changed == false) {
1422 /* existing loops: close the selection */
1424 changed |= bm_vert_connect_pair(bm,
1425 (BMVert *)((BMEditSelection *)bm->selected.first)->ele,
1426 (BMVert *)((BMEditSelection *)bm->selected.last)->ele);
1427
1428 if (changed) {
1429 return true;
1430 }
1431 }
1432 }
1433 }
1434
1435 else {
1436 /* no faces, simply connect the verts by edges */
1437 BMEditSelection *ese_prev;
1438 ese_prev = static_cast<BMEditSelection *>(bm->selected.first);
1439 ese = ese_prev->next;
1440
1441 do {
1442 if (BM_edge_exists((BMVert *)ese_prev->ele, (BMVert *)ese->ele)) {
1443 /* pass, edge exists (and will be selected) */
1444 }
1445 else {
1446 BMEdge *e;
1447 e = BM_edge_create(
1448 bm, (BMVert *)ese_prev->ele, (BMVert *)ese->ele, nullptr, eBMCreateFlag(0));
1449 BM_edge_select_set(bm, e, true);
1450 changed = true;
1451 }
1452 } while ((void)(ese_prev = ese), (ese = ese->next));
1453
1454 if (changed == false) {
1455 /* existing loops: close the selection */
1457 BMEdge *e;
1458 ese_prev = static_cast<BMEditSelection *>(bm->selected.first);
1459 ese = static_cast<BMEditSelection *>(bm->selected.last);
1460 e = BM_edge_create(
1461 bm, (BMVert *)ese_prev->ele, (BMVert *)ese->ele, nullptr, eBMCreateFlag(0));
1462 BM_edge_select_set(bm, e, true);
1463 }
1464 }
1465
1466 return true;
1467 }
1468 }
1469
1470 return false;
1471}
1472
1478{
1479 ListBase selected_orig = {nullptr, nullptr};
1480 int edges_len = 0;
1481 bool side = false;
1482
1483 /* first check all edges are OK */
1484 LISTBASE_FOREACH (BMEditSelection *, ese, &bm->selected) {
1485 if (ese->htype == BM_EDGE) {
1486 edges_len += 1;
1487 }
1488 else {
1489 return false;
1490 }
1491 }
1492 /* if this is a mixed selection, bail out! */
1493 if (bm->totedgesel != edges_len) {
1494 return false;
1495 }
1496
1497 std::swap(bm->selected, selected_orig);
1498
1499 /* convert edge selection into 2 ordered loops (where the first edge ends up in the middle) */
1500 LISTBASE_FOREACH (BMEditSelection *, ese, &selected_orig) {
1501 BMEdge *e_curr = (BMEdge *)ese->ele;
1502 BMEdge *e_prev = ese->prev ? (BMEdge *)ese->prev->ele : nullptr;
1503 BMLoop *l_curr;
1504 BMLoop *l_prev;
1505 BMVert *v;
1506
1507 if (e_prev) {
1508 BMFace *f = BM_edge_pair_share_face_by_len(e_curr, e_prev, &l_curr, &l_prev, true);
1509 if (f) {
1510 if ((e_curr->v1 != l_curr->v) == (e_prev->v1 != l_prev->v)) {
1511 side = !side;
1512 }
1513 }
1514 else if (is_quad_flip_v3(e_curr->v1->co, e_curr->v2->co, e_prev->v2->co, e_prev->v1->co)) {
1515 side = !side;
1516 }
1517 }
1518
1519 v = (&e_curr->v1)[side];
1520 if (!bm->selected.last || (BMVert *)((BMEditSelection *)bm->selected.last)->ele != v) {
1522 }
1523
1524 v = (&e_curr->v1)[!side];
1525 if (!bm->selected.first || (BMVert *)((BMEditSelection *)bm->selected.first)->ele != v) {
1527 }
1528
1529 e_prev = e_curr;
1530 }
1531
1532 *r_selected = bm->selected;
1533 bm->selected = selected_orig;
1534
1535 return true;
1536}
1537
1539{
1540 const Scene *scene = CTX_data_scene(C);
1541 ViewLayer *view_layer = CTX_data_view_layer(C);
1542 uint failed_selection_order_len = 0;
1543 uint failed_connect_len = 0;
1545 scene, view_layer, CTX_wm_view3d(C));
1546
1547 for (Object *obedit : objects) {
1549 BMesh *bm = em->bm;
1550 const bool is_pair = (em->bm->totvertsel == 2);
1551 ListBase selected_orig = {nullptr, nullptr};
1552
1553 if (bm->totvertsel == 0) {
1554 continue;
1555 }
1556
1557 /* when there is only 2 vertices, we can ignore selection order */
1558 if (is_pair) {
1559 if (!edbm_connect_vert_pair(em, static_cast<Mesh *>(obedit->data), op)) {
1560 failed_connect_len++;
1561 }
1562 continue;
1563 }
1564
1565 if (bm->selected.first) {
1566 BMEditSelection *ese = static_cast<BMEditSelection *>(bm->selected.first);
1567 if (ese->htype == BM_EDGE) {
1569 std::swap(bm->selected, selected_orig);
1570 }
1571 }
1572 }
1573
1575
1578
1580
1582 params.calc_looptris = true;
1583 params.calc_normals = false;
1584 params.is_destructive = true;
1585 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
1586 }
1587 else {
1588 failed_selection_order_len++;
1589 }
1590
1591 if (!BLI_listbase_is_empty(&selected_orig)) {
1593 bm->selected = selected_orig;
1594 }
1595 }
1596
1597 if (failed_selection_order_len == objects.size()) {
1598 BKE_report(op->reports, RPT_ERROR, "Invalid selection order");
1599 return OPERATOR_CANCELLED;
1600 }
1601 if (failed_connect_len == objects.size()) {
1602 BKE_report(op->reports, RPT_ERROR, "Could not connect vertices");
1603 return OPERATOR_CANCELLED;
1604 }
1605
1606 return OPERATOR_FINISHED;
1607}
1608
1610{
1611 /* identifiers */
1612 ot->name = "Vertex Connect Path";
1613 ot->idname = "MESH_OT_vert_connect_path";
1614 ot->description = "Connect vertices by their selection order, creating edges, splitting faces";
1615
1616 /* API callbacks. */
1618 ot->poll = ED_operator_editmesh;
1619
1620 /* flags */
1621 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1622}
1623
1625
1626/* -------------------------------------------------------------------- */
1629
1631{
1632 const Scene *scene = CTX_data_scene(C);
1633 ViewLayer *view_layer = CTX_data_view_layer(C);
1635 scene, view_layer, CTX_wm_view3d(C));
1636 for (Object *obedit : objects) {
1638
1639 if (em->bm->totfacesel == 0) {
1640 continue;
1641 }
1642
1644 em, op, "faces.out", true, "connect_verts_concave faces=%hf", BM_ELEM_SELECT))
1645 {
1646 continue;
1647 }
1649 params.calc_looptris = true;
1650 params.calc_normals = false;
1651 params.is_destructive = true;
1652 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
1653 }
1654
1655 return OPERATOR_FINISHED;
1656}
1657
1659{
1660 /* identifiers */
1661 ot->name = "Split Concave Faces";
1662 ot->idname = "MESH_OT_vert_connect_concave";
1663 ot->description = "Make all faces convex";
1664
1665 /* API callbacks. */
1667 ot->poll = ED_operator_editmesh;
1668
1669 /* flags */
1670 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1671}
1672
1674
1675/* -------------------------------------------------------------------- */
1678
1680{
1681 const Scene *scene = CTX_data_scene(C);
1682 ViewLayer *view_layer = CTX_data_view_layer(C);
1683 const float angle_limit = RNA_float_get(op->ptr, "angle_limit");
1685 scene, view_layer, CTX_wm_view3d(C));
1686
1687 for (Object *obedit : objects) {
1689
1690 if (em->bm->totfacesel == 0) {
1691 continue;
1692 }
1693
1695 op,
1696 "faces.out",
1697 true,
1698 "connect_verts_nonplanar faces=%hf angle_limit=%f",
1700 angle_limit))
1701 {
1702 continue;
1703 }
1704
1706 params.calc_looptris = true;
1707 params.calc_normals = false;
1708 params.is_destructive = true;
1709 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
1710 }
1711
1712 return OPERATOR_FINISHED;
1713}
1714
1716{
1717 PropertyRNA *prop;
1718
1719 /* identifiers */
1720 ot->name = "Split Non-Planar Faces";
1721 ot->idname = "MESH_OT_vert_connect_nonplanar";
1722 ot->description = "Split non-planar faces that exceed the angle threshold";
1723
1724 /* API callbacks. */
1726 ot->poll = ED_operator_editmesh;
1727
1728 /* flags */
1729 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1730
1731 /* props */
1732 prop = RNA_def_float_rotation(ot->srna,
1733 "angle_limit",
1734 0,
1735 nullptr,
1736 0.0f,
1737 DEG2RADF(180.0f),
1738 "Max Angle",
1739 "Angle limit",
1740 0.0f,
1741 DEG2RADF(180.0f));
1743}
1744
1746
1747/* -------------------------------------------------------------------- */
1750
1752{
1753 const Scene *scene = CTX_data_scene(C);
1754 ViewLayer *view_layer = CTX_data_view_layer(C);
1756 scene, view_layer, CTX_wm_view3d(C));
1757
1758 const int repeat = RNA_int_get(op->ptr, "repeat");
1759 const float fac = RNA_float_get(op->ptr, "factor");
1760
1761 int totobjects = 0;
1762
1763 for (Object *obedit : objects) {
1765
1766 if (em->bm->totfacesel == 0) {
1767 continue;
1768 }
1769
1771 continue;
1772 }
1773
1774 totobjects++;
1775
1776 if (!EDBM_op_callf(
1777 em, op, "planar_faces faces=%hf iterations=%i factor=%f", BM_ELEM_SELECT, repeat, fac))
1778 {
1779 continue;
1780 }
1781
1783 params.calc_looptris = true;
1784 params.calc_normals = true;
1785 params.is_destructive = true;
1786 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
1787 }
1788
1789 return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1790}
1791
1793{
1794 /* identifiers */
1795 ot->name = "Make Planar Faces";
1796 ot->idname = "MESH_OT_face_make_planar";
1797 ot->description = "Flatten selected faces";
1798
1799 /* API callbacks. */
1801 ot->poll = ED_operator_editmesh;
1802
1803 /* flags */
1804 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1805
1806 /* props */
1807 RNA_def_float(ot->srna, "factor", 1.0f, -10.0f, 10.0f, "Factor", "", 0.0f, 1.0f);
1808 RNA_def_int(ot->srna, "repeat", 1, 1, 10000, "Iterations", "", 1, 200);
1809}
1810
1812
1813/* -------------------------------------------------------------------- */
1816
1818{
1819 BMesh *bm = em->bm;
1820 if (bm->totedgesel == 0) {
1821 return false;
1822 }
1823
1825
1827 em, op, "edges.out", false, "split_edges edges=%he", BM_ELEM_SELECT))
1828 {
1829 return false;
1830 }
1831
1833
1836 params.calc_looptris = true;
1837 params.calc_normals = false;
1838 params.is_destructive = true;
1839 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
1840
1841 return true;
1842}
1843
1845{
1846 BMesh *bm = em->bm;
1847
1848 /* Note that tracking vertices through the 'split_edges' operator is complicated.
1849 * Instead, tag loops for selection. */
1850 if (bm->totvertsel == 0) {
1851 return false;
1852 }
1853
1855
1856 /* Flush from vertices to edges. */
1857 BMIter iter;
1858 BMEdge *eed;
1859 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
1861 if (eed->l != nullptr) {
1864 {
1866 }
1867 /* Store selection in loop tags. */
1868 BMLoop *l_iter = eed->l;
1869 do {
1871 } while ((l_iter = l_iter->radial_next) != eed->l);
1872 }
1873 }
1874
1875 if (!EDBM_op_callf(em,
1876 op,
1877 "split_edges edges=%he verts=%hv use_verts=%b",
1880 true))
1881 {
1882 return false;
1883 }
1884
1885 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
1886 if (eed->l != nullptr) {
1887 BMLoop *l_iter = eed->l;
1888 do {
1889 if (BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
1890 BM_vert_select_set(em->bm, l_iter->v, true);
1891 }
1892 } while ((l_iter = l_iter->radial_next) != eed->l);
1893 }
1894 else {
1895 /* Split out wire. */
1896 for (int i = 0; i < 2; i++) {
1897 BMVert *v = *(&eed->v1 + i);
1899 if (eed != BM_DISK_EDGE_NEXT(eed, v)) {
1900 BM_vert_separate(bm, v, &eed, 1, true, nullptr, nullptr);
1901 }
1902 }
1903 }
1904 }
1905 }
1906
1908
1911 params.calc_looptris = true;
1912 params.calc_normals = false;
1913 params.is_destructive = true;
1914 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
1915
1916 return true;
1917}
1918
1920{
1921 const int type = RNA_enum_get(op->ptr, "type");
1922
1923 const Scene *scene = CTX_data_scene(C);
1924 ViewLayer *view_layer = CTX_data_view_layer(C);
1926 scene, view_layer, CTX_wm_view3d(C));
1927 for (Object *obedit : objects) {
1929
1930 switch (type) {
1931 case BM_VERT:
1932 if (!edbm_edge_split_selected_verts(op, obedit, em)) {
1933 continue;
1934 }
1935 break;
1936 case BM_EDGE:
1937 if (!edbm_edge_split_selected_edges(op, obedit, em)) {
1938 continue;
1939 }
1940 break;
1941 default:
1942 BLI_assert(0);
1943 }
1944 }
1945
1946 return OPERATOR_FINISHED;
1947}
1948
1950{
1951 /* identifiers */
1952 ot->name = "Edge Split";
1953 ot->idname = "MESH_OT_edge_split";
1954 ot->description = "Split selected edges so that each neighbor face gets its own copy";
1955
1956 /* API callbacks. */
1957 ot->exec = edbm_edge_split_exec;
1958 ot->poll = ED_operator_editmesh;
1959
1960 /* flags */
1961 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1962
1963 /* properties */
1964 static const EnumPropertyItem merge_type_items[] = {
1965 {BM_EDGE, "EDGE", 0, "Faces by Edges", "Split faces along selected edges"},
1966 {BM_VERT,
1967 "VERT",
1968 0,
1969 "Faces & Edges by Vertices",
1970 "Split faces and edges connected to selected vertices"},
1971 {0, nullptr, 0, nullptr, nullptr},
1972 };
1973
1974 ot->prop = RNA_def_enum(
1975 ot->srna, "type", merge_type_items, BM_EDGE, "Type", "Method to use for splitting");
1976}
1977
1979
1980/* -------------------------------------------------------------------- */
1983
1985{
1986 const Scene *scene = CTX_data_scene(C);
1987 ViewLayer *view_layer = CTX_data_view_layer(C);
1989 scene, view_layer, CTX_wm_view3d(C));
1990 bool changed = false;
1991
1992 for (Object *obedit : objects) {
1994 if (em->bm->totvertsel == 0) {
1995 continue;
1996 }
1997
1998 BMOperator bmop;
1999 BMesh *bm = em->bm;
2000 changed = true;
2001
2002 EDBM_op_init(em,
2003 &bmop,
2004 op,
2005 "duplicate geom=%hvef use_select_history=%b use_edge_flip_from_face=%b",
2007 true,
2008 true);
2009
2010 BMO_op_exec(bm, &bmop);
2011
2012 /* de-select all would clear otherwise */
2014
2016
2018 bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
2019
2020 /* Rebuild edit-selection. */
2022
2023 if (!EDBM_op_finish(em, &bmop, op, true)) {
2024 continue;
2025 }
2027 params.calc_looptris = true;
2028 params.calc_normals = false;
2029 params.is_destructive = true;
2030 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2031 }
2032
2033 return (changed) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
2034}
2035
2037 wmOperator *op,
2038 const wmEvent * /*event*/)
2039{
2040 WM_cursor_wait(true);
2041 const wmOperatorStatus retval = edbm_duplicate_exec(C, op);
2042 WM_cursor_wait(false);
2043
2044 return retval;
2045}
2046
2048{
2049 /* identifiers */
2050 ot->name = "Duplicate";
2051 ot->description = "Duplicate selected vertices, edges or faces";
2052 ot->idname = "MESH_OT_duplicate";
2053
2054 /* API callbacks. */
2055 ot->invoke = edbm_duplicate_invoke;
2056 ot->exec = edbm_duplicate_exec;
2057
2058 ot->poll = ED_operator_editmesh;
2059
2060 /* to give to transform */
2061 RNA_def_int(ot->srna,
2062 "mode",
2064 0,
2065 INT_MAX,
2066 "Mode",
2067 "",
2068 0,
2069 INT_MAX);
2070}
2071
2073{
2074 BMLoopNorEditDataArray *lnors_ed_arr = nullptr;
2075 if (CustomData_has_layer_named(&bm->ldata, CD_PROP_INT16_2D, "custom_normal")) {
2076 /* The mesh has custom normal data, update these too.
2077 * Otherwise they will be left in a mangled state.
2078 */
2080 lnors_ed_arr = BM_loop_normal_editdata_array_init(bm, true);
2081 }
2082
2083 return lnors_ed_arr;
2084}
2085
2087{
2088 if (!lnors_ed_arr) {
2089 return false;
2090 }
2091
2092 if (lnors_ed_arr->totloop == 0) {
2093 /* No loops normals to flip, exit early! */
2094 return false;
2095 }
2096
2097 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
2099
2100 /* We need to recreate the custom normal array because the clnors_data will
2101 * be mangled because we swapped the loops around when we flipped the faces. */
2102 BMLoopNorEditDataArray *lnors_ed_arr_new_full = BM_loop_normal_editdata_array_init(bm, true);
2103
2104 {
2105 /* We need to recalculate all loop normals in the affected area. Even the ones that are not
2106 * going to be flipped because the clnors data is mangled. */
2107
2108 BMLoopNorEditData *lnor_ed_new_full = lnors_ed_arr_new_full->lnor_editdata;
2109 for (int i = 0; i < lnors_ed_arr_new_full->totloop; i++, lnor_ed_new_full++) {
2110
2111 BMLoopNorEditData *lnor_ed =
2112 lnors_ed_arr->lidx_to_lnor_editdata[lnor_ed_new_full->loop_index];
2113
2114 BLI_assert(lnor_ed != nullptr);
2115
2117 bm->lnor_spacearr->lspacearr[lnor_ed_new_full->loop_index],
2118 lnor_ed->nloc,
2119 lnor_ed_new_full->clnors_data);
2120 }
2121 }
2122
2123 BMFace *f;
2124 BMLoop *l, *l_start;
2125 BMIter iter_f;
2126 BM_ITER_MESH (f, &iter_f, bm, BM_FACES_OF_MESH) {
2127 /* Flip all the custom loop normals on the selected faces. */
2129 continue;
2130 }
2131
2132 /* Because the winding has changed, we need to go the reverse way around the face to get the
2133 * correct placement of the normals. However we need to derive the old loop index to get the
2134 * correct data. Note that the first loop index is the same though. So the loop starts and ends
2135 * in the same place as before the flip.
2136 */
2137
2138 l_start = l = BM_FACE_FIRST_LOOP(f);
2139 int old_index = BM_elem_index_get(l);
2140 do {
2141 int loop_index = BM_elem_index_get(l);
2142
2143 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lidx_to_lnor_editdata[old_index];
2144 BMLoopNorEditData *lnor_ed_new = lnors_ed_arr_new_full->lidx_to_lnor_editdata[loop_index];
2145 BLI_assert(lnor_ed != nullptr && lnor_ed_new != nullptr);
2146
2147 negate_v3(lnor_ed->nloc);
2148
2150 bm->lnor_spacearr->lspacearr[loop_index], lnor_ed->nloc, lnor_ed_new->clnors_data);
2151
2152 old_index++;
2153 l = l->prev;
2154 } while (l != l_start);
2155 }
2156 BM_loop_normal_editdata_array_free(lnors_ed_arr_new_full);
2157 return true;
2158}
2159
2161
2162/* -------------------------------------------------------------------- */
2165
2167{
2168 if (!CustomData_has_layer_named(&em->bm->ldata, CD_PROP_INT16_2D, "custom_normal")) {
2169 return;
2170 }
2171
2172 /* The mesh has custom normal data, flip them. */
2173 BMesh *bm = em->bm;
2174
2177 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
2178
2179 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
2180 negate_v3(lnor_ed->nloc);
2181
2183 bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->nloc, lnor_ed->clnors_data);
2184 }
2187 params.calc_looptris = true;
2188 params.calc_normals = false;
2189 params.is_destructive = false;
2190 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2191}
2192
2194{
2195 if (EDBM_op_callf(em, op, "flip_quad_tessellation faces=%hf", BM_ELEM_SELECT)) {
2197 params.calc_looptris = true;
2198 params.calc_normals = false;
2199 params.is_destructive = false;
2200 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2201 }
2202}
2203
2205{
2206
2207 bool has_flipped_faces = false;
2208
2209 /* See if we have any custom normals to flip. */
2211
2212 if (EDBM_op_callf(em, op, "reverse_faces faces=%hf flip_multires=%b", BM_ELEM_SELECT, true)) {
2213 has_flipped_faces = true;
2214 }
2215
2216 if (flip_custom_normals(em->bm, lnors_ed_arr) || has_flipped_faces) {
2218 params.calc_looptris = true;
2219 params.calc_normals = false;
2220 params.is_destructive = false;
2221 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2222 }
2223
2224 if (lnors_ed_arr != nullptr) {
2226 }
2227}
2228
2230{
2231 const Scene *scene = CTX_data_scene(C);
2232 ViewLayer *view_layer = CTX_data_view_layer(C);
2234 scene, view_layer, CTX_wm_view3d(C));
2235
2236 for (Object *obedit : objects) {
2238 if (em->bm->totfacesel == 0) {
2239 continue;
2240 }
2241 edbm_flip_quad_tessellation(op, obedit, em);
2242 }
2243
2244 return OPERATOR_FINISHED;
2245}
2246
2248{
2249 const bool only_clnors = RNA_boolean_get(op->ptr, "only_clnors");
2250
2251 const Scene *scene = CTX_data_scene(C);
2252 ViewLayer *view_layer = CTX_data_view_layer(C);
2254 scene, view_layer, CTX_wm_view3d(C));
2255
2256 for (Object *obedit : objects) {
2258
2259 if (only_clnors) {
2260 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
2261 continue;
2262 }
2264 }
2265 else {
2266 if (em->bm->totfacesel == 0) {
2267 continue;
2268 }
2269 edbm_flip_normals_face_winding(op, obedit, em);
2270 }
2271 }
2272
2273 return OPERATOR_FINISHED;
2274}
2275
2277{
2278 /* identifiers */
2279 ot->name = "Flip Normals";
2280 ot->description = "Flip the direction of selected faces' normals (and of their vertices)";
2281 ot->idname = "MESH_OT_flip_normals";
2282
2283 /* API callbacks. */
2284 ot->exec = edbm_flip_normals_exec;
2285 ot->poll = ED_operator_editmesh;
2286
2287 /* flags */
2288 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2289
2290 RNA_def_boolean(ot->srna,
2291 "only_clnors",
2292 false,
2293 "Custom Normals Only",
2294 "Only flip the custom loop normals of the selected elements");
2295}
2296
2298
2299/* -------------------------------------------------------------------- */
2302
2307{
2308 BMEdge *eed;
2309 BMIter iter;
2310 const bool use_ccw = RNA_boolean_get(op->ptr, "use_ccw");
2311
2312 int tot_failed_all = 0;
2313 bool no_selected_edges = true, invalid_selected_edges = true;
2314
2315 const Scene *scene = CTX_data_scene(C);
2316 ViewLayer *view_layer = CTX_data_view_layer(C);
2318 scene, view_layer, CTX_wm_view3d(C));
2319 for (Object *obedit : objects) {
2321 int tot = 0;
2322
2323 if (em->bm->totedgesel == 0) {
2324 continue;
2325 }
2326 no_selected_edges = false;
2327
2328 /* first see if we have two adjacent faces */
2329 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
2332 BMFace *fa, *fb;
2333 if (BM_edge_face_pair(eed, &fa, &fb)) {
2334 /* if both faces are selected we rotate between them,
2335 * otherwise - rotate between 2 unselected - but not mixed */
2338 tot++;
2339 }
2340 }
2341 }
2342 }
2343
2344 /* OK, we don't have two adjacent faces, but we do have two selected ones.
2345 * that's an error condition. */
2346 if (tot == 0) {
2347 continue;
2348 }
2349 invalid_selected_edges = false;
2350
2351 BMOperator bmop;
2352 EDBM_op_init(em, &bmop, op, "rotate_edges edges=%he use_ccw=%b", BM_ELEM_TAG, use_ccw);
2353
2354 /* avoids leaving old verts selected which can be a problem running multiple times,
2355 * since this means the edges become selected around the face
2356 * which then attempt to rotate */
2358
2359 BMO_op_exec(em->bm, &bmop);
2360 /* edges may rotate into hidden vertices, if this does _not_ run we get an illogical state */
2362 em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_HIDDEN, true);
2364 em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true);
2365
2366 const int tot_rotate = BMO_slot_buffer_len(bmop.slots_out, "edges.out");
2367 const int tot_failed = tot - tot_rotate;
2368
2369 tot_failed_all += tot_failed;
2370
2371 if (tot_failed != 0) {
2372 /* If some edges fail to rotate, we need to re-select them,
2373 * otherwise we can end up with invalid selection
2374 * (unselected edge between 2 selected faces). */
2376 }
2377
2379
2380 if (!EDBM_op_finish(em, &bmop, op, true)) {
2381 continue;
2382 }
2383
2385 params.calc_looptris = true;
2386 params.calc_normals = false;
2387 params.is_destructive = true;
2388 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2389 }
2390
2391 if (no_selected_edges) {
2392 BKE_report(
2393 op->reports, RPT_ERROR, "Select edges or face pairs for edge loops to rotate about");
2394 return OPERATOR_CANCELLED;
2395 }
2396
2397 /* Ok, we don't have two adjacent faces, but we do have two selected ones.
2398 * that's an error condition. */
2399 if (invalid_selected_edges) {
2400 BKE_report(op->reports, RPT_ERROR, "Could not find any selected edges that can be rotated");
2401 return OPERATOR_CANCELLED;
2402 }
2403
2404 if (tot_failed_all != 0) {
2405 BKE_reportf(op->reports, RPT_WARNING, "Unable to rotate %d edge(s)", tot_failed_all);
2406 }
2407
2408 return OPERATOR_FINISHED;
2409}
2410
2412{
2413 /* identifiers */
2414 ot->name = "Rotate Selected Edge";
2415 ot->description = "Rotate selected edge or adjoining faces";
2416 ot->idname = "MESH_OT_edge_rotate";
2417
2418 /* API callbacks. */
2420 ot->poll = ED_operator_editmesh;
2421
2422 /* flags */
2423 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2424
2425 /* props */
2426 RNA_def_boolean(ot->srna, "use_ccw", false, "Counter Clockwise", "");
2427}
2428
2430
2431/* -------------------------------------------------------------------- */
2434
2436{
2437 const bool unselected = RNA_boolean_get(op->ptr, "unselected");
2438 const Scene *scene = CTX_data_scene(C);
2439 ViewLayer *view_layer = CTX_data_view_layer(C);
2440 bool changed = false;
2441
2443 scene, view_layer, CTX_wm_view3d(C));
2444 for (Object *obedit : objects) {
2446 BMesh *bm = em->bm;
2447
2448 if (unselected) {
2449 if (em->selectmode & SCE_SELECT_VERTEX) {
2450 if (bm->totvertsel == bm->totvert) {
2451 continue;
2452 }
2453 }
2454 else if (em->selectmode & SCE_SELECT_EDGE) {
2455 if (bm->totedgesel == bm->totedge) {
2456 continue;
2457 }
2458 }
2459 else if (em->selectmode & SCE_SELECT_FACE) {
2460 if (bm->totfacesel == bm->totface) {
2461 continue;
2462 }
2463 }
2464 }
2465 else {
2466 if (bm->totvertsel == 0) {
2467 continue;
2468 }
2469 }
2470
2471 if (EDBM_mesh_hide(em, unselected)) {
2473 params.calc_looptris = true;
2474 params.calc_normals = false;
2475 params.is_destructive = false;
2476 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2477 changed = true;
2478 }
2479 }
2480
2481 if (!changed) {
2482 return OPERATOR_CANCELLED;
2483 }
2484
2485 return OPERATOR_FINISHED;
2486}
2487
2489{
2490 /* identifiers */
2491 ot->name = "Hide Selected";
2492 ot->idname = "MESH_OT_hide";
2493 ot->description = "Hide (un)selected vertices, edges or faces";
2494
2495 /* API callbacks. */
2496 ot->exec = edbm_hide_exec;
2497 ot->poll = ED_operator_editmesh;
2498
2499 /* flags */
2500 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2501
2502 /* props */
2504 ot->srna, "unselected", false, "Unselected", "Hide unselected rather than selected");
2505}
2506
2508
2509/* -------------------------------------------------------------------- */
2512
2514{
2515 const bool select = RNA_boolean_get(op->ptr, "select");
2516 const Scene *scene = CTX_data_scene(C);
2517 ViewLayer *view_layer = CTX_data_view_layer(C);
2518
2520 scene, view_layer, CTX_wm_view3d(C));
2521 for (Object *obedit : objects) {
2523
2524 if (EDBM_mesh_reveal(em, select)) {
2526 params.calc_looptris = true;
2527 params.calc_normals = false;
2528 params.is_destructive = false;
2529 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2530 }
2531 }
2532
2533 return OPERATOR_FINISHED;
2534}
2535
2537{
2538 /* identifiers */
2539 ot->name = "Reveal Hidden";
2540 ot->idname = "MESH_OT_reveal";
2541 ot->description = "Reveal all hidden vertices, edges and faces";
2542
2543 /* API callbacks. */
2544 ot->exec = edbm_reveal_exec;
2545 ot->poll = ED_operator_editmesh;
2546
2547 /* flags */
2548 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2549
2550 RNA_def_boolean(ot->srna, "select", true, "Select", "");
2551}
2552
2554
2555/* -------------------------------------------------------------------- */
2558
2560{
2561 const Scene *scene = CTX_data_scene(C);
2562 ViewLayer *view_layer = CTX_data_view_layer(C);
2563 const bool inside = RNA_boolean_get(op->ptr, "inside");
2564
2566 scene, view_layer, CTX_wm_view3d(C));
2567 for (Object *obedit : objects) {
2569
2570 if (em->bm->totfacesel == 0) {
2571 continue;
2572 }
2573
2574 BMLoopNorEditDataArray *lnors_ed_arr = nullptr;
2575
2576 if (inside) {
2577 /* Save custom normal data for later so we can flip them correctly. */
2578 lnors_ed_arr = flip_custom_normals_init_data(em->bm);
2579 }
2580
2581 if (!EDBM_op_callf(em, op, "recalc_face_normals faces=%hf", BM_ELEM_SELECT)) {
2582 continue;
2583 }
2584
2585 if (inside) {
2586 EDBM_op_callf(em, op, "reverse_faces faces=%hf flip_multires=%b", BM_ELEM_SELECT, true);
2587 flip_custom_normals(em->bm, lnors_ed_arr);
2588 if (lnors_ed_arr != nullptr) {
2590 }
2591 }
2592
2594 params.calc_looptris = true;
2595 params.calc_normals = false;
2596 params.is_destructive = false;
2597 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2598 }
2599
2600 return OPERATOR_FINISHED;
2601}
2602
2604{
2605 /* identifiers */
2606 ot->name = "Recalculate Normals";
2607 ot->description = "Make face and vertex normals point either outside or inside the mesh";
2608 ot->idname = "MESH_OT_normals_make_consistent";
2609
2610 /* API callbacks. */
2612 ot->poll = ED_operator_editmesh;
2613
2614 /* flags */
2615 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2616
2617 RNA_def_boolean(ot->srna, "inside", false, "Inside", "");
2618}
2619
2621
2622/* -------------------------------------------------------------------- */
2625
2627{
2628 const float fac = RNA_float_get(op->ptr, "factor");
2629
2630 const bool xaxis = RNA_boolean_get(op->ptr, "xaxis");
2631 const bool yaxis = RNA_boolean_get(op->ptr, "yaxis");
2632 const bool zaxis = RNA_boolean_get(op->ptr, "zaxis");
2633 int repeat = RNA_int_get(op->ptr, "repeat");
2634
2635 if (!repeat) {
2636 repeat = 1;
2637 }
2638
2639 const Scene *scene = CTX_data_scene(C);
2640 ViewLayer *view_layer = CTX_data_view_layer(C);
2641 int tot_selected = 0, tot_locked = 0;
2643 scene, view_layer, CTX_wm_view3d(C));
2644 for (Object *obedit : objects) {
2645 Mesh *mesh = static_cast<Mesh *>(obedit->data);
2647 bool mirrx = false, mirry = false, mirrz = false;
2648 float clip_dist = 0.0f;
2649 const bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
2650
2651 if (em->bm->totvertsel == 0) {
2652 continue;
2653 }
2654
2656 tot_locked++;
2657 continue;
2658 }
2659
2660 tot_selected++;
2661
2662 /* mirror before smooth */
2663 if (((Mesh *)obedit->data)->symmetry & ME_SYMMETRY_X) {
2664 EDBM_verts_mirror_cache_begin(em, 0, false, true, false, use_topology);
2665 }
2666
2667 /* if there is a mirror modifier with clipping, flag the verts that
2668 * are within tolerance of the plane(s) of reflection
2669 */
2670 LISTBASE_FOREACH (ModifierData *, md, &obedit->modifiers) {
2671 if (md->type == eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) {
2673
2674 if (mmd->flag & MOD_MIR_CLIPPING) {
2675 if (mmd->flag & MOD_MIR_AXIS_X) {
2676 mirrx = true;
2677 }
2678 if (mmd->flag & MOD_MIR_AXIS_Y) {
2679 mirry = true;
2680 }
2681 if (mmd->flag & MOD_MIR_AXIS_Z) {
2682 mirrz = true;
2683 }
2684
2685 clip_dist = mmd->tolerance;
2686 }
2687 }
2688 }
2689
2690 for (int i = 0; i < repeat; i++) {
2691 if (!EDBM_op_callf(
2692 em,
2693 op,
2694 "smooth_vert verts=%hv factor=%f mirror_clip_x=%b mirror_clip_y=%b mirror_clip_z=%b "
2695 "clip_dist=%f use_axis_x=%b use_axis_y=%b use_axis_z=%b",
2697 fac,
2698 mirrx,
2699 mirry,
2700 mirrz,
2701 clip_dist,
2702 xaxis,
2703 yaxis,
2704 zaxis))
2705 {
2706 continue;
2707 }
2708 }
2709
2710 /* NOTE: redundant calculation could be avoided if the EDBM API could skip calculation. */
2711 bool calc_normals = false;
2712
2713 /* apply mirror */
2714 if (((Mesh *)obedit->data)->symmetry & ME_SYMMETRY_X) {
2717 calc_normals = true;
2718 }
2719
2721 params.calc_looptris = true;
2722 params.calc_normals = calc_normals;
2723 params.is_destructive = false;
2724 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2725 }
2726
2727 if (tot_selected == 0 && !tot_locked) {
2728 BKE_report(op->reports, RPT_WARNING, "No selected vertex");
2729 }
2730
2731 return tot_selected ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
2732}
2733
2735{
2736 /* identifiers */
2737 ot->name = "Smooth Vertices";
2738 ot->description = "Flatten angles of selected vertices";
2739 ot->idname = "MESH_OT_vertices_smooth";
2740
2741 /* API callbacks. */
2743 ot->poll = ED_operator_editmesh;
2744
2745 /* flags */
2746 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2747
2748 ot->prop = RNA_def_float_factor(
2749 ot->srna, "factor", 0.0f, -10.0f, 10.0f, "Smoothing", "Smoothing factor", 0.0f, 1.0f);
2751 ot->srna, "repeat", 1, 1, 1000, "Repeat", "Number of times to smooth the mesh", 1, 100);
2752
2754
2755 RNA_def_boolean(ot->srna, "xaxis", true, "X-Axis", "Smooth along the X axis");
2756 RNA_def_boolean(ot->srna, "yaxis", true, "Y-Axis", "Smooth along the Y axis");
2757 RNA_def_boolean(ot->srna, "zaxis", true, "Z-Axis", "Smooth along the Z axis");
2758
2759 /* Set generic modal callbacks. */
2761}
2762
2764
2765/* -------------------------------------------------------------------- */
2768
2770{
2771 int tot_selected = 0, tot_locked = 0;
2772 const Scene *scene = CTX_data_scene(C);
2773 ViewLayer *view_layer = CTX_data_view_layer(C);
2774
2775 const float lambda_factor = RNA_float_get(op->ptr, "lambda_factor");
2776 const float lambda_border = RNA_float_get(op->ptr, "lambda_border");
2777 const bool usex = RNA_boolean_get(op->ptr, "use_x");
2778 const bool usey = RNA_boolean_get(op->ptr, "use_y");
2779 const bool usez = RNA_boolean_get(op->ptr, "use_z");
2780 const bool preserve_volume = RNA_boolean_get(op->ptr, "preserve_volume");
2781 int repeat = RNA_int_get(op->ptr, "repeat");
2782
2783 if (!repeat) {
2784 repeat = 1;
2785 }
2786
2788 scene, view_layer, CTX_wm_view3d(C));
2789 for (Object *obedit : objects) {
2791 Mesh *mesh = static_cast<Mesh *>(obedit->data);
2792 bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
2793
2794 if (em->bm->totvertsel == 0) {
2795 continue;
2796 }
2797
2799 tot_locked++;
2800 continue;
2801 }
2802
2803 tot_selected++;
2804
2805 /* Mirror before smooth. */
2806 if (((Mesh *)obedit->data)->symmetry & ME_SYMMETRY_X) {
2807 EDBM_verts_mirror_cache_begin(em, 0, false, true, false, use_topology);
2808 }
2809
2810 bool failed_repeat_loop = false;
2811 for (int i = 0; i < repeat; i++) {
2812 if (!EDBM_op_callf(em,
2813 op,
2814 "smooth_laplacian_vert verts=%hv lambda_factor=%f lambda_border=%f "
2815 "use_x=%b use_y=%b use_z=%b preserve_volume=%b",
2817 lambda_factor,
2818 lambda_border,
2819 usex,
2820 usey,
2821 usez,
2822 preserve_volume))
2823 {
2824 failed_repeat_loop = true;
2825 break;
2826 }
2827 }
2828 if (failed_repeat_loop) {
2829 continue;
2830 }
2831
2832 /* NOTE: redundant calculation could be avoided if the EDBM API could skip calculation. */
2833 bool calc_normals = false;
2834
2835 /* Apply mirror. */
2836 if (((Mesh *)obedit->data)->symmetry & ME_SYMMETRY_X) {
2839 calc_normals = true;
2840 }
2841
2843 params.calc_looptris = true;
2844 params.calc_normals = calc_normals;
2845 params.is_destructive = false;
2846 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2847 }
2848
2849 if (tot_selected == 0 && !tot_locked) {
2850 BKE_report(op->reports, RPT_WARNING, "No selected vertex");
2851 }
2852
2853 return tot_selected ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
2854}
2855
2857{
2858 /* identifiers */
2859 ot->name = "Laplacian Smooth Vertices";
2860 ot->description = "Laplacian smooth of selected vertices";
2861 ot->idname = "MESH_OT_vertices_smooth_laplacian";
2862
2863 /* API callbacks. */
2865 ot->poll = ED_operator_editmesh;
2866
2867 /* flags */
2868 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2869
2871 ot->srna, "repeat", 1, 1, 1000, "Number of iterations to smooth the mesh", "", 1, 200);
2873 ot->srna, "lambda_factor", 1.0f, 1e-7f, 1000.0f, "Lambda factor", "", 1e-7f, 1000.0f);
2874 RNA_def_float(ot->srna,
2875 "lambda_border",
2876 5e-5f,
2877 1e-7f,
2878 1000.0f,
2879 "Lambda factor in border",
2880 "",
2881 1e-7f,
2882 1000.0f);
2883
2885
2886 RNA_def_boolean(ot->srna, "use_x", true, "Smooth X Axis", "Smooth object along X axis");
2887 RNA_def_boolean(ot->srna, "use_y", true, "Smooth Y Axis", "Smooth object along Y axis");
2888 RNA_def_boolean(ot->srna, "use_z", true, "Smooth Z Axis", "Smooth object along Z axis");
2889 RNA_def_boolean(ot->srna,
2890 "preserve_volume",
2891 true,
2892 "Preserve Volume",
2893 "Apply volume preservation after smooth");
2894}
2895
2897
2898/* -------------------------------------------------------------------- */
2901
2902static void mesh_set_smooth_faces(BMEditMesh *em, short smooth)
2903{
2904 BMIter iter;
2905 BMFace *efa;
2906
2907 if (em == nullptr) {
2908 return;
2909 }
2910
2911 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2913 BM_elem_flag_set(efa, BM_ELEM_SMOOTH, smooth);
2914 }
2915 }
2916}
2917
2919{
2920 const Scene *scene = CTX_data_scene(C);
2921 ViewLayer *view_layer = CTX_data_view_layer(C);
2923 scene, view_layer, CTX_wm_view3d(C));
2924 for (Object *obedit : objects) {
2926
2927 if (em->bm->totfacesel == 0) {
2928 continue;
2929 }
2930
2931 mesh_set_smooth_faces(em, 1);
2933 params.calc_looptris = false;
2934 params.calc_normals = false;
2935 params.is_destructive = false;
2936 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2937 }
2938
2939 return OPERATOR_FINISHED;
2940}
2941
2943{
2944 /* identifiers */
2945 ot->name = "Shade Smooth";
2946 ot->description = "Display faces smooth (using vertex normals)";
2947 ot->idname = "MESH_OT_faces_shade_smooth";
2948
2949 /* API callbacks. */
2951 ot->poll = ED_operator_editmesh;
2952
2953 /* flags */
2954 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2955}
2956
2958
2959/* -------------------------------------------------------------------- */
2962
2964{
2965 const Scene *scene = CTX_data_scene(C);
2966 ViewLayer *view_layer = CTX_data_view_layer(C);
2968 scene, view_layer, CTX_wm_view3d(C));
2969 for (Object *obedit : objects) {
2971
2972 if (em->bm->totfacesel == 0) {
2973 continue;
2974 }
2975
2976 mesh_set_smooth_faces(em, 0);
2978 params.calc_looptris = false;
2979 params.calc_normals = false;
2980 params.is_destructive = false;
2981 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2982 }
2983
2984 return OPERATOR_FINISHED;
2985}
2986
2988{
2989 /* identifiers */
2990 ot->name = "Shade Flat";
2991 ot->description = "Display faces flat";
2992 ot->idname = "MESH_OT_faces_shade_flat";
2993
2994 /* API callbacks. */
2996 ot->poll = ED_operator_editmesh;
2997
2998 /* flags */
2999 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3000}
3001
3003
3004/* -------------------------------------------------------------------- */
3007
3009{
3010 /* get the direction from RNA */
3011 const bool use_ccw = RNA_boolean_get(op->ptr, "use_ccw");
3012
3013 const Scene *scene = CTX_data_scene(C);
3014 ViewLayer *view_layer = CTX_data_view_layer(C);
3016 scene, view_layer, CTX_wm_view3d(C));
3017 for (Object *obedit : objects) {
3019
3020 if (em->bm->totfacesel == 0) {
3021 continue;
3022 }
3023
3024 BMOperator bmop;
3025
3026 EDBM_op_init(em, &bmop, op, "rotate_uvs faces=%hf use_ccw=%b", BM_ELEM_SELECT, use_ccw);
3027
3028 BMO_op_exec(em->bm, &bmop);
3029
3030 if (!EDBM_op_finish(em, &bmop, op, true)) {
3031 continue;
3032 }
3033
3035 params.calc_looptris = false;
3036 params.calc_normals = false;
3037 params.is_destructive = false;
3038 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
3039 }
3040
3041 return OPERATOR_FINISHED;
3042}
3043
3045{
3046 const Scene *scene = CTX_data_scene(C);
3047 ViewLayer *view_layer = CTX_data_view_layer(C);
3049 scene, view_layer, CTX_wm_view3d(C));
3050 for (Object *obedit : objects) {
3052
3053 if (em->bm->totfacesel == 0) {
3054 continue;
3055 }
3056
3057 BMOperator bmop;
3058
3059 EDBM_op_init(em, &bmop, op, "reverse_uvs faces=%hf", BM_ELEM_SELECT);
3060
3061 BMO_op_exec(em->bm, &bmop);
3062
3063 if (!EDBM_op_finish(em, &bmop, op, true)) {
3064 continue;
3065 }
3067 params.calc_looptris = false;
3068 params.calc_normals = false;
3069 params.is_destructive = false;
3070 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
3071 }
3072
3073 return OPERATOR_FINISHED;
3074}
3075
3077{
3078 /* get the direction from RNA */
3079 const bool use_ccw = RNA_boolean_get(op->ptr, "use_ccw");
3080
3081 const Scene *scene = CTX_data_scene(C);
3082 ViewLayer *view_layer = CTX_data_view_layer(C);
3084 scene, view_layer, CTX_wm_view3d(C));
3085
3086 for (uint ob_index = 0; ob_index < objects.size(); ob_index++) {
3087 Object *ob = objects[ob_index];
3089 if (em->bm->totfacesel == 0) {
3090 continue;
3091 }
3092
3093 BMOperator bmop;
3094
3099 if (!layer) {
3100 continue;
3101 }
3102
3103 int color_index = BKE_attribute_to_index(
3105 EDBM_op_init(em,
3106 &bmop,
3107 op,
3108 "rotate_colors faces=%hf use_ccw=%b color_index=%i",
3110 use_ccw,
3111 color_index);
3112
3113 BMO_op_exec(em->bm, &bmop);
3114
3115 if (!EDBM_op_finish(em, &bmop, op, true)) {
3116 continue;
3117 }
3118
3119 /* dependencies graph and notification stuff */
3121 params.calc_looptris = false;
3122 params.calc_normals = false;
3123 params.is_destructive = false;
3124 EDBM_update(static_cast<Mesh *>(ob->data), &params);
3125 }
3126
3127 return OPERATOR_FINISHED;
3128}
3129
3131{
3132 const Scene *scene = CTX_data_scene(C);
3133 ViewLayer *view_layer = CTX_data_view_layer(C);
3135 scene, view_layer, CTX_wm_view3d(C));
3136
3137 for (Object *obedit : objects) {
3139
3140 if (em->bm->totfacesel == 0) {
3141 continue;
3142 }
3143
3144 Mesh *mesh = BKE_object_get_original_mesh(obedit);
3148 if (!layer) {
3149 continue;
3150 }
3151
3152 BMOperator bmop;
3153
3154 int color_index = BKE_attribute_to_index(
3157 em, &bmop, op, "reverse_colors faces=%hf color_index=%i", BM_ELEM_SELECT, color_index);
3158
3159 BMO_op_exec(em->bm, &bmop);
3160
3161 if (!EDBM_op_finish(em, &bmop, op, true)) {
3162 continue;
3163 }
3164
3166 params.calc_looptris = false;
3167 params.calc_normals = false;
3168 params.is_destructive = false;
3169 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
3170 }
3171
3172 return OPERATOR_FINISHED;
3173}
3174
3176{
3177 /* identifiers */
3178 ot->name = "Rotate UVs";
3179 ot->idname = "MESH_OT_uvs_rotate";
3180 ot->description = "Rotate UV coordinates inside faces";
3181
3182 /* API callbacks. */
3183 ot->exec = edbm_rotate_uvs_exec;
3184 ot->poll = ED_operator_editmesh;
3185
3186 /* flags */
3187 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3188
3189 /* props */
3190 RNA_def_boolean(ot->srna, "use_ccw", false, "Counter Clockwise", "");
3191}
3192
3194{
3195 /* identifiers */
3196 ot->name = "Reverse UVs";
3197 ot->idname = "MESH_OT_uvs_reverse";
3198 ot->description = "Flip direction of UV coordinates inside faces";
3199
3200 /* API callbacks. */
3201 ot->exec = edbm_reverse_uvs_exec;
3202 ot->poll = ED_operator_editmesh;
3203
3204 /* flags */
3205 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3206
3207 /* props */
3208 // RNA_def_enum(ot->srna, "axis", axis_items, DIRECTION_CW, "Axis", "Axis to mirror UVs around");
3209}
3210
3212{
3213 /* identifiers */
3214 ot->name = "Rotate Colors";
3215 ot->idname = "MESH_OT_colors_rotate";
3216 ot->description = "Rotate face corner color attribute inside faces";
3217
3218 /* API callbacks. */
3220 ot->poll = ED_operator_editmesh;
3221
3222 /* flags */
3223 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3224
3225 /* props */
3226 RNA_def_boolean(ot->srna, "use_ccw", false, "Counter Clockwise", "");
3227}
3228
3230{
3231 /* identifiers */
3232 ot->name = "Reverse Colors";
3233 ot->idname = "MESH_OT_colors_reverse";
3234 ot->description = "Flip direction of face corner color attribute inside faces";
3235
3236 /* API callbacks. */
3238 ot->poll = ED_operator_editmesh;
3239
3240 /* flags */
3241 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3242
3243 /* props */
3244#if 0
3245 RNA_def_enum(ot->srna, "axis", axis_items, DIRECTION_CW, "Axis", "Axis to mirror colors around");
3246#endif
3247}
3248
3250
3251/* -------------------------------------------------------------------- */
3254
3255enum {
3261};
3262
3264 const bool use_first,
3265 const bool use_uvmerge,
3266 wmOperator *wmop)
3267{
3268 BMVert *mergevert;
3269 BMEditSelection *ese;
3270
3271 /* operator could be called directly from shortcut or python,
3272 * so do extra check for data here
3273 */
3274
3275 /* While #merge_type_itemf does a sanity check, this operation runs on all edit-mode objects.
3276 * Some of them may not have the expected selection state. */
3277 if (use_first == false) {
3278 if (!em->bm->selected.last || ((BMEditSelection *)em->bm->selected.last)->htype != BM_VERT) {
3279 return false;
3280 }
3281
3282 ese = static_cast<BMEditSelection *>(em->bm->selected.last);
3283 mergevert = (BMVert *)ese->ele;
3284 }
3285 else {
3286 if (!em->bm->selected.first || ((BMEditSelection *)em->bm->selected.first)->htype != BM_VERT) {
3287 return false;
3288 }
3289
3290 ese = static_cast<BMEditSelection *>(em->bm->selected.first);
3291 mergevert = (BMVert *)ese->ele;
3292 }
3293
3294 if (!BM_elem_flag_test(mergevert, BM_ELEM_SELECT)) {
3295 return false;
3296 }
3297
3298 if (use_uvmerge) {
3299 if (!EDBM_op_callf(
3300 em, wmop, "pointmerge_facedata verts=%hv vert_snap=%e", BM_ELEM_SELECT, mergevert))
3301 {
3302 return false;
3303 }
3304 }
3305
3306 if (!EDBM_op_callf(em, wmop, "pointmerge verts=%hv merge_co=%v", BM_ELEM_SELECT, mergevert->co))
3307 {
3308 return false;
3309 }
3310
3311 return true;
3312}
3313
3314static bool merge_target(BMEditMesh *em,
3315 Scene *scene,
3316 Object *ob,
3317 const bool use_cursor,
3318 const bool use_uvmerge,
3319 wmOperator *wmop)
3320{
3321 BMIter iter;
3322 BMVert *v;
3323 float co[3], cent[3] = {0.0f, 0.0f, 0.0f};
3324 const float *vco = nullptr;
3325
3326 if (use_cursor) {
3327 vco = scene->cursor.location;
3328 copy_v3_v3(co, vco);
3329 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
3330 mul_m4_v3(ob->world_to_object().ptr(), co);
3331 }
3332 else {
3333 float fac;
3334 int i = 0;
3335 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
3337 continue;
3338 }
3339 add_v3_v3(cent, v->co);
3340 i++;
3341 }
3342
3343 if (!i) {
3344 return false;
3345 }
3346
3347 fac = 1.0f / float(i);
3348 mul_v3_fl(cent, fac);
3349 copy_v3_v3(co, cent);
3350 vco = co;
3351 }
3352
3353 if (!vco) {
3354 return false;
3355 }
3356
3357 if (use_uvmerge) {
3358 if (!EDBM_op_callf(em, wmop, "average_vert_facedata verts=%hv", BM_ELEM_SELECT)) {
3359 return false;
3360 }
3361 }
3362
3363 if (!EDBM_op_callf(em, wmop, "pointmerge verts=%hv merge_co=%v", BM_ELEM_SELECT, co)) {
3364 return false;
3365 }
3366
3367 return true;
3368}
3369
3371{
3372 Scene *scene = CTX_data_scene(C);
3373 ViewLayer *view_layer = CTX_data_view_layer(C);
3375 scene, view_layer, CTX_wm_view3d(C));
3376 const int type = RNA_enum_get(op->ptr, "type");
3377 const bool uvs = RNA_boolean_get(op->ptr, "uvs");
3378
3379 for (Object *obedit : objects) {
3381
3382 if (em->bm->totvertsel == 0) {
3383 continue;
3384 }
3385
3387
3388 bool ok = false;
3389 switch (type) {
3390 case MESH_MERGE_CENTER:
3391 ok = merge_target(em, scene, obedit, false, uvs, op);
3392 break;
3393 case MESH_MERGE_CURSOR:
3394 ok = merge_target(em, scene, obedit, true, uvs, op);
3395 break;
3396 case MESH_MERGE_LAST:
3397 ok = merge_firstlast(em, false, uvs, op);
3398 break;
3399 case MESH_MERGE_FIRST:
3400 ok = merge_firstlast(em, true, uvs, op);
3401 break;
3403 ok = EDBM_op_callf(em, op, "collapse edges=%he uvs=%b", BM_ELEM_SELECT, uvs);
3404 break;
3405 default:
3406 BLI_assert(0);
3407 break;
3408 }
3409
3410 if (!ok) {
3411 continue;
3412 }
3413
3415
3417 params.calc_looptris = true;
3418 params.calc_normals = false;
3419 params.is_destructive = true;
3420 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
3421
3422 /* once collapsed, we can't have edge/face selection */
3423 if ((em->selectmode & SCE_SELECT_VERTEX) == 0) {
3425 }
3426 /* Only active object supported, see comment below. */
3428 break;
3429 }
3430 }
3431
3432 return OPERATOR_FINISHED;
3433}
3434
3436 {MESH_MERGE_CENTER, "CENTER", 0, "At Center", ""},
3437 {MESH_MERGE_CURSOR, "CURSOR", 0, "At Cursor", ""},
3438 {MESH_MERGE_COLLAPSE, "COLLAPSE", 0, "Collapse", ""},
3439 {MESH_MERGE_FIRST, "FIRST", 0, "At First", ""},
3440 {MESH_MERGE_LAST, "LAST", 0, "At Last", ""},
3441 {0, nullptr, 0, nullptr, nullptr},
3442};
3443
3445 PointerRNA * /*ptr*/,
3446 PropertyRNA * /*prop*/,
3447 bool *r_free)
3448{
3449 if (!C) { /* needed for docs */
3450 return merge_type_items;
3451 }
3452
3453 Object *obedit = CTX_data_edit_object(C);
3454 if (obedit && obedit->type == OB_MESH) {
3455 EnumPropertyItem *item = nullptr;
3456 int totitem = 0;
3458
3459 /* Keep these first so that their automatic shortcuts don't change. */
3463
3464 /* Only active object supported:
3465 * In practice it doesn't make sense to run this operation on non-active meshes
3466 * since selecting will activate - we could have a separate code-path for these but it's a
3467 * hassle for now just apply to the active (first) object. */
3468 if (em->selectmode & SCE_SELECT_VERTEX) {
3469 if (em->bm->selected.first && em->bm->selected.last &&
3470 ((BMEditSelection *)em->bm->selected.first)->htype == BM_VERT &&
3471 ((BMEditSelection *)em->bm->selected.last)->htype == BM_VERT)
3472 {
3475 }
3476 else if (em->bm->selected.first &&
3477 ((BMEditSelection *)em->bm->selected.first)->htype == BM_VERT)
3478 {
3480 }
3481 else if (em->bm->selected.last &&
3482 ((BMEditSelection *)em->bm->selected.last)->htype == BM_VERT)
3483 {
3485 }
3486 }
3487
3488 RNA_enum_item_end(&item, &totitem);
3489
3490 *r_free = true;
3491
3492 return item;
3493 }
3494
3495 /* Get all items e.g. when creating keymap item. */
3496 return merge_type_items;
3497}
3498
3500{
3501 /* identifiers */
3502 ot->name = "Merge";
3503 ot->description = "Merge selected vertices";
3504 ot->idname = "MESH_OT_merge";
3505
3506 /* API callbacks. */
3507 ot->exec = edbm_merge_exec;
3508 ot->invoke = WM_menu_invoke;
3509 ot->poll = ED_operator_editmesh;
3510
3511 /* flags */
3512 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3513
3514 /* properties */
3515 ot->prop = RNA_def_enum(
3516 ot->srna, "type", merge_type_items, MESH_MERGE_CENTER, "Type", "Merge method to use");
3518
3520
3521 RNA_def_boolean(ot->srna, "uvs", false, "UVs", "Move UVs according to merge");
3522}
3523
3525
3526/* -------------------------------------------------------------------- */
3529
3531{
3532 const float threshold = RNA_float_get(op->ptr, "threshold");
3533 const bool use_unselected = RNA_boolean_get(op->ptr, "use_unselected");
3534 const bool use_sharp_edge_from_normals = RNA_boolean_get(op->ptr, "use_sharp_edge_from_normals");
3535
3536 int count_multi = 0;
3537
3538 const Scene *scene = CTX_data_scene(C);
3539 ViewLayer *view_layer = CTX_data_view_layer(C);
3541 scene, view_layer, CTX_wm_view3d(C));
3542
3543 for (Object *obedit : objects) {
3545
3546 /* Selection used as target with 'use_unselected'. */
3547 if (em->bm->totvertsel == 0) {
3548 continue;
3549 }
3550
3551 BMOperator bmop;
3552 const int totvert_orig = em->bm->totvert;
3553
3554 /* avoid losing selection state (select -> tags) */
3555 char htype_select;
3556 if (em->selectmode & SCE_SELECT_VERTEX) {
3557 htype_select = BM_VERT;
3558 }
3559 else if (em->selectmode & SCE_SELECT_EDGE) {
3560 htype_select = BM_EDGE;
3561 }
3562 else {
3563 htype_select = BM_FACE;
3564 }
3565
3567
3568 /* store selection as tags */
3569 BM_mesh_elem_hflag_enable_test(em->bm, htype_select, BM_ELEM_TAG, true, true, BM_ELEM_SELECT);
3570
3571 if (use_unselected) {
3572 EDBM_automerge(obedit, false, BM_ELEM_SELECT, threshold);
3573 }
3574 else {
3575 EDBM_op_init(em, &bmop, op, "find_doubles verts=%hv dist=%f", BM_ELEM_SELECT, threshold);
3576
3577 BMO_op_exec(em->bm, &bmop);
3578
3579 if (!EDBM_op_callf(em, op, "weld_verts targetmap=%S", &bmop, "targetmap.out")) {
3580 BMO_op_finish(em->bm, &bmop);
3581 continue;
3582 }
3583
3584 if (!EDBM_op_finish(em, &bmop, op, true)) {
3585 continue;
3586 }
3587 }
3588
3589 const int count = (totvert_orig - em->bm->totvert);
3590
3591 /* restore selection from tags */
3592 BM_mesh_elem_hflag_enable_test(em->bm, htype_select, BM_ELEM_SELECT, true, true, BM_ELEM_TAG);
3594
3595 BM_custom_loop_normals_from_vector_layer(em->bm, use_sharp_edge_from_normals);
3596
3597 if (count) {
3598 count_multi += count;
3600 params.calc_looptris = true;
3601 params.calc_normals = false;
3602 params.is_destructive = true;
3603 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
3604 }
3605 }
3606
3607 BKE_reportf(op->reports,
3608 RPT_INFO,
3609 count_multi == 1 ? RPT_("Removed %d vertex") : RPT_("Removed %d vertices"),
3610 count_multi);
3611
3612 return OPERATOR_FINISHED;
3613}
3614
3616{
3617 /* identifiers */
3618 ot->name = "Merge by Distance";
3619 ot->description = "Merge vertices based on their proximity";
3620 ot->idname = "MESH_OT_remove_doubles";
3621
3622 /* API callbacks. */
3624 ot->poll = ED_operator_editmesh;
3625
3626 /* flags */
3627 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3628
3630 "threshold",
3631 1e-4f,
3632 1e-6f,
3633 50.0f,
3634 "Merge Distance",
3635 "Maximum distance between elements to merge",
3636 1e-5f,
3637 10.0f);
3638 RNA_def_boolean(ot->srna,
3639 "use_unselected",
3640 false,
3641 "Unselected",
3642 "Merge selected to other unselected vertices");
3643
3644 RNA_def_boolean(ot->srna,
3645 "use_sharp_edge_from_normals",
3646 false,
3647 "Sharp Edges",
3648 "Calculate sharp edges using custom normal data (when available)");
3649}
3650
3652
3653/* -------------------------------------------------------------------- */
3656
3657/* BMESH_TODO this should be properly encapsulated in a bmop. but later. */
3658static bool shape_propagate(BMEditMesh *em, bool use_symmetry)
3659{
3660 BMIter iter;
3661 BMVert *eve = nullptr;
3662 float *co;
3663 int totshape = CustomData_number_of_layers(&em->bm->vdata, CD_SHAPEKEY);
3664
3666 return false;
3667 }
3668
3669 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
3671 BMVert *mirr = use_symmetry ? EDBM_verts_mirror_get(em, eve) : nullptr;
3672
3673 if (!mirr || !BM_elem_flag_test(mirr, BM_ELEM_SELECT) ||
3675 {
3676 continue;
3677 }
3678 }
3679
3680 for (int i = 0; i < totshape; i++) {
3681 co = static_cast<float *>(
3683 copy_v3_v3(co, eve->co);
3684 }
3685 }
3686 return true;
3687}
3688
3690{
3691 const Scene *scene = CTX_data_scene(C);
3692 ViewLayer *view_layer = CTX_data_view_layer(C);
3693 int tot_shapekeys = 0;
3694 int tot_selected_verts_objects = 0;
3695 int tot_locked = 0;
3696
3698 scene, view_layer, CTX_wm_view3d(C));
3699 for (Object *obedit : objects) {
3700 Mesh *mesh = static_cast<Mesh *>(obedit->data);
3701 BMEditMesh *em = mesh->runtime->edit_mesh.get();
3702
3703 if (em->bm->totvertsel == 0) {
3704 continue;
3705 }
3706
3707 /* Check for locked shape keys. */
3709 tot_locked++;
3710 continue;
3711 }
3712
3713 tot_selected_verts_objects++;
3714
3715 const bool use_symmetry = (mesh->symmetry & ME_SYMMETRY_X) != 0;
3716
3717 if (use_symmetry) {
3718 const bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
3719
3720 EDBM_verts_mirror_cache_begin(em, 0, false, false, false, use_topology);
3721 }
3722
3723 if (shape_propagate(em, use_symmetry)) {
3724 tot_shapekeys++;
3725 }
3726
3727 if (use_symmetry) {
3729 }
3730
3732 params.calc_looptris = false;
3733 params.calc_normals = false;
3734 params.is_destructive = false;
3735 EDBM_update(mesh, &params);
3736 }
3737
3738 if (tot_selected_verts_objects == 0) {
3739 if (!tot_locked) {
3740 BKE_report(op->reports, RPT_ERROR, "No selected vertex");
3741 }
3742 return OPERATOR_CANCELLED;
3743 }
3744 if (tot_shapekeys == 0) {
3745 BKE_report(op->reports, RPT_ERROR, "Mesh(es) do not have shape keys");
3746 return OPERATOR_CANCELLED;
3747 }
3748
3749 return OPERATOR_FINISHED;
3750}
3751
3753{
3754 /* identifiers */
3755 ot->name = "Shape Propagate";
3756 ot->description = "Apply selected vertex locations to all other shape keys";
3757 ot->idname = "MESH_OT_shape_propagate_to_all";
3758
3759 /* API callbacks. */
3761 ot->poll = ED_operator_editmesh;
3762
3763 /* flags */
3764 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3765}
3766
3768
3769/* -------------------------------------------------------------------- */
3772
3773/* BMESH_TODO this should be properly encapsulated in a bmop. but later. */
3775{
3776 Object *obedit_ref = CTX_data_edit_object(C);
3777 Mesh *me_ref = static_cast<Mesh *>(obedit_ref->data);
3778 Key *key_ref = me_ref->key;
3779 KeyBlock *kb_ref = nullptr;
3780 BMEditMesh *em_ref = me_ref->runtime->edit_mesh.get();
3781 BMVert *eve;
3782 BMIter iter;
3783 const Scene *scene = CTX_data_scene(C);
3784 ViewLayer *view_layer = CTX_data_view_layer(C);
3785 float co[3], *sco;
3786 int totshape_ref = 0;
3787
3788 const float blend = RNA_float_get(op->ptr, "blend");
3789 int shape_ref = RNA_enum_get(op->ptr, "shape");
3790 const bool use_add = RNA_boolean_get(op->ptr, "add");
3791
3792 /* Sanity check. */
3793 totshape_ref = CustomData_number_of_layers(&em_ref->bm->vdata, CD_SHAPEKEY);
3794
3795 if (totshape_ref == 0 || shape_ref < 0) {
3796 BKE_report(op->reports, RPT_ERROR, "Active mesh does not have shape keys");
3797 return OPERATOR_CANCELLED;
3798 }
3799 if (shape_ref >= totshape_ref) {
3800 /* This case occurs if operator was used before on object with more keys than current one. */
3801 shape_ref = 0; /* default to basis */
3802 }
3803
3804 /* Get shape key - needed for finding reference shape (for add mode only). */
3805 if (key_ref) {
3806 kb_ref = static_cast<KeyBlock *>(BLI_findlink(&key_ref->block, shape_ref));
3807 }
3808
3809 int tot_selected_verts_objects = 0, tot_locked = 0;
3811 scene, view_layer, CTX_wm_view3d(C));
3812 for (Object *obedit : objects) {
3813 Mesh *mesh = static_cast<Mesh *>(obedit->data);
3814 Key *key = mesh->key;
3815 KeyBlock *kb = nullptr;
3816 BMEditMesh *em = mesh->runtime->edit_mesh.get();
3817 int shape;
3818
3819 if (em->bm->totvertsel == 0) {
3820 continue;
3821 }
3822
3824 tot_locked++;
3825 continue;
3826 }
3827
3828 tot_selected_verts_objects++;
3829
3830 if (!key) {
3831 continue;
3832 }
3833 kb = BKE_keyblock_find_name(key, kb_ref->name);
3834 shape = BLI_findindex(&key->block, kb);
3835
3836 if (kb) {
3837 const bool use_symmetry = (mesh->symmetry & ME_SYMMETRY_X) != 0;
3838
3839 if (use_symmetry) {
3840 const bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
3841
3842 EDBM_verts_mirror_cache_begin(em, 0, false, true, false, use_topology);
3843 }
3844
3845 /* Perform blending on selected vertices. */
3846 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
3848 continue;
3849 }
3850
3851 /* Get coordinates of shapekey we're blending from. */
3852 sco = static_cast<float *>(
3853 CustomData_bmesh_get_n(&em->bm->vdata, eve->head.data, CD_SHAPEKEY, shape));
3854 copy_v3_v3(co, sco);
3855
3856 if (use_add) {
3857 /* In add mode, we add relative shape key offset. */
3858 const float *rco = static_cast<const float *>(
3860 sub_v3_v3v3(co, co, rco);
3861
3862 madd_v3_v3fl(eve->co, co, blend);
3863 }
3864 else {
3865 /* In blend mode, we interpolate to the shape key. */
3866 interp_v3_v3v3(eve->co, eve->co, co, blend);
3867 }
3868 }
3869
3870 if (use_symmetry) {
3873 }
3874
3876 params.calc_looptris = true;
3877 params.calc_normals = true;
3878 params.is_destructive = false;
3879 EDBM_update(mesh, &params);
3880 }
3881 }
3882
3883 if (tot_selected_verts_objects == 0 && !tot_locked) {
3884 BKE_report(op->reports, RPT_ERROR, "No selected vertex");
3885 }
3886
3887 return tot_selected_verts_objects ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
3888}
3889
3891 PointerRNA * /*ptr*/,
3892 PropertyRNA * /*prop*/,
3893 bool *r_free)
3894{
3895 Object *obedit = CTX_data_edit_object(C);
3896 BMEditMesh *em;
3897 EnumPropertyItem *item = nullptr;
3898 int totitem = 0;
3899
3900 if ((obedit && obedit->type == OB_MESH) && (em = BKE_editmesh_from_object(obedit)) &&
3902 {
3903 EnumPropertyItem tmp = {0, "", 0, "", ""};
3904 int a;
3905
3906 for (a = 0; a < em->bm->vdata.totlayer; a++) {
3907 if (em->bm->vdata.layers[a].type != CD_SHAPEKEY) {
3908 continue;
3909 }
3910
3911 tmp.value = totitem;
3912 tmp.identifier = em->bm->vdata.layers[a].name;
3913 tmp.name = em->bm->vdata.layers[a].name;
3914 /* RNA_enum_item_add sets totitem itself! */
3915 RNA_enum_item_add(&item, &totitem, &tmp);
3916 }
3917 }
3918
3919 RNA_enum_item_end(&item, &totitem);
3920 *r_free = true;
3921
3922 return item;
3923}
3924
3926{
3927 uiLayout *layout = op->layout;
3928 Object *obedit = CTX_data_edit_object(C);
3929 Mesh *mesh = static_cast<Mesh *>(obedit->data);
3930
3931 PointerRNA ptr_key = RNA_id_pointer_create((ID *)mesh->key);
3932
3933 uiLayoutSetPropSep(layout, true);
3934 uiLayoutSetPropDecorate(layout, false);
3935
3937 layout, op->ptr, "shape", &ptr_key, "key_blocks", std::nullopt, ICON_SHAPEKEY_DATA);
3938 layout->prop(op->ptr, "blend", UI_ITEM_NONE, std::nullopt, ICON_NONE);
3939 layout->prop(op->ptr, "add", UI_ITEM_NONE, std::nullopt, ICON_NONE);
3940}
3941
3943{
3944 PropertyRNA *prop;
3945
3946 /* identifiers */
3947 ot->name = "Blend from Shape";
3948 ot->description = "Blend in shape from a shape key";
3949 ot->idname = "MESH_OT_blend_from_shape";
3950
3951 /* API callbacks. */
3953 /* disable because search popup closes too easily */
3954 // ot->invoke = WM_operator_props_popup_call;
3956 ot->poll = ED_operator_editmesh;
3957
3958 /* flags */
3959 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3960
3961 /* properties */
3962 prop = RNA_def_enum(
3963 ot->srna, "shape", rna_enum_dummy_NULL_items, 0, "Shape", "Shape key to use for blending");
3966 RNA_def_float(ot->srna, "blend", 1.0f, -1e3f, 1e3f, "Blend", "Blending factor", -2.0f, 2.0f);
3967 RNA_def_boolean(ot->srna, "add", true, "Add", "Add rather than blend between shapes");
3968}
3969
3971
3972/* -------------------------------------------------------------------- */
3975
3977{
3978 const float thickness = RNA_float_get(op->ptr, "thickness");
3979
3980 const Scene *scene = CTX_data_scene(C);
3981 ViewLayer *view_layer = CTX_data_view_layer(C);
3983 scene, view_layer, CTX_wm_view3d(C));
3984 for (Object *obedit : objects) {
3986 BMesh *bm = em->bm;
3987
3988 if (em->bm->totfacesel == 0) {
3989 continue;
3990 }
3991
3992 BMOperator bmop;
3993
3994 if (!EDBM_op_init(em, &bmop, op, "solidify geom=%hf thickness=%f", BM_ELEM_SELECT, thickness))
3995 {
3996 continue;
3997 }
3998
3999 /* deselect only the faces in the region to be solidified (leave wire
4000 * edges and loose verts selected, as there will be no corresponding
4001 * geometry selected below) */
4003
4004 /* run the solidify operator */
4005 BMO_op_exec(bm, &bmop);
4006
4007 /* select the newly generated faces */
4009
4010 /* No need to flush the selection, any selection history is no longer valid. */
4012
4013 if (!EDBM_op_finish(em, &bmop, op, true)) {
4014 continue;
4015 }
4016
4018 params.calc_looptris = true;
4019 params.calc_normals = false;
4020 params.is_destructive = true;
4021 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
4022 }
4023
4024 return OPERATOR_FINISHED;
4025}
4026
4028{
4029 PropertyRNA *prop;
4030 /* identifiers */
4031 ot->name = "Solidify";
4032 ot->description = "Create a solid skin by extruding, compensating for sharp angles";
4033 ot->idname = "MESH_OT_solidify";
4034
4035 /* API callbacks. */
4036 ot->exec = edbm_solidify_exec;
4037 ot->poll = ED_operator_editmesh;
4038
4039 /* flags */
4040 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4041
4043 ot->srna, "thickness", 0.01f, -1e4f, 1e4f, "Thickness", "", -10.0f, 10.0f);
4044 RNA_def_property_ui_range(prop, -10.0, 10.0, 0.1, 4);
4045}
4046
4048
4049/* -------------------------------------------------------------------- */
4052
4053enum {
4057};
4058
4061 Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
4062{
4063 Object *obedit = base_old->object;
4064 BMeshCreateParams create_params{};
4065 create_params.use_toolflags = true;
4066 BMesh *bm_new = BM_mesh_create(&bm_mesh_allocsize_default, &create_params);
4067 BM_mesh_elem_toolflags_ensure(bm_new); /* Needed for 'duplicate' BMO. */
4068
4069 const bool use_custom_normals = (bm_old->lnor_spacearr != nullptr);
4070 if (use_custom_normals) {
4071 /* Needed so the temporary normal layer is copied too. */
4073 }
4074 else {
4076 }
4077
4078 /* Take into account user preferences for duplicating actions. */
4079 const eDupli_ID_Flags dupflag = eDupli_ID_Flags(USER_DUP_MESH | (U.dupflag & USER_DUP_ACT));
4080 Base *base_new = blender::ed::object::add_duplicate(bmain, scene, view_layer, base_old, dupflag);
4081
4082 /* normally would call directly after but in this case delay recalc */
4083 // DAG_relations_tag_update(bmain);
4084
4085 /* new in 2.5 */
4087 base_new->object,
4090 false);
4091
4093
4094 BMO_op_callf(bm_old,
4096 "duplicate geom=%hvef dest=%p",
4098 bm_new);
4099 BMO_op_callf(bm_old,
4101 "delete geom=%hvef context=%i",
4103 DEL_FACES);
4104
4105 /* deselect loose data - this used to get deleted,
4106 * we could de-select edges and verts only, but this turns out to be less complicated
4107 * since de-selecting all skips selection flushing logic */
4109
4110 if (use_custom_normals) {
4112 }
4113 else {
4114 BM_mesh_normals_update(bm_new);
4115 }
4116
4117 BMeshToMeshParams to_mesh_params{};
4118 BM_mesh_bm_to_me(bmain, bm_new, static_cast<Mesh *>(base_new->object->data), &to_mesh_params);
4119
4120 BM_mesh_free(bm_new);
4121 ((Mesh *)base_new->object->data)->runtime->edit_mesh = nullptr;
4122
4123 return base_new;
4124}
4125
4127 Scene *scene,
4128 ViewLayer *view_layer,
4129 Base *base_old,
4130 BMesh *bm_old,
4131 BMVert **verts,
4132 uint verts_len,
4133 BMEdge **edges,
4134 uint edges_len,
4135 BMFace **faces,
4136 uint faces_len)
4137{
4138 BMAllocTemplate bm_new_allocsize{};
4139 bm_new_allocsize.totvert = verts_len;
4140 bm_new_allocsize.totedge = edges_len;
4141 bm_new_allocsize.totloop = faces_len * 3;
4142 bm_new_allocsize.totface = faces_len;
4143
4144 const bool use_custom_normals = (bm_old->lnor_spacearr != nullptr);
4145
4146 Object *obedit = base_old->object;
4147
4148 BMeshCreateParams create_params{};
4149 BMesh *bm_new = BM_mesh_create(&bm_new_allocsize, &create_params);
4150
4151 if (use_custom_normals) {
4152 /* Needed so the temporary normal layer is copied too. */
4153 BM_mesh_copy_init_customdata_all_layers(bm_new, bm_old, BM_ALL, &bm_new_allocsize);
4154 }
4155 else {
4156 BM_mesh_copy_init_customdata(bm_new, bm_old, &bm_new_allocsize);
4157 }
4158
4159 /* Take into account user preferences for duplicating actions. */
4160 const eDupli_ID_Flags dupflag = eDupli_ID_Flags(USER_DUP_MESH | (U.dupflag & USER_DUP_ACT));
4161 Base *base_new = blender::ed::object::add_duplicate(bmain, scene, view_layer, base_old, dupflag);
4162
4163 /* normally would call directly after but in this case delay recalc */
4164 // DAG_relations_tag_update(bmain);
4165
4166 /* new in 2.5 */
4168 base_new->object,
4171 false);
4172
4174
4175 BM_mesh_copy_arrays(bm_old, bm_new, verts, verts_len, edges, edges_len, faces, faces_len);
4176
4177 if (use_custom_normals) {
4179 }
4180
4181 for (uint i = 0; i < verts_len; i++) {
4182 BM_vert_kill(bm_old, verts[i]);
4183 }
4184 BMeshToMeshParams to_mesh_params{};
4185 BM_mesh_bm_to_me(bmain, bm_new, static_cast<Mesh *>(base_new->object->data), &to_mesh_params);
4186
4187 BM_mesh_free(bm_new);
4188 ((Mesh *)base_new->object->data)->runtime->edit_mesh = nullptr;
4189
4190 return base_new;
4191}
4192
4194 Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
4195{
4197
4198 /* we may have tags from previous operators */
4200
4201 /* sel -> tag */
4203 bm_old, BM_FACE | BM_EDGE | BM_VERT, BM_ELEM_TAG, true, false, BM_ELEM_SELECT);
4204
4205 Base *base_new = mesh_separate_tagged(bmain, scene, view_layer, base_old, bm_old);
4206
4208
4209 return (base_new != nullptr);
4210}
4211
4218static void mesh_separate_material_assign_mat_nr(Main *bmain, Object *ob, const short mat_nr)
4219{
4220 ID *obdata = static_cast<ID *>(ob->data);
4221
4222 const short *totcolp = BKE_id_material_len_p(obdata);
4223 Material ***matarar = BKE_id_material_array_p(obdata);
4224
4225 if ((totcolp && matarar) == 0) {
4226 BLI_assert(0);
4227 return;
4228 }
4229
4230 if (*totcolp) {
4231 Material *ma_ob;
4232 Material *ma_obdata;
4233 char matbit;
4234
4235 if (mat_nr < ob->totcol) {
4236 ma_ob = ob->mat[mat_nr];
4237 matbit = ob->matbits[mat_nr];
4238 }
4239 else {
4240 ma_ob = nullptr;
4241 matbit = 0;
4242 }
4243
4244 if (mat_nr < *totcolp) {
4245 ma_obdata = (*matarar)[mat_nr];
4246 }
4247 else {
4248 ma_obdata = nullptr;
4249 }
4250
4251 BKE_id_material_clear(bmain, obdata);
4252 BKE_id_material_resize(bmain, obdata, 1, true);
4254
4255 ob->mat[0] = ma_ob;
4256 id_us_plus((ID *)ma_ob);
4257 ob->matbits[0] = matbit;
4258 (*matarar)[0] = ma_obdata;
4259 id_us_plus((ID *)ma_obdata);
4260 }
4261 else {
4262 BKE_id_material_clear(bmain, obdata);
4263 }
4264}
4265
4267 Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
4268{
4269 BMFace *f_cmp, *f;
4270 BMIter iter;
4271 bool result = false;
4272
4274
4275 while ((f_cmp = static_cast<BMFace *>(BM_iter_at_index(bm_old, BM_FACES_OF_MESH, nullptr, 0)))) {
4276 Base *base_new;
4277 const short mat_nr = f_cmp->mat_nr;
4278 int tot = 0;
4279
4281
4282 BM_ITER_MESH (f, &iter, bm_old, BM_FACES_OF_MESH) {
4283 if (f->mat_nr == mat_nr) {
4284 BMLoop *l_iter;
4285 BMLoop *l_first;
4286
4288 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
4289 do {
4292 } while ((l_iter = l_iter->next) != l_first);
4293
4294 tot++;
4295 }
4296 }
4297
4298 /* leave the current object with some materials */
4299 if (tot == bm_old->totface) {
4300 mesh_separate_material_assign_mat_nr(bmain, base_old->object, mat_nr);
4301
4302 /* since we're in editmode, must set faces here */
4303 BM_ITER_MESH (f, &iter, bm_old, BM_FACES_OF_MESH) {
4304 f->mat_nr = 0;
4305 }
4306 break;
4307 }
4308
4309 /* Move selection into a separate object */
4310 base_new = mesh_separate_tagged(bmain, scene, view_layer, base_old, bm_old);
4311 if (base_new) {
4312 mesh_separate_material_assign_mat_nr(bmain, base_new->object, mat_nr);
4313 }
4314
4315 result |= (base_new != nullptr);
4316 }
4317
4319
4320 return result;
4321}
4322
4324 Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
4325{
4326 /* Without this, we duplicate the object mode mesh for each loose part.
4327 * This can get very slow especially for large meshes with many parts
4328 * which would duplicate the mesh on entering edit-mode. */
4329 const bool clear_object_data = true;
4330
4331 bool result = false;
4332
4333 blender::Array<BMVert *> vert_groups(bm_old->totvert);
4334 blender::Array<BMEdge *> edge_groups(bm_old->totedge);
4335 blender::Array<BMFace *> face_groups(bm_old->totface);
4336
4337 int(*groups)[3] = nullptr;
4338 int groups_len = BM_mesh_calc_edge_groups_as_arrays(
4339 bm_old, vert_groups.data(), edge_groups.data(), face_groups.data(), &groups);
4340 if (groups_len <= 1) {
4341 MEM_SAFE_FREE(groups);
4342 return false;
4343 }
4344
4345 if (clear_object_data) {
4346 ED_mesh_geometry_clear(static_cast<Mesh *>(base_old->object->data));
4347 }
4348
4350
4351 /* Separate out all groups except the first. */
4352 uint group_ofs[3] = {uint(groups[0][0]), uint(groups[0][1]), uint(groups[0][2])};
4353 for (int i = 1; i < groups_len; i++) {
4354 Base *base_new = mesh_separate_arrays(bmain,
4355 scene,
4356 view_layer,
4357 base_old,
4358 bm_old,
4359 vert_groups.data() + group_ofs[0],
4360 groups[i][0],
4361 edge_groups.data() + group_ofs[1],
4362 groups[i][1],
4363 face_groups.data() + group_ofs[2],
4364 groups[i][2]);
4365 result |= (base_new != nullptr);
4366
4367 group_ofs[0] += groups[i][0];
4368 group_ofs[1] += groups[i][1];
4369 group_ofs[2] += groups[i][2];
4370 }
4371
4372 Mesh *me_old = static_cast<Mesh *>(base_old->object->data);
4374
4375 if (clear_object_data) {
4376 BMeshToMeshParams to_mesh_params{};
4377 to_mesh_params.update_shapekey_indices = true;
4378 BM_mesh_bm_to_me(nullptr, bm_old, me_old, &to_mesh_params);
4379 }
4380
4381 MEM_freeN(groups);
4382 return result;
4383}
4384
4386{
4387 Main *bmain = CTX_data_main(C);
4388 Scene *scene = CTX_data_scene(C);
4389 ViewLayer *view_layer = CTX_data_view_layer(C);
4390 const int type = RNA_enum_get(op->ptr, "type");
4391 bool changed_multi = false;
4392
4393 if (ED_operator_editmesh(C)) {
4394 uint empty_selection_len = 0;
4396 scene, view_layer, CTX_wm_view3d(C));
4397 for (const int base_index : bases.index_range()) {
4398 Base *base = bases[base_index];
4400
4401 if (type == 0) {
4402 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
4403 /* when all objects has no selection */
4404 if (++empty_selection_len == bases.size()) {
4405 BKE_report(op->reports, RPT_ERROR, "Nothing selected");
4406 }
4407 continue;
4408 }
4409 }
4410
4411 /* editmode separate */
4412 bool changed = false;
4413 switch (type) {
4415 changed = mesh_separate_selected(bmain, scene, view_layer, base, em->bm);
4416 break;
4418 changed = mesh_separate_material(bmain, scene, view_layer, base, em->bm);
4419 break;
4421 changed = mesh_separate_loose(bmain, scene, view_layer, base, em->bm);
4422 break;
4423 default:
4424 BLI_assert(0);
4425 break;
4426 }
4427
4428 if (changed) {
4430 params.calc_looptris = true;
4431 params.calc_normals = false;
4432 params.is_destructive = true;
4433 EDBM_update(static_cast<Mesh *>(base->object->data), &params);
4434 }
4435 changed_multi |= changed;
4436 }
4437 }
4438 else {
4439 if (type == MESH_SEPARATE_SELECTED) {
4440 BKE_report(op->reports, RPT_ERROR, "Selection not supported in object mode");
4441 return OPERATOR_CANCELLED;
4442 }
4443
4444 /* object mode separate */
4445 CTX_DATA_BEGIN (C, Base *, base_iter, selected_editable_bases) {
4446 Object *ob = base_iter->object;
4447 if (ob->type != OB_MESH) {
4448 continue;
4449 }
4450 Mesh *mesh = static_cast<Mesh *>(ob->data);
4451 if (!BKE_id_is_editable(bmain, &mesh->id)) {
4452 continue;
4453 }
4454
4455 BMeshCreateParams create_params{};
4456 create_params.use_toolflags = true;
4457 BMesh *bm_old = BM_mesh_create(&bm_mesh_allocsize_default, &create_params);
4458
4459 BMeshFromMeshParams from_mesh_params{};
4460 from_mesh_params.calc_face_normal = true;
4461 from_mesh_params.calc_vert_normal = true;
4462 BM_mesh_bm_from_me(bm_old, mesh, &from_mesh_params);
4463
4464 bool changed = false;
4465 switch (type) {
4467 changed = mesh_separate_material(bmain, scene, view_layer, base_iter, bm_old);
4468 break;
4470 changed = mesh_separate_loose(bmain, scene, view_layer, base_iter, bm_old);
4471 break;
4472 default:
4473 BLI_assert(0);
4474 break;
4475 }
4476
4477 if (changed) {
4478 BMeshToMeshParams to_mesh_params{};
4479 to_mesh_params.calc_object_remap = true;
4480 BM_mesh_bm_to_me(bmain, bm_old, mesh, &to_mesh_params);
4481
4484 }
4485
4486 BM_mesh_free(bm_old);
4487
4488 changed_multi |= changed;
4489 }
4491 }
4492
4493 if (changed_multi) {
4494 /* delay depsgraph recalc until all objects are duplicated */
4498
4499 return OPERATOR_FINISHED;
4500 }
4501
4502 return OPERATOR_CANCELLED;
4503}
4504
4506{
4507 static const EnumPropertyItem prop_separate_types[] = {
4508 {MESH_SEPARATE_SELECTED, "SELECTED", 0, "Selection", ""},
4509 {MESH_SEPARATE_MATERIAL, "MATERIAL", 0, "By Material", ""},
4510 {MESH_SEPARATE_LOOSE, "LOOSE", 0, "By Loose Parts", ""},
4511 {0, nullptr, 0, nullptr, nullptr},
4512 };
4513
4514 /* identifiers */
4515 ot->name = "Separate";
4516 ot->description = "Separate selected geometry into a new mesh";
4517 ot->idname = "MESH_OT_separate";
4518
4519 /* API callbacks. */
4520 ot->invoke = WM_menu_invoke;
4521 ot->exec = edbm_separate_exec;
4522 ot->poll = ED_operator_scene_editable; /* object and editmode */
4523
4524 /* flags */
4525 ot->flag = OPTYPE_UNDO;
4526
4527 ot->prop = RNA_def_enum(
4528 ot->srna, "type", prop_separate_types, MESH_SEPARATE_SELECTED, "Type", "");
4529}
4530
4532
4533/* -------------------------------------------------------------------- */
4536
4538{
4539 const bool use_beauty = RNA_boolean_get(op->ptr, "use_beauty");
4540
4541 bool has_selected_edges = false, has_faces_filled = false;
4542
4543 const Scene *scene = CTX_data_scene(C);
4544 ViewLayer *view_layer = CTX_data_view_layer(C);
4546 scene, view_layer, CTX_wm_view3d(C));
4547 for (Object *obedit : objects) {
4549
4550 const int totface_orig = em->bm->totface;
4551
4552 if (em->bm->totedgesel == 0) {
4553 continue;
4554 }
4555 has_selected_edges = true;
4556
4557 BMOperator bmop;
4558 if (!EDBM_op_init(
4559 em, &bmop, op, "triangle_fill edges=%he use_beauty=%b", BM_ELEM_SELECT, use_beauty))
4560 {
4561 continue;
4562 }
4563
4564 BMO_op_exec(em->bm, &bmop);
4565
4566 /* cancel if nothing was done */
4567 if (totface_orig == em->bm->totface) {
4568 EDBM_op_finish(em, &bmop, op, true);
4569 continue;
4570 }
4571 has_faces_filled = true;
4572
4573 /* select new geometry */
4575 em->bm, bmop.slots_out, "geom.out", BM_FACE | BM_EDGE, BM_ELEM_SELECT, true);
4576
4577 if (!EDBM_op_finish(em, &bmop, op, true)) {
4578 continue;
4579 }
4580
4582 params.calc_looptris = true;
4583 params.calc_normals = false;
4584 params.is_destructive = true;
4585 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
4586 }
4587
4588 if (!has_selected_edges) {
4589 BKE_report(op->reports, RPT_ERROR, "No edges selected");
4590 return OPERATOR_CANCELLED;
4591 }
4592
4593 if (!has_faces_filled) {
4594 BKE_report(op->reports, RPT_WARNING, "No faces filled");
4595 return OPERATOR_CANCELLED;
4596 }
4597
4598 return OPERATOR_FINISHED;
4599}
4600
4602{
4603 /* identifiers */
4604 ot->name = "Fill";
4605 ot->idname = "MESH_OT_fill";
4606 ot->description = "Fill a selected edge loop with faces";
4607 ot->translation_context = BLT_I18NCONTEXT_ID_MESH;
4608
4609 /* API callbacks. */
4610 ot->exec = edbm_fill_exec;
4611 ot->poll = ED_operator_editmesh;
4612
4613 /* flags */
4614 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4615
4616 RNA_def_boolean(ot->srna, "use_beauty", true, "Beauty", "Use best triangulation division");
4617}
4618
4620
4621/* -------------------------------------------------------------------- */
4624
4625static bool bm_edge_test_fill_grid_cb(BMEdge *e, void * /*bm_v*/)
4626{
4628}
4629
4631{
4632 BMIter iter;
4633 BMEdge *e_iter;
4634 BMVert *v_pair[2];
4635 int i = 0;
4636 BM_ITER_ELEM (e_iter, &iter, v, BM_EDGES_OF_VERT) {
4637 if (BM_elem_flag_test(e_iter, BM_ELEM_TAG)) {
4638 v_pair[i++] = BM_edge_other_vert(e_iter, v);
4639 }
4640 }
4641 BLI_assert(i == 2);
4642
4643 return fabsf(float(M_PI) - angle_v3v3v3(v_pair[0]->co, v->co, v_pair[1]->co));
4644}
4645
4649static bool edbm_fill_grid_prepare(BMesh *bm, int offset, int *span_p, const bool span_calc)
4650{
4651 /* angle differences below this value are considered 'even'
4652 * in that they shouldn't be used to calculate corners used for the 'span' */
4653 const float eps_even = 1e-3f;
4654 BMEdge *e;
4655 BMIter iter;
4656 int count;
4657 int span = *span_p;
4658
4659 ListBase eloops = {nullptr};
4660 BMEdgeLoopStore *el_store;
4661 // LinkData *el_store;
4662
4664 el_store = static_cast<BMEdgeLoopStore *>(eloops.first);
4665
4666 if (count != 1) {
4667 /* Let the operator use the selection flags,
4668 * most likely failing with an error in this case. */
4669 BM_mesh_edgeloops_free(&eloops);
4670 return false;
4671 }
4672
4673 /* Only tag edges that are part of a loop. */
4674 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
4676 }
4677 const int verts_len = BM_edgeloop_length_get(el_store);
4678 const int edges_len = verts_len - (BM_edgeloop_is_closed(el_store) ? 0 : 1);
4679 BMEdge **edges = MEM_malloc_arrayN<BMEdge *>(edges_len, __func__);
4680 BM_edgeloop_edges_get(el_store, edges);
4681 for (int i = 0; i < edges_len; i++) {
4683 }
4684
4685 if (span_calc) {
4686 span = verts_len / 4;
4687 }
4688 else {
4689 span = min_ii(span, (verts_len / 2) - 1);
4690 }
4691 offset = mod_i(offset, verts_len);
4692
4693 if ((count == 1) && ((verts_len & 1) == 0) && (verts_len == edges_len)) {
4694
4695 /* be clever! detect 2 edge loops from one closed edge loop */
4698 LinkData *v_act_link;
4699 int i;
4700
4701 if (v_act && (v_act_link = static_cast<LinkData *>(
4702 BLI_findptr(verts, v_act, offsetof(LinkData, data)))))
4703 {
4704 /* pass */
4705 }
4706 else {
4707 /* find the vertex with the best angle (a corner vertex) */
4708 LinkData *v_link_best = nullptr;
4709 float angle_best = -1.0f;
4710 LISTBASE_FOREACH (LinkData *, v_link, verts) {
4711 const float angle = edbm_fill_grid_vert_tag_angle(static_cast<BMVert *>(v_link->data));
4712 if ((angle > angle_best) || (v_link_best == nullptr)) {
4713 angle_best = angle;
4714 v_link_best = v_link;
4715 }
4716 }
4717
4718 v_act_link = v_link_best;
4719 v_act = static_cast<BMVert *>(v_act_link->data);
4720 }
4721
4722 /* set this vertex first */
4723 BLI_listbase_rotate_first(verts, v_act_link);
4724
4725 if (offset != 0) {
4726 v_act_link = static_cast<LinkData *>(BLI_findlink(verts, offset));
4727 v_act = static_cast<BMVert *>(v_act_link->data);
4728 BLI_listbase_rotate_first(verts, v_act_link);
4729 }
4730
4731 /* Run again to update the edge order from the rotated vertex list. */
4732 BM_edgeloop_edges_get(el_store, edges);
4733
4734 if (span_calc) {
4735 /* calculate the span by finding the next corner in 'verts'
4736 * we don't know what defines a corner exactly so find the 4 verts
4737 * in the loop with the greatest angle.
4738 * Tag them and use the first tagged vertex to calculate the span.
4739 *
4740 * NOTE: we may have already checked 'edbm_fill_grid_vert_tag_angle()' on each
4741 * vert, but advantage of de-duplicating is minimal. */
4742 SortPtrByFloat *ele_sort = MEM_malloc_arrayN<SortPtrByFloat>(verts_len, __func__);
4743 LinkData *v_link;
4744 for (v_link = static_cast<LinkData *>(verts->first), i = 0; v_link;
4745 v_link = v_link->next, i++)
4746 {
4747 BMVert *v = static_cast<BMVert *>(v_link->data);
4748 const float angle = edbm_fill_grid_vert_tag_angle(v);
4749 ele_sort[i].sort_value = angle;
4750 ele_sort[i].data = v_link;
4751
4752 /* Do not allow the best corner or the diagonally opposite corner to be detected. */
4753 if (ELEM(i, 0, verts_len / 2)) {
4754 ele_sort[i].sort_value = 0;
4755 }
4756 }
4757
4758 qsort(ele_sort, verts_len, sizeof(*ele_sort), BLI_sortutil_cmp_float_reverse);
4759
4760 /* Check that we have at least 3 corners.
4761 * The excluded corners are the last and second from last elements (both reset to 0).
4762 * The best remaining corner is `ele_sort[0]`
4763 * if the angle on the best remaining corner is roughly the same as the third-last,
4764 * then we can't calculate 3+ corners - fallback to the even span. */
4765 if ((ele_sort[0].sort_value - ele_sort[verts_len - 3].sort_value) > eps_even) {
4766 span = BLI_findindex(verts, ele_sort[0].data);
4767 }
4768 MEM_freeN(ele_sort);
4769 }
4770 /* end span calc */
4771 int start = 0;
4772
4773 /* The algorithm needs to iterate the shorter distance, between the best and second best vert.
4774 * If the second best vert is near the beginning of the loop, it starts at 0 and walks forward.
4775 * If, instead, the second best vert is near the end of the loop, then it starts at the second
4776 * best vertex and walks to the end of the loop. */
4777 if (span > verts_len / 2) {
4778 span = (verts_len)-span;
4779 start = (verts_len / 2) - span;
4780 }
4781
4782 /* un-flag 'rails' */
4783 for (i = start; i < start + span; i++) {
4785 BM_elem_flag_disable(edges[(verts_len / 2) + i], BM_ELEM_TAG);
4786 }
4787 }
4788 /* else let the bmesh-operator handle it */
4789
4790 BM_mesh_edgeloops_free(&eloops);
4791 MEM_freeN(edges);
4792
4793 *span_p = span;
4794
4795 return true;
4796}
4797
4802
4816{
4817 FillGridSplitJoin *split_join = MEM_callocN<FillGridSplitJoin>(__func__);
4818
4819 /* Split the selection into an island. */
4820 BMOperator split_op;
4821 BMO_op_init(em->bm, &split_op, 0, "split");
4823 em->bm, &split_op, split_op.slots_in, "geom", BM_FACE | BM_EDGE | BM_VERT, BM_ELEM_SELECT);
4824 BMO_op_exec(em->bm, &split_op);
4825
4826 /* Setup the weld op that will undo the split.
4827 * Switch the selection to the corresponding edges on the island instead of the edges around the
4828 * hole, so fill_grid will interpolate using the face and loop data from the island.
4829 * Also create a new map for the weld, which maps pairs of verts instead of pairs of edges. */
4830 BMO_op_init(em->bm, &split_join->weld_op, 0, "weld_verts");
4831 BMOpSlot *weld_target_map = BMO_slot_get(split_join->weld_op.slots_in, "targetmap");
4832 BMOIter siter;
4833 BMEdge *e;
4834 BMO_ITER (e, &siter, split_op.slots_out, "boundary_map.out", 0) {
4835 BMEdge *e_dst = static_cast<BMEdge *>(BMO_iter_map_value_ptr(&siter));
4836
4837 BLI_assert(e_dst);
4838
4839 /* For edges, flip the selection from the edge of the hole to the edge of the island. */
4841
4842 /* When these match, the source edge has been deleted. */
4843 if (e != e_dst) {
4845
4846 /* For verts, flip the selection from the edge of the hole to the edge of the island.
4847 * Also add it to the weld map. But check selection first. Don't try to add the same vert to
4848 * the map more than once. If the selection was changed false, it's already been processed.
4849 */
4853 BMO_slot_map_elem_insert(&split_join->weld_op, weld_target_map, e->v1, e_dst->v1);
4854 }
4858 BMO_slot_map_elem_insert(&split_join->weld_op, weld_target_map, e->v2, e_dst->v2);
4859 }
4860 }
4861 }
4862
4863 /* Store the island for removal once it has been replaced by new fill_grid geometry. */
4864 BMO_op_init(em->bm, &split_join->delete_op, 0, "delete");
4865 BMO_slot_int_set(split_join->delete_op.slots_in, "context", DEL_FACES);
4867 em->bm, split_op.slots_out, "geom.out", BM_FACE, BM_ELEM_SELECT, false);
4869 &split_join->delete_op,
4870 split_join->delete_op.slots_in,
4871 "geom",
4872 BM_FACE,
4874
4875 /* Clean up the split operator. */
4876 BMO_op_finish(em->bm, &split_op);
4877
4878 return split_join;
4879}
4880
4890 wmOperator *op,
4891 FillGridSplitJoin *split_join,
4892 bool changed)
4893{
4894
4895 /* If fill_grid worked, delete the replaced faces. Otherwise, restore original selection. */
4896 if (changed) {
4897 BMO_op_exec(em->bm, &split_join->delete_op);
4898 }
4899 else {
4901 em->bm, split_join->delete_op.slots_in, "geom", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
4902 }
4903 BMO_op_finish(em->bm, &split_join->delete_op);
4904
4905 /* If fill_grid created geometry from faces after those faces had been split
4906 * from the rest of the mesh, the geometry it generated will be inward-facing.
4907 * (using the fill_grid on an island instead of a hole is 'inside out'.) Fix it.
4908 * This is the same as #edbm_flip_normals_face_winding without the #EDBM_update
4909 * since that will happen later. */
4910 if (changed) {
4912 EDBM_op_callf(em, op, "reverse_faces faces=%hf flip_multires=%b", BM_ELEM_SELECT, true);
4913 if (lnors_ed_arr) {
4914 flip_custom_normals(em->bm, lnors_ed_arr);
4916 }
4917 }
4918
4919 /* Put the mesh back together. */
4920 BMO_op_exec(em->bm, &split_join->weld_op);
4921 BMO_op_finish(em->bm, &split_join->weld_op);
4922
4923 MEM_freeN(split_join);
4924}
4925
4927{
4928 const bool use_interp_simple = RNA_boolean_get(op->ptr, "use_interp_simple");
4929
4930 const Scene *scene = CTX_data_scene(C);
4931 ViewLayer *view_layer = CTX_data_view_layer(C);
4933 scene, view_layer, CTX_wm_view3d(C));
4934 for (uint ob_index = 0; ob_index < objects.size(); ob_index++) {
4935
4936 Object *obedit = objects[ob_index];
4938 if (em->bm->totedgesel == 0) {
4939 continue;
4940 }
4941
4942 bool use_prepare = true;
4943 const bool use_smooth = edbm_add_edge_face__smooth_get(em->bm);
4944
4945 FillGridSplitJoin *split_join = nullptr;
4946 if (em->bm->totfacesel != 0) {
4947 split_join = edbm_fill_grid_split_join_init(em);
4948 }
4949
4950 const int totedge_orig = em->bm->totedge;
4951 const int totface_orig = em->bm->totface;
4952
4953 if (use_prepare) {
4954 /* use when we have a single loop selected */
4955 PropertyRNA *prop_span = RNA_struct_find_property(op->ptr, "span");
4956 PropertyRNA *prop_offset = RNA_struct_find_property(op->ptr, "offset");
4957 bool calc_span;
4958
4959 int span;
4960 int offset;
4961
4962 /* Only reuse on redo because these settings need to match the current selection.
4963 * We never want to use them on other geometry, repeat last for eg, see: #60777. */
4964 if (((op->flag & OP_IS_INVOKE) || (op->flag & OP_IS_REPEAT_LAST) == 0) &&
4965 RNA_property_is_set(op->ptr, prop_span))
4966 {
4967 span = RNA_property_int_get(op->ptr, prop_span);
4968 calc_span = false;
4969 }
4970 else {
4971 /* Will be overwritten if possible. */
4972 span = 0;
4973 calc_span = true;
4974 }
4975
4976 offset = RNA_property_int_get(op->ptr, prop_offset);
4977
4978 /* in simple cases, move selection for tags, but also support more advanced cases */
4979 use_prepare = edbm_fill_grid_prepare(em->bm, offset, &span, calc_span);
4980
4981 RNA_property_int_set(op->ptr, prop_span, span);
4982 }
4983 /* end tricky prepare code */
4984
4985 bool changed = EDBM_op_call_and_selectf(
4986 em,
4987 op,
4988 "faces.out",
4989 true,
4990 "grid_fill edges=%he mat_nr=%i use_smooth=%b use_interp_simple=%b",
4991 use_prepare ? BM_ELEM_TAG : BM_ELEM_SELECT,
4992 em->mat_nr,
4993 use_smooth,
4994 use_interp_simple);
4995
4996 /* Check that the results match the return value. */
4997 const bool has_geometry_changed = totedge_orig != em->bm->totedge ||
4998 totface_orig != em->bm->totface;
4999 BLI_assert(changed == has_geometry_changed);
5000 UNUSED_VARS_NDEBUG(has_geometry_changed);
5001
5002 /* If a split/join in progress, finish it. */
5003 if (split_join) {
5004 edbm_fill_grid_split_join_finish(em, op, split_join, changed);
5005 }
5006
5007 /* Update the object. */
5008 if (changed || split_join) {
5010 params.calc_looptris = true;
5011 params.calc_normals = false;
5012 params.is_destructive = true;
5013 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5014 }
5015 else {
5016 /* NOTE: Even if there were no mesh changes, #EDBM_op_finish() changed the BMesh pointer
5017 * inside of edit mesh, so need to tell evaluated objects to sync new BMesh pointer to their
5018 * edit mesh structures. */
5019 DEG_id_tag_update(&obedit->id, 0);
5020 }
5021 }
5022
5023 return OPERATOR_FINISHED;
5024}
5025
5027{
5028 PropertyRNA *prop;
5029
5030 /* identifiers */
5031 ot->name = "Grid Fill";
5032 ot->description = "Fill grid from two loops";
5033 ot->idname = "MESH_OT_fill_grid";
5034
5035 /* API callbacks. */
5036 ot->exec = edbm_fill_grid_exec;
5037 ot->poll = ED_operator_editmesh;
5038
5039 /* flags */
5040 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5041
5042 /* properties */
5043 prop = RNA_def_int(ot->srna, "span", 1, 1, 1000, "Span", "Number of grid columns", 1, 100);
5045 prop = RNA_def_int(ot->srna,
5046 "offset",
5047 0,
5048 -1000,
5049 1000,
5050 "Offset",
5051 "Vertex that is the corner of the grid",
5052 -100,
5053 100);
5055 RNA_def_boolean(ot->srna,
5056 "use_interp_simple",
5057 false,
5058 "Simple Blending",
5059 "Use simple interpolation of grid vertices");
5060}
5061
5063
5064/* -------------------------------------------------------------------- */
5067
5069{
5070 const int sides = RNA_int_get(op->ptr, "sides");
5071
5072 const Scene *scene = CTX_data_scene(C);
5073 ViewLayer *view_layer = CTX_data_view_layer(C);
5075 scene, view_layer, CTX_wm_view3d(C));
5076
5077 for (Object *obedit : objects) {
5079
5080 if (em->bm->totedgesel == 0) {
5081 continue;
5082 }
5083
5085 em, op, "faces.out", true, "holes_fill edges=%he sides=%i", BM_ELEM_SELECT, sides))
5086 {
5087 continue;
5088 }
5089
5091 params.calc_looptris = true;
5092 params.calc_normals = false;
5093 params.is_destructive = true;
5094 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5095 }
5096
5097 return OPERATOR_FINISHED;
5098}
5099
5101{
5102 /* identifiers */
5103 ot->name = "Fill Holes";
5104 ot->idname = "MESH_OT_fill_holes";
5105 ot->description = "Fill in holes (boundary edge loops)";
5106
5107 /* API callbacks. */
5108 ot->exec = edbm_fill_holes_exec;
5109 ot->poll = ED_operator_editmesh;
5110
5111 /* flags */
5112 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5113
5114 RNA_def_int(ot->srna,
5115 "sides",
5116 4,
5117 0,
5118 1000,
5119 "Sides",
5120 "Number of sides in hole required to fill (zero fills all holes)",
5121 0,
5122 100);
5123}
5124
5126
5127/* -------------------------------------------------------------------- */
5130
5132{
5133 const Scene *scene = CTX_data_scene(C);
5134 ViewLayer *view_layer = CTX_data_view_layer(C);
5136 scene, view_layer, CTX_wm_view3d(C));
5137
5138 const float angle_max = M_PI;
5139 const float angle_limit = RNA_float_get(op->ptr, "angle_limit");
5140 char hflag;
5141
5142 for (Object *obedit : objects) {
5144
5145 if (em->bm->totfacesel == 0) {
5146 continue;
5147 }
5148
5149 if (angle_limit >= angle_max) {
5150 hflag = BM_ELEM_SELECT;
5151 }
5152 else {
5153 BMIter iter;
5154 BMEdge *e;
5155
5156 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
5160 BM_edge_calc_face_angle_ex(e, angle_max) < angle_limit));
5161 }
5162 hflag = BM_ELEM_TAG;
5163 }
5164
5166 em, op, "geom.out", true, "beautify_fill faces=%hf edges=%he", BM_ELEM_SELECT, hflag))
5167 {
5168 continue;
5169 }
5170
5172 params.calc_looptris = true;
5173 params.calc_normals = false;
5174 params.is_destructive = true;
5175 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5176 }
5177
5178 return OPERATOR_FINISHED;
5179}
5180
5182{
5183 PropertyRNA *prop;
5184
5185 /* identifiers */
5186 ot->name = "Beautify Faces";
5187 ot->idname = "MESH_OT_beautify_fill";
5188 ot->description = "Rearrange some faces to try to get less degenerated geometry";
5189
5190 /* API callbacks. */
5192 ot->poll = ED_operator_editmesh;
5193
5194 /* flags */
5195 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5196
5197 /* props */
5198 prop = RNA_def_float_rotation(ot->srna,
5199 "angle_limit",
5200 0,
5201 nullptr,
5202 0.0f,
5203 DEG2RADF(180.0f),
5204 "Max Angle",
5205 "Angle limit",
5206 0.0f,
5207 DEG2RADF(180.0f));
5209}
5210
5212
5213/* -------------------------------------------------------------------- */
5216
5218{
5219 const float offset = RNA_float_get(op->ptr, "offset");
5220 const bool use_relative_offset = RNA_boolean_get(op->ptr, "use_relative_offset");
5221 const int center_mode = RNA_enum_get(op->ptr, "center_mode");
5222
5223 const Scene *scene = CTX_data_scene(C);
5224 ViewLayer *view_layer = CTX_data_view_layer(C);
5226 scene, view_layer, CTX_wm_view3d(C));
5227 for (Object *obedit : objects) {
5229
5230 if (em->bm->totfacesel == 0) {
5231 continue;
5232 }
5233
5234 BMOperator bmop;
5235 EDBM_op_init(em,
5236 &bmop,
5237 op,
5238 "poke faces=%hf offset=%f use_relative_offset=%b center_mode=%i",
5240 offset,
5241 use_relative_offset,
5242 center_mode);
5243 BMO_op_exec(em->bm, &bmop);
5244
5246
5248 em->bm, bmop.slots_out, "verts.out", BM_VERT, BM_ELEM_SELECT, true);
5250 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
5251
5252 if (!EDBM_op_finish(em, &bmop, op, true)) {
5253 continue;
5254 }
5255
5257 params.calc_looptris = true;
5258 params.calc_normals = true;
5259 params.is_destructive = true;
5260 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5261 }
5262
5263 return OPERATOR_FINISHED;
5264}
5265
5267{
5268 static const EnumPropertyItem poke_center_modes[] = {
5270 "MEDIAN_WEIGHTED",
5271 0,
5272 "Weighted Median",
5273 "Weighted median face center"},
5274 {BMOP_POKE_MEDIAN, "MEDIAN", 0, "Median", "Median face center"},
5275 {BMOP_POKE_BOUNDS, "BOUNDS", 0, "Bounds", "Face bounds center"},
5276 {0, nullptr, 0, nullptr, nullptr},
5277 };
5278
5279 /* identifiers */
5280 ot->name = "Poke Faces";
5281 ot->idname = "MESH_OT_poke";
5282 ot->description = "Split a face into a fan";
5283
5284 /* API callbacks. */
5285 ot->exec = edbm_poke_face_exec;
5286 ot->poll = ED_operator_editmesh;
5287
5288 /* flags */
5289 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5290
5292 ot->srna, "offset", 0.0f, -1e3f, 1e3f, "Poke Offset", "Poke Offset", -1.0f, 1.0f);
5293 RNA_def_boolean(ot->srna,
5294 "use_relative_offset",
5295 false,
5296 "Offset Relative",
5297 "Scale the offset by surrounding geometry");
5298 RNA_def_enum(ot->srna,
5299 "center_mode",
5300 poke_center_modes,
5302 "Poke Center",
5303 "Poke face center calculation");
5304}
5305
5307
5308/* -------------------------------------------------------------------- */
5311
5313{
5314 const int quad_method = RNA_enum_get(op->ptr, "quad_method");
5315 const int ngon_method = RNA_enum_get(op->ptr, "ngon_method");
5316 const Scene *scene = CTX_data_scene(C);
5317 ViewLayer *view_layer = CTX_data_view_layer(C);
5318
5320 scene, view_layer, CTX_wm_view3d(C));
5321 for (Object *obedit : objects) {
5323
5324 if (em->bm->totfacesel == 0) {
5325 continue;
5326 }
5327
5328 BMOperator bmop;
5329 BMOIter oiter;
5330 BMFace *f;
5331
5333
5334 EDBM_op_init(em,
5335 &bmop,
5336 op,
5337 "triangulate faces=%hf quad_method=%i ngon_method=%i",
5339 quad_method,
5340 ngon_method);
5341 BMO_op_exec(em->bm, &bmop);
5342
5343 /* select the output */
5345 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
5346
5347 /* remove the doubles */
5348 BMO_ITER (f, &oiter, bmop.slots_out, "face_map_double.out", BM_FACE) {
5349 BM_face_kill(em->bm, f);
5350 }
5351
5353
5354 if (!EDBM_op_finish(em, &bmop, op, true)) {
5355 continue;
5356 }
5357
5359
5361 params.calc_looptris = true;
5362 params.calc_normals = false;
5363 params.is_destructive = true;
5364 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5365 }
5366
5367 return OPERATOR_FINISHED;
5368}
5369
5371{
5372 /* identifiers */
5373 ot->name = "Triangulate Faces";
5374 ot->idname = "MESH_OT_quads_convert_to_tris";
5375 ot->description = "Triangulate selected faces";
5376
5377 /* API callbacks. */
5379 ot->poll = ED_operator_editmesh;
5380
5381 /* flags */
5382 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5383
5384 RNA_def_enum(ot->srna,
5385 "quad_method",
5388 "Quad Method",
5389 "Method for splitting the quads into triangles");
5390 RNA_def_enum(ot->srna,
5391 "ngon_method",
5394 "N-gon Method",
5395 "Method for splitting the n-gons into triangles");
5396}
5397
5399
5400/* -------------------------------------------------------------------- */
5403
5404#if 0 /* See comments at top of bmo_join_triangles.cc */
5405# define USE_JOIN_TRIANGLE_TESTING_API
5406#endif
5407
5409{
5410 const Scene *scene = CTX_data_scene(C);
5411 ViewLayer *view_layer = CTX_data_view_layer(C);
5412
5414 scene, view_layer, CTX_wm_view3d(C));
5415
5416 const bool do_seam = RNA_boolean_get(op->ptr, "seam");
5417 const bool do_sharp = RNA_boolean_get(op->ptr, "sharp");
5418 const bool do_uvs = RNA_boolean_get(op->ptr, "uvs");
5419 const bool do_vcols = RNA_boolean_get(op->ptr, "vcols");
5420 const bool do_materials = RNA_boolean_get(op->ptr, "materials");
5421
5422#ifdef USE_JOIN_TRIANGLE_TESTING_API
5423 int merge_limit = RNA_int_get(op->ptr, "merge_limit");
5424 int neighbor_debug = RNA_int_get(op->ptr, "neighbor_debug");
5425#endif
5426
5427 const float topology_influence = RNA_float_get(op->ptr, "topology_influence");
5428 const bool deselect_joined = RNA_boolean_get(op->ptr, "deselect_joined");
5429
5430 float angle_face_threshold, angle_shape_threshold;
5431 bool is_face_pair;
5432 {
5433 int totelem_sel[3];
5434 EDBM_mesh_stats_multi(objects, nullptr, totelem_sel);
5435 is_face_pair = (totelem_sel[2] == 2);
5436 }
5437
5438 /* When joining exactly 2 faces, no limit.
5439 * this is useful for one off joins while editing. */
5440 {
5441 PropertyRNA *prop;
5442 prop = RNA_struct_find_property(op->ptr, "face_threshold");
5443 if (is_face_pair && (RNA_property_is_set(op->ptr, prop) == false)) {
5444 angle_face_threshold = DEG2RADF(180.0f);
5445 }
5446 else {
5447 angle_face_threshold = RNA_property_float_get(op->ptr, prop);
5448 }
5449
5450 prop = RNA_struct_find_property(op->ptr, "shape_threshold");
5451 if (is_face_pair && (RNA_property_is_set(op->ptr, prop) == false)) {
5452 angle_shape_threshold = DEG2RADF(180.0f);
5453 }
5454 else {
5455 angle_shape_threshold = RNA_property_float_get(op->ptr, prop);
5456 }
5457 }
5458
5459 for (Object *obedit : objects) {
5461
5462 if (em->bm->totfacesel == 0) {
5463 continue;
5464 }
5465
5467
5468 bool extend_selection = (deselect_joined == false);
5469
5470#ifdef USE_JOIN_TRIANGLE_TESTING_API
5471 if (merge_limit != -1) {
5472 extend_selection = false;
5473 }
5474#endif
5475
5477 em,
5478 op,
5479 "faces.out",
5480 extend_selection,
5481 "join_triangles faces=%hf angle_face_threshold=%f angle_shape_threshold=%f "
5482 "cmp_seam=%b cmp_sharp=%b cmp_uvs=%b cmp_vcols=%b cmp_materials=%b "
5483#ifdef USE_JOIN_TRIANGLE_TESTING_API
5484 "merge_limit=%i neighbor_debug=%i "
5485#endif
5486 "topology_influence=%f deselect_joined=%b",
5488 angle_face_threshold,
5489 angle_shape_threshold,
5490 do_seam,
5491 do_sharp,
5492 do_uvs,
5493 do_vcols,
5494 do_materials,
5495#ifdef USE_JOIN_TRIANGLE_TESTING_API
5496 merge_limit + 1,
5497 neighbor_debug,
5498#endif
5499 topology_influence,
5500 deselect_joined))
5501 {
5502 continue;
5503 }
5504
5505 if (deselect_joined) {
5506 /* When de-selecting faces outside of face mode:
5507 * failing to flush would leave an invalid selection. */
5510 }
5511 }
5512
5514
5516 params.calc_looptris = true;
5517 params.calc_normals = false;
5518 params.is_destructive = true;
5519 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5520 }
5521
5522 return OPERATOR_FINISHED;
5523}
5524
5526{
5527 PropertyRNA *prop;
5528
5529#ifdef USE_JOIN_TRIANGLE_TESTING_API
5530 prop = RNA_def_int(ot->srna,
5531 "merge_limit",
5532 0,
5533 -1,
5534 INT_MAX,
5535 "Merge Limit",
5536 "Maximum number of merges",
5537 -1,
5538 INT_MAX);
5540
5541 prop = RNA_def_int(ot->srna,
5542 "neighbor_debug",
5543 0,
5544 0,
5545 INT_MAX,
5546 "Neighbor Debug",
5547 "Neighbor to highlight",
5548 0,
5549 INT_MAX);
5551#endif
5552
5553 prop = RNA_def_float_rotation(ot->srna,
5554 "face_threshold",
5555 0,
5556 nullptr,
5557 0.0f,
5558 DEG2RADF(180.0f),
5559 "Max Face Angle",
5560 "Face angle limit",
5561 0.0f,
5562 DEG2RADF(180.0f));
5564
5565 prop = RNA_def_float_rotation(ot->srna,
5566 "shape_threshold",
5567 0,
5568 nullptr,
5569 0.0f,
5570 DEG2RADF(180.0f),
5571 "Max Shape Angle",
5572 "Shape angle limit",
5573 0.0f,
5574 DEG2RADF(180.0f));
5576
5577 prop = RNA_def_float_factor(ot->srna,
5578 "topology_influence",
5579 0.0f,
5580 0.0f,
5581 2.0f,
5582 "Topology Influence",
5583 "How much to prioritize regular grids of quads as well as "
5584 "quads that touch existing quads",
5585 0.0f,
5586 2.0f);
5587
5588 RNA_def_boolean(ot->srna, "uvs", false, "Compare UVs", "");
5589 RNA_def_boolean(ot->srna, "vcols", false, "Compare Color Attributes", "");
5590 RNA_def_boolean(ot->srna, "seam", false, "Compare Seam", "");
5591 RNA_def_boolean(ot->srna, "sharp", false, "Compare Sharp", "");
5592 RNA_def_boolean(ot->srna, "materials", false, "Compare Materials", "");
5593
5594 RNA_def_boolean(ot->srna,
5595 "deselect_joined",
5596 false,
5597 "Deselect Joined",
5598 "Only select remaining triangles that were not merged");
5599}
5600
5602{
5603 /* identifiers */
5604 ot->name = "Triangles to Quads";
5605 ot->idname = "MESH_OT_tris_convert_to_quads";
5606 ot->description = "Merge triangles into four sided polygons where possible";
5607
5608 /* API callbacks. */
5610 ot->poll = ED_operator_editmesh;
5611
5612 /* flags */
5613 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5614
5616}
5617
5619
5620/* -------------------------------------------------------------------- */
5628
5630{
5631 const float ratio = RNA_float_get(op->ptr, "ratio");
5632 bool use_vertex_group = RNA_boolean_get(op->ptr, "use_vertex_group");
5633 const float vertex_group_factor = RNA_float_get(op->ptr, "vertex_group_factor");
5634 const bool invert_vertex_group = RNA_boolean_get(op->ptr, "invert_vertex_group");
5635 const bool use_symmetry = RNA_boolean_get(op->ptr, "use_symmetry");
5636 const float symmetry_eps = 0.00002f;
5637 const int symmetry_axis = use_symmetry ? RNA_enum_get(op->ptr, "symmetry_axis") : -1;
5638
5639 /* nop */
5640 if (ratio == 1.0f) {
5641 return OPERATOR_FINISHED;
5642 }
5643
5644 const Scene *scene = CTX_data_scene(C);
5645 ViewLayer *view_layer = CTX_data_view_layer(C);
5647 scene, view_layer, CTX_wm_view3d(C));
5648
5649 for (Object *obedit : objects) {
5651 BMesh *bm = em->bm;
5652 if (bm->totedgesel == 0) {
5653 continue;
5654 }
5655
5656 float *vweights = MEM_malloc_arrayN<float>(bm->totvert, __func__);
5657 {
5658 const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
5659 const int defbase_act = BKE_object_defgroup_active_index_get(obedit) - 1;
5660
5661 if (use_vertex_group && (cd_dvert_offset == -1)) {
5662 BKE_report(op->reports, RPT_WARNING, "No active vertex group");
5663 use_vertex_group = false;
5664 }
5665
5666 BMIter iter;
5667 BMVert *v;
5668 int i;
5670 float weight = 0.0f;
5672 if (use_vertex_group) {
5673 const MDeformVert *dv = static_cast<const MDeformVert *>(
5674 BM_ELEM_CD_GET_VOID_P(v, cd_dvert_offset));
5675 weight = BKE_defvert_find_weight(dv, defbase_act);
5676 if (invert_vertex_group) {
5677 weight = 1.0f - weight;
5678 }
5679 }
5680 else {
5681 weight = 1.0f;
5682 }
5683 }
5684
5685 vweights[i] = weight;
5686 BM_elem_index_set(v, i); /* set_inline */
5687 }
5688 bm->elem_index_dirty &= ~BM_VERT;
5689 }
5690
5691 float ratio_adjust;
5692
5693 if ((bm->totface == bm->totfacesel) || (ratio == 0.0f)) {
5694 ratio_adjust = ratio;
5695 }
5696 else {
5704
5705 int totface_basis = 0;
5706 int totface_adjacent = 0;
5707 BMIter iter;
5708 BMFace *f;
5709 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
5710 /* count faces during decimation, ngons are triangulated */
5711 const int f_len = f->len > 4 ? (f->len - 2) : 1;
5712 totface_basis += f_len;
5713
5714 BMLoop *l_iter, *l_first;
5715 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
5716 do {
5717 if (vweights[BM_elem_index_get(l_iter->v)] != 0.0f) {
5718 totface_adjacent += f_len;
5719 break;
5720 }
5721 } while ((l_iter = l_iter->next) != l_first);
5722 }
5723
5724 ratio_adjust = ratio;
5725 ratio_adjust = 1.0f - ratio_adjust;
5726 ratio_adjust *= float(totface_adjacent) / float(totface_basis);
5727 ratio_adjust = 1.0f - ratio_adjust;
5728 }
5729
5731 em->bm, ratio_adjust, vweights, vertex_group_factor, false, symmetry_axis, symmetry_eps);
5732
5733 MEM_freeN(vweights);
5734
5735 {
5736 short selectmode = em->selectmode;
5737 if ((selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) == 0) {
5738 /* ensure we flush edges -> faces */
5739 selectmode |= SCE_SELECT_EDGE;
5740 }
5741 EDBM_selectmode_flush_ex(em, selectmode);
5742 }
5744 params.calc_looptris = true;
5745 params.calc_normals = true;
5746 params.is_destructive = true;
5747 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5748 }
5749
5750 return OPERATOR_FINISHED;
5751}
5752
5753static bool edbm_decimate_check(bContext * /*C*/, wmOperator * /*op*/)
5754{
5755 return true;
5756}
5757
5758static void edbm_decimate_ui(bContext * /*C*/, wmOperator *op)
5759{
5760 uiLayout *layout = op->layout, *row, *col, *sub;
5761
5762 uiLayoutSetPropSep(layout, true);
5763
5764 layout->prop(op->ptr, "ratio", UI_ITEM_NONE, std::nullopt, ICON_NONE);
5765
5766 layout->prop(op->ptr, "use_vertex_group", UI_ITEM_NONE, std::nullopt, ICON_NONE);
5767 col = &layout->column(false);
5768 uiLayoutSetActive(col, RNA_boolean_get(op->ptr, "use_vertex_group"));
5769 col->prop(op->ptr, "vertex_group_factor", UI_ITEM_NONE, std::nullopt, ICON_NONE);
5770 col->prop(op->ptr, "invert_vertex_group", UI_ITEM_NONE, std::nullopt, ICON_NONE);
5771
5772 row = &layout->row(true, IFACE_("Symmetry"));
5773 row->prop(op->ptr, "use_symmetry", UI_ITEM_NONE, "", ICON_NONE);
5774 sub = &row->row(true);
5775 uiLayoutSetActive(sub, RNA_boolean_get(op->ptr, "use_symmetry"));
5776 sub->prop(op->ptr, "symmetry_axis", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
5777}
5778
5780{
5781 /* identifiers */
5782 ot->name = "Decimate Geometry";
5783 ot->idname = "MESH_OT_decimate";
5784 ot->description = "Simplify geometry by collapsing edges";
5785
5786 /* API callbacks. */
5787 ot->exec = edbm_decimate_exec;
5788 ot->check = edbm_decimate_check;
5789 ot->ui = edbm_decimate_ui;
5790 ot->poll = ED_operator_editmesh;
5791
5792 /* flags */
5793 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5794
5795 /* NOTE: keep in sync with 'rna_def_modifier_decimate'. */
5796 RNA_def_float(ot->srna, "ratio", 1.0f, 0.0f, 1.0f, "Ratio", "", 0.0f, 1.0f);
5797
5798 RNA_def_boolean(ot->srna,
5799 "use_vertex_group",
5800 false,
5801 "Vertex Group",
5802 "Use active vertex group as an influence");
5803 RNA_def_float(ot->srna,
5804 "vertex_group_factor",
5805 1.0f,
5806 0.0f,
5807 1000.0f,
5808 "Weight",
5809 "Vertex group strength",
5810 0.0f,
5811 10.0f);
5813 ot->srna, "invert_vertex_group", false, "Invert", "Invert vertex group influence");
5814
5815 RNA_def_boolean(ot->srna, "use_symmetry", false, "Symmetry", "Maintain symmetry on an axis");
5816
5817 RNA_def_enum(ot->srna, "symmetry_axis", rna_enum_axis_xyz_items, 1, "Axis", "Axis of symmetry");
5818}
5819
5821
5822/* -------------------------------------------------------------------- */
5825
5827{
5828 PropertyRNA *prop;
5829
5830 prop = RNA_def_boolean(ot->srna,
5831 "use_verts",
5832 value,
5833 "Dissolve Vertices",
5834 "Dissolve remaining vertices which connect to only two edges");
5835
5836 if (flag) {
5838 }
5839}
5841{
5842 RNA_def_boolean(ot->srna,
5843 "use_face_split",
5844 false,
5845 "Face Split",
5846 "Split off face corners to maintain surrounding geometry");
5847}
5849{
5850 RNA_def_boolean(ot->srna,
5851 "use_boundary_tear",
5852 false,
5853 "Tear Boundary",
5854 "Split off face corners instead of merging faces");
5855}
5857{
5859 ot->srna,
5860 "angle_threshold",
5861 0,
5862 nullptr,
5863 0.0f,
5864 DEG2RADF(180.0f),
5865 "Angle Threshold",
5866 "Remaining vertices which separate edge pairs are preserved if their edge angle exceeds "
5867 "this threshold.",
5868 0.0f,
5869 DEG2RADF(180.0f));
5871 if (flag) {
5873 }
5874}
5875
5877{
5878 const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split");
5879 const bool use_boundary_tear = RNA_boolean_get(op->ptr, "use_boundary_tear");
5880
5881 const Scene *scene = CTX_data_scene(C);
5882 ViewLayer *view_layer = CTX_data_view_layer(C);
5884 scene, view_layer, CTX_wm_view3d(C));
5885
5886 for (Object *obedit : objects) {
5888
5889 if (em->bm->totvertsel == 0) {
5890 continue;
5891 }
5892
5894
5895 if (!EDBM_op_callf(em,
5896 op,
5897 "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b",
5899 use_face_split,
5900 use_boundary_tear))
5901 {
5902 continue;
5903 }
5904
5906
5908 params.calc_looptris = true;
5909 params.calc_normals = false;
5910 params.is_destructive = true;
5911 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5912 }
5913
5914 return OPERATOR_FINISHED;
5915}
5916
5918{
5919 /* identifiers */
5920 ot->name = "Dissolve Vertices";
5921 ot->description = "Dissolve vertices, merge edges and faces";
5922 ot->idname = "MESH_OT_dissolve_verts";
5923
5924 /* API callbacks. */
5926 ot->poll = ED_operator_editmesh;
5927
5928 /* flags */
5929 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5930
5933}
5934
5936
5937/* -------------------------------------------------------------------- */
5940
5942{
5943 const bool use_verts = RNA_boolean_get(op->ptr, "use_verts");
5944 const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split");
5945 const float angle_threshold = RNA_float_get(op->ptr, "angle_threshold");
5946
5947 const Scene *scene = CTX_data_scene(C);
5948 ViewLayer *view_layer = CTX_data_view_layer(C);
5950 scene, view_layer, CTX_wm_view3d(C));
5951 for (Object *obedit : objects) {
5953
5954 if (em->bm->totedgesel == 0) {
5955 continue;
5956 }
5957
5959
5960 if (!EDBM_op_callf(
5961 em,
5962 op,
5963 "dissolve_edges edges=%he use_verts=%b use_face_split=%b angle_threshold=%f",
5965 use_verts,
5966 use_face_split,
5967 angle_threshold))
5968 {
5969 continue;
5970 }
5971
5973
5975 params.calc_looptris = true;
5976 params.calc_normals = false;
5977 params.is_destructive = true;
5978 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5979 }
5980
5981 return OPERATOR_FINISHED;
5982}
5983
5985{
5986 /* identifiers */
5987 ot->name = "Dissolve Edges";
5988 ot->description = "Dissolve edges, merging faces";
5989 ot->idname = "MESH_OT_dissolve_edges";
5990
5991 /* API callbacks. */
5993 ot->poll = ED_operator_editmesh;
5994
5995 /* flags */
5996 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5997
6001}
6002
6004
6005/* -------------------------------------------------------------------- */
6008
6010{
6011 const bool use_verts = RNA_boolean_get(op->ptr, "use_verts");
6012 const Scene *scene = CTX_data_scene(C);
6013 ViewLayer *view_layer = CTX_data_view_layer(C);
6015 scene, view_layer, CTX_wm_view3d(C));
6016 for (Object *obedit : objects) {
6018
6019 if (em->bm->totfacesel == 0) {
6020 continue;
6021 }
6022
6024
6026 op,
6027 "region.out",
6028 true,
6029 "dissolve_faces faces=%hf use_verts=%b",
6031 use_verts))
6032 {
6033 continue;
6034 }
6035
6037
6039 params.calc_looptris = true;
6040 params.calc_normals = false;
6041 params.is_destructive = true;
6042 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
6043 }
6044
6045 return OPERATOR_FINISHED;
6046}
6047
6049{
6050 /* identifiers */
6051 ot->name = "Dissolve Faces";
6052 ot->description = "Dissolve faces";
6053 ot->idname = "MESH_OT_dissolve_faces";
6054
6055 /* API callbacks. */
6057 ot->poll = ED_operator_editmesh;
6058
6059 /* flags */
6060 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6061
6063}
6064
6066
6067/* -------------------------------------------------------------------- */
6070
6072{
6073 Object *obedit = CTX_data_edit_object(C);
6075 PropertyRNA *prop;
6076
6077 prop = RNA_struct_find_property(op->ptr, "use_verts");
6078 if (!RNA_property_is_set(op->ptr, prop)) {
6079 /* always enable in edge-mode */
6080 if ((em->selectmode & SCE_SELECT_FACE) == 0) {
6081 RNA_property_boolean_set(op->ptr, prop, true);
6082 }
6083 }
6084
6085 if (em->selectmode & SCE_SELECT_VERTEX) {
6086 return edbm_dissolve_verts_exec(C, op);
6087 }
6088 if (em->selectmode & SCE_SELECT_EDGE) {
6089 return edbm_dissolve_edges_exec(C, op);
6090 }
6091 return edbm_dissolve_faces_exec(C, op);
6092}
6093
6094static bool dissolve_mode_poll_property(const bContext *C, wmOperator *op, const PropertyRNA *prop)
6095{
6096 UNUSED_VARS(op);
6097
6098 const char *prop_id = RNA_property_identifier(prop);
6099
6100 Object *obedit = CTX_data_edit_object(C);
6101 const BMEditMesh *em = BKE_editmesh_from_object(obedit);
6102 bool is_edge_select_mode = false;
6103
6104 if (em->selectmode & SCE_SELECT_VERTEX) {
6105 /* Pass. */
6106 }
6107 if (em->selectmode & SCE_SELECT_EDGE) {
6108 is_edge_select_mode = true;
6109 }
6110
6111 if (!is_edge_select_mode) {
6112 /* Angle Threshold is only used in edge select mode. */
6113 if (STREQ(prop_id, "angle_threshold")) {
6114 return false;
6115 }
6116 }
6117 return true;
6118}
6119
6121{
6122 /* identifiers */
6123 ot->name = "Dissolve Selection";
6124 ot->description = "Dissolve geometry based on the selection mode";
6125 ot->idname = "MESH_OT_dissolve_mode";
6126
6127 /* API callbacks. */
6129 ot->poll = ED_operator_editmesh;
6130 ot->poll_property = dissolve_mode_poll_property;
6131
6132 /* flags */
6133 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6134
6139}
6140
6142
6143/* -------------------------------------------------------------------- */
6146
6148{
6149 const float angle_limit = RNA_float_get(op->ptr, "angle_limit");
6150 const bool use_dissolve_boundaries = RNA_boolean_get(op->ptr, "use_dissolve_boundaries");
6151 const int delimit = RNA_enum_get(op->ptr, "delimit");
6152 char dissolve_flag;
6153
6154 const Scene *scene = CTX_data_scene(C);
6155 ViewLayer *view_layer = CTX_data_view_layer(C);
6157 scene, view_layer, CTX_wm_view3d(C));
6158 for (Object *obedit : objects) {
6160 BMesh *bm = em->bm;
6161
6162 if ((bm->totvertsel == 0) && (bm->totedgesel == 0) && (bm->totfacesel == 0)) {
6163 continue;
6164 }
6165
6167
6168 if (em->selectmode == SCE_SELECT_FACE) {
6169 /* flush selection to tags and untag edges/verts with partially selected faces */
6170 BMIter iter;
6171 BMIter liter;
6172
6173 BMElem *ele;
6174 BMFace *f;
6175 BMLoop *l;
6176
6177 BM_ITER_MESH (ele, &iter, bm, BM_VERTS_OF_MESH) {
6179 }
6180 BM_ITER_MESH (ele, &iter, bm, BM_EDGES_OF_MESH) {
6182 }
6183
6184 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
6186 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
6189 }
6190 }
6191 }
6192
6193 dissolve_flag = BM_ELEM_TAG;
6194 }
6195 else {
6196 dissolve_flag = BM_ELEM_SELECT;
6197 }
6198
6200 em,
6201 op,
6202 "region.out",
6203 true,
6204 "dissolve_limit edges=%he verts=%hv angle_limit=%f use_dissolve_boundaries=%b delimit=%i",
6205 dissolve_flag,
6206 dissolve_flag,
6207 angle_limit,
6208 use_dissolve_boundaries,
6209 delimit);
6210
6212
6214 params.calc_looptris = true;
6215 params.calc_normals = false;
6216 params.is_destructive = true;
6217 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
6218 }
6219
6220 return OPERATOR_FINISHED;
6221}
6222
6224{
6225 PropertyRNA *prop;
6226
6227 /* identifiers */
6228 ot->name = "Limited Dissolve";
6229 ot->idname = "MESH_OT_dissolve_limited";
6230 ot->description =
6231 "Dissolve selected edges and vertices, limited by the angle of surrounding geometry";
6232
6233 /* API callbacks. */
6235 ot->poll = ED_operator_editmesh;
6236
6237 /* flags */
6238 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6239
6240 prop = RNA_def_float_rotation(ot->srna,
6241 "angle_limit",
6242 0,
6243 nullptr,
6244 0.0f,
6245 DEG2RADF(180.0f),
6246 "Max Angle",
6247 "Angle limit",
6248 0.0f,
6249 DEG2RADF(180.0f));
6251 RNA_def_boolean(ot->srna,
6252 "use_dissolve_boundaries",
6253 false,
6254 "All Boundaries",
6255 "Dissolve all vertices in between face boundaries");
6256 RNA_def_enum_flag(ot->srna,
6257 "delimit",
6260 "Delimit",
6261 "Delimit dissolve operation");
6262}
6263
6265
6266/* -------------------------------------------------------------------- */
6269
6271{
6272 const Scene *scene = CTX_data_scene(C);
6273 ViewLayer *view_layer = CTX_data_view_layer(C);
6274 int totelem_old[3] = {0, 0, 0};
6275 int totelem_new[3] = {0, 0, 0};
6276
6278 scene, view_layer, CTX_wm_view3d(C));
6279
6280 for (Object *obedit : objects) {
6282 BMesh *bm = em->bm;
6283 totelem_old[0] += bm->totvert;
6284 totelem_old[1] += bm->totedge;
6285 totelem_old[2] += bm->totface;
6286 } /* objects */
6287
6288 const float thresh = RNA_float_get(op->ptr, "threshold");
6289
6290 for (Object *obedit : objects) {
6292 BMesh *bm = em->bm;
6293
6294 if (!EDBM_op_callf(em, op, "dissolve_degenerate edges=%he dist=%f", BM_ELEM_SELECT, thresh)) {
6295 continue;
6296 }
6297
6298 /* tricky to maintain correct selection here, so just flush up from verts */
6300
6302 params.calc_looptris = true;
6303 params.calc_normals = false;
6304 params.is_destructive = true;
6305 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
6306
6307 totelem_new[0] += bm->totvert;
6308 totelem_new[1] += bm->totedge;
6309 totelem_new[2] += bm->totface;
6310 }
6311
6312 edbm_report_delete_info(op->reports, totelem_old, totelem_new);
6313
6314 return OPERATOR_FINISHED;
6315}
6316
6318{
6319 /* identifiers */
6320 ot->name = "Degenerate Dissolve";
6321 ot->idname = "MESH_OT_dissolve_degenerate";
6322 ot->description = "Dissolve zero area faces and zero length edges";
6323
6324 /* API callbacks. */
6326 ot->poll = ED_operator_editmesh;
6327
6328 /* flags */
6329 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6330
6332 "threshold",
6333 1e-4f,
6334 1e-6f,
6335 50.0f,
6336 "Merge Distance",
6337 "Maximum distance between elements to merge",
6338 1e-5f,
6339 10.0f);
6340}
6341
6343
6344/* -------------------------------------------------------------------- */
6347
6348/* internally uses dissolve */
6350{
6351 const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split");
6352 const Scene *scene = CTX_data_scene(C);
6353 ViewLayer *view_layer = CTX_data_view_layer(C);
6354
6356 scene, view_layer, CTX_wm_view3d(C));
6357 for (Object *obedit : objects) {
6359
6360 if (em->bm->totedgesel == 0) {
6361 continue;
6362 }
6363
6364 /* deal with selection */
6365 {
6366 BMEdge *e;
6367 BMIter iter;
6368
6370
6371 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
6372 if (BM_elem_flag_test(e, BM_ELEM_SELECT) && e->l) {
6373 BMLoop *l_iter = e->l;
6374 do {
6376 } while ((l_iter = l_iter->radial_next) != e->l);
6377 }
6378 }
6379 }
6380
6381 if (!EDBM_op_callf(
6382 em,
6383 op,
6384 "dissolve_edges edges=%he use_verts=%b use_face_split=%b angle_threshold=%f",
6386 true,
6387 use_face_split,
6388 M_PI))
6389 {
6390 continue;
6391 }
6392
6394
6396
6398 params.calc_looptris = true;
6399 params.calc_normals = false;
6400 params.is_destructive = true;
6401 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
6402 }
6403
6404 return OPERATOR_FINISHED;
6405}
6406
6408{
6409 /* identifiers */
6410 ot->name = "Delete Edge Loop";
6411 ot->description = "Delete an edge loop by merging the faces on each side";
6412 ot->idname = "MESH_OT_delete_edgeloop";
6413
6414 /* API callbacks. */
6416 ot->poll = ED_operator_editmesh;
6417
6418 /* flags */
6419 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6420
6421 RNA_def_boolean(ot->srna,
6422 "use_face_split",
6423 true,
6424 "Face Split",
6425 "Split off face corners to maintain surrounding geometry");
6426}
6427
6429
6430/* -------------------------------------------------------------------- */
6433
6435{
6436 const Scene *scene = CTX_data_scene(C);
6437 ViewLayer *view_layer = CTX_data_view_layer(C);
6439 scene, view_layer, CTX_wm_view3d(C));
6440 for (Object *obedit : objects) {
6442 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
6443 continue;
6444 }
6446
6447 BMOperator bmop;
6448 EDBM_op_init(em, &bmop, op, "split geom=%hvef use_only_faces=%b", BM_ELEM_SELECT, false);
6449 BMO_op_exec(em->bm, &bmop);
6452 em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
6453
6455
6456 if (!EDBM_op_finish(em, &bmop, op, true)) {
6457 continue;
6458 }
6459
6460 /* Geometry has changed, need to recalculate normals and tessellation. */
6462 params.calc_looptris = true;
6463 params.calc_normals = true;
6464 params.is_destructive = true;
6465 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
6466 }
6467
6468 return OPERATOR_FINISHED;
6469}
6470
6472{
6473 /* identifiers */
6474 ot->name = "Split";
6475 ot->idname = "MESH_OT_split";
6476 ot->description = "Split off selected geometry from connected unselected geometry";
6477
6478 /* API callbacks. */
6479 ot->exec = edbm_split_exec;
6480 ot->poll = ED_operator_editmesh;
6481
6482 /* flags */
6483 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6484}
6485
6487
6488/* -------------------------------------------------------------------- */
6494
6495enum {
6511};
6512
6515 float srt;
6518};
6519
6520static int bmelemsort_comp(const void *v1, const void *v2)
6521{
6522 const BMElemSort *x1 = static_cast<const BMElemSort *>(v1);
6523 const BMElemSort *x2 = static_cast<const BMElemSort *>(v2);
6524
6525 return (x1->srt > x2->srt) - (x1->srt < x2->srt);
6526}
6527
6528/* Reorders vertices/edges/faces using a given methods. Loops are not supported. */
6530 Scene *scene,
6531 Object *ob,
6532 RegionView3D *rv3d,
6533 const int types,
6534 const int flag,
6535 const int action,
6536 const int reverse,
6537 const uint seed)
6538{
6540
6541 BMVert *ve;
6542 BMEdge *ed;
6543 BMFace *fa;
6544 BMIter iter;
6545
6546 /* In all five elements below, 0 = vertices, 1 = edges, 2 = faces. */
6547 /* Just to mark protected elements. */
6548 char *pblock[3] = {nullptr, nullptr, nullptr}, *pb;
6549 BMElemSort *sblock[3] = {nullptr, nullptr, nullptr}, *sb;
6550 uint *map[3] = {nullptr, nullptr, nullptr}, *mp;
6551 int totelem[3] = {0, 0, 0};
6552 int affected[3] = {0, 0, 0};
6553 int i, j;
6554
6555 if (!(types && flag && action)) {
6556 return;
6557 }
6558
6559 if (types & BM_VERT) {
6560 totelem[0] = em->bm->totvert;
6561 }
6562 if (types & BM_EDGE) {
6563 totelem[1] = em->bm->totedge;
6564 }
6565 if (types & BM_FACE) {
6566 totelem[2] = em->bm->totface;
6567 }
6568
6569 if (ELEM(action, SRT_VIEW_ZAXIS, SRT_VIEW_XAXIS)) {
6570 float mat[4][4];
6571 float fact = reverse ? -1.0 : 1.0;
6572 int coidx = (action == SRT_VIEW_ZAXIS) ? 2 : 0;
6573
6574 /* Apply the view matrix to the object matrix. */
6575 mul_m4_m4m4(mat, rv3d->viewmat, ob->object_to_world().ptr());
6576
6577 if (totelem[0]) {
6578 pb = pblock[0] = MEM_calloc_arrayN<char>(totelem[0], __func__);
6579 sb = sblock[0] = MEM_calloc_arrayN<BMElemSort>(totelem[0], __func__);
6580
6581 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6582 if (BM_elem_flag_test(ve, flag)) {
6583 float co[3];
6584 mul_v3_m4v3(co, mat, ve->co);
6585
6586 pb[i] = false;
6587 sb[affected[0]].org_idx = i;
6588 sb[affected[0]++].srt = co[coidx] * fact;
6589 }
6590 else {
6591 pb[i] = true;
6592 }
6593 }
6594 }
6595
6596 if (totelem[1]) {
6597 pb = pblock[1] = MEM_calloc_arrayN<char>(totelem[1], __func__);
6598 sb = sblock[1] = MEM_calloc_arrayN<BMElemSort>(totelem[1], __func__);
6599
6600 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6601 if (BM_elem_flag_test(ed, flag)) {
6602 float co[3];
6603 mid_v3_v3v3(co, ed->v1->co, ed->v2->co);
6604 mul_m4_v3(mat, co);
6605
6606 pb[i] = false;
6607 sb[affected[1]].org_idx = i;
6608 sb[affected[1]++].srt = co[coidx] * fact;
6609 }
6610 else {
6611 pb[i] = true;
6612 }
6613 }
6614 }
6615
6616 if (totelem[2]) {
6617 pb = pblock[2] = MEM_calloc_arrayN<char>(totelem[2], __func__);
6618 sb = sblock[2] = MEM_calloc_arrayN<BMElemSort>(totelem[2], __func__);
6619
6620 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6621 if (BM_elem_flag_test(fa, flag)) {
6622 float co[3];
6624 mul_m4_v3(mat, co);
6625
6626 pb[i] = false;
6627 sb[affected[2]].org_idx = i;
6628 sb[affected[2]++].srt = co[coidx] * fact;
6629 }
6630 else {
6631 pb[i] = true;
6632 }
6633 }
6634 }
6635 }
6636
6637 else if (action == SRT_CURSOR_DISTANCE) {
6638 float cur[3];
6639 float mat[4][4];
6640 float fact = reverse ? -1.0 : 1.0;
6641
6642 copy_v3_v3(cur, scene->cursor.location);
6643
6644 invert_m4_m4(mat, ob->object_to_world().ptr());
6645 mul_m4_v3(mat, cur);
6646
6647 if (totelem[0]) {
6648 pb = pblock[0] = MEM_calloc_arrayN<char>(totelem[0], __func__);
6649 sb = sblock[0] = MEM_calloc_arrayN<BMElemSort>(totelem[0], __func__);
6650
6651 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6652 if (BM_elem_flag_test(ve, flag)) {
6653 pb[i] = false;
6654 sb[affected[0]].org_idx = i;
6655 sb[affected[0]++].srt = len_squared_v3v3(cur, ve->co) * fact;
6656 }
6657 else {
6658 pb[i] = true;
6659 }
6660 }
6661 }
6662
6663 if (totelem[1]) {
6664 pb = pblock[1] = MEM_calloc_arrayN<char>(totelem[1], __func__);
6665 sb = sblock[1] = MEM_calloc_arrayN<BMElemSort>(totelem[1], __func__);
6666
6667 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6668 if (BM_elem_flag_test(ed, flag)) {
6669 float co[3];
6670 mid_v3_v3v3(co, ed->v1->co, ed->v2->co);
6671
6672 pb[i] = false;
6673 sb[affected[1]].org_idx = i;
6674 sb[affected[1]++].srt = len_squared_v3v3(cur, co) * fact;
6675 }
6676 else {
6677 pb[i] = true;
6678 }
6679 }
6680 }
6681
6682 if (totelem[2]) {
6683 pb = pblock[2] = MEM_calloc_arrayN<char>(totelem[2], __func__);
6684 sb = sblock[2] = MEM_calloc_arrayN<BMElemSort>(totelem[2], __func__);
6685
6686 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6687 if (BM_elem_flag_test(fa, flag)) {
6688 float co[3];
6690
6691 pb[i] = false;
6692 sb[affected[2]].org_idx = i;
6693 sb[affected[2]++].srt = len_squared_v3v3(cur, co) * fact;
6694 }
6695 else {
6696 pb[i] = true;
6697 }
6698 }
6699 }
6700 }
6701
6702 /* Faces only! */
6703 else if (action == SRT_MATERIAL && totelem[2]) {
6704 pb = pblock[2] = MEM_calloc_arrayN<char>(totelem[2], __func__);
6705 sb = sblock[2] = MEM_calloc_arrayN<BMElemSort>(totelem[2], __func__);
6706
6707 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6708 if (BM_elem_flag_test(fa, flag)) {
6709 /* Reverse materials' order, not order of faces inside each mat! */
6710 /* NOTE: cannot use totcol, as mat_nr may sometimes be greater... */
6711 float srt = reverse ? float(MAXMAT - fa->mat_nr) : float(fa->mat_nr);
6712 pb[i] = false;
6713 sb[affected[2]].org_idx = i;
6714 /* Multiplying with totface and adding i ensures us
6715 * we keep current order for all faces of same mat. */
6716 sb[affected[2]++].srt = srt * float(totelem[2]) + float(i);
6717 // printf("e: %d; srt: %f; final: %f\n", i, srt, srt * float(totface) + float(i));
6718 }
6719 else {
6720 pb[i] = true;
6721 }
6722 }
6723 }
6724
6725 else if (action == SRT_SELECTED) {
6726 uint *tbuf[3] = {nullptr, nullptr, nullptr}, *tb;
6727
6728 if (totelem[0]) {
6729 tb = tbuf[0] = MEM_calloc_arrayN<uint>(totelem[0], __func__);
6730 mp = map[0] = MEM_calloc_arrayN<uint>(totelem[0], __func__);
6731
6732 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6733 if (BM_elem_flag_test(ve, flag)) {
6734 mp[affected[0]++] = i;
6735 }
6736 else {
6737 *tb = i;
6738 tb++;
6739 }
6740 }
6741 }
6742
6743 if (totelem[1]) {
6744 tb = tbuf[1] = MEM_calloc_arrayN<uint>(totelem[1], __func__);
6745 mp = map[1] = MEM_calloc_arrayN<uint>(totelem[1], __func__);
6746
6747 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6748 if (BM_elem_flag_test(ed, flag)) {
6749 mp[affected[1]++] = i;
6750 }
6751 else {
6752 *tb = i;
6753 tb++;
6754 }
6755 }
6756 }
6757
6758 if (totelem[2]) {
6759 tb = tbuf[2] = MEM_calloc_arrayN<uint>(totelem[2], __func__);
6760 mp = map[2] = MEM_calloc_arrayN<uint>(totelem[2], __func__);
6761
6762 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6763 if (BM_elem_flag_test(fa, flag)) {
6764 mp[affected[2]++] = i;
6765 }
6766 else {
6767 *tb = i;
6768 tb++;
6769 }
6770 }
6771 }
6772
6773 for (j = 3; j--;) {
6774 int tot = totelem[j];
6775 int aff = affected[j];
6776 tb = tbuf[j];
6777 mp = map[j];
6778 if (!(tb && mp)) {
6779 continue;
6780 }
6781 if (ELEM(aff, 0, tot)) {
6782 MEM_freeN(tb);
6783 MEM_freeN(mp);
6784 map[j] = nullptr;
6785 continue;
6786 }
6787 if (reverse) {
6788 memcpy(tb + (tot - aff), mp, aff * sizeof(int));
6789 }
6790 else {
6791 memcpy(mp + aff, tb, (tot - aff) * sizeof(int));
6792 tb = mp;
6793 mp = map[j] = tbuf[j];
6794 tbuf[j] = tb;
6795 }
6796
6797 /* Reverse mapping, we want an org2new one! */
6798 for (i = tot, tb = tbuf[j] + tot - 1; i--; tb--) {
6799 mp[*tb] = i;
6800 }
6801 MEM_freeN(tbuf[j]);
6802 }
6803 }
6804
6805 else if (action == SRT_RANDOMIZE) {
6806 if (totelem[0]) {
6807 /* Re-init random generator for each element type, to get consistent random when
6808 * enabling/disabling an element type. */
6810 pb = pblock[0] = MEM_calloc_arrayN<char>(totelem[0], __func__);
6811 sb = sblock[0] = MEM_calloc_arrayN<BMElemSort>(totelem[0], __func__);
6812
6813 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6814 if (BM_elem_flag_test(ve, flag)) {
6815 pb[i] = false;
6816 sb[affected[0]].org_idx = i;
6817 sb[affected[0]++].srt = BLI_rng_get_float(rng);
6818 }
6819 else {
6820 pb[i] = true;
6821 }
6822 }
6823
6824 BLI_rng_free(rng);
6825 }
6826
6827 if (totelem[1]) {
6829 pb = pblock[1] = MEM_calloc_arrayN<char>(totelem[1], __func__);
6830 sb = sblock[1] = MEM_calloc_arrayN<BMElemSort>(totelem[1], __func__);
6831
6832 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6833 if (BM_elem_flag_test(ed, flag)) {
6834 pb[i] = false;
6835 sb[affected[1]].org_idx = i;
6836 sb[affected[1]++].srt = BLI_rng_get_float(rng);
6837 }
6838 else {
6839 pb[i] = true;
6840 }
6841 }
6842
6843 BLI_rng_free(rng);
6844 }
6845
6846 if (totelem[2]) {
6848 pb = pblock[2] = MEM_calloc_arrayN<char>(totelem[2], __func__);
6849 sb = sblock[2] = MEM_calloc_arrayN<BMElemSort>(totelem[2], __func__);
6850
6851 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6852 if (BM_elem_flag_test(fa, flag)) {
6853 pb[i] = false;
6854 sb[affected[2]].org_idx = i;
6855 sb[affected[2]++].srt = BLI_rng_get_float(rng);
6856 }
6857 else {
6858 pb[i] = true;
6859 }
6860 }
6861
6862 BLI_rng_free(rng);
6863 }
6864 }
6865
6866 else if (action == SRT_REVERSE) {
6867 if (totelem[0]) {
6868 pb = pblock[0] = MEM_calloc_arrayN<char>(totelem[0], __func__);
6869 sb = sblock[0] = MEM_calloc_arrayN<BMElemSort>(totelem[0], __func__);
6870
6871 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6872 if (BM_elem_flag_test(ve, flag)) {
6873 pb[i] = false;
6874 sb[affected[0]].org_idx = i;
6875 sb[affected[0]++].srt = float(-i);
6876 }
6877 else {
6878 pb[i] = true;
6879 }
6880 }
6881 }
6882
6883 if (totelem[1]) {
6884 pb = pblock[1] = MEM_calloc_arrayN<char>(totelem[1], __func__);
6885 sb = sblock[1] = MEM_calloc_arrayN<BMElemSort>(totelem[1], __func__);
6886
6887 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6888 if (BM_elem_flag_test(ed, flag)) {
6889 pb[i] = false;
6890 sb[affected[1]].org_idx = i;
6891 sb[affected[1]++].srt = float(-i);
6892 }
6893 else {
6894 pb[i] = true;
6895 }
6896 }
6897 }
6898
6899 if (totelem[2]) {
6900 pb = pblock[2] = MEM_calloc_arrayN<char>(totelem[2], __func__);
6901 sb = sblock[2] = MEM_calloc_arrayN<BMElemSort>(totelem[2], __func__);
6902
6903 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6904 if (BM_elem_flag_test(fa, flag)) {
6905 pb[i] = false;
6906 sb[affected[2]].org_idx = i;
6907 sb[affected[2]++].srt = float(-i);
6908 }
6909 else {
6910 pb[i] = true;
6911 }
6912 }
6913 }
6914 }
6915
6916 // printf("%d vertices: %d to be affected...\n", totelem[0], affected[0]);
6917 // printf("%d edges: %d to be affected...\n", totelem[1], affected[1]);
6918 // printf("%d faces: %d to be affected...\n", totelem[2], affected[2]);
6919 if (affected[0] == 0 && affected[1] == 0 && affected[2] == 0) {
6920 for (j = 3; j--;) {
6921 if (pblock[j]) {
6922 MEM_freeN(pblock[j]);
6923 }
6924 if (sblock[j]) {
6925 MEM_freeN(sblock[j]);
6926 }
6927 if (map[j]) {
6928 MEM_freeN(map[j]);
6929 }
6930 }
6931 return;
6932 }
6933
6934 /* Sort affected elements, and populate mapping arrays, if needed. */
6935 for (j = 3; j--;) {
6936 pb = pblock[j];
6937 sb = sblock[j];
6938 if (pb && sb && !map[j]) {
6939 const char *p_blk;
6940 BMElemSort *s_blk;
6941 int tot = totelem[j];
6942 int aff = affected[j];
6943
6944 qsort(sb, aff, sizeof(BMElemSort), bmelemsort_comp);
6945
6946 mp = map[j] = MEM_malloc_arrayN<uint>(tot, __func__);
6947 p_blk = pb + tot - 1;
6948 s_blk = sb + aff - 1;
6949 for (i = tot; i--; p_blk--) {
6950 if (*p_blk) { /* Protected! */
6951 mp[i] = i;
6952 }
6953 else {
6954 mp[s_blk->org_idx] = i;
6955 s_blk--;
6956 }
6957 }
6958 }
6959 if (pb) {
6960 MEM_freeN(pb);
6961 }
6962 if (sb) {
6963 MEM_freeN(sb);
6964 }
6965 }
6966
6967 BM_mesh_remap(em->bm, map[0], map[1], map[2]);
6968
6970 params.calc_looptris = (totelem[2] != 0);
6971 params.calc_normals = false;
6972 params.is_destructive = true;
6973 EDBM_update(static_cast<Mesh *>(ob->data), &params);
6974
6975 DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_GEOMETRY);
6977
6978 for (j = 3; j--;) {
6979 if (map[j]) {
6980 MEM_freeN(map[j]);
6981 }
6982 }
6983}
6984
6986{
6987 Scene *scene = CTX_data_scene(C);
6988 ViewLayer *view_layer = CTX_data_view_layer(C);
6989 Object *ob_active = CTX_data_edit_object(C);
6990
6991 /* may be nullptr */
6993
6994 const int action = RNA_enum_get(op->ptr, "type");
6995 PropertyRNA *prop_elem_types = RNA_struct_find_property(op->ptr, "elements");
6996 const bool use_reverse = RNA_boolean_get(op->ptr, "reverse");
6997 uint seed = RNA_int_get(op->ptr, "seed");
6998 int elem_types = 0;
6999
7000 if (ELEM(action, SRT_VIEW_ZAXIS, SRT_VIEW_XAXIS)) {
7001 if (rv3d == nullptr) {
7002 BKE_report(op->reports, RPT_ERROR, "View not found, cannot sort by view axis");
7003 return OPERATOR_CANCELLED;
7004 }
7005 }
7006
7007 /* If no elem_types set, use current selection mode to set it! */
7008 if (RNA_property_is_set(op->ptr, prop_elem_types)) {
7009 elem_types = RNA_property_enum_get(op->ptr, prop_elem_types);
7010 }
7011 else {
7012 BMEditMesh *em = BKE_editmesh_from_object(ob_active);
7013 if (em->selectmode & SCE_SELECT_VERTEX) {
7014 elem_types |= BM_VERT;
7015 }
7016 if (em->selectmode & SCE_SELECT_EDGE) {
7017 elem_types |= BM_EDGE;
7018 }
7019 if (em->selectmode & SCE_SELECT_FACE) {
7020 elem_types |= BM_FACE;
7021 }
7022 RNA_enum_set(op->ptr, "elements", elem_types);
7023 }
7024
7026 scene, view_layer, CTX_wm_view3d(C));
7027
7028 for (uint ob_index = 0; ob_index < objects.size(); ob_index++) {
7029 Object *ob = objects[ob_index];
7031 BMesh *bm = em->bm;
7032
7033 if (!((elem_types & BM_VERT && bm->totvertsel > 0) ||
7034 (elem_types & BM_EDGE && bm->totedgesel > 0) ||
7035 (elem_types & BM_FACE && bm->totfacesel > 0)))
7036 {
7037 continue;
7038 }
7039
7040 int seed_iter = seed;
7041
7042 /* This gives a consistent result regardless of object order */
7043 if (ob_index) {
7044 seed_iter += BLI_ghashutil_strhash_p(ob->id.name);
7045 }
7046
7048 C, scene, ob, rv3d, elem_types, BM_ELEM_SELECT, action, use_reverse, seed_iter);
7049 }
7050 return OPERATOR_FINISHED;
7051}
7052
7054 wmOperator *op,
7055 const PropertyRNA *prop)
7056{
7057 const char *prop_id = RNA_property_identifier(prop);
7058 const int action = RNA_enum_get(op->ptr, "type");
7059
7060 /* Only show seed for randomize action! */
7061 if (STREQ(prop_id, "seed")) {
7062 if (action == SRT_RANDOMIZE) {
7063 return true;
7064 }
7065 return false;
7066 }
7067
7068 /* Hide seed for reverse and randomize actions! */
7069 if (STREQ(prop_id, "reverse")) {
7070 if (ELEM(action, SRT_RANDOMIZE, SRT_REVERSE)) {
7071 return false;
7072 }
7073 return true;
7074 }
7075
7076 return true;
7077}
7078
7080{
7081 static const EnumPropertyItem type_items[] = {
7083 "VIEW_ZAXIS",
7084 0,
7085 "View Z Axis",
7086 "Sort selected elements from farthest to nearest one in current view"},
7088 "VIEW_XAXIS",
7089 0,
7090 "View X Axis",
7091 "Sort selected elements from left to right one in current view"},
7093 "CURSOR_DISTANCE",
7094 0,
7095 "Cursor Distance",
7096 "Sort selected elements from nearest to farthest from 3D cursor"},
7097 {SRT_MATERIAL,
7098 "MATERIAL",
7099 0,
7100 "Material",
7101 "Sort selected faces from smallest to greatest material index"},
7102 {SRT_SELECTED,
7103 "SELECTED",
7104 0,
7105 "Selected",
7106 "Move all selected elements in first places, preserving their relative order.\n"
7107 "Warning: This will affect unselected elements' indices as well"},
7108 {SRT_RANDOMIZE, "RANDOMIZE", 0, "Randomize", "Randomize order of selected elements"},
7109 {SRT_REVERSE, "REVERSE", 0, "Reverse", "Reverse current order of selected elements"},
7110 {0, nullptr, 0, nullptr, nullptr},
7111 };
7112
7113 static const EnumPropertyItem elem_items[] = {
7114 {BM_VERT, "VERT", 0, "Vertices", ""},
7115 {BM_EDGE, "EDGE", 0, "Edges", ""},
7116 {BM_FACE, "FACE", 0, "Faces", ""},
7117 {0, nullptr, 0, nullptr, nullptr},
7118 };
7119
7120 /* identifiers */
7121 ot->name = "Sort Mesh Elements";
7122 ot->description =
7123 "The order of selected vertices/edges/faces is modified, based on a given method";
7124 ot->idname = "MESH_OT_sort_elements";
7125
7126 /* API callbacks. */
7127 ot->invoke = WM_menu_invoke;
7129 ot->poll = ED_operator_editmesh;
7130 ot->poll_property = edbm_sort_elements_poll_property;
7131
7132 /* flags */
7133 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
7134
7135 /* properties */
7136 ot->prop = RNA_def_enum(ot->srna,
7137 "type",
7138 type_items,
7140 "Type",
7141 "Type of reordering operation to apply");
7142 RNA_def_enum_flag(ot->srna,
7143 "elements",
7144 elem_items,
7145 BM_VERT,
7146 "Elements",
7147 "Which elements to affect (vertices, edges and/or faces)");
7148 RNA_def_boolean(ot->srna, "reverse", false, "Reverse", "Reverse the sorting effect");
7149 RNA_def_int(ot->srna, "seed", 0, 0, INT_MAX, "Seed", "Seed for random-based operations", 0, 255);
7150}
7151
7153
7154/* -------------------------------------------------------------------- */
7157
7158enum {
7162};
7163
7165{
7166 /* tags boundary edges from a face selection */
7167 BMIter iter;
7168 BMFace *f;
7169 BMEdge *e;
7170 int totface_del = 0;
7171
7173
7174 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
7178 }
7179 else {
7180 BMIter fiter;
7181 bool is_all_sel = true;
7182 /* check if its only used by selected faces */
7183 BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) {
7185 /* Tag face for removal. */
7186 if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
7188 totface_del++;
7189 }
7190 }
7191 else {
7192 is_all_sel = false;
7193 }
7194 }
7195
7196 if (is_all_sel == false) {
7198 }
7199 }
7200 }
7201 }
7202
7203 return totface_del;
7204}
7205
7207 BMEditMesh *em,
7208 Mesh *mesh,
7209 const bool use_pairs,
7210 const bool use_cyclic,
7211 const bool use_merge,
7212 const float merge_factor,
7213 const int twist_offset)
7214{
7215 BMOperator bmop;
7216 char edge_hflag;
7217 int totface_del = 0;
7218 BMFace **totface_del_arr = nullptr;
7219 const bool use_faces = (em->bm->totfacesel != 0);
7220 bool changed = false;
7221
7222 if (use_faces) {
7223 /* NOTE: When all faces are selected, all faces will be deleted with no edge-loops remaining.
7224 * In this case bridge will fail with a waning and delete all faces.
7225 * Ideally it's possible to detect cases when deleting faces leaves remaining edge-loops.
7226 * While this can be done in trivial cases - by checking the number of selected faces matches
7227 * the number of faces, that won't work for more involved cases involving hidden faces
7228 * and wire edges. One option could be to copy & restore the edit-mesh however
7229 * this is quite an expensive operation - to properly handle clearly invalid input.
7230 * Accept this limitation, the user must undo to restore the previous state, see: #123405. */
7231
7232 BMIter iter;
7233 BMFace *f;
7234 int i;
7235
7236 totface_del = edbm_bridge_tag_boundary_edges(em->bm);
7237 totface_del_arr = static_cast<BMFace **>(
7238 MEM_mallocN(sizeof(*totface_del_arr) * totface_del, __func__));
7239
7240 i = 0;
7241 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
7243 totface_del_arr[i++] = f;
7244 }
7245 }
7246 edge_hflag = BM_ELEM_TAG;
7247 }
7248 else {
7249 edge_hflag = BM_ELEM_SELECT;
7250 }
7251
7252 EDBM_op_init(em,
7253 &bmop,
7254 op,
7255 "bridge_loops edges=%he use_pairs=%b use_cyclic=%b use_merge=%b merge_factor=%f "
7256 "twist_offset=%i",
7257 edge_hflag,
7258 use_pairs,
7259 use_cyclic,
7260 use_merge,
7261 merge_factor,
7262 twist_offset);
7263
7264 if (use_faces && totface_del) {
7265 int i;
7267 for (i = 0; i < totface_del; i++) {
7268 BM_elem_flag_enable(totface_del_arr[i], BM_ELEM_TAG);
7269 }
7270 BMO_op_callf(em->bm,
7272 "delete geom=%hf context=%i",
7275 changed = true;
7276 }
7277
7278 BMO_op_exec(em->bm, &bmop);
7279
7281 /* when merge is used the edges are joined and remain selected */
7282 if (use_merge == false) {
7285 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
7286
7287 changed = true;
7288 }
7289
7290 if (use_merge == false) {
7291 EdgeRingOpSubdProps op_props;
7292 mesh_operator_edgering_props_get(op, &op_props);
7293
7294 if (op_props.cuts) {
7295 BMOperator bmop_subd;
7296 /* we only need face normals updated */
7298
7299 BMO_op_initf(em->bm,
7300 &bmop_subd,
7301 0,
7302 "subdivide_edgering edges=%S interp_mode=%i cuts=%i smooth=%f "
7303 "profile_shape=%i profile_shape_factor=%f",
7304 &bmop,
7305 "edges.out",
7306 op_props.interp_mode,
7307 op_props.cuts,
7308 op_props.smooth,
7309 op_props.profile_shape,
7310 op_props.profile_shape_factor);
7311 BMO_op_exec(em->bm, &bmop_subd);
7313 em->bm, bmop_subd.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
7314 BMO_op_finish(em->bm, &bmop_subd);
7315
7316 changed = true;
7317 }
7318 }
7319 }
7320
7321 if (totface_del_arr) {
7322 MEM_freeN(totface_del_arr);
7323 }
7324
7325 if (EDBM_op_finish(em, &bmop, op, true)) {
7326 changed = true;
7327 }
7328
7329 if (changed) {
7331 params.calc_looptris = true;
7332 params.calc_normals = false;
7333 params.is_destructive = true;
7334 EDBM_update(mesh, &params);
7335 }
7336
7337 /* Always return finished so the user can select different options. */
7338 return OPERATOR_FINISHED;
7339}
7340
7342{
7343 const int type = RNA_enum_get(op->ptr, "type");
7344 const bool use_pairs = (type == MESH_BRIDGELOOP_PAIRS);
7345 const bool use_cyclic = (type == MESH_BRIDGELOOP_CLOSED);
7346 const bool use_merge = RNA_boolean_get(op->ptr, "use_merge");
7347 const float merge_factor = RNA_float_get(op->ptr, "merge_factor");
7348 const int twist_offset = RNA_int_get(op->ptr, "twist_offset");
7349 const Scene *scene = CTX_data_scene(C);
7350 ViewLayer *view_layer = CTX_data_view_layer(C);
7351
7353 scene, view_layer, CTX_wm_view3d(C));
7354 for (Object *obedit : objects) {
7356
7357 if (em->bm->totvertsel == 0) {
7358 continue;
7359 }
7360
7362 em,
7363 static_cast<Mesh *>(obedit->data),
7364 use_pairs,
7365 use_cyclic,
7366 use_merge,
7367 merge_factor,
7368 twist_offset);
7369 }
7370 return OPERATOR_FINISHED;
7371}
7372
7374{
7375 static const EnumPropertyItem type_items[] = {
7376 {MESH_BRIDGELOOP_SINGLE, "SINGLE", 0, "Open Loop", ""},
7377 {MESH_BRIDGELOOP_CLOSED, "CLOSED", 0, "Closed Loop", ""},
7378 {MESH_BRIDGELOOP_PAIRS, "PAIRS", 0, "Loop Pairs", ""},
7379 {0, nullptr, 0, nullptr, nullptr},
7380 };
7381
7382 /* identifiers */
7383 ot->name = "Bridge Edge Loops";
7384 ot->description = "Create a bridge of faces between two or more selected edge loops";
7385 ot->idname = "MESH_OT_bridge_edge_loops";
7386
7387 /* API callbacks. */
7389 ot->poll = ED_operator_editmesh;
7390
7391 /* flags */
7392 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
7393
7394 ot->prop = RNA_def_enum(ot->srna,
7395 "type",
7396 type_items,
7398 "Connect Loops",
7399 "Method of bridging multiple loops");
7400
7401 RNA_def_boolean(ot->srna, "use_merge", false, "Merge", "Merge rather than creating faces");
7402 RNA_def_float(ot->srna, "merge_factor", 0.5f, 0.0f, 1.0f, "Merge Factor", "", 0.0f, 1.0f);
7403 RNA_def_int(ot->srna,
7404 "twist_offset",
7405 0,
7406 -1000,
7407 1000,
7408 "Twist",
7409 "Twist offset for closed loops",
7410 -1000,
7411 1000);
7412
7414}
7415
7417
7418/* -------------------------------------------------------------------- */
7421
7423{
7424 const bool use_boundary = RNA_boolean_get(op->ptr, "use_boundary");
7425 const bool use_even_offset = RNA_boolean_get(op->ptr, "use_even_offset");
7426 const bool use_replace = RNA_boolean_get(op->ptr, "use_replace");
7427 const bool use_relative_offset = RNA_boolean_get(op->ptr, "use_relative_offset");
7428 const bool use_crease = RNA_boolean_get(op->ptr, "use_crease");
7429 const float crease_weight = RNA_float_get(op->ptr, "crease_weight");
7430 const float thickness = RNA_float_get(op->ptr, "thickness");
7431 const float offset = RNA_float_get(op->ptr, "offset");
7432
7433 const Scene *scene = CTX_data_scene(C);
7434 ViewLayer *view_layer = CTX_data_view_layer(C);
7436 scene, view_layer, CTX_wm_view3d(C));
7437 for (Object *obedit : objects) {
7439
7440 if (em->bm->totfacesel == 0) {
7441 continue;
7442 }
7443
7444 BMOperator bmop;
7445
7446 EDBM_op_init(em,
7447 &bmop,
7448 op,
7449 "wireframe faces=%hf use_replace=%b use_boundary=%b use_even_offset=%b "
7450 "use_relative_offset=%b "
7451 "use_crease=%b crease_weight=%f thickness=%f offset=%f",
7453 use_replace,
7454 use_boundary,
7455 use_even_offset,
7456 use_relative_offset,
7457 use_crease,
7458 crease_weight,
7459 thickness,
7460 offset);
7461
7462 BMO_op_exec(em->bm, &bmop);
7463
7466 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
7467
7468 if (!EDBM_op_finish(em, &bmop, op, true)) {
7469 continue;
7470 }
7471
7473 params.calc_looptris = true;
7474 params.calc_normals = false;
7475 params.is_destructive = true;
7476 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7477 }
7478
7479 return OPERATOR_FINISHED;
7480}
7481
7483{
7484 PropertyRNA *prop;
7485
7486 /* identifiers */
7487 ot->name = "Wireframe";
7488 ot->idname = "MESH_OT_wireframe";
7489 ot->description = "Create a solid wireframe from faces";
7490
7491 /* API callbacks. */
7492 ot->exec = edbm_wireframe_exec;
7493 ot->poll = ED_operator_editmesh;
7494
7495 /* flags */
7496 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
7497
7498 /* properties */
7499 RNA_def_boolean(ot->srna, "use_boundary", true, "Boundary", "Inset face boundaries");
7500 RNA_def_boolean(ot->srna,
7501 "use_even_offset",
7502 true,
7503 "Offset Even",
7504 "Scale the offset to give more even thickness");
7505 RNA_def_boolean(ot->srna,
7506 "use_relative_offset",
7507 false,
7508 "Offset Relative",
7509 "Scale the offset by surrounding geometry");
7510 RNA_def_boolean(ot->srna, "use_replace", true, "Replace", "Remove original faces");
7512 ot->srna, "thickness", 0.01f, 0.0f, 1e4f, "Thickness", "", 0.0f, 10.0f);
7513 /* use 1 rather than 10 for max else dragging the button moves too far */
7514 RNA_def_property_ui_range(prop, 0.0, 1.0, 0.01, 4);
7515 RNA_def_float_distance(ot->srna, "offset", 0.01f, 0.0f, 1e4f, "Offset", "", 0.0f, 10.0f);
7516 RNA_def_boolean(ot->srna,
7517 "use_crease",
7518 false,
7519 "Crease",
7520 "Crease hub edges for an improved subdivision surface");
7521 prop = RNA_def_float(
7522 ot->srna, "crease_weight", 0.01f, 0.0f, 1e3f, "Crease Weight", "", 0.0f, 1.0f);
7523 RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 2);
7524}
7525
7527
7528/* -------------------------------------------------------------------- */
7531
7533{
7534 const bool use_cap_endpoint = RNA_boolean_get(op->ptr, "use_cap_endpoint");
7535 bool changed_multi = false;
7536 Scene *scene = CTX_data_scene(C);
7537 ViewLayer *view_layer = CTX_data_view_layer(C);
7539 scene, view_layer, CTX_wm_view3d(C));
7540 for (Base *base : bases) {
7541 Object *obedit = base->object;
7543
7544 if (em->bm->totedgesel == 0) {
7545 continue;
7546 }
7547
7548 BMOperator bmop;
7549 EDBM_op_init(em,
7550 &bmop,
7551 op,
7552 "offset_edgeloops edges=%he use_cap_endpoint=%b",
7554 use_cap_endpoint);
7555
7556 BMO_op_exec(em->bm, &bmop);
7557
7559
7561 em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true);
7562
7563 if (EDBM_op_finish(em, &bmop, op, true)) {
7565 params.calc_looptris = true;
7566 params.calc_normals = false;
7567 params.is_destructive = true;
7568 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7569 changed_multi = true;
7570 }
7571 }
7572
7573 if (changed_multi) {
7579 if (scene->toolsettings->selectmode == SCE_SELECT_FACE) {
7581 }
7582 }
7583
7584 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
7585}
7586
7588{
7589 /* identifiers */
7590 ot->name = "Offset Edge Loop";
7591 ot->idname = "MESH_OT_offset_edge_loops";
7592 ot->description = "Create offset edge loop from the current selection";
7593
7594 /* API callbacks. */
7596 ot->poll = ED_operator_editmesh;
7597
7598 /* Keep internal, since this is only meant to be accessed via
7599 * `MESH_OT_offset_edge_loops_slide`. */
7600
7601 /* flags */
7603
7605 ot->srna, "use_cap_endpoint", false, "Cap Endpoint", "Extend loop around end-points");
7606}
7607
7609
7610/* -------------------------------------------------------------------- */
7613
7614#ifdef WITH_BULLET
7615static wmOperatorStatus edbm_convex_hull_exec(bContext *C, wmOperator *op)
7616{
7617 const bool use_existing_faces = RNA_boolean_get(op->ptr, "use_existing_faces");
7618 const bool delete_unused = RNA_boolean_get(op->ptr, "delete_unused");
7619 const bool make_holes = RNA_boolean_get(op->ptr, "make_holes");
7620 const bool join_triangles = RNA_boolean_get(op->ptr, "join_triangles");
7621
7622 float angle_face_threshold = RNA_float_get(op->ptr, "face_threshold");
7623 float angle_shape_threshold = RNA_float_get(op->ptr, "shape_threshold");
7624
7625 const Scene *scene = CTX_data_scene(C);
7626 ViewLayer *view_layer = CTX_data_view_layer(C);
7628 scene, view_layer, CTX_wm_view3d(C));
7629 for (Object *obedit : objects) {
7631
7632 if (em->bm->totvertsel == 0) {
7633 continue;
7634 }
7635
7636 BMOperator bmop;
7637
7638 EDBM_op_init(em,
7639 &bmop,
7640 op,
7641 "convex_hull input=%hvef "
7642 "use_existing_faces=%b",
7644 use_existing_faces);
7645 BMO_op_exec(em->bm, &bmop);
7646
7647 /* Hull fails if input is coplanar */
7649 EDBM_op_finish(em, &bmop, op, true);
7650 continue;
7651 }
7652
7654 em->bm, bmop.slots_out, "geom.out", BM_FACE, BM_ELEM_SELECT, true);
7655
7656 /* Delete unused vertices, edges, and faces */
7657 if (delete_unused) {
7658 if (!EDBM_op_callf(
7659 em, op, "delete geom=%S context=%i", &bmop, "geom_unused.out", DEL_ONLYTAGGED))
7660 {
7661 EDBM_op_finish(em, &bmop, op, true);
7662 continue;
7663 }
7664 }
7665
7666 /* Delete hole edges/faces */
7667 if (make_holes) {
7668 if (!EDBM_op_callf(
7669 em, op, "delete geom=%S context=%i", &bmop, "geom_holes.out", DEL_ONLYTAGGED))
7670 {
7671 EDBM_op_finish(em, &bmop, op, true);
7672 continue;
7673 }
7674 }
7675
7676 /* Merge adjacent triangles */
7677 if (join_triangles) {
7679 op,
7680 "faces.out",
7681 true,
7682 "join_triangles faces=%S "
7683 "angle_face_threshold=%f angle_shape_threshold=%f",
7684 &bmop,
7685 "geom.out",
7686 angle_face_threshold,
7687 angle_shape_threshold))
7688 {
7689 EDBM_op_finish(em, &bmop, op, true);
7690 continue;
7691 }
7692 }
7693
7694 if (!EDBM_op_finish(em, &bmop, op, true)) {
7695 continue;
7696 }
7697
7699 params.calc_looptris = true;
7700 params.calc_normals = false;
7701 params.is_destructive = true;
7702 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7704 }
7705
7706 return OPERATOR_FINISHED;
7707}
7708
7710{
7711 /* identifiers */
7712 ot->name = "Convex Hull";
7713 ot->description = "Enclose selected vertices in a convex polyhedron";
7714 ot->idname = "MESH_OT_convex_hull";
7715
7716 /* API callbacks. */
7717 ot->exec = edbm_convex_hull_exec;
7718 ot->poll = ED_operator_editmesh;
7719
7720 /* flags */
7721 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
7722
7723 /* props */
7724 RNA_def_boolean(ot->srna,
7725 "delete_unused",
7726 true,
7727 "Delete Unused",
7728 "Delete selected elements that are not used by the hull");
7729
7730 RNA_def_boolean(ot->srna,
7731 "use_existing_faces",
7732 true,
7733 "Use Existing Faces",
7734 "Skip hull triangles that are covered by a pre-existing face");
7735
7736 RNA_def_boolean(ot->srna,
7737 "make_holes",
7738 false,
7739 "Make Holes",
7740 "Delete selected faces that are used by the hull");
7741
7743 ot->srna, "join_triangles", true, "Join Triangles", "Merge adjacent triangles into quads");
7744
7746}
7747#endif /* WITH_BULLET */
7748
7750
7751/* -------------------------------------------------------------------- */
7754
7756{
7757 const float thresh = RNA_float_get(op->ptr, "threshold");
7758 const Scene *scene = CTX_data_scene(C);
7759 ViewLayer *view_layer = CTX_data_view_layer(C);
7761 scene, view_layer, CTX_wm_view3d(C));
7762
7763 for (Object *obedit : objects) {
7765
7766 if (em->bm->totvertsel == 0) {
7767 continue;
7768 }
7769
7770 BMOperator bmop;
7771 EDBM_op_init(em,
7772 &bmop,
7773 op,
7774 "symmetrize input=%hvef direction=%i dist=%f",
7776 RNA_enum_get(op->ptr, "direction"),
7777 thresh);
7778 BMO_op_exec(em->bm, &bmop);
7779
7781
7783 em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
7784
7785 if (!EDBM_op_finish(em, &bmop, op, true)) {
7786 continue;
7787 }
7789 params.calc_looptris = true;
7790 params.calc_normals = false;
7791 params.is_destructive = true;
7792 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7794 }
7795
7796 return OPERATOR_FINISHED;
7797}
7798
7800{
7801 /* identifiers */
7802 ot->name = "Symmetrize";
7803 ot->description = "Enforce symmetry (both form and topological) across an axis";
7804 ot->idname = "MESH_OT_symmetrize";
7805
7806 /* API callbacks. */
7807 ot->exec = mesh_symmetrize_exec;
7808 ot->poll = ED_operator_editmesh;
7809
7810 /* flags */
7811 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
7812
7813 ot->prop = RNA_def_enum(ot->srna,
7814 "direction",
7817 "Direction",
7818 "Which sides to copy from and to");
7819 RNA_def_float(ot->srna,
7820 "threshold",
7821 1e-4f,
7822 0.0f,
7823 10.0f,
7824 "Threshold",
7825 "Limit for snap middle vertices to the axis center",
7826 1e-5f,
7827 0.1f);
7828}
7829
7831
7832/* -------------------------------------------------------------------- */
7835
7837{
7838 const float eps = 0.00001f;
7839 const float eps_sq = eps * eps;
7840 const bool use_topology = false;
7841
7842 const float thresh = RNA_float_get(op->ptr, "threshold");
7843 const float fac = RNA_float_get(op->ptr, "factor");
7844 const bool use_center = RNA_boolean_get(op->ptr, "use_center");
7845 const int axis_dir = RNA_enum_get(op->ptr, "direction");
7846
7847 /* Vertices stats (total over all selected objects). */
7848 int totvertfound = 0, totvertmirr = 0, totvertfail = 0, totobjects = 0;
7849
7850 /* Axis. */
7851 int axis = axis_dir % 3;
7852 bool axis_sign = axis != axis_dir;
7853
7854 const Scene *scene = CTX_data_scene(C);
7855 ViewLayer *view_layer = CTX_data_view_layer(C);
7857 scene, view_layer, CTX_wm_view3d(C));
7858
7859 for (Object *obedit : objects) {
7861 BMesh *bm = em->bm;
7862
7863 if (em->bm->totvertsel == 0) {
7864 continue;
7865 }
7866
7868 continue;
7869 }
7870
7871 totobjects++;
7872
7873 /* Only allocate memory after checking whether to skip object. */
7874 int *index = MEM_malloc_arrayN<int>(bm->totvert, __func__);
7875
7876 /* Vertex iter. */
7877 BMIter iter;
7878 BMVert *v;
7879 int i;
7880
7881 EDBM_verts_mirror_cache_begin_ex(em, axis, true, true, false, use_topology, thresh, index);
7882
7884
7886
7888 if ((BM_elem_flag_test(v, BM_ELEM_SELECT) != false) &&
7889 (BM_elem_flag_test(v, BM_ELEM_TAG) == false))
7890 {
7891 int i_mirr = index[i];
7892 if (i_mirr != -1) {
7893
7894 BMVert *v_mirr = BM_vert_at_index(bm, index[i]);
7895
7896 if (v != v_mirr) {
7897 float co[3], co_mirr[3];
7898
7899 if ((v->co[axis] > v_mirr->co[axis]) == axis_sign) {
7900 std::swap(v, v_mirr);
7901 }
7902
7903 copy_v3_v3(co_mirr, v_mirr->co);
7904 co_mirr[axis] *= -1.0f;
7905
7906 if (len_squared_v3v3(v->co, co_mirr) > eps_sq) {
7907 totvertmirr++;
7908 }
7909
7910 interp_v3_v3v3(co, v->co, co_mirr, fac);
7911
7912 copy_v3_v3(v->co, co);
7913
7914 co[axis] *= -1.0f;
7915 copy_v3_v3(v_mirr->co, co);
7916
7919 totvertfound++;
7920 }
7921 else {
7922 if (use_center) {
7923
7924 if (fabsf(v->co[axis]) > eps) {
7925 totvertmirr++;
7926 }
7927
7928 v->co[axis] = 0.0f;
7929 }
7931 totvertfound++;
7932 }
7933 }
7934 else {
7935 totvertfail++;
7936 }
7937 }
7938 }
7940 params.calc_looptris = false;
7941 params.calc_normals = false;
7942 params.is_destructive = false;
7943 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7944
7945 /* No need to end cache, just free the array. */
7946 MEM_freeN(index);
7947 }
7948
7949 if (totvertfail) {
7950 BKE_reportf(op->reports,
7952 "%d already symmetrical, %d pairs mirrored, %d failed",
7953 totvertfound - totvertmirr,
7954 totvertmirr,
7955 totvertfail);
7956 }
7957 else if (totobjects) {
7958 BKE_reportf(op->reports,
7959 RPT_INFO,
7960 "%d already symmetrical, %d pairs mirrored",
7961 totvertfound - totvertmirr,
7962 totvertmirr);
7963 }
7964
7965 return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
7966}
7967
7969{
7970 /* identifiers */
7971 ot->name = "Snap to Symmetry";
7972 ot->description = "Snap vertex pairs to their mirrored locations";
7973 ot->idname = "MESH_OT_symmetry_snap";
7974
7975 /* API callbacks. */
7977 ot->poll = ED_operator_editmesh;
7978
7979 /* flags */
7980 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
7981
7982 ot->prop = RNA_def_enum(ot->srna,
7983 "direction",
7986 "Direction",
7987 "Which sides to copy from and to");
7989 "threshold",
7990 0.05f,
7991 0.0f,
7992 10.0f,
7993 "Threshold",
7994 "Distance within which matching vertices are searched",
7995 1e-4f,
7996 1.0f);
7997 RNA_def_float(ot->srna,
7998 "factor",
7999 0.5f,
8000 0.0f,
8001 1.0f,
8002 "Factor",
8003 "Mix factor of the locations of the vertices",
8004 0.0f,
8005 1.0f);
8007 ot->srna, "use_center", true, "Center", "Snap middle vertices to the axis center");
8008}
8009
8011
8012#if defined(WITH_FREESTYLE)
8013
8014/* -------------------------------------------------------------------- */
8017
8018static wmOperatorStatus edbm_mark_freestyle_edge_exec(bContext *C, wmOperator *op)
8019{
8020 BMEdge *eed;
8021 BMIter iter;
8022 FreestyleEdge *fed;
8023 const bool clear = RNA_boolean_get(op->ptr, "clear");
8024 const Scene *scene = CTX_data_scene(C);
8025 ViewLayer *view_layer = CTX_data_view_layer(C);
8026
8028 scene, view_layer, CTX_wm_view3d(C));
8029 for (Object *obedit : objects) {
8031
8032 if (em == nullptr) {
8033 continue;
8034 }
8035
8036 BMesh *bm = em->bm;
8037
8038 if (bm->totedgesel == 0) {
8039 continue;
8040 }
8041
8044 }
8045
8046 if (clear) {
8047 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
8049 fed = static_cast<FreestyleEdge *>(
8051 fed->flag &= ~FREESTYLE_EDGE_MARK;
8052 }
8053 }
8054 }
8055 else {
8056 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
8058 fed = static_cast<FreestyleEdge *>(
8060 fed->flag |= FREESTYLE_EDGE_MARK;
8061 }
8062 }
8063 }
8064
8065 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
8066 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
8067 }
8068
8069 return OPERATOR_FINISHED;
8070}
8071
8072void MESH_OT_mark_freestyle_edge(wmOperatorType *ot)
8073{
8074 PropertyRNA *prop;
8075
8076 /* identifiers */
8077 ot->name = "Mark Freestyle Edge";
8078 ot->description = "(Un)mark selected edges as Freestyle feature edges";
8079 ot->idname = "MESH_OT_mark_freestyle_edge";
8080
8081 /* API callbacks. */
8082 ot->exec = edbm_mark_freestyle_edge_exec;
8083 ot->poll = ED_operator_editmesh;
8084
8085 /* flags */
8086 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
8087
8088 prop = RNA_def_boolean(ot->srna, "clear", false, "Clear", "");
8090}
8091
8093
8094/* -------------------------------------------------------------------- */
8097
8098static wmOperatorStatus edbm_mark_freestyle_face_exec(bContext *C, wmOperator *op)
8099{
8100 BMFace *efa;
8101 BMIter iter;
8102 FreestyleFace *ffa;
8103 const bool clear = RNA_boolean_get(op->ptr, "clear");
8104 const Scene *scene = CTX_data_scene(C);
8105 ViewLayer *view_layer = CTX_data_view_layer(C);
8106
8108 scene, view_layer, CTX_wm_view3d(C));
8109 for (Object *obedit : objects) {
8111
8112 if (em == nullptr) {
8113 continue;
8114 }
8115
8116 if (em->bm->totfacesel == 0) {
8117 continue;
8118 }
8119
8122 }
8123
8124 if (clear) {
8125 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
8127 ffa = static_cast<FreestyleFace *>(
8129 ffa->flag &= ~FREESTYLE_FACE_MARK;
8130 }
8131 }
8132 }
8133 else {
8134 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
8136 ffa = static_cast<FreestyleFace *>(
8138 ffa->flag |= FREESTYLE_FACE_MARK;
8139 }
8140 }
8141 }
8142
8143 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
8144 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
8145 }
8146
8147 return OPERATOR_FINISHED;
8148}
8149
8150void MESH_OT_mark_freestyle_face(wmOperatorType *ot)
8151{
8152 PropertyRNA *prop;
8153
8154 /* identifiers */
8155 ot->name = "Mark Freestyle Face";
8156 ot->description = "(Un)mark selected faces for exclusion from Freestyle feature edge detection";
8157 ot->idname = "MESH_OT_mark_freestyle_face";
8158
8159 /* API callbacks. */
8160 ot->exec = edbm_mark_freestyle_face_exec;
8161 ot->poll = ED_operator_editmesh;
8162
8163 /* flags */
8164 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
8165
8166 prop = RNA_def_boolean(ot->srna, "clear", false, "Clear", "");
8168}
8169
8171
8172#endif /* WITH_FREESTYLE */
8173
8174/* -------------------------------------------------------------------- */
8177
8178/* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
8179/* NOTE: We could add more here, like e.g. a switch between local or global coordinates of target,
8180 * use number-input to type in explicit vector values. */
8181enum {
8182 /* Generic commands. */
8185
8186 /* Point To operator. */
8191
8197};
8198
8200{
8201 static const EnumPropertyItem modal_items[] = {
8202 {EDBM_CLNOR_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
8203 {EDBM_CLNOR_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
8204
8205 /* Point To operator. */
8206 {EDBM_CLNOR_MODAL_POINTTO_RESET, "RESET", 0, "Reset", "Reset normals to initial ones"},
8208 "INVERT",
8209 0,
8210 "Invert",
8211 "Toggle inversion of affected normals"},
8213 "SPHERIZE",
8214 0,
8215 "Spherize",
8216 "Interpolate between new and original normals"},
8217 {EDBM_CLNOR_MODAL_POINTTO_ALIGN, "ALIGN", 0, "Align", "Make all affected normals parallel"},
8218
8220 "USE_MOUSE",
8221 0,
8222 "Use Mouse",
8223 "Follow mouse cursor position"},
8225 "USE_PIVOT",
8226 0,
8227 "Use Pivot",
8228 "Use current rotation/scaling pivot point coordinates"},
8230 "USE_OBJECT",
8231 0,
8232 "Use Object",
8233 "Use current edited object's location"},
8235 "SET_USE_3DCURSOR",
8236 0,
8237 "Set and Use 3D Cursor",
8238 "Set new 3D cursor position and use it"},
8240 "SET_USE_SELECTED",
8241 0,
8242 "Select and Use Mesh Item",
8243 "Select new active mesh element and use its location"},
8244 {0, nullptr, 0, nullptr, nullptr},
8245 };
8246 static const char *keymap_name = "Custom Normals Modal Map";
8247
8248 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, keymap_name);
8249
8250 /* We only need to add map once */
8251 if (keymap && keymap->modal_items) {
8252 return nullptr;
8253 }
8254
8255 keymap = WM_modalkeymap_ensure(keyconf, keymap_name, modal_items);
8256
8257 WM_modalkeymap_assign(keymap, "MESH_OT_point_normals");
8258
8259 return keymap;
8260}
8261
8262#define CLNORS_VALID_VEC_LEN (1e-4f)
8263
8265
8266/* -------------------------------------------------------------------- */
8269
8270enum {
8273};
8274
8277 "COORDINATES",
8278 0,
8279 "Coordinates",
8280 "Use static coordinates (defined by various means)"},
8281 {EDBM_CLNOR_POINTTO_MODE_MOUSE, "MOUSE", 0, "Mouse", "Follow mouse cursor"},
8282 {0, nullptr, 0, nullptr, nullptr},
8283};
8284
8285/* Initialize loop normal data */
8287{
8288 Object *obedit = CTX_data_edit_object(C);
8290 BMesh *bm = em->bm;
8291
8294
8295 op->customdata = lnors_ed_arr;
8296
8297 return (lnors_ed_arr->totloop != 0);
8298}
8299
8301{
8302 if (op->customdata != nullptr) {
8303 return true;
8304 }
8305 return point_normals_init(C, op);
8306}
8307
8309{
8310 if (op->customdata != nullptr) {
8311 BMLoopNorEditDataArray *lnors_ed_arr = static_cast<BMLoopNorEditDataArray *>(op->customdata);
8313 op->customdata = nullptr;
8314 }
8315}
8316
8318{
8320 ED_workspace_status_text(C, nullptr);
8321}
8322
8324{
8325 WorkspaceStatus status(C);
8326
8327 status.opmodal(IFACE_("Confirm"), op->type, EDBM_CLNOR_MODAL_CONFIRM);
8328 status.opmodal(IFACE_("Cancel"), op->type, EDBM_CLNOR_MODAL_CANCEL);
8329 status.opmodal(IFACE_("Reset"), op->type, EDBM_CLNOR_MODAL_POINTTO_RESET);
8330
8331 status.opmodal(IFACE_("Invert"),
8332 op->type,
8334 RNA_boolean_get(op->ptr, "invert"));
8335 status.opmodal(IFACE_("Spherize"),
8336 op->type,
8338 RNA_boolean_get(op->ptr, "spherize"));
8339 status.opmodal(IFACE_("Align"),
8340 op->type,
8342 RNA_boolean_get(op->ptr, "align"));
8343
8344 status.opmodal(IFACE_("Use mouse"),
8345 op->type,
8348
8349 status.opmodal(IFACE_("Use Pivot"), op->type, EDBM_CLNOR_MODAL_POINTTO_USE_PIVOT);
8350 status.opmodal(IFACE_("Use Object"), op->type, EDBM_CLNOR_MODAL_POINTTO_USE_OBJECT);
8351 status.opmodal(
8352 IFACE_("Set and use 3D cursor"), op->type, EDBM_CLNOR_MODAL_POINTTO_SET_USE_3DCURSOR);
8353 status.opmodal(
8354 IFACE_("Select and use mesh item"), op->type, EDBM_CLNOR_MODAL_POINTTO_SET_USE_SELECTED);
8355}
8356
8357/* TODO: move that to generic function in BMesh? */
8358static void bmesh_selected_verts_center_calc(BMesh *bm, float *r_center)
8359{
8360 BMVert *v;
8361 BMIter viter;
8362 int i = 0;
8363
8364 zero_v3(r_center);
8365 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
8367 add_v3_v3(r_center, v->co);
8368 i++;
8369 }
8370 }
8371 mul_v3_fl(r_center, 1.0f / float(i));
8372}
8373
8374static void point_normals_apply(bContext *C, wmOperator *op, float target[3], const bool do_reset)
8375{
8376 Object *obedit = CTX_data_edit_object(C);
8377 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
8378 BMLoopNorEditDataArray *lnors_ed_arr = static_cast<BMLoopNorEditDataArray *>(op->customdata);
8379
8380 const bool do_invert = RNA_boolean_get(op->ptr, "invert");
8381 const bool do_spherize = RNA_boolean_get(op->ptr, "spherize");
8382 const bool do_align = RNA_boolean_get(op->ptr, "align");
8383 float center[3];
8384
8385 if (do_align && !do_reset) {
8387 }
8388
8389 sub_v3_v3(target, obedit->loc); /* Move target to local coordinates. */
8390
8391 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
8392 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
8393 if (do_reset) {
8394 copy_v3_v3(lnor_ed->nloc, lnor_ed->niloc);
8395 }
8396 else if (do_spherize) {
8397 /* Note that this is *not* real spherical interpolation.
8398 * Probably good enough in this case though? */
8399 const float strength = RNA_float_get(op->ptr, "spherize_strength");
8400 float spherized_normal[3];
8401
8402 sub_v3_v3v3(spherized_normal, target, lnor_ed->loc);
8403
8404 /* otherwise, multiplication by strength is meaningless... */
8405 normalize_v3(spherized_normal);
8406
8407 mul_v3_fl(spherized_normal, strength);
8408 mul_v3_v3fl(lnor_ed->nloc, lnor_ed->niloc, 1.0f - strength);
8409 add_v3_v3(lnor_ed->nloc, spherized_normal);
8410 }
8411 else if (do_align) {
8412 sub_v3_v3v3(lnor_ed->nloc, target, center);
8413 }
8414 else {
8415 sub_v3_v3v3(lnor_ed->nloc, target, lnor_ed->loc);
8416 }
8417
8418 if (do_invert && !do_reset) {
8419 negate_v3(lnor_ed->nloc);
8420 }
8421 if (normalize_v3(lnor_ed->nloc) >= CLNORS_VALID_VEC_LEN) {
8423 bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->nloc, lnor_ed->clnors_data);
8424 }
8425 }
8426}
8427
8429{
8430 /* As this operator passes events through, we can't be sure the user didn't exit edit-mode.
8431 * or performed some other operation. */
8432 if (!WM_operator_poll(C, op->type)) {
8434 return OPERATOR_CANCELLED;
8435 }
8436
8437 View3D *v3d = CTX_wm_view3d(C);
8438 Scene *scene = CTX_data_scene(C);
8439 Object *obedit = CTX_data_edit_object(C);
8441 BMesh *bm = em->bm;
8442
8443 float target[3];
8444
8446 int mode = RNA_enum_get(op->ptr, "mode");
8447 int new_mode = mode;
8448 bool force_mousemove = false;
8449 bool do_reset = false;
8450
8451 PropertyRNA *prop_target = RNA_struct_find_property(op->ptr, "target_location");
8452
8453 if (event->type == EVT_MODAL_MAP) {
8454 switch (event->val) {
8456 RNA_property_float_get_array(op->ptr, prop_target, target);
8458 break;
8459
8461 do_reset = true;
8463 break;
8464
8466 do_reset = true;
8468 break;
8469
8471 PropertyRNA *prop_invert = RNA_struct_find_property(op->ptr, "invert");
8473 op->ptr, prop_invert, !RNA_property_boolean_get(op->ptr, prop_invert));
8474 RNA_property_float_get_array(op->ptr, prop_target, target);
8476 break;
8477 }
8478
8480 PropertyRNA *prop_spherize = RNA_struct_find_property(op->ptr, "spherize");
8482 op->ptr, prop_spherize, !RNA_property_boolean_get(op->ptr, prop_spherize));
8483 RNA_property_float_get_array(op->ptr, prop_target, target);
8485 break;
8486 }
8487
8489 PropertyRNA *prop_align = RNA_struct_find_property(op->ptr, "align");
8491 op->ptr, prop_align, !RNA_property_boolean_get(op->ptr, prop_align));
8492 RNA_property_float_get_array(op->ptr, prop_target, target);
8494 break;
8495 }
8496
8499 /* We want to immediately update to mouse cursor position... */
8500 force_mousemove = true;
8502 break;
8503
8506 copy_v3_v3(target, obedit->loc);
8508 break;
8509
8513 copy_v3_v3(target, scene->cursor.location);
8515 break;
8516
8521 params.sel_op = SEL_OP_SET;
8522 if (EDBM_select_pick(C, event->mval, params)) {
8523 /* Point to newly selected active. */
8525
8526 add_v3_v3(target, obedit->loc);
8528 }
8529 break;
8530 }
8533 switch (scene->toolsettings->transform_pivot_point) {
8534 case V3D_AROUND_CENTER_BOUNDS: /* calculateCenterBound */
8535 {
8536 BMVert *v;
8537 BMIter viter;
8538 float min[3], max[3];
8539 int i = 0;
8540
8541 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
8543 if (i) {
8544 minmax_v3v3_v3(min, max, v->co);
8545 }
8546 else {
8547 copy_v3_v3(min, v->co);
8548 copy_v3_v3(max, v->co);
8549 }
8550 i++;
8551 }
8552 }
8553 mid_v3_v3v3(target, min, max);
8554 add_v3_v3(target, obedit->loc);
8555 break;
8556 }
8557
8560 add_v3_v3(target, obedit->loc);
8561 break;
8562 }
8563
8564 case V3D_AROUND_CURSOR:
8565 copy_v3_v3(target, scene->cursor.location);
8566 break;
8567
8568 case V3D_AROUND_ACTIVE:
8569 if (!blender::ed::object::calc_active_center_for_editmode(obedit, false, target)) {
8570 zero_v3(target);
8571 }
8572 add_v3_v3(target, obedit->loc);
8573 break;
8574
8575 default:
8576 BKE_report(op->reports, RPT_WARNING, "Does not support Individual Origins as pivot");
8577 copy_v3_v3(target, obedit->loc);
8578 }
8580 break;
8581 }
8582 default:
8583 break;
8584 }
8585 }
8586
8587 if (new_mode != mode) {
8588 mode = new_mode;
8589 RNA_enum_set(op->ptr, "mode", mode);
8590 }
8591
8592 /* Only handle mouse-move event in case we are in mouse mode. */
8593 if (event->type == MOUSEMOVE || force_mousemove) {
8594 if (mode == EDBM_CLNOR_POINTTO_MODE_MOUSE) {
8595 ARegion *region = CTX_wm_region(C);
8596 float center[3];
8597
8599
8600 ED_view3d_win_to_3d_int(v3d, region, center, event->mval, target);
8601
8603 }
8604 }
8605
8606 if (ret != OPERATOR_PASS_THROUGH) {
8608 RNA_property_float_set_array(op->ptr, prop_target, target);
8609 }
8610
8611 if (point_normals_ensure(C, op)) {
8612 point_normals_apply(C, op, target, do_reset);
8614 params.calc_looptris = true;
8615 params.calc_normals = false;
8616 params.is_destructive = false;
8617 /* Recheck booleans. */
8618 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
8619
8621 }
8622 else {
8624 }
8625 }
8626
8629 }
8630
8631 /* If we allow other tools to run, we can't be sure if they will re-allocate
8632 * the data this operator uses, see: #68159.
8633 * Free the data here, then use #point_normals_ensure to add it back on demand. */
8634 if (ret == OPERATOR_PASS_THROUGH) {
8635 /* Don't free on mouse-move, causes creation/freeing of the loop data in an inefficient way. */
8636 if (!ISMOUSE_MOTION(event->type)) {
8638 }
8639 }
8640 return ret;
8641}
8642
8644 wmOperator *op,
8645 const wmEvent * /*event*/)
8646{
8647 if (!point_normals_init(C, op)) {
8649 return OPERATOR_CANCELLED;
8650 }
8651
8653
8655
8658}
8659
8660/* TODO: make this work on multiple objects at once */
8662{
8663 Object *obedit = CTX_data_edit_object(C);
8664
8665 if (!point_normals_init(C, op)) {
8667 return OPERATOR_CANCELLED;
8668 }
8669
8670 /* Note that 'mode' is ignored in exec case,
8671 * we directly use vector stored in target_location, whatever that is. */
8672
8673 float target[3];
8674 RNA_float_get_array(op->ptr, "target_location", target);
8675
8676 point_normals_apply(C, op, target, false);
8677
8679 params.calc_looptris = true;
8680 params.calc_normals = false;
8681 params.is_destructive = false;
8682 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
8684
8685 return OPERATOR_FINISHED;
8686}
8687
8688static bool point_normals_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop, void * /*user_data*/)
8689{
8690 const char *prop_id = RNA_property_identifier(prop);
8691
8692 /* Only show strength option if spherize is enabled. */
8693 if (STREQ(prop_id, "spherize_strength")) {
8694 return RNA_boolean_get(ptr, "spherize");
8695 }
8696
8697 /* Else, show it! */
8698 return true;
8699}
8700
8702{
8703 uiLayout *layout = op->layout;
8705
8707
8708 uiLayoutSetPropSep(layout, true);
8709
8710 /* Main auto-draw call */
8711 uiDefAutoButsRNA(layout,
8712 &ptr,
8714 nullptr,
8715 nullptr,
8717 false);
8718}
8719
8721{
8722 /* identifiers */
8723 ot->name = "Point Normals to Target";
8724 ot->description = "Point selected custom normals to specified Target";
8725 ot->idname = "MESH_OT_point_normals";
8726
8727 /* API callbacks. */
8729 ot->invoke = edbm_point_normals_invoke;
8731 ot->poll = ED_operator_editmesh;
8733 ot->cancel = point_normals_cancel;
8734
8735 /* flags */
8737
8738 ot->prop = RNA_def_enum(ot->srna,
8739 "mode",
8742 "Mode",
8743 "How to define coordinates to point custom normals to");
8745
8746 RNA_def_boolean(ot->srna, "invert", false, "Invert", "Invert affected normals");
8747
8748 RNA_def_boolean(ot->srna, "align", false, "Align", "Make all affected normals parallel");
8749
8751 "target_location",
8752 3,
8753 nullptr,
8754 -FLT_MAX,
8755 FLT_MAX,
8756 "Target",
8757 "Target location to which normals will point",
8758 -1000.0f,
8759 1000.0f);
8760
8762 ot->srna, "spherize", false, "Spherize", "Interpolate between original and new normals");
8763
8764 RNA_def_float(ot->srna,
8765 "spherize_strength",
8766 0.1,
8767 0.0f,
8768 1.0f,
8769 "Spherize Strength",
8770 "Ratio of spherized normal to original normal",
8771 0.0f,
8772 1.0f);
8773}
8774
8776
8777/* -------------------------------------------------------------------- */
8780
8781static void normals_merge(BMesh *bm, BMLoopNorEditDataArray *lnors_ed_arr)
8782{
8783 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
8784
8785 BLI_SMALLSTACK_DECLARE(clnors, short *);
8786
8787 BLI_assert(bm->lnor_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
8788
8790
8791 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
8793
8794 if (BM_elem_flag_test(lnor_ed->loop, BM_ELEM_TAG)) {
8795 continue;
8796 }
8797
8798 MLoopNorSpace *lnor_space = bm->lnor_spacearr->lspacearr[lnor_ed->loop_index];
8799
8800 if ((lnor_space->flags & MLNOR_SPACE_IS_SINGLE) == 0) {
8801 LinkNode *loops = lnor_space->loops;
8802 float avg_normal[3] = {0.0f, 0.0f, 0.0f};
8803 short *clnors_data;
8804
8805 for (; loops; loops = loops->next) {
8806 BMLoop *l = static_cast<BMLoop *>(loops->link);
8807 const int loop_index = BM_elem_index_get(l);
8808
8809 BMLoopNorEditData *lnor_ed_tmp = lnors_ed_arr->lidx_to_lnor_editdata[loop_index];
8810 BLI_assert(lnor_ed_tmp->loop_index == loop_index && lnor_ed_tmp->loop == l);
8811 add_v3_v3(avg_normal, lnor_ed_tmp->nloc);
8812 BLI_SMALLSTACK_PUSH(clnors, lnor_ed_tmp->clnors_data);
8814 }
8815 if (normalize_v3(avg_normal) < CLNORS_VALID_VEC_LEN) {
8816 /* If avg normal is nearly 0, set clnor to default value. */
8817 zero_v3(avg_normal);
8818 }
8819 while ((clnors_data = static_cast<short *>(BLI_SMALLSTACK_POP(clnors)))) {
8820 BKE_lnor_space_custom_normal_to_data(lnor_space, avg_normal, clnors_data);
8821 }
8822 }
8823 }
8824}
8825
8827{
8828 BMFace *f;
8829 BMLoop *l, *l_curr, *l_first;
8830 BMIter fiter;
8831
8832 BLI_assert(bm->lnor_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
8833
8835
8836 BLI_SMALLSTACK_DECLARE(loop_stack, BMLoop *);
8837
8838 const int cd_clnors_offset = CustomData_get_offset_named(
8839 &bm->ldata, CD_PROP_INT16_2D, "custom_normal");
8840 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
8842
8843 l_curr = l_first = BM_FACE_FIRST_LOOP(f);
8844 do {
8845 if (BM_elem_flag_test(l_curr->v, BM_ELEM_SELECT) &&
8846 (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) ||
8848 {
8849 if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
8850 !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG))
8851 {
8852 const int loop_index = BM_elem_index_get(l_curr);
8853 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l_curr, cd_clnors_offset));
8855 bm->lnor_spacearr->lspacearr[loop_index], f->no, clnors);
8856 }
8857 else {
8858 BMVert *v_pivot = l_curr->v;
8859 UNUSED_VARS_NDEBUG(v_pivot);
8860 BMEdge *e_next;
8861 const BMEdge *e_org = l_curr->e;
8862 BMLoop *lfan_pivot, *lfan_pivot_next;
8863
8864 lfan_pivot = l_curr;
8865 e_next = lfan_pivot->e;
8866 float avg_normal[3] = {0.0f};
8867
8868 while (true) {
8869 lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next);
8870 if (lfan_pivot_next) {
8871 BLI_assert(lfan_pivot_next->v == v_pivot);
8872 }
8873 else {
8874 e_next = (lfan_pivot->e == e_next) ? lfan_pivot->prev->e : lfan_pivot->e;
8875 }
8876
8877 BLI_SMALLSTACK_PUSH(loop_stack, lfan_pivot);
8878 add_v3_v3(avg_normal, lfan_pivot->f->no);
8879
8880 if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) {
8881 break;
8882 }
8883 lfan_pivot = lfan_pivot_next;
8884 }
8885 if (normalize_v3(avg_normal) < CLNORS_VALID_VEC_LEN) {
8886 /* If avg normal is nearly 0, set clnor to default value. */
8887 zero_v3(avg_normal);
8888 }
8889 while ((l = static_cast<BMLoop *>(BLI_SMALLSTACK_POP(loop_stack)))) {
8890 const int l_index = BM_elem_index_get(l);
8891 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
8893 bm->lnor_spacearr->lspacearr[l_index], avg_normal, clnors);
8894 }
8895 }
8896 }
8897 } while ((l_curr = l_curr->next) != l_first);
8898 }
8899}
8900
8901static wmOperatorStatus normals_split_merge(bContext *C, const bool do_merge)
8902{
8903 const Scene *scene = CTX_data_scene(C);
8904 ViewLayer *view_layer = CTX_data_view_layer(C);
8906 scene, view_layer, CTX_wm_view3d(C));
8907
8908 for (Object *obedit : objects) {
8910 BMesh *bm = em->bm;
8911 BMEdge *e;
8912 BMIter eiter;
8913
8915
8916 /* Note that we need temp lnor editing data for all loops of all affected vertices, since by
8917 * setting some faces/edges as smooth we are going to change clnors spaces... See also #65809.
8918 */
8919 BMLoopNorEditDataArray *lnors_ed_arr = do_merge ?
8921 nullptr;
8922
8923 mesh_set_smooth_faces(em, do_merge);
8924
8925 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
8927 BM_elem_flag_set(e, BM_ELEM_SMOOTH, do_merge);
8928 }
8929 }
8930
8931 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
8933
8934 if (do_merge) {
8935 normals_merge(bm, lnors_ed_arr);
8936 }
8937 else {
8939 }
8940
8941 if (lnors_ed_arr) {
8943 }
8944
8946 params.calc_looptris = true;
8947 params.calc_normals = false;
8948 params.is_destructive = false;
8949 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
8950 }
8951
8952 return OPERATOR_FINISHED;
8953}
8954
8956{
8957 return normals_split_merge(C, true);
8958}
8959
8961{
8962 /* identifiers */
8963 ot->name = "Merge Normals";
8964 ot->description = "Merge custom normals of selected vertices";
8965 ot->idname = "MESH_OT_merge_normals";
8966
8967 /* API callbacks. */
8969 ot->poll = ED_operator_editmesh;
8970
8971 /* flags */
8972 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
8973}
8974
8976{
8977 return normals_split_merge(C, false);
8978}
8979
8981{
8982 /* identifiers */
8983 ot->name = "Split Normals";
8984 ot->description = "Split custom normals of selected vertices";
8985 ot->idname = "MESH_OT_split_normals";
8986
8987 /* API callbacks. */
8989 ot->poll = ED_operator_editmesh;
8990
8991 /* flags */
8992 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
8993}
8994
8996
8997/* -------------------------------------------------------------------- */
9000
9001enum {
9005};
9006
9009 "CUSTOM_NORMAL",
9010 0,
9011 "Custom Normal",
9012 "Take average of vertex normals"},
9014 "FACE_AREA",
9015 0,
9016 "Face Area",
9017 "Set all vertex normals by face area"},
9019 "CORNER_ANGLE",
9020 0,
9021 "Corner Angle",
9022 "Set all vertex normals by corner angle"},
9023 {0, nullptr, 0, nullptr, nullptr},
9024};
9025
9027{
9028 const Scene *scene = CTX_data_scene(C);
9029 ViewLayer *view_layer = CTX_data_view_layer(C);
9031 scene, view_layer, CTX_wm_view3d(C));
9032 const int average_type = RNA_enum_get(op->ptr, "average_type");
9033 const float absweight = float(RNA_int_get(op->ptr, "weight"));
9034 const float threshold = RNA_float_get(op->ptr, "threshold");
9035
9036 HeapSimple *loop_weight = BLI_heapsimple_new();
9037 BLI_SMALLSTACK_DECLARE(loop_stack, BMLoop *);
9038
9039 for (uint ob_index = 0; ob_index < objects.size(); ob_index++) {
9041 BLI_assert(BLI_heapsimple_is_empty(loop_weight));
9042
9043 Object *obedit = objects[ob_index];
9045 BMesh *bm = em->bm;
9046 BMFace *f;
9047 BMLoop *l, *l_curr, *l_first;
9048 BMIter fiter;
9049
9050 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
9052
9053 const int cd_clnors_offset = CustomData_get_offset_named(
9054 &bm->ldata, CD_PROP_INT16_2D, "custom_normal");
9055
9056 float weight = absweight / 50.0f;
9057 if (absweight == 100.0f) {
9058 weight = float(SHRT_MAX);
9059 }
9060 else if (absweight == 1.0f) {
9061 weight = 1 / float(SHRT_MAX);
9062 }
9063 else if ((weight - 1) * 25 > 1) {
9064 weight = (weight - 1) * 25;
9065 }
9066
9068
9069 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
9070 l_curr = l_first = BM_FACE_FIRST_LOOP(f);
9071 do {
9072 if (BM_elem_flag_test(l_curr->v, BM_ELEM_SELECT) &&
9073 (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) ||
9075 {
9076 if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
9077 !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG))
9078 {
9079 const int loop_index = BM_elem_index_get(l_curr);
9080 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l_curr, cd_clnors_offset));
9082 bm->lnor_spacearr->lspacearr[loop_index], f->no, clnors);
9083 }
9084 else {
9085 BMVert *v_pivot = l_curr->v;
9086 UNUSED_VARS_NDEBUG(v_pivot);
9087 BMEdge *e_next;
9088 const BMEdge *e_org = l_curr->e;
9089 BMLoop *lfan_pivot, *lfan_pivot_next;
9090
9091 lfan_pivot = l_curr;
9092 e_next = lfan_pivot->e;
9093
9094 while (true) {
9095 lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next);
9096 if (lfan_pivot_next) {
9097 BLI_assert(lfan_pivot_next->v == v_pivot);
9098 }
9099 else {
9100 e_next = (lfan_pivot->e == e_next) ? lfan_pivot->prev->e : lfan_pivot->e;
9101 }
9102
9103 float val = 1.0f;
9104 if (average_type == EDBM_CLNOR_AVERAGE_FACE_AREA) {
9105 val = 1.0f / BM_face_calc_area(lfan_pivot->f);
9106 }
9107 else if (average_type == EDBM_CLNOR_AVERAGE_ANGLE) {
9108 val = 1.0f / BM_loop_calc_face_angle(lfan_pivot);
9109 }
9110
9111 BLI_heapsimple_insert(loop_weight, val, lfan_pivot);
9112
9113 if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) {
9114 break;
9115 }
9116 lfan_pivot = lfan_pivot_next;
9117 }
9118
9119 float wnor[3], avg_normal[3] = {0.0f}, count = 0;
9120 float val = BLI_heapsimple_top_value(loop_weight);
9121
9122 while (!BLI_heapsimple_is_empty(loop_weight)) {
9123 const float cur_val = BLI_heapsimple_top_value(loop_weight);
9124 if (!compare_ff(val, cur_val, threshold)) {
9125 count++;
9126 val = cur_val;
9127 }
9128 l = static_cast<BMLoop *>(BLI_heapsimple_pop_min(loop_weight));
9129 BLI_SMALLSTACK_PUSH(loop_stack, l);
9130
9131 const float n_weight = pow(weight, count);
9132
9133 if (average_type == EDBM_CLNOR_AVERAGE_LOOP) {
9134 const int l_index = BM_elem_index_get(l);
9135 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
9137 bm->lnor_spacearr->lspacearr[l_index], clnors, wnor);
9138 }
9139 else {
9140 copy_v3_v3(wnor, l->f->no);
9141 }
9142 mul_v3_fl(wnor, (1.0f / cur_val) * (1.0f / n_weight));
9143 add_v3_v3(avg_normal, wnor);
9144 }
9145
9146 if (normalize_v3(avg_normal) < CLNORS_VALID_VEC_LEN) {
9147 /* If avg normal is nearly 0, set clnor to default value. */
9148 zero_v3(avg_normal);
9149 }
9150 while ((l = static_cast<BMLoop *>(BLI_SMALLSTACK_POP(loop_stack)))) {
9151 const int l_index = BM_elem_index_get(l);
9152 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
9154 bm->lnor_spacearr->lspacearr[l_index], avg_normal, clnors);
9155 }
9156 }
9157 }
9158 } while ((l_curr = l_curr->next) != l_first);
9159 }
9160
9162 params.calc_looptris = true;
9163 params.calc_normals = false;
9164 params.is_destructive = false;
9165 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
9166 }
9167
9168 BLI_heapsimple_free(loop_weight, nullptr);
9169
9170 return OPERATOR_FINISHED;
9171}
9172
9174 PropertyRNA *prop,
9175 void * /*user_data*/)
9176{
9177 const char *prop_id = RNA_property_identifier(prop);
9178 const int average_type = RNA_enum_get(ptr, "average_type");
9179
9180 /* Only show weight/threshold options when not in loop average type. */
9181 const bool is_clor_average_loop = average_type == EDBM_CLNOR_AVERAGE_LOOP;
9182 if (STREQ(prop_id, "weight")) {
9183 return !is_clor_average_loop;
9184 }
9185 if (STREQ(prop_id, "threshold")) {
9186 return !is_clor_average_loop;
9187 }
9188
9189 /* Else, show it! */
9190 return true;
9191}
9192
9194{
9195 uiLayout *layout = op->layout;
9197
9199
9200 uiLayoutSetPropSep(layout, true);
9201
9202 /* Main auto-draw call */
9203 uiDefAutoButsRNA(layout,
9204 &ptr,
9206 nullptr,
9207 nullptr,
9209 false);
9210}
9211
9213{
9214 /* identifiers */
9215 ot->name = "Average Normals";
9216 ot->description = "Average custom normals of selected vertices";
9217 ot->idname = "MESH_OT_average_normals";
9218
9219 /* API callbacks. */
9221 ot->poll = ED_operator_editmesh;
9223
9224 /* flags */
9225 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9226
9227 ot->prop = RNA_def_enum(ot->srna,
9228 "average_type",
9231 "Type",
9232 "Averaging method");
9233
9234 RNA_def_int(ot->srna, "weight", 50, 1, 100, "Weight", "Weight applied per face", 1, 100);
9235
9236 RNA_def_float(ot->srna,
9237 "threshold",
9238 0.01f,
9239 0,
9240 10,
9241 "Threshold",
9242 "Threshold value for different weights to be considered equal",
9243 0,
9244 5);
9245}
9246
9248
9249/* -------------------------------------------------------------------- */
9252
9253enum {
9259};
9260
9262 {EDBM_CLNOR_TOOLS_COPY, "COPY", 0, "Copy Normal", "Copy normal to the internal clipboard"},
9264 "PASTE",
9265 0,
9266 "Paste Normal",
9267 "Paste normal from the internal clipboard"},
9268 {EDBM_CLNOR_TOOLS_ADD, "ADD", 0, "Add Normal", "Add normal vector with selection"},
9270 "MULTIPLY",
9271 0,
9272 "Multiply Normal",
9273 "Multiply normal vector with selection"},
9275 "RESET",
9276 0,
9277 "Reset Normal",
9278 "Reset the internal clipboard and/or normal of selected element"},
9279 {0, nullptr, 0, nullptr, nullptr},
9280};
9281
9283{
9284 Scene *scene = CTX_data_scene(C);
9285 ViewLayer *view_layer = CTX_data_view_layer(C);
9287 scene, view_layer, CTX_wm_view3d(C));
9288 const int mode = RNA_enum_get(op->ptr, "mode");
9289 const bool absolute = RNA_boolean_get(op->ptr, "absolute");
9290 float *normal_vector = scene->toolsettings->normal_vector;
9291 bool done_copy = false;
9292
9293 for (Object *obedit : objects) {
9295 BMesh *bm = em->bm;
9296
9297 if (bm->totloop == 0) {
9298 continue;
9299 }
9300
9303 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
9304
9305 switch (mode) {
9307 if (bm->totfacesel == 0 && bm->totvertsel == 0) {
9309 continue;
9310 }
9311
9312 if (done_copy ||
9313 (bm->totfacesel != 1 && lnors_ed_arr->totloop != 1 && bm->totvertsel != 1))
9314 {
9315 BKE_report(op->reports,
9316 RPT_ERROR,
9317 "Can only copy one custom normal, vertex normal or face normal");
9319 continue;
9320 }
9321 if (lnors_ed_arr->totloop == 1) {
9322 copy_v3_v3(scene->toolsettings->normal_vector, lnors_ed_arr->lnor_editdata->nloc);
9323 }
9324 else if (bm->totfacesel == 1) {
9325 BMFace *f;
9326 BMIter fiter;
9327 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
9330 }
9331 }
9332 }
9333 else {
9334 /* 'Vertex' normal, i.e. common set of loop normals on the same vertex,
9335 * only if they are all the same. */
9336 bool are_same_lnors = true;
9337 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9338 if (!compare_v3v3(lnors_ed_arr->lnor_editdata->nloc, lnor_ed->nloc, 1e-4f)) {
9339 are_same_lnors = false;
9340 }
9341 }
9342 if (are_same_lnors) {
9343 copy_v3_v3(scene->toolsettings->normal_vector, lnors_ed_arr->lnor_editdata->nloc);
9344 }
9345 }
9346 done_copy = true;
9347 break;
9348
9350 if (!absolute) {
9351 if (normalize_v3(normal_vector) < CLNORS_VALID_VEC_LEN) {
9352 /* If normal is nearly 0, do nothing. */
9353 break;
9354 }
9355 }
9356 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9357 if (absolute) {
9358 float abs_normal[3];
9359 copy_v3_v3(abs_normal, lnor_ed->loc);
9360 negate_v3(abs_normal);
9361 add_v3_v3(abs_normal, normal_vector);
9362
9363 if (normalize_v3(abs_normal) < CLNORS_VALID_VEC_LEN) {
9364 /* If abs normal is nearly 0, set clnor to initial value. */
9365 copy_v3_v3(abs_normal, lnor_ed->niloc);
9366 }
9367 BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index],
9368 abs_normal,
9369 lnor_ed->clnors_data);
9370 }
9371 else {
9372 BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index],
9373 normal_vector,
9374 lnor_ed->clnors_data);
9375 }
9376 }
9377 break;
9378
9380 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9381 mul_v3_v3(lnor_ed->nloc, normal_vector);
9382
9383 if (normalize_v3(lnor_ed->nloc) < CLNORS_VALID_VEC_LEN) {
9384 /* If abs normal is nearly 0, set clnor to initial value. */
9385 copy_v3_v3(lnor_ed->nloc, lnor_ed->niloc);
9386 }
9387 BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index],
9388 lnor_ed->nloc,
9389 lnor_ed->clnors_data);
9390 }
9391 break;
9392
9394 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9395 add_v3_v3(lnor_ed->nloc, normal_vector);
9396
9397 if (normalize_v3(lnor_ed->nloc) < CLNORS_VALID_VEC_LEN) {
9398 /* If abs normal is nearly 0, set clnor to initial value. */
9399 copy_v3_v3(lnor_ed->nloc, lnor_ed->niloc);
9400 }
9401 BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index],
9402 lnor_ed->nloc,
9403 lnor_ed->clnors_data);
9404 }
9405 break;
9406
9408 zero_v3(normal_vector);
9409 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9410 BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index],
9411 normal_vector,
9412 lnor_ed->clnors_data);
9413 }
9414 break;
9415
9416 default:
9417 BLI_assert(0);
9418 break;
9419 }
9420
9422
9424 params.calc_looptris = true;
9425 params.calc_normals = false;
9426 params.is_destructive = false;
9427 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
9428 }
9429
9430 return OPERATOR_FINISHED;
9431}
9432
9433static bool normals_tools_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop, void * /*user_data*/)
9434{
9435 const char *prop_id = RNA_property_identifier(prop);
9436 const int mode = RNA_enum_get(ptr, "mode");
9437
9438 /* Only show absolute option in paste mode. */
9439 if (STREQ(prop_id, "absolute")) {
9440 return (mode == EDBM_CLNOR_TOOLS_PASTE);
9441 }
9442
9443 /* Else, show it! */
9444 return true;
9445}
9446
9448{
9449 uiLayout *layout = op->layout;
9451
9453
9454 /* Main auto-draw call */
9455 uiDefAutoButsRNA(layout,
9456 &ptr,
9458 nullptr,
9459 nullptr,
9461 false);
9462}
9463
9465{
9466 /* identifiers */
9467 ot->name = "Normals Vector Tools";
9468 ot->description = "Custom normals tools using Normal Vector of UI";
9469 ot->idname = "MESH_OT_normals_tools";
9470
9471 /* API callbacks. */
9473 ot->poll = ED_operator_editmesh;
9475
9476 /* flags */
9477 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9478
9479 ot->prop = RNA_def_enum(ot->srna,
9480 "mode",
9483 "Mode",
9484 "Mode of tools taking input from interface");
9486
9487 RNA_def_boolean(ot->srna,
9488 "absolute",
9489 false,
9490 "Absolute Coordinates",
9491 "Copy Absolute coordinates of Normal vector");
9492}
9493
9495
9496/* -------------------------------------------------------------------- */
9499
9501{
9502 const Scene *scene = CTX_data_scene(C);
9503 ViewLayer *view_layer = CTX_data_view_layer(C);
9505 scene, view_layer, CTX_wm_view3d(C));
9506
9507 for (Object *obedit : objects) {
9509 BMesh *bm = em->bm;
9510 if (bm->totfacesel == 0) {
9511 continue;
9512 }
9513
9514 BMFace *f;
9515 BMVert *v;
9516 BMEdge *e;
9517 BMLoop *l;
9518 BMIter fiter, viter, eiter, liter;
9519
9520 const bool keep_sharp = RNA_boolean_get(op->ptr, "keep_sharp");
9521
9523
9524 float(*vert_normals)[3] = static_cast<float(*)[3]>(
9525 MEM_mallocN(sizeof(*vert_normals) * bm->totvert, __func__));
9526 {
9527 int v_index;
9528 BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, v_index) {
9529 BM_vert_calc_normal_ex(v, BM_ELEM_SELECT, vert_normals[v_index]);
9530 }
9531 }
9532
9533 BLI_bitmap *loop_set = BLI_BITMAP_NEW(bm->totloop, __func__);
9534 const int cd_clnors_offset = CustomData_get_offset_named(
9535 &bm->ldata, CD_PROP_INT16_2D, "custom_normal");
9536
9537 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
9538 BM_ITER_ELEM (e, &eiter, f, BM_EDGES_OF_FACE) {
9539 if (!keep_sharp ||
9541 {
9542 BM_ITER_ELEM (v, &viter, e, BM_VERTS_OF_EDGE) {
9544 const int l_index = BM_elem_index_get(l);
9545 const int v_index = BM_elem_index_get(l->v);
9546
9547 if (!is_zero_v3(vert_normals[v_index])) {
9548 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
9550 bm->lnor_spacearr->lspacearr[l_index], vert_normals[v_index], clnors);
9551
9552 if (bm->lnor_spacearr->lspacearr[l_index]->flags & MLNOR_SPACE_IS_SINGLE) {
9553 BLI_BITMAP_ENABLE(loop_set, l_index);
9554 }
9555 else {
9556 LinkNode *loops = bm->lnor_spacearr->lspacearr[l_index]->loops;
9557 for (; loops; loops = loops->next) {
9558 BLI_BITMAP_ENABLE(loop_set, BM_elem_index_get((BMLoop *)loops->link));
9559 }
9560 }
9561 }
9562 }
9563 }
9564 }
9565 }
9566
9567 int v_index;
9568 BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, v_index) {
9569 BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
9570 if (BLI_BITMAP_TEST(loop_set, BM_elem_index_get(l))) {
9571 const int loop_index = BM_elem_index_get(l);
9572 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
9574 bm->lnor_spacearr->lspacearr[loop_index], vert_normals[v_index], clnors);
9575 }
9576 }
9577 }
9578
9579 MEM_freeN(loop_set);
9580 MEM_freeN(vert_normals);
9582 params.calc_looptris = true;
9583 params.calc_normals = false;
9584 params.is_destructive = false;
9585 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
9586 }
9587
9588 return OPERATOR_FINISHED;
9589}
9590
9592{
9593 /* identifiers */
9594 ot->name = "Set Normals from Faces";
9595 ot->description = "Set the custom normals from the selected faces ones";
9596 ot->idname = "MESH_OT_set_normals_from_faces";
9597
9598 /* API callbacks. */
9600 ot->poll = ED_operator_editmesh;
9601
9602 /* flags */
9603 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9604
9606 ot->srna, "keep_sharp", false, "Keep Sharp Edges", "Do not set sharp edges to face");
9607}
9608
9610
9611/* -------------------------------------------------------------------- */
9614
9616{
9617 const Scene *scene = CTX_data_scene(C);
9618 ViewLayer *view_layer = CTX_data_view_layer(C);
9620 scene, view_layer, CTX_wm_view3d(C));
9621
9622 for (Object *obedit : objects) {
9624 BMesh *bm = em->bm;
9625 BMFace *f;
9626 BMLoop *l;
9627 BMIter fiter, liter;
9628
9631
9632 float(*smooth_normal)[3] = static_cast<float(*)[3]>(
9633 MEM_callocN(sizeof(*smooth_normal) * lnors_ed_arr->totloop, __func__));
9634
9635 /* NOTE(@mont29): This is weird choice of operation, taking all loops of faces of current
9636 * vertex. Could lead to some rather far away loops weighting as much as very close ones
9637 * (topologically speaking), with complex polygons.
9638 * Using topological distance here (rather than geometrical one)
9639 * makes sense IMHO, but would rather go with a more consistent and flexible code,
9640 * we could even add max topological distance to take into account, and a weighting curve.
9641 * Would do that later though, think for now we can live with that choice. */
9642 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
9643 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9644 l = lnor_ed->loop;
9645 float loop_normal[3];
9646
9647 BM_ITER_ELEM (f, &fiter, l->v, BM_FACES_OF_VERT) {
9648 BMLoop *l_other;
9649 BM_ITER_ELEM (l_other, &liter, f, BM_LOOPS_OF_FACE) {
9650 const int l_index_other = BM_elem_index_get(l_other);
9651 short *clnors = static_cast<short *>(
9652 BM_ELEM_CD_GET_VOID_P(l_other, lnors_ed_arr->cd_custom_normal_offset));
9654 bm->lnor_spacearr->lspacearr[l_index_other], clnors, loop_normal);
9655 add_v3_v3(smooth_normal[i], loop_normal);
9656 }
9657 }
9658 }
9659
9660 const float factor = RNA_float_get(op->ptr, "factor");
9661
9662 lnor_ed = lnors_ed_arr->lnor_editdata;
9663 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9664 float current_normal[3];
9665
9666 if (normalize_v3(smooth_normal[i]) < CLNORS_VALID_VEC_LEN) {
9667 /* Skip in case the smooth normal is invalid. */
9668 continue;
9669 }
9670
9672 bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->clnors_data, current_normal);
9673
9674 /* NOTE: again, this is not true spherical interpolation that normals would need...
9675 * But it's probably good enough for now. */
9676 mul_v3_fl(current_normal, 1.0f - factor);
9677 mul_v3_fl(smooth_normal[i], factor);
9678 add_v3_v3(current_normal, smooth_normal[i]);
9679
9680 if (normalize_v3(current_normal) < CLNORS_VALID_VEC_LEN) {
9681 /* Skip in case the smoothed normal is invalid. */
9682 continue;
9683 }
9684
9686 bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], current_normal, lnor_ed->clnors_data);
9687 }
9688
9690 MEM_freeN(smooth_normal);
9691
9693 params.calc_looptris = true;
9694 params.calc_normals = false;
9695 params.is_destructive = false;
9696 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
9697 }
9698
9699 return OPERATOR_FINISHED;
9700}
9701
9703{
9704 /* identifiers */
9705 ot->name = "Smooth Normals Vectors";
9706 ot->description = "Smooth custom normals based on adjacent vertex normals";
9707 ot->idname = "MESH_OT_smooth_normals";
9708
9709 /* API callbacks. */
9711 ot->poll = ED_operator_editmesh;
9712
9713 /* flags */
9714 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9715
9716 RNA_def_float(ot->srna,
9717 "factor",
9718 0.5f,
9719 0.0f,
9720 1.0f,
9721 "Factor",
9722 "Specifies weight of smooth vs original normal",
9723 0.0f,
9724 1.0f);
9725}
9726
9728
9729/* -------------------------------------------------------------------- */
9732
9734{
9735 const Scene *scene = CTX_data_scene(C);
9736 ViewLayer *view_layer = CTX_data_view_layer(C);
9738 scene, view_layer, CTX_wm_view3d(C));
9739
9740 for (Object *obedit : objects) {
9742 BMesh *bm = em->bm;
9743 BMFace *f;
9744 BMIter fiter;
9745 const int face_strength = RNA_enum_get(op->ptr, "face_strength");
9746 const bool set = RNA_boolean_get(op->ptr, "set");
9747
9749
9750 const char *layer_id = MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID;
9751 int cd_prop_int_index = CustomData_get_named_layer_index(&bm->pdata, CD_PROP_INT32, layer_id);
9752 if (cd_prop_int_index == -1) {
9753 BM_data_layer_add_named(bm, &bm->pdata, CD_PROP_INT32, layer_id);
9754 cd_prop_int_index = CustomData_get_named_layer_index(&bm->pdata, CD_PROP_INT32, layer_id);
9755 }
9756 cd_prop_int_index -= CustomData_get_layer_index(&bm->pdata, CD_PROP_INT32);
9757 const int cd_prop_int_offset = CustomData_get_n_offset(
9758 &bm->pdata, CD_PROP_INT32, cd_prop_int_index);
9759
9761
9762 if (set) {
9763 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
9765 int *strength = static_cast<int *>(BM_ELEM_CD_GET_VOID_P(f, cd_prop_int_offset));
9766 *strength = face_strength;
9767 }
9768 }
9769 }
9770 else {
9771 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
9772 const int *strength = static_cast<int *>(BM_ELEM_CD_GET_VOID_P(f, cd_prop_int_offset));
9773 if (*strength == face_strength) {
9774 BM_face_select_set(bm, f, true);
9776 }
9777 else {
9778 BM_face_select_set(bm, f, false);
9779 }
9780 }
9781 }
9782
9784 params.calc_looptris = false;
9785 params.calc_normals = false;
9786 params.is_destructive = false;
9787 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
9788 }
9789
9790 return OPERATOR_FINISHED;
9791}
9792
9794 {FACE_STRENGTH_WEAK, "WEAK", 0, "Weak", ""},
9795 {FACE_STRENGTH_MEDIUM, "MEDIUM", 0, "Medium", ""},
9796 {FACE_STRENGTH_STRONG, "STRONG", 0, "Strong", ""},
9797 {0, nullptr, 0, nullptr, nullptr},
9798};
9799
9801{
9802 /* identifiers */
9803 ot->name = "Face Normals Strength";
9804 ot->description = "Set/Get strength of face (used in Weighted Normal modifier)";
9805 ot->idname = "MESH_OT_mod_weighted_strength";
9806
9807 /* API callbacks. */
9809 ot->poll = ED_operator_editmesh;
9810
9811 /* flags */
9812 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9813
9814 ot->prop = RNA_def_boolean(ot->srna, "set", false, "Set Value", "Set value of faces");
9815
9816 ot->prop = RNA_def_enum(
9817 ot->srna,
9818 "face_strength",
9821 "Face Strength",
9822 "Strength to use for assigning or selecting face influence for weighted normal modifier");
9823}
9824
9826{
9827 /* identifiers */
9828 ot->name = "Flip Quad Tessellation";
9829 ot->description = "Flips the tessellation of selected quads";
9830 ot->idname = "MESH_OT_flip_quad_tessellation";
9831
9833 ot->poll = ED_operator_editmesh;
9834
9835 /* flags */
9836 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9837}
9838
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:932
@ ATTR_DOMAIN_MASK_CORNER
const struct CustomDataLayer * BKE_attribute_search(const AttributeOwner &owner, blender::StringRef name, eCustomDataMask type, AttrDomainMask domain_mask)
Definition attribute.cc:657
#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)
void * CustomData_bmesh_get(const CustomData *data, void *block, eCustomDataType type)
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:596
float BKE_defvert_find_weight(const MDeformVert *dvert, int defgroup)
Definition deform.cc:763
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:1949
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:2503
void id_us_plus(ID *id)
Definition lib_id.cc:353
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_SPACEARR_BMLOOP_PTR
Definition BKE_mesh.h:293
void BKE_lnor_space_custom_normal_to_data(const MLoopNorSpace *lnor_space, const float custom_lnor[3], short r_clnor_data[2])
@ MLNOR_SPACE_IS_SINGLE
Definition BKE_mesh.h:271
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
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
#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)
#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:1009
@ ID_RECALC_GEOMETRY_ALL_MODES
Definition DNA_ID.h:1089
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
@ CD_MDEFORMVERT
@ CD_PROP_INT32
@ CD_FREESTYLE_EDGE
@ CD_FREESTYLE_FACE
@ CD_PROP_INT16_2D
#define MAXMAT
@ ME_SYMMETRY_X
@ ME_EDIT_MIRROR_TOPO
@ FREESTYLE_EDGE_MARK
@ FREESTYLE_FACE_MARK
@ eModifierMode_Realtime
@ MOD_MIR_AXIS_Z
@ MOD_MIR_CLIPPING
@ MOD_MIR_AXIS_X
@ MOD_MIR_AXIS_Y
@ MOD_TRIANGULATE_NGON_BEAUTY
@ eModifierType_Mirror
#define MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID
@ MOD_TRIANGULATE_QUAD_BEAUTY
Object is a sort of wrapper for general info.
@ OB_MESH
@ PROP_SMOOTH
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
eDupli_ID_Flags
@ USER_DUP_MESH
@ USER_DUP_ACT
@ V3D_AROUND_ACTIVE
@ V3D_AROUND_CENTER_BOUNDS
@ V3D_AROUND_CURSOR
@ V3D_AROUND_CENTER_MEDIAN
@ OP_IS_REPEAT_LAST
@ OP_IS_MODAL_GRAB_CURSOR
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
void EDBM_flag_disable_all(BMEditMesh *em, char hflag)
void EDBM_automerge(Object *obedit, bool update, char hflag, float dist)
void void EDBM_redo_state_restore_and_free(BMBackup *backup, BMEditMesh *em, bool recalc_looptris) ATTR_NONNULL(1
void EDBM_update(Mesh *mesh, const EDBMUpdate_Params *params)
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)
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)
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)
void EDBM_select_flush(BMEditMesh *em)
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:1040
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.
@ PROP_ENUM
Definition RNA_types.hh:154
PropertyFlag
Definition RNA_types.hh:286
@ PROP_NEVER_UNLINK
Definition RNA_types.hh:358
@ PROP_ENUM_NO_TRANSLATE
Definition RNA_types.hh:406
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
@ PROP_HIDDEN
Definition RNA_types.hh:324
@ PROP_NONE
Definition RNA_types.hh:221
#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
void uiLayoutSetActive(uiLayout *layout, bool active)
void uiItemPointerR(uiLayout *layout, PointerRNA *ptr, blender::StringRefNull propname, PointerRNA *searchptr, blender::StringRefNull searchpropname, std::optional< blender::StringRefNull > name, int icon)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
#define NC_GEOM
Definition WM_types.hh:390
#define ND_DRAW
Definition WM_types.hh:458
#define ND_DATA
Definition WM_types.hh:506
ReportList * reports
Definition WM_types.hh:1025
#define ND_SELECT
Definition WM_types.hh:505
#define NC_OBJECT
Definition WM_types.hh:376
@ 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 U
#define BM_elem_cb_check_hflag_disabled_simple(type, hflag_n)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SELECT
@ BM_ELEM_SMOOTH
@ BM_ELEM_TAG
#define BM_DISK_EDGE_NEXT(e, v)
#define BM_ALL_NOLOOP
@ BM_SPACEARR_DIRTY_ALL
#define BM_ALL
#define BM_FACE_FIRST_LOOP(p)
#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_add(BMesh *bm, CustomData *data, int type)
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)
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)
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)
@ 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)
@ BMO_FLAG_RESPECT_HIDE
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
@ DEL_ONLYTAGGED
@ DEL_FACES_KEEP_BOUNDARY
@ DEL_EDGESFACES
@ DEL_ONLYFACES
@ BMO_DELIM_NORMAL
BLI_INLINE void BMO_slot_map_elem_insert(BMOperator *op, BMOpSlot *slot, const void *element, void *val)
@ FACE_STRENGTH_STRONG
@ FACE_STRENGTH_WEAK
@ FACE_STRENGTH_MEDIUM
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)
@ SUBDIV_SELECT_ORIG
@ SUBD_FALLOFF_LIN
@ SUBD_CORNER_FAN
@ SUBD_CORNER_STRAIGHT_CUT
@ SUBD_CORNER_PATH
@ SUBD_CORNER_INNERVERT
@ SUBD_RING_INTERP_SURF
@ SUBD_RING_INTERP_PATH
@ SUBD_RING_INTERP_LINEAR
@ BMOP_POKE_MEDIAN_WEIGHTED
@ BMOP_POKE_BOUNDS
@ BMOP_POKE_MEDIAN
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:43
void opmodal(std::string text, const wmOperatorType *ot, int propvalue, bool inverted=false)
Definition area.cc:1005
const T * data() const
Definition BLI_array.hh:301
int64_t size() const
IndexRange index_range() const
#define offsetof(t, d)
#define fabsf(x)
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)
@ SRT_VIEW_XAXIS
@ SRT_SELECTED
@ SRT_RANDOMIZE
@ SRT_MATERIAL
@ SRT_CURSOR_DISTANCE
@ SRT_REVERSE
@ SRT_VIEW_ZAXIS
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)
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)
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[]
@ EDBM_CLNOR_AVERAGE_LOOP
@ EDBM_CLNOR_AVERAGE_FACE_AREA
@ EDBM_CLNOR_AVERAGE_ANGLE
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)
@ MESH_SEPARATE_LOOSE
@ MESH_SEPARATE_MATERIAL
@ MESH_SEPARATE_SELECTED
void MESH_OT_delete(wmOperatorType *ot)
static wmOperatorStatus edbm_mod_weighted_strength_exec(bContext *C, wmOperator *op)
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)
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)
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)
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)
@ 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 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)
static bool edbm_add_edge_face__smooth_get(BMesh *bm)
static bool average_normals_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop, void *)
@ MESH_BRIDGELOOP_PAIRS
@ MESH_BRIDGELOOP_SINGLE
@ MESH_BRIDGELOOP_CLOSED
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)
@ MESH_DELETE_EDGE_FACE
@ MESH_DELETE_EDGE
@ MESH_DELETE_FACE
@ MESH_DELETE_VERT
@ MESH_DELETE_ONLY_FACE
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)
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)
@ EDBM_CLNOR_TOOLS_RESET
@ EDBM_CLNOR_TOOLS_COPY
@ EDBM_CLNOR_TOOLS_ADD
@ EDBM_CLNOR_TOOLS_MULTIPLY
@ EDBM_CLNOR_TOOLS_PASTE
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
@ EDBM_CLNOR_POINTTO_MODE_MOUSE
@ EDBM_CLNOR_POINTTO_MODE_COORDINATES
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)
static wmOperatorStatus edbm_split_normals_exec(bContext *C, wmOperator *)
@ MESH_MERGE_LAST
@ MESH_MERGE_CENTER
@ MESH_MERGE_CURSOR
@ MESH_MERGE_FIRST
@ MESH_MERGE_COLLAPSE
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)
#define MEM_SAFE_FREE(v)
#define CD_MASK_COLOR_ALL
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
return ret
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
BMHeader head
BMVert * v1
BMVert * v2
struct BMLoop * l
short selectmode
struct BMEditSelection * next
BMHeader head
short mat_nr
BMHeader head
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:623
const char * name
Definition RNA_types.hh:627
Definition DNA_ID.h:404
char name[66]
Definition DNA_ID.h:415
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:264
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]
uiLayout & column(bool align)
uiLayout & row(bool align)
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:754
short val
Definition WM_types.hh:756
int mval[2]
Definition WM_types.hh:760
const void * modal_items
StructRNA * srna
Definition WM_types.hh:1124
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:4227
wmOperatorType * ot
Definition wm_files.cc:4226
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:929
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:956
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:139