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
4070
4071 /* Take into account user preferences for duplicating actions. */
4072 const eDupli_ID_Flags dupflag = eDupli_ID_Flags(USER_DUP_MESH | (U.dupflag & USER_DUP_ACT));
4073 Base *base_new = blender::ed::object::add_duplicate(bmain, scene, view_layer, base_old, dupflag);
4074
4075 /* normally would call directly after but in this case delay recalc */
4076 // DAG_relations_tag_update(bmain);
4077
4078 /* new in 2.5 */
4080 base_new->object,
4083 false);
4084
4086
4087 BMO_op_callf(bm_old,
4089 "duplicate geom=%hvef dest=%p",
4091 bm_new);
4092 BMO_op_callf(bm_old,
4094 "delete geom=%hvef context=%i",
4096 DEL_FACES);
4097
4098 /* deselect loose data - this used to get deleted,
4099 * we could de-select edges and verts only, but this turns out to be less complicated
4100 * since de-selecting all skips selection flushing logic */
4102
4103 BM_mesh_normals_update(bm_new);
4104
4105 BMeshToMeshParams to_mesh_params{};
4106 BM_mesh_bm_to_me(bmain, bm_new, static_cast<Mesh *>(base_new->object->data), &to_mesh_params);
4107
4108 BM_mesh_free(bm_new);
4109 ((Mesh *)base_new->object->data)->runtime->edit_mesh = nullptr;
4110
4111 return base_new;
4112}
4113
4115 Scene *scene,
4116 ViewLayer *view_layer,
4117 Base *base_old,
4118 BMesh *bm_old,
4119 BMVert **verts,
4120 uint verts_len,
4121 BMEdge **edges,
4122 uint edges_len,
4123 BMFace **faces,
4124 uint faces_len)
4125{
4126 BMAllocTemplate bm_new_allocsize{};
4127 bm_new_allocsize.totvert = verts_len;
4128 bm_new_allocsize.totedge = edges_len;
4129 bm_new_allocsize.totloop = faces_len * 3;
4130 bm_new_allocsize.totface = faces_len;
4131
4132 const bool use_custom_normals = (bm_old->lnor_spacearr != nullptr);
4133
4134 Object *obedit = base_old->object;
4135
4136 BMeshCreateParams create_params{};
4137 BMesh *bm_new = BM_mesh_create(&bm_new_allocsize, &create_params);
4138
4139 if (use_custom_normals) {
4140 /* Needed so the temporary normal layer is copied too. */
4141 BM_mesh_copy_init_customdata_all_layers(bm_new, bm_old, BM_ALL, &bm_new_allocsize);
4142 }
4143 else {
4144 BM_mesh_copy_init_customdata(bm_new, bm_old, &bm_new_allocsize);
4145 }
4146
4147 /* Take into account user preferences for duplicating actions. */
4148 const eDupli_ID_Flags dupflag = eDupli_ID_Flags(USER_DUP_MESH | (U.dupflag & USER_DUP_ACT));
4149 Base *base_new = blender::ed::object::add_duplicate(bmain, scene, view_layer, base_old, dupflag);
4150
4151 /* normally would call directly after but in this case delay recalc */
4152 // DAG_relations_tag_update(bmain);
4153
4154 /* new in 2.5 */
4156 base_new->object,
4159 false);
4160
4162
4163 BM_mesh_copy_arrays(bm_old, bm_new, verts, verts_len, edges, edges_len, faces, faces_len);
4164
4165 if (use_custom_normals) {
4167 }
4168
4169 for (uint i = 0; i < verts_len; i++) {
4170 BM_vert_kill(bm_old, verts[i]);
4171 }
4172 BMeshToMeshParams to_mesh_params{};
4173 BM_mesh_bm_to_me(bmain, bm_new, static_cast<Mesh *>(base_new->object->data), &to_mesh_params);
4174
4175 BM_mesh_free(bm_new);
4176 ((Mesh *)base_new->object->data)->runtime->edit_mesh = nullptr;
4177
4178 return base_new;
4179}
4180
4182 Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
4183{
4184 /* we may have tags from previous operators */
4186
4187 /* sel -> tag */
4189 bm_old, BM_FACE | BM_EDGE | BM_VERT, BM_ELEM_TAG, true, false, BM_ELEM_SELECT);
4190
4191 return (mesh_separate_tagged(bmain, scene, view_layer, base_old, bm_old) != nullptr);
4192}
4193
4200static void mesh_separate_material_assign_mat_nr(Main *bmain, Object *ob, const short mat_nr)
4201{
4202 ID *obdata = static_cast<ID *>(ob->data);
4203
4204 const short *totcolp = BKE_id_material_len_p(obdata);
4205 Material ***matarar = BKE_id_material_array_p(obdata);
4206
4207 if ((totcolp && matarar) == 0) {
4208 BLI_assert(0);
4209 return;
4210 }
4211
4212 if (*totcolp) {
4213 Material *ma_ob;
4214 Material *ma_obdata;
4215 char matbit;
4216
4217 if (mat_nr < ob->totcol) {
4218 ma_ob = ob->mat[mat_nr];
4219 matbit = ob->matbits[mat_nr];
4220 }
4221 else {
4222 ma_ob = nullptr;
4223 matbit = 0;
4224 }
4225
4226 if (mat_nr < *totcolp) {
4227 ma_obdata = (*matarar)[mat_nr];
4228 }
4229 else {
4230 ma_obdata = nullptr;
4231 }
4232
4233 BKE_id_material_clear(bmain, obdata);
4234 BKE_id_material_resize(bmain, obdata, 1, true);
4236
4237 ob->mat[0] = ma_ob;
4238 id_us_plus((ID *)ma_ob);
4239 ob->matbits[0] = matbit;
4240 (*matarar)[0] = ma_obdata;
4241 id_us_plus((ID *)ma_obdata);
4242 }
4243 else {
4244 BKE_id_material_clear(bmain, obdata);
4245 }
4246}
4247
4249 Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
4250{
4251 BMFace *f_cmp, *f;
4252 BMIter iter;
4253 bool result = false;
4254
4255 while ((f_cmp = static_cast<BMFace *>(BM_iter_at_index(bm_old, BM_FACES_OF_MESH, nullptr, 0)))) {
4256 Base *base_new;
4257 const short mat_nr = f_cmp->mat_nr;
4258 int tot = 0;
4259
4261
4262 BM_ITER_MESH (f, &iter, bm_old, BM_FACES_OF_MESH) {
4263 if (f->mat_nr == mat_nr) {
4264 BMLoop *l_iter;
4265 BMLoop *l_first;
4266
4268 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
4269 do {
4272 } while ((l_iter = l_iter->next) != l_first);
4273
4274 tot++;
4275 }
4276 }
4277
4278 /* leave the current object with some materials */
4279 if (tot == bm_old->totface) {
4280 mesh_separate_material_assign_mat_nr(bmain, base_old->object, mat_nr);
4281
4282 /* since we're in editmode, must set faces here */
4283 BM_ITER_MESH (f, &iter, bm_old, BM_FACES_OF_MESH) {
4284 f->mat_nr = 0;
4285 }
4286 break;
4287 }
4288
4289 /* Move selection into a separate object */
4290 base_new = mesh_separate_tagged(bmain, scene, view_layer, base_old, bm_old);
4291 if (base_new) {
4292 mesh_separate_material_assign_mat_nr(bmain, base_new->object, mat_nr);
4293 }
4294
4295 result |= (base_new != nullptr);
4296 }
4297
4298 return result;
4299}
4300
4302 Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
4303{
4304 /* Without this, we duplicate the object mode mesh for each loose part.
4305 * This can get very slow especially for large meshes with many parts
4306 * which would duplicate the mesh on entering edit-mode. */
4307 const bool clear_object_data = true;
4308
4309 bool result = false;
4310
4311 blender::Array<BMVert *> vert_groups(bm_old->totvert);
4312 blender::Array<BMEdge *> edge_groups(bm_old->totedge);
4313 blender::Array<BMFace *> face_groups(bm_old->totface);
4314
4315 int(*groups)[3] = nullptr;
4316 int groups_len = BM_mesh_calc_edge_groups_as_arrays(
4317 bm_old, vert_groups.data(), edge_groups.data(), face_groups.data(), &groups);
4318 if (groups_len <= 1) {
4319 MEM_SAFE_FREE(groups);
4320 return false;
4321 }
4322
4323 if (clear_object_data) {
4324 ED_mesh_geometry_clear(static_cast<Mesh *>(base_old->object->data));
4325 }
4326
4328
4329 /* Separate out all groups except the first. */
4330 uint group_ofs[3] = {uint(groups[0][0]), uint(groups[0][1]), uint(groups[0][2])};
4331 for (int i = 1; i < groups_len; i++) {
4332 Base *base_new = mesh_separate_arrays(bmain,
4333 scene,
4334 view_layer,
4335 base_old,
4336 bm_old,
4337 vert_groups.data() + group_ofs[0],
4338 groups[i][0],
4339 edge_groups.data() + group_ofs[1],
4340 groups[i][1],
4341 face_groups.data() + group_ofs[2],
4342 groups[i][2]);
4343 result |= (base_new != nullptr);
4344
4345 group_ofs[0] += groups[i][0];
4346 group_ofs[1] += groups[i][1];
4347 group_ofs[2] += groups[i][2];
4348 }
4349
4350 Mesh *me_old = static_cast<Mesh *>(base_old->object->data);
4352
4353 if (clear_object_data) {
4354 BMeshToMeshParams to_mesh_params{};
4355 to_mesh_params.update_shapekey_indices = true;
4356 BM_mesh_bm_to_me(nullptr, bm_old, me_old, &to_mesh_params);
4357 }
4358
4359 MEM_freeN(groups);
4360 return result;
4361}
4362
4364{
4365 Main *bmain = CTX_data_main(C);
4366 Scene *scene = CTX_data_scene(C);
4367 ViewLayer *view_layer = CTX_data_view_layer(C);
4368 const int type = RNA_enum_get(op->ptr, "type");
4369 bool changed_multi = false;
4370
4371 if (ED_operator_editmesh(C)) {
4372 uint empty_selection_len = 0;
4374 scene, view_layer, CTX_wm_view3d(C));
4375 for (const int base_index : bases.index_range()) {
4376 Base *base = bases[base_index];
4378
4379 if (type == 0) {
4380 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
4381 /* when all objects has no selection */
4382 if (++empty_selection_len == bases.size()) {
4383 BKE_report(op->reports, RPT_ERROR, "Nothing selected");
4384 }
4385 continue;
4386 }
4387 }
4388
4389 /* editmode separate */
4390 bool changed = false;
4391 switch (type) {
4393 changed = mesh_separate_selected(bmain, scene, view_layer, base, em->bm);
4394 break;
4396 changed = mesh_separate_material(bmain, scene, view_layer, base, em->bm);
4397 break;
4399 changed = mesh_separate_loose(bmain, scene, view_layer, base, em->bm);
4400 break;
4401 default:
4402 BLI_assert(0);
4403 break;
4404 }
4405
4406 if (changed) {
4408 params.calc_looptris = true;
4409 params.calc_normals = false;
4410 params.is_destructive = true;
4411 EDBM_update(static_cast<Mesh *>(base->object->data), &params);
4412 }
4413 changed_multi |= changed;
4414 }
4415 }
4416 else {
4417 if (type == MESH_SEPARATE_SELECTED) {
4418 BKE_report(op->reports, RPT_ERROR, "Selection not supported in object mode");
4419 return OPERATOR_CANCELLED;
4420 }
4421
4422 /* object mode separate */
4423 CTX_DATA_BEGIN (C, Base *, base_iter, selected_editable_bases) {
4424 Object *ob = base_iter->object;
4425 if (ob->type != OB_MESH) {
4426 continue;
4427 }
4428 Mesh *mesh = static_cast<Mesh *>(ob->data);
4429 if (!BKE_id_is_editable(bmain, &mesh->id)) {
4430 continue;
4431 }
4432
4433 BMeshCreateParams create_params{};
4434 create_params.use_toolflags = true;
4435 BMesh *bm_old = BM_mesh_create(&bm_mesh_allocsize_default, &create_params);
4436
4437 BMeshFromMeshParams from_mesh_params{};
4438 BM_mesh_bm_from_me(bm_old, mesh, &from_mesh_params);
4439
4440 bool changed = false;
4441 switch (type) {
4443 changed = mesh_separate_material(bmain, scene, view_layer, base_iter, bm_old);
4444 break;
4446 changed = mesh_separate_loose(bmain, scene, view_layer, base_iter, bm_old);
4447 break;
4448 default:
4449 BLI_assert(0);
4450 break;
4451 }
4452
4453 if (changed) {
4454 BMeshToMeshParams to_mesh_params{};
4455 to_mesh_params.calc_object_remap = true;
4456 BM_mesh_bm_to_me(bmain, bm_old, mesh, &to_mesh_params);
4457
4460 }
4461
4462 BM_mesh_free(bm_old);
4463
4464 changed_multi |= changed;
4465 }
4467 }
4468
4469 if (changed_multi) {
4470 /* delay depsgraph recalc until all objects are duplicated */
4474
4475 return OPERATOR_FINISHED;
4476 }
4477
4478 return OPERATOR_CANCELLED;
4479}
4480
4482{
4483 static const EnumPropertyItem prop_separate_types[] = {
4484 {MESH_SEPARATE_SELECTED, "SELECTED", 0, "Selection", ""},
4485 {MESH_SEPARATE_MATERIAL, "MATERIAL", 0, "By Material", ""},
4486 {MESH_SEPARATE_LOOSE, "LOOSE", 0, "By Loose Parts", ""},
4487 {0, nullptr, 0, nullptr, nullptr},
4488 };
4489
4490 /* identifiers */
4491 ot->name = "Separate";
4492 ot->description = "Separate selected geometry into a new mesh";
4493 ot->idname = "MESH_OT_separate";
4494
4495 /* API callbacks. */
4496 ot->invoke = WM_menu_invoke;
4497 ot->exec = edbm_separate_exec;
4498 ot->poll = ED_operator_scene_editable; /* object and editmode */
4499
4500 /* flags */
4501 ot->flag = OPTYPE_UNDO;
4502
4503 ot->prop = RNA_def_enum(
4504 ot->srna, "type", prop_separate_types, MESH_SEPARATE_SELECTED, "Type", "");
4505}
4506
4508
4509/* -------------------------------------------------------------------- */
4512
4514{
4515 const bool use_beauty = RNA_boolean_get(op->ptr, "use_beauty");
4516
4517 bool has_selected_edges = false, has_faces_filled = false;
4518
4519 const Scene *scene = CTX_data_scene(C);
4520 ViewLayer *view_layer = CTX_data_view_layer(C);
4522 scene, view_layer, CTX_wm_view3d(C));
4523 for (Object *obedit : objects) {
4525
4526 const int totface_orig = em->bm->totface;
4527
4528 if (em->bm->totedgesel == 0) {
4529 continue;
4530 }
4531 has_selected_edges = true;
4532
4533 BMOperator bmop;
4534 if (!EDBM_op_init(
4535 em, &bmop, op, "triangle_fill edges=%he use_beauty=%b", BM_ELEM_SELECT, use_beauty))
4536 {
4537 continue;
4538 }
4539
4540 BMO_op_exec(em->bm, &bmop);
4541
4542 /* cancel if nothing was done */
4543 if (totface_orig == em->bm->totface) {
4544 EDBM_op_finish(em, &bmop, op, true);
4545 continue;
4546 }
4547 has_faces_filled = true;
4548
4549 /* select new geometry */
4551 em->bm, bmop.slots_out, "geom.out", BM_FACE | BM_EDGE, BM_ELEM_SELECT, true);
4552
4553 if (!EDBM_op_finish(em, &bmop, op, true)) {
4554 continue;
4555 }
4556
4558 params.calc_looptris = true;
4559 params.calc_normals = false;
4560 params.is_destructive = true;
4561 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
4562 }
4563
4564 if (!has_selected_edges) {
4565 BKE_report(op->reports, RPT_ERROR, "No edges selected");
4566 return OPERATOR_CANCELLED;
4567 }
4568
4569 if (!has_faces_filled) {
4570 BKE_report(op->reports, RPT_WARNING, "No faces filled");
4571 return OPERATOR_CANCELLED;
4572 }
4573
4574 return OPERATOR_FINISHED;
4575}
4576
4578{
4579 /* identifiers */
4580 ot->name = "Fill";
4581 ot->idname = "MESH_OT_fill";
4582 ot->description = "Fill a selected edge loop with faces";
4583 ot->translation_context = BLT_I18NCONTEXT_ID_MESH;
4584
4585 /* API callbacks. */
4586 ot->exec = edbm_fill_exec;
4587 ot->poll = ED_operator_editmesh;
4588
4589 /* flags */
4590 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4591
4592 RNA_def_boolean(ot->srna, "use_beauty", true, "Beauty", "Use best triangulation division");
4593}
4594
4596
4597/* -------------------------------------------------------------------- */
4600
4601static bool bm_edge_test_fill_grid_cb(BMEdge *e, void * /*bm_v*/)
4602{
4604}
4605
4607{
4608 BMIter iter;
4609 BMEdge *e_iter;
4610 BMVert *v_pair[2];
4611 int i = 0;
4612 BM_ITER_ELEM (e_iter, &iter, v, BM_EDGES_OF_VERT) {
4613 if (BM_elem_flag_test(e_iter, BM_ELEM_TAG)) {
4614 v_pair[i++] = BM_edge_other_vert(e_iter, v);
4615 }
4616 }
4617 BLI_assert(i == 2);
4618
4619 return fabsf(float(M_PI) - angle_v3v3v3(v_pair[0]->co, v->co, v_pair[1]->co));
4620}
4621
4625static bool edbm_fill_grid_prepare(BMesh *bm, int offset, int *span_p, const bool span_calc)
4626{
4627 /* angle differences below this value are considered 'even'
4628 * in that they shouldn't be used to calculate corners used for the 'span' */
4629 const float eps_even = 1e-3f;
4630 BMEdge *e;
4631 BMIter iter;
4632 int count;
4633 int span = *span_p;
4634
4635 ListBase eloops = {nullptr};
4636 BMEdgeLoopStore *el_store;
4637 // LinkData *el_store;
4638
4640 el_store = static_cast<BMEdgeLoopStore *>(eloops.first);
4641
4642 if (count != 1) {
4643 /* Let the operator use the selection flags,
4644 * most likely failing with an error in this case. */
4645 BM_mesh_edgeloops_free(&eloops);
4646 return false;
4647 }
4648
4649 /* Only tag edges that are part of a loop. */
4650 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
4652 }
4653 const int verts_len = BM_edgeloop_length_get(el_store);
4654 const int edges_len = verts_len - (BM_edgeloop_is_closed(el_store) ? 0 : 1);
4655 BMEdge **edges = MEM_malloc_arrayN<BMEdge *>(edges_len, __func__);
4656 BM_edgeloop_edges_get(el_store, edges);
4657 for (int i = 0; i < edges_len; i++) {
4659 }
4660
4661 if (span_calc) {
4662 span = verts_len / 4;
4663 }
4664 else {
4665 span = min_ii(span, (verts_len / 2) - 1);
4666 }
4667 offset = mod_i(offset, verts_len);
4668
4669 if ((count == 1) && ((verts_len & 1) == 0) && (verts_len == edges_len)) {
4670
4671 /* be clever! detect 2 edge loops from one closed edge loop */
4674 LinkData *v_act_link;
4675 int i;
4676
4677 if (v_act && (v_act_link = static_cast<LinkData *>(
4678 BLI_findptr(verts, v_act, offsetof(LinkData, data)))))
4679 {
4680 /* pass */
4681 }
4682 else {
4683 /* find the vertex with the best angle (a corner vertex) */
4684 LinkData *v_link_best = nullptr;
4685 float angle_best = -1.0f;
4686 LISTBASE_FOREACH (LinkData *, v_link, verts) {
4687 const float angle = edbm_fill_grid_vert_tag_angle(static_cast<BMVert *>(v_link->data));
4688 if ((angle > angle_best) || (v_link_best == nullptr)) {
4689 angle_best = angle;
4690 v_link_best = v_link;
4691 }
4692 }
4693
4694 v_act_link = v_link_best;
4695 v_act = static_cast<BMVert *>(v_act_link->data);
4696 }
4697
4698 /* set this vertex first */
4699 BLI_listbase_rotate_first(verts, v_act_link);
4700
4701 if (offset != 0) {
4702 v_act_link = static_cast<LinkData *>(BLI_findlink(verts, offset));
4703 v_act = static_cast<BMVert *>(v_act_link->data);
4704 BLI_listbase_rotate_first(verts, v_act_link);
4705 }
4706
4707 /* Run again to update the edge order from the rotated vertex list. */
4708 BM_edgeloop_edges_get(el_store, edges);
4709
4710 if (span_calc) {
4711 /* calculate the span by finding the next corner in 'verts'
4712 * we don't know what defines a corner exactly so find the 4 verts
4713 * in the loop with the greatest angle.
4714 * Tag them and use the first tagged vertex to calculate the span.
4715 *
4716 * NOTE: we may have already checked 'edbm_fill_grid_vert_tag_angle()' on each
4717 * vert, but advantage of de-duplicating is minimal. */
4718 SortPtrByFloat *ele_sort = MEM_malloc_arrayN<SortPtrByFloat>(verts_len, __func__);
4719 LinkData *v_link;
4720 for (v_link = static_cast<LinkData *>(verts->first), i = 0; v_link;
4721 v_link = v_link->next, i++)
4722 {
4723 BMVert *v = static_cast<BMVert *>(v_link->data);
4724 const float angle = edbm_fill_grid_vert_tag_angle(v);
4725 ele_sort[i].sort_value = angle;
4726 ele_sort[i].data = v_link;
4727
4728 /* Do not allow the best corner or the diagonally opposite corner to be detected. */
4729 if (ELEM(i, 0, verts_len / 2)) {
4730 ele_sort[i].sort_value = 0;
4731 }
4732 }
4733
4734 qsort(ele_sort, verts_len, sizeof(*ele_sort), BLI_sortutil_cmp_float_reverse);
4735
4736 /* Check that we have at least 3 corners.
4737 * The excluded corners are the last and second from last elements (both reset to 0).
4738 * The best remaining corner is `ele_sort[0]`
4739 * if the angle on the best remaining corner is roughly the same as the third-last,
4740 * then we can't calculate 3+ corners - fallback to the even span. */
4741 if ((ele_sort[0].sort_value - ele_sort[verts_len - 3].sort_value) > eps_even) {
4742 span = BLI_findindex(verts, ele_sort[0].data);
4743 }
4744 MEM_freeN(ele_sort);
4745 }
4746 /* end span calc */
4747 int start = 0;
4748
4749 /* The algorithm needs to iterate the shorter distance, between the best and second best vert.
4750 * If the second best vert is near the beginning of the loop, it starts at 0 and walks forward.
4751 * If, instead, the second best vert is near the end of the loop, then it starts at the second
4752 * best vertex and walks to the end of the loop. */
4753 if (span > verts_len / 2) {
4754 span = (verts_len)-span;
4755 start = (verts_len / 2) - span;
4756 }
4757
4758 /* un-flag 'rails' */
4759 for (i = start; i < start + span; i++) {
4761 BM_elem_flag_disable(edges[(verts_len / 2) + i], BM_ELEM_TAG);
4762 }
4763 }
4764 /* else let the bmesh-operator handle it */
4765
4766 BM_mesh_edgeloops_free(&eloops);
4767 MEM_freeN(edges);
4768
4769 *span_p = span;
4770
4771 return true;
4772}
4773
4778
4792{
4793 FillGridSplitJoin *split_join = MEM_callocN<FillGridSplitJoin>(__func__);
4794
4795 /* Split the selection into an island. */
4796 BMOperator split_op;
4797 BMO_op_init(em->bm, &split_op, 0, "split");
4799 em->bm, &split_op, split_op.slots_in, "geom", BM_FACE | BM_EDGE | BM_VERT, BM_ELEM_SELECT);
4800 BMO_op_exec(em->bm, &split_op);
4801
4802 /* Setup the weld op that will undo the split.
4803 * Switch the selection to the corresponding edges on the island instead of the edges around the
4804 * hole, so fill_grid will interpolate using the face and loop data from the island.
4805 * Also create a new map for the weld, which maps pairs of verts instead of pairs of edges. */
4806 BMO_op_init(em->bm, &split_join->weld_op, 0, "weld_verts");
4807 BMOpSlot *weld_target_map = BMO_slot_get(split_join->weld_op.slots_in, "targetmap");
4808 BMOIter siter;
4809 BMEdge *e;
4810 BMO_ITER (e, &siter, split_op.slots_out, "boundary_map.out", 0) {
4811 BMEdge *e_dst = static_cast<BMEdge *>(BMO_iter_map_value_ptr(&siter));
4812
4813 BLI_assert(e_dst);
4814
4815 /* For edges, flip the selection from the edge of the hole to the edge of the island. */
4817
4818 /* When these match, the source edge has been deleted. */
4819 if (e != e_dst) {
4821
4822 /* For verts, flip the selection from the edge of the hole to the edge of the island.
4823 * Also add it to the weld map. But check selection first. Don't try to add the same vert to
4824 * the map more than once. If the selection was changed false, it's already been processed.
4825 */
4829 BMO_slot_map_elem_insert(&split_join->weld_op, weld_target_map, e->v1, e_dst->v1);
4830 }
4834 BMO_slot_map_elem_insert(&split_join->weld_op, weld_target_map, e->v2, e_dst->v2);
4835 }
4836 }
4837 }
4838
4839 /* Store the island for removal once it has been replaced by new fill_grid geometry. */
4840 BMO_op_init(em->bm, &split_join->delete_op, 0, "delete");
4841 BMO_slot_int_set(split_join->delete_op.slots_in, "context", DEL_FACES);
4843 em->bm, split_op.slots_out, "geom.out", BM_FACE, BM_ELEM_SELECT, false);
4845 &split_join->delete_op,
4846 split_join->delete_op.slots_in,
4847 "geom",
4848 BM_FACE,
4850
4851 /* Clean up the split operator. */
4852 BMO_op_finish(em->bm, &split_op);
4853
4854 return split_join;
4855}
4856
4866 wmOperator *op,
4867 FillGridSplitJoin *split_join,
4868 bool changed)
4869{
4870
4871 /* If fill_grid worked, delete the replaced faces. Otherwise, restore original selection. */
4872 if (changed) {
4873 BMO_op_exec(em->bm, &split_join->delete_op);
4874 }
4875 else {
4877 em->bm, split_join->delete_op.slots_in, "geom", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
4878 }
4879 BMO_op_finish(em->bm, &split_join->delete_op);
4880
4881 /* If fill_grid created geometry from faces after those faces had been split
4882 * from the rest of the mesh, the geometry it generated will be inward-facing.
4883 * (using the fill_grid on an island instead of a hole is 'inside out'.) Fix it.
4884 * This is the same as #edbm_flip_normals_face_winding without the #EDBM_update
4885 * since that will happen later. */
4886 if (changed) {
4888 EDBM_op_callf(em, op, "reverse_faces faces=%hf flip_multires=%b", BM_ELEM_SELECT, true);
4889 if (lnors_ed_arr) {
4890 flip_custom_normals(em->bm, lnors_ed_arr);
4892 }
4893 }
4894
4895 /* Put the mesh back together. */
4896 BMO_op_exec(em->bm, &split_join->weld_op);
4897 BMO_op_finish(em->bm, &split_join->weld_op);
4898
4899 MEM_freeN(split_join);
4900}
4901
4903{
4904 const bool use_interp_simple = RNA_boolean_get(op->ptr, "use_interp_simple");
4905
4906 const Scene *scene = CTX_data_scene(C);
4907 ViewLayer *view_layer = CTX_data_view_layer(C);
4909 scene, view_layer, CTX_wm_view3d(C));
4910 for (uint ob_index = 0; ob_index < objects.size(); ob_index++) {
4911
4912 Object *obedit = objects[ob_index];
4914 if (em->bm->totedgesel == 0) {
4915 continue;
4916 }
4917
4918 bool use_prepare = true;
4919 const bool use_smooth = edbm_add_edge_face__smooth_get(em->bm);
4920
4921 FillGridSplitJoin *split_join = nullptr;
4922 if (em->bm->totfacesel != 0) {
4923 split_join = edbm_fill_grid_split_join_init(em);
4924 }
4925
4926 const int totedge_orig = em->bm->totedge;
4927 const int totface_orig = em->bm->totface;
4928
4929 if (use_prepare) {
4930 /* use when we have a single loop selected */
4931 PropertyRNA *prop_span = RNA_struct_find_property(op->ptr, "span");
4932 PropertyRNA *prop_offset = RNA_struct_find_property(op->ptr, "offset");
4933 bool calc_span;
4934
4935 int span;
4936 int offset;
4937
4938 /* Only reuse on redo because these settings need to match the current selection.
4939 * We never want to use them on other geometry, repeat last for eg, see: #60777. */
4940 if (((op->flag & OP_IS_INVOKE) || (op->flag & OP_IS_REPEAT_LAST) == 0) &&
4941 RNA_property_is_set(op->ptr, prop_span))
4942 {
4943 span = RNA_property_int_get(op->ptr, prop_span);
4944 calc_span = false;
4945 }
4946 else {
4947 /* Will be overwritten if possible. */
4948 span = 0;
4949 calc_span = true;
4950 }
4951
4952 offset = RNA_property_int_get(op->ptr, prop_offset);
4953
4954 /* in simple cases, move selection for tags, but also support more advanced cases */
4955 use_prepare = edbm_fill_grid_prepare(em->bm, offset, &span, calc_span);
4956
4957 RNA_property_int_set(op->ptr, prop_span, span);
4958 }
4959 /* end tricky prepare code */
4960
4961 bool changed = EDBM_op_call_and_selectf(
4962 em,
4963 op,
4964 "faces.out",
4965 true,
4966 "grid_fill edges=%he mat_nr=%i use_smooth=%b use_interp_simple=%b",
4967 use_prepare ? BM_ELEM_TAG : BM_ELEM_SELECT,
4968 em->mat_nr,
4969 use_smooth,
4970 use_interp_simple);
4971
4972 /* Check that the results match the return value. */
4973 const bool has_geometry_changed = totedge_orig != em->bm->totedge ||
4974 totface_orig != em->bm->totface;
4975 BLI_assert(changed == has_geometry_changed);
4976 UNUSED_VARS_NDEBUG(has_geometry_changed);
4977
4978 /* If a split/join in progress, finish it. */
4979 if (split_join) {
4980 edbm_fill_grid_split_join_finish(em, op, split_join, changed);
4981 }
4982
4983 /* Update the object. */
4984 if (changed || split_join) {
4986 params.calc_looptris = true;
4987 params.calc_normals = false;
4988 params.is_destructive = true;
4989 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
4990 }
4991 else {
4992 /* NOTE: Even if there were no mesh changes, #EDBM_op_finish() changed the BMesh pointer
4993 * inside of edit mesh, so need to tell evaluated objects to sync new BMesh pointer to their
4994 * edit mesh structures. */
4995 DEG_id_tag_update(&obedit->id, 0);
4996 }
4997 }
4998
4999 return OPERATOR_FINISHED;
5000}
5001
5003{
5004 PropertyRNA *prop;
5005
5006 /* identifiers */
5007 ot->name = "Grid Fill";
5008 ot->description = "Fill grid from two loops";
5009 ot->idname = "MESH_OT_fill_grid";
5010
5011 /* API callbacks. */
5012 ot->exec = edbm_fill_grid_exec;
5013 ot->poll = ED_operator_editmesh;
5014
5015 /* flags */
5016 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5017
5018 /* properties */
5019 prop = RNA_def_int(ot->srna, "span", 1, 1, 1000, "Span", "Number of grid columns", 1, 100);
5021 prop = RNA_def_int(ot->srna,
5022 "offset",
5023 0,
5024 -1000,
5025 1000,
5026 "Offset",
5027 "Vertex that is the corner of the grid",
5028 -100,
5029 100);
5031 RNA_def_boolean(ot->srna,
5032 "use_interp_simple",
5033 false,
5034 "Simple Blending",
5035 "Use simple interpolation of grid vertices");
5036}
5037
5039
5040/* -------------------------------------------------------------------- */
5043
5045{
5046 const int sides = RNA_int_get(op->ptr, "sides");
5047
5048 const Scene *scene = CTX_data_scene(C);
5049 ViewLayer *view_layer = CTX_data_view_layer(C);
5051 scene, view_layer, CTX_wm_view3d(C));
5052
5053 for (Object *obedit : objects) {
5055
5056 if (em->bm->totedgesel == 0) {
5057 continue;
5058 }
5059
5061 em, op, "faces.out", true, "holes_fill edges=%he sides=%i", BM_ELEM_SELECT, sides))
5062 {
5063 continue;
5064 }
5065
5067 params.calc_looptris = true;
5068 params.calc_normals = false;
5069 params.is_destructive = true;
5070 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5071 }
5072
5073 return OPERATOR_FINISHED;
5074}
5075
5077{
5078 /* identifiers */
5079 ot->name = "Fill Holes";
5080 ot->idname = "MESH_OT_fill_holes";
5081 ot->description = "Fill in holes (boundary edge loops)";
5082
5083 /* API callbacks. */
5084 ot->exec = edbm_fill_holes_exec;
5085 ot->poll = ED_operator_editmesh;
5086
5087 /* flags */
5088 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5089
5090 RNA_def_int(ot->srna,
5091 "sides",
5092 4,
5093 0,
5094 1000,
5095 "Sides",
5096 "Number of sides in hole required to fill (zero fills all holes)",
5097 0,
5098 100);
5099}
5100
5102
5103/* -------------------------------------------------------------------- */
5106
5108{
5109 const Scene *scene = CTX_data_scene(C);
5110 ViewLayer *view_layer = CTX_data_view_layer(C);
5112 scene, view_layer, CTX_wm_view3d(C));
5113
5114 const float angle_max = M_PI;
5115 const float angle_limit = RNA_float_get(op->ptr, "angle_limit");
5116 char hflag;
5117
5118 for (Object *obedit : objects) {
5120
5121 if (em->bm->totfacesel == 0) {
5122 continue;
5123 }
5124
5125 if (angle_limit >= angle_max) {
5126 hflag = BM_ELEM_SELECT;
5127 }
5128 else {
5129 BMIter iter;
5130 BMEdge *e;
5131
5132 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
5136 BM_edge_calc_face_angle_ex(e, angle_max) < angle_limit));
5137 }
5138 hflag = BM_ELEM_TAG;
5139 }
5140
5142 em, op, "geom.out", true, "beautify_fill faces=%hf edges=%he", BM_ELEM_SELECT, hflag))
5143 {
5144 continue;
5145 }
5146
5148 params.calc_looptris = true;
5149 params.calc_normals = false;
5150 params.is_destructive = true;
5151 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5152 }
5153
5154 return OPERATOR_FINISHED;
5155}
5156
5158{
5159 PropertyRNA *prop;
5160
5161 /* identifiers */
5162 ot->name = "Beautify Faces";
5163 ot->idname = "MESH_OT_beautify_fill";
5164 ot->description = "Rearrange some faces to try to get less degenerated geometry";
5165
5166 /* API callbacks. */
5168 ot->poll = ED_operator_editmesh;
5169
5170 /* flags */
5171 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5172
5173 /* props */
5174 prop = RNA_def_float_rotation(ot->srna,
5175 "angle_limit",
5176 0,
5177 nullptr,
5178 0.0f,
5179 DEG2RADF(180.0f),
5180 "Max Angle",
5181 "Angle limit",
5182 0.0f,
5183 DEG2RADF(180.0f));
5185}
5186
5188
5189/* -------------------------------------------------------------------- */
5192
5194{
5195 const float offset = RNA_float_get(op->ptr, "offset");
5196 const bool use_relative_offset = RNA_boolean_get(op->ptr, "use_relative_offset");
5197 const int center_mode = RNA_enum_get(op->ptr, "center_mode");
5198
5199 const Scene *scene = CTX_data_scene(C);
5200 ViewLayer *view_layer = CTX_data_view_layer(C);
5202 scene, view_layer, CTX_wm_view3d(C));
5203 for (Object *obedit : objects) {
5205
5206 if (em->bm->totfacesel == 0) {
5207 continue;
5208 }
5209
5210 BMOperator bmop;
5211 EDBM_op_init(em,
5212 &bmop,
5213 op,
5214 "poke faces=%hf offset=%f use_relative_offset=%b center_mode=%i",
5216 offset,
5217 use_relative_offset,
5218 center_mode);
5219 BMO_op_exec(em->bm, &bmop);
5220
5222
5224 em->bm, bmop.slots_out, "verts.out", BM_VERT, BM_ELEM_SELECT, true);
5226 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
5227
5228 if (!EDBM_op_finish(em, &bmop, op, true)) {
5229 continue;
5230 }
5231
5233 params.calc_looptris = true;
5234 params.calc_normals = true;
5235 params.is_destructive = true;
5236 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5237 }
5238
5239 return OPERATOR_FINISHED;
5240}
5241
5243{
5244 static const EnumPropertyItem poke_center_modes[] = {
5246 "MEDIAN_WEIGHTED",
5247 0,
5248 "Weighted Median",
5249 "Weighted median face center"},
5250 {BMOP_POKE_MEDIAN, "MEDIAN", 0, "Median", "Median face center"},
5251 {BMOP_POKE_BOUNDS, "BOUNDS", 0, "Bounds", "Face bounds center"},
5252 {0, nullptr, 0, nullptr, nullptr},
5253 };
5254
5255 /* identifiers */
5256 ot->name = "Poke Faces";
5257 ot->idname = "MESH_OT_poke";
5258 ot->description = "Split a face into a fan";
5259
5260 /* API callbacks. */
5261 ot->exec = edbm_poke_face_exec;
5262 ot->poll = ED_operator_editmesh;
5263
5264 /* flags */
5265 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5266
5268 ot->srna, "offset", 0.0f, -1e3f, 1e3f, "Poke Offset", "Poke Offset", -1.0f, 1.0f);
5269 RNA_def_boolean(ot->srna,
5270 "use_relative_offset",
5271 false,
5272 "Offset Relative",
5273 "Scale the offset by surrounding geometry");
5274 RNA_def_enum(ot->srna,
5275 "center_mode",
5276 poke_center_modes,
5278 "Poke Center",
5279 "Poke face center calculation");
5280}
5281
5283
5284/* -------------------------------------------------------------------- */
5287
5289{
5290 const int quad_method = RNA_enum_get(op->ptr, "quad_method");
5291 const int ngon_method = RNA_enum_get(op->ptr, "ngon_method");
5292 const Scene *scene = CTX_data_scene(C);
5293 ViewLayer *view_layer = CTX_data_view_layer(C);
5294
5296 scene, view_layer, CTX_wm_view3d(C));
5297 for (Object *obedit : objects) {
5299
5300 if (em->bm->totfacesel == 0) {
5301 continue;
5302 }
5303
5304 BMOperator bmop;
5305 BMOIter oiter;
5306 BMFace *f;
5307
5309
5310 EDBM_op_init(em,
5311 &bmop,
5312 op,
5313 "triangulate faces=%hf quad_method=%i ngon_method=%i",
5315 quad_method,
5316 ngon_method);
5317 BMO_op_exec(em->bm, &bmop);
5318
5319 /* select the output */
5321 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
5322
5323 /* remove the doubles */
5324 BMO_ITER (f, &oiter, bmop.slots_out, "face_map_double.out", BM_FACE) {
5325 BM_face_kill(em->bm, f);
5326 }
5327
5329
5330 if (!EDBM_op_finish(em, &bmop, op, true)) {
5331 continue;
5332 }
5333
5335
5337 params.calc_looptris = true;
5338 params.calc_normals = false;
5339 params.is_destructive = true;
5340 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5341 }
5342
5343 return OPERATOR_FINISHED;
5344}
5345
5347{
5348 /* identifiers */
5349 ot->name = "Triangulate Faces";
5350 ot->idname = "MESH_OT_quads_convert_to_tris";
5351 ot->description = "Triangulate selected faces";
5352
5353 /* API callbacks. */
5355 ot->poll = ED_operator_editmesh;
5356
5357 /* flags */
5358 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5359
5360 RNA_def_enum(ot->srna,
5361 "quad_method",
5364 "Quad Method",
5365 "Method for splitting the quads into triangles");
5366 RNA_def_enum(ot->srna,
5367 "ngon_method",
5370 "N-gon Method",
5371 "Method for splitting the n-gons into triangles");
5372}
5373
5375
5376/* -------------------------------------------------------------------- */
5379
5380#if 0 /* See comments at top of bmo_join_triangles.cc */
5381# define USE_JOIN_TRIANGLE_TESTING_API
5382#endif
5383
5385{
5386 const Scene *scene = CTX_data_scene(C);
5387 ViewLayer *view_layer = CTX_data_view_layer(C);
5388
5390 scene, view_layer, CTX_wm_view3d(C));
5391
5392 const bool do_seam = RNA_boolean_get(op->ptr, "seam");
5393 const bool do_sharp = RNA_boolean_get(op->ptr, "sharp");
5394 const bool do_uvs = RNA_boolean_get(op->ptr, "uvs");
5395 const bool do_vcols = RNA_boolean_get(op->ptr, "vcols");
5396 const bool do_materials = RNA_boolean_get(op->ptr, "materials");
5397
5398#ifdef USE_JOIN_TRIANGLE_TESTING_API
5399 int merge_limit = RNA_int_get(op->ptr, "merge_limit");
5400 int neighbor_debug = RNA_int_get(op->ptr, "neighbor_debug");
5401#endif
5402
5403 const float topology_influence = RNA_float_get(op->ptr, "topology_influence");
5404 const bool deselect_joined = RNA_boolean_get(op->ptr, "deselect_joined");
5405
5406 float angle_face_threshold, angle_shape_threshold;
5407 bool is_face_pair;
5408 {
5409 int totelem_sel[3];
5410 EDBM_mesh_stats_multi(objects, nullptr, totelem_sel);
5411 is_face_pair = (totelem_sel[2] == 2);
5412 }
5413
5414 /* When joining exactly 2 faces, no limit.
5415 * this is useful for one off joins while editing. */
5416 {
5417 PropertyRNA *prop;
5418 prop = RNA_struct_find_property(op->ptr, "face_threshold");
5419 if (is_face_pair && (RNA_property_is_set(op->ptr, prop) == false)) {
5420 angle_face_threshold = DEG2RADF(180.0f);
5421 }
5422 else {
5423 angle_face_threshold = RNA_property_float_get(op->ptr, prop);
5424 }
5425
5426 prop = RNA_struct_find_property(op->ptr, "shape_threshold");
5427 if (is_face_pair && (RNA_property_is_set(op->ptr, prop) == false)) {
5428 angle_shape_threshold = DEG2RADF(180.0f);
5429 }
5430 else {
5431 angle_shape_threshold = RNA_property_float_get(op->ptr, prop);
5432 }
5433 }
5434
5435 for (Object *obedit : objects) {
5437
5438 if (em->bm->totfacesel == 0) {
5439 continue;
5440 }
5441
5443
5444 bool extend_selection = (deselect_joined == false);
5445
5446#ifdef USE_JOIN_TRIANGLE_TESTING_API
5447 if (merge_limit != -1) {
5448 extend_selection = false;
5449 }
5450#endif
5451
5453 em,
5454 op,
5455 "faces.out",
5456 extend_selection,
5457 "join_triangles faces=%hf angle_face_threshold=%f angle_shape_threshold=%f "
5458 "cmp_seam=%b cmp_sharp=%b cmp_uvs=%b cmp_vcols=%b cmp_materials=%b "
5459#ifdef USE_JOIN_TRIANGLE_TESTING_API
5460 "merge_limit=%i neighbor_debug=%i "
5461#endif
5462 "topology_influence=%f deselect_joined=%b",
5464 angle_face_threshold,
5465 angle_shape_threshold,
5466 do_seam,
5467 do_sharp,
5468 do_uvs,
5469 do_vcols,
5470 do_materials,
5471#ifdef USE_JOIN_TRIANGLE_TESTING_API
5472 merge_limit + 1,
5473 neighbor_debug,
5474#endif
5475 topology_influence,
5476 deselect_joined))
5477 {
5478 continue;
5479 }
5480
5481 if (deselect_joined) {
5482 /* When de-selecting faces outside of face mode:
5483 * failing to flush would leave an invalid selection. */
5486 }
5487 }
5488
5490
5492 params.calc_looptris = true;
5493 params.calc_normals = false;
5494 params.is_destructive = true;
5495 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5496 }
5497
5498 return OPERATOR_FINISHED;
5499}
5500
5502{
5503 PropertyRNA *prop;
5504
5505#ifdef USE_JOIN_TRIANGLE_TESTING_API
5506 prop = RNA_def_int(ot->srna,
5507 "merge_limit",
5508 0,
5509 -1,
5510 INT_MAX,
5511 "Merge Limit",
5512 "Maximum number of merges",
5513 -1,
5514 INT_MAX);
5516
5517 prop = RNA_def_int(ot->srna,
5518 "neighbor_debug",
5519 0,
5520 0,
5521 INT_MAX,
5522 "Neighbor Debug",
5523 "Neighbor to highlight",
5524 0,
5525 INT_MAX);
5527#endif
5528
5529 prop = RNA_def_float_rotation(ot->srna,
5530 "face_threshold",
5531 0,
5532 nullptr,
5533 0.0f,
5534 DEG2RADF(180.0f),
5535 "Max Face Angle",
5536 "Face angle limit",
5537 0.0f,
5538 DEG2RADF(180.0f));
5540
5541 prop = RNA_def_float_rotation(ot->srna,
5542 "shape_threshold",
5543 0,
5544 nullptr,
5545 0.0f,
5546 DEG2RADF(180.0f),
5547 "Max Shape Angle",
5548 "Shape angle limit",
5549 0.0f,
5550 DEG2RADF(180.0f));
5552
5553 prop = RNA_def_float_factor(ot->srna,
5554 "topology_influence",
5555 0.0f,
5556 0.0f,
5557 2.0f,
5558 "Topology Influence",
5559 "How much to prioritize regular grids of quads as well as "
5560 "quads that touch existing quads",
5561 0.0f,
5562 2.0f);
5563
5564 RNA_def_boolean(ot->srna, "uvs", false, "Compare UVs", "");
5565 RNA_def_boolean(ot->srna, "vcols", false, "Compare Color Attributes", "");
5566 RNA_def_boolean(ot->srna, "seam", false, "Compare Seam", "");
5567 RNA_def_boolean(ot->srna, "sharp", false, "Compare Sharp", "");
5568 RNA_def_boolean(ot->srna, "materials", false, "Compare Materials", "");
5569
5570 RNA_def_boolean(ot->srna,
5571 "deselect_joined",
5572 false,
5573 "Deselect Joined",
5574 "Only select remaining triangles that were not merged");
5575}
5576
5578{
5579 /* identifiers */
5580 ot->name = "Triangles to Quads";
5581 ot->idname = "MESH_OT_tris_convert_to_quads";
5582 ot->description = "Merge triangles into four sided polygons where possible";
5583
5584 /* API callbacks. */
5586 ot->poll = ED_operator_editmesh;
5587
5588 /* flags */
5589 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5590
5592}
5593
5595
5596/* -------------------------------------------------------------------- */
5604
5606{
5607 const float ratio = RNA_float_get(op->ptr, "ratio");
5608 bool use_vertex_group = RNA_boolean_get(op->ptr, "use_vertex_group");
5609 const float vertex_group_factor = RNA_float_get(op->ptr, "vertex_group_factor");
5610 const bool invert_vertex_group = RNA_boolean_get(op->ptr, "invert_vertex_group");
5611 const bool use_symmetry = RNA_boolean_get(op->ptr, "use_symmetry");
5612 const float symmetry_eps = 0.00002f;
5613 const int symmetry_axis = use_symmetry ? RNA_enum_get(op->ptr, "symmetry_axis") : -1;
5614
5615 /* nop */
5616 if (ratio == 1.0f) {
5617 return OPERATOR_FINISHED;
5618 }
5619
5620 const Scene *scene = CTX_data_scene(C);
5621 ViewLayer *view_layer = CTX_data_view_layer(C);
5623 scene, view_layer, CTX_wm_view3d(C));
5624
5625 for (Object *obedit : objects) {
5627 BMesh *bm = em->bm;
5628 if (bm->totedgesel == 0) {
5629 continue;
5630 }
5631
5632 float *vweights = MEM_malloc_arrayN<float>(bm->totvert, __func__);
5633 {
5634 const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
5635 const int defbase_act = BKE_object_defgroup_active_index_get(obedit) - 1;
5636
5637 if (use_vertex_group && (cd_dvert_offset == -1)) {
5638 BKE_report(op->reports, RPT_WARNING, "No active vertex group");
5639 use_vertex_group = false;
5640 }
5641
5642 BMIter iter;
5643 BMVert *v;
5644 int i;
5646 float weight = 0.0f;
5648 if (use_vertex_group) {
5649 const MDeformVert *dv = static_cast<const MDeformVert *>(
5650 BM_ELEM_CD_GET_VOID_P(v, cd_dvert_offset));
5651 weight = BKE_defvert_find_weight(dv, defbase_act);
5652 if (invert_vertex_group) {
5653 weight = 1.0f - weight;
5654 }
5655 }
5656 else {
5657 weight = 1.0f;
5658 }
5659 }
5660
5661 vweights[i] = weight;
5662 BM_elem_index_set(v, i); /* set_inline */
5663 }
5664 bm->elem_index_dirty &= ~BM_VERT;
5665 }
5666
5667 float ratio_adjust;
5668
5669 if ((bm->totface == bm->totfacesel) || (ratio == 0.0f)) {
5670 ratio_adjust = ratio;
5671 }
5672 else {
5680
5681 int totface_basis = 0;
5682 int totface_adjacent = 0;
5683 BMIter iter;
5684 BMFace *f;
5685 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
5686 /* count faces during decimation, ngons are triangulated */
5687 const int f_len = f->len > 4 ? (f->len - 2) : 1;
5688 totface_basis += f_len;
5689
5690 BMLoop *l_iter, *l_first;
5691 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
5692 do {
5693 if (vweights[BM_elem_index_get(l_iter->v)] != 0.0f) {
5694 totface_adjacent += f_len;
5695 break;
5696 }
5697 } while ((l_iter = l_iter->next) != l_first);
5698 }
5699
5700 ratio_adjust = ratio;
5701 ratio_adjust = 1.0f - ratio_adjust;
5702 ratio_adjust *= float(totface_adjacent) / float(totface_basis);
5703 ratio_adjust = 1.0f - ratio_adjust;
5704 }
5705
5707 em->bm, ratio_adjust, vweights, vertex_group_factor, false, symmetry_axis, symmetry_eps);
5708
5709 MEM_freeN(vweights);
5710
5711 {
5712 short selectmode = em->selectmode;
5713 if ((selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) == 0) {
5714 /* ensure we flush edges -> faces */
5715 selectmode |= SCE_SELECT_EDGE;
5716 }
5717 EDBM_selectmode_flush_ex(em, selectmode);
5718 }
5720 params.calc_looptris = true;
5721 params.calc_normals = true;
5722 params.is_destructive = true;
5723 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5724 }
5725
5726 return OPERATOR_FINISHED;
5727}
5728
5729static bool edbm_decimate_check(bContext * /*C*/, wmOperator * /*op*/)
5730{
5731 return true;
5732}
5733
5734static void edbm_decimate_ui(bContext * /*C*/, wmOperator *op)
5735{
5736 uiLayout *layout = op->layout, *row, *col, *sub;
5737
5738 uiLayoutSetPropSep(layout, true);
5739
5740 layout->prop(op->ptr, "ratio", UI_ITEM_NONE, std::nullopt, ICON_NONE);
5741
5742 layout->prop(op->ptr, "use_vertex_group", UI_ITEM_NONE, std::nullopt, ICON_NONE);
5743 col = &layout->column(false);
5744 uiLayoutSetActive(col, RNA_boolean_get(op->ptr, "use_vertex_group"));
5745 col->prop(op->ptr, "vertex_group_factor", UI_ITEM_NONE, std::nullopt, ICON_NONE);
5746 col->prop(op->ptr, "invert_vertex_group", UI_ITEM_NONE, std::nullopt, ICON_NONE);
5747
5748 row = &layout->row(true, IFACE_("Symmetry"));
5749 row->prop(op->ptr, "use_symmetry", UI_ITEM_NONE, "", ICON_NONE);
5750 sub = &row->row(true);
5751 uiLayoutSetActive(sub, RNA_boolean_get(op->ptr, "use_symmetry"));
5752 sub->prop(op->ptr, "symmetry_axis", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
5753}
5754
5756{
5757 /* identifiers */
5758 ot->name = "Decimate Geometry";
5759 ot->idname = "MESH_OT_decimate";
5760 ot->description = "Simplify geometry by collapsing edges";
5761
5762 /* API callbacks. */
5763 ot->exec = edbm_decimate_exec;
5764 ot->check = edbm_decimate_check;
5765 ot->ui = edbm_decimate_ui;
5766 ot->poll = ED_operator_editmesh;
5767
5768 /* flags */
5769 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5770
5771 /* NOTE: keep in sync with 'rna_def_modifier_decimate'. */
5772 RNA_def_float(ot->srna, "ratio", 1.0f, 0.0f, 1.0f, "Ratio", "", 0.0f, 1.0f);
5773
5774 RNA_def_boolean(ot->srna,
5775 "use_vertex_group",
5776 false,
5777 "Vertex Group",
5778 "Use active vertex group as an influence");
5779 RNA_def_float(ot->srna,
5780 "vertex_group_factor",
5781 1.0f,
5782 0.0f,
5783 1000.0f,
5784 "Weight",
5785 "Vertex group strength",
5786 0.0f,
5787 10.0f);
5789 ot->srna, "invert_vertex_group", false, "Invert", "Invert vertex group influence");
5790
5791 RNA_def_boolean(ot->srna, "use_symmetry", false, "Symmetry", "Maintain symmetry on an axis");
5792
5793 RNA_def_enum(ot->srna, "symmetry_axis", rna_enum_axis_xyz_items, 1, "Axis", "Axis of symmetry");
5794}
5795
5797
5798/* -------------------------------------------------------------------- */
5801
5803{
5804 PropertyRNA *prop;
5805
5806 prop = RNA_def_boolean(ot->srna,
5807 "use_verts",
5808 value,
5809 "Dissolve Vertices",
5810 "Dissolve remaining vertices which connect to only two edges");
5811
5812 if (flag) {
5814 }
5815}
5817{
5818 RNA_def_boolean(ot->srna,
5819 "use_face_split",
5820 false,
5821 "Face Split",
5822 "Split off face corners to maintain surrounding geometry");
5823}
5825{
5826 RNA_def_boolean(ot->srna,
5827 "use_boundary_tear",
5828 false,
5829 "Tear Boundary",
5830 "Split off face corners instead of merging faces");
5831}
5833{
5835 ot->srna,
5836 "angle_threshold",
5837 0,
5838 nullptr,
5839 0.0f,
5840 DEG2RADF(180.0f),
5841 "Angle Threshold",
5842 "Remaining vertices which separate edge pairs are preserved if their edge angle exceeds "
5843 "this threshold.",
5844 0.0f,
5845 DEG2RADF(180.0f));
5847 if (flag) {
5849 }
5850}
5851
5853{
5854 const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split");
5855 const bool use_boundary_tear = RNA_boolean_get(op->ptr, "use_boundary_tear");
5856
5857 const Scene *scene = CTX_data_scene(C);
5858 ViewLayer *view_layer = CTX_data_view_layer(C);
5860 scene, view_layer, CTX_wm_view3d(C));
5861
5862 for (Object *obedit : objects) {
5864
5865 if (em->bm->totvertsel == 0) {
5866 continue;
5867 }
5868
5870
5871 if (!EDBM_op_callf(em,
5872 op,
5873 "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b",
5875 use_face_split,
5876 use_boundary_tear))
5877 {
5878 continue;
5879 }
5880
5882
5884 params.calc_looptris = true;
5885 params.calc_normals = false;
5886 params.is_destructive = true;
5887 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5888 }
5889
5890 return OPERATOR_FINISHED;
5891}
5892
5894{
5895 /* identifiers */
5896 ot->name = "Dissolve Vertices";
5897 ot->description = "Dissolve vertices, merge edges and faces";
5898 ot->idname = "MESH_OT_dissolve_verts";
5899
5900 /* API callbacks. */
5902 ot->poll = ED_operator_editmesh;
5903
5904 /* flags */
5905 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5906
5909}
5910
5912
5913/* -------------------------------------------------------------------- */
5916
5918{
5919 const bool use_verts = RNA_boolean_get(op->ptr, "use_verts");
5920 const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split");
5921 const float angle_threshold = RNA_float_get(op->ptr, "angle_threshold");
5922
5923 const Scene *scene = CTX_data_scene(C);
5924 ViewLayer *view_layer = CTX_data_view_layer(C);
5926 scene, view_layer, CTX_wm_view3d(C));
5927 for (Object *obedit : objects) {
5929
5930 if (em->bm->totedgesel == 0) {
5931 continue;
5932 }
5933
5935
5936 if (!EDBM_op_callf(
5937 em,
5938 op,
5939 "dissolve_edges edges=%he use_verts=%b use_face_split=%b angle_threshold=%f",
5941 use_verts,
5942 use_face_split,
5943 angle_threshold))
5944 {
5945 continue;
5946 }
5947
5949
5951 params.calc_looptris = true;
5952 params.calc_normals = false;
5953 params.is_destructive = true;
5954 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5955 }
5956
5957 return OPERATOR_FINISHED;
5958}
5959
5961{
5962 /* identifiers */
5963 ot->name = "Dissolve Edges";
5964 ot->description = "Dissolve edges, merging faces";
5965 ot->idname = "MESH_OT_dissolve_edges";
5966
5967 /* API callbacks. */
5969 ot->poll = ED_operator_editmesh;
5970
5971 /* flags */
5972 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5973
5977}
5978
5980
5981/* -------------------------------------------------------------------- */
5984
5986{
5987 const bool use_verts = RNA_boolean_get(op->ptr, "use_verts");
5988 const Scene *scene = CTX_data_scene(C);
5989 ViewLayer *view_layer = CTX_data_view_layer(C);
5991 scene, view_layer, CTX_wm_view3d(C));
5992 for (Object *obedit : objects) {
5994
5995 if (em->bm->totfacesel == 0) {
5996 continue;
5997 }
5998
6000
6002 op,
6003 "region.out",
6004 true,
6005 "dissolve_faces faces=%hf use_verts=%b",
6007 use_verts))
6008 {
6009 continue;
6010 }
6011
6013
6015 params.calc_looptris = true;
6016 params.calc_normals = false;
6017 params.is_destructive = true;
6018 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
6019 }
6020
6021 return OPERATOR_FINISHED;
6022}
6023
6025{
6026 /* identifiers */
6027 ot->name = "Dissolve Faces";
6028 ot->description = "Dissolve faces";
6029 ot->idname = "MESH_OT_dissolve_faces";
6030
6031 /* API callbacks. */
6033 ot->poll = ED_operator_editmesh;
6034
6035 /* flags */
6036 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6037
6039}
6040
6042
6043/* -------------------------------------------------------------------- */
6046
6048{
6049 Object *obedit = CTX_data_edit_object(C);
6051 PropertyRNA *prop;
6052
6053 prop = RNA_struct_find_property(op->ptr, "use_verts");
6054 if (!RNA_property_is_set(op->ptr, prop)) {
6055 /* always enable in edge-mode */
6056 if ((em->selectmode & SCE_SELECT_FACE) == 0) {
6057 RNA_property_boolean_set(op->ptr, prop, true);
6058 }
6059 }
6060
6061 if (em->selectmode & SCE_SELECT_VERTEX) {
6062 return edbm_dissolve_verts_exec(C, op);
6063 }
6064 if (em->selectmode & SCE_SELECT_EDGE) {
6065 return edbm_dissolve_edges_exec(C, op);
6066 }
6067 return edbm_dissolve_faces_exec(C, op);
6068}
6069
6070static bool dissolve_mode_poll_property(const bContext *C, wmOperator *op, const PropertyRNA *prop)
6071{
6072 UNUSED_VARS(op);
6073
6074 const char *prop_id = RNA_property_identifier(prop);
6075
6076 Object *obedit = CTX_data_edit_object(C);
6077 const BMEditMesh *em = BKE_editmesh_from_object(obedit);
6078 bool is_edge_select_mode = false;
6079
6080 if (em->selectmode & SCE_SELECT_VERTEX) {
6081 /* Pass. */
6082 }
6083 if (em->selectmode & SCE_SELECT_EDGE) {
6084 is_edge_select_mode = true;
6085 }
6086
6087 if (!is_edge_select_mode) {
6088 /* Angle Threshold is only used in edge select mode. */
6089 if (STREQ(prop_id, "angle_threshold")) {
6090 return false;
6091 }
6092 }
6093 return true;
6094}
6095
6097{
6098 /* identifiers */
6099 ot->name = "Dissolve Selection";
6100 ot->description = "Dissolve geometry based on the selection mode";
6101 ot->idname = "MESH_OT_dissolve_mode";
6102
6103 /* API callbacks. */
6105 ot->poll = ED_operator_editmesh;
6106 ot->poll_property = dissolve_mode_poll_property;
6107
6108 /* flags */
6109 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6110
6115}
6116
6118
6119/* -------------------------------------------------------------------- */
6122
6124{
6125 const float angle_limit = RNA_float_get(op->ptr, "angle_limit");
6126 const bool use_dissolve_boundaries = RNA_boolean_get(op->ptr, "use_dissolve_boundaries");
6127 const int delimit = RNA_enum_get(op->ptr, "delimit");
6128 char dissolve_flag;
6129
6130 const Scene *scene = CTX_data_scene(C);
6131 ViewLayer *view_layer = CTX_data_view_layer(C);
6133 scene, view_layer, CTX_wm_view3d(C));
6134 for (Object *obedit : objects) {
6136 BMesh *bm = em->bm;
6137
6138 if ((bm->totvertsel == 0) && (bm->totedgesel == 0) && (bm->totfacesel == 0)) {
6139 continue;
6140 }
6141
6143
6144 if (em->selectmode == SCE_SELECT_FACE) {
6145 /* flush selection to tags and untag edges/verts with partially selected faces */
6146 BMIter iter;
6147 BMIter liter;
6148
6149 BMElem *ele;
6150 BMFace *f;
6151 BMLoop *l;
6152
6153 BM_ITER_MESH (ele, &iter, bm, BM_VERTS_OF_MESH) {
6155 }
6156 BM_ITER_MESH (ele, &iter, bm, BM_EDGES_OF_MESH) {
6158 }
6159
6160 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
6162 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
6165 }
6166 }
6167 }
6168
6169 dissolve_flag = BM_ELEM_TAG;
6170 }
6171 else {
6172 dissolve_flag = BM_ELEM_SELECT;
6173 }
6174
6176 em,
6177 op,
6178 "region.out",
6179 true,
6180 "dissolve_limit edges=%he verts=%hv angle_limit=%f use_dissolve_boundaries=%b delimit=%i",
6181 dissolve_flag,
6182 dissolve_flag,
6183 angle_limit,
6184 use_dissolve_boundaries,
6185 delimit);
6186
6188
6190 params.calc_looptris = true;
6191 params.calc_normals = false;
6192 params.is_destructive = true;
6193 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
6194 }
6195
6196 return OPERATOR_FINISHED;
6197}
6198
6200{
6201 PropertyRNA *prop;
6202
6203 /* identifiers */
6204 ot->name = "Limited Dissolve";
6205 ot->idname = "MESH_OT_dissolve_limited";
6206 ot->description =
6207 "Dissolve selected edges and vertices, limited by the angle of surrounding geometry";
6208
6209 /* API callbacks. */
6211 ot->poll = ED_operator_editmesh;
6212
6213 /* flags */
6214 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6215
6216 prop = RNA_def_float_rotation(ot->srna,
6217 "angle_limit",
6218 0,
6219 nullptr,
6220 0.0f,
6221 DEG2RADF(180.0f),
6222 "Max Angle",
6223 "Angle limit",
6224 0.0f,
6225 DEG2RADF(180.0f));
6227 RNA_def_boolean(ot->srna,
6228 "use_dissolve_boundaries",
6229 false,
6230 "All Boundaries",
6231 "Dissolve all vertices in between face boundaries");
6232 RNA_def_enum_flag(ot->srna,
6233 "delimit",
6236 "Delimit",
6237 "Delimit dissolve operation");
6238}
6239
6241
6242/* -------------------------------------------------------------------- */
6245
6247{
6248 const Scene *scene = CTX_data_scene(C);
6249 ViewLayer *view_layer = CTX_data_view_layer(C);
6250 int totelem_old[3] = {0, 0, 0};
6251 int totelem_new[3] = {0, 0, 0};
6252
6254 scene, view_layer, CTX_wm_view3d(C));
6255
6256 for (Object *obedit : objects) {
6258 BMesh *bm = em->bm;
6259 totelem_old[0] += bm->totvert;
6260 totelem_old[1] += bm->totedge;
6261 totelem_old[2] += bm->totface;
6262 } /* objects */
6263
6264 const float thresh = RNA_float_get(op->ptr, "threshold");
6265
6266 for (Object *obedit : objects) {
6268 BMesh *bm = em->bm;
6269
6270 if (!EDBM_op_callf(em, op, "dissolve_degenerate edges=%he dist=%f", BM_ELEM_SELECT, thresh)) {
6271 continue;
6272 }
6273
6274 /* tricky to maintain correct selection here, so just flush up from verts */
6276
6278 params.calc_looptris = true;
6279 params.calc_normals = false;
6280 params.is_destructive = true;
6281 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
6282
6283 totelem_new[0] += bm->totvert;
6284 totelem_new[1] += bm->totedge;
6285 totelem_new[2] += bm->totface;
6286 }
6287
6288 edbm_report_delete_info(op->reports, totelem_old, totelem_new);
6289
6290 return OPERATOR_FINISHED;
6291}
6292
6294{
6295 /* identifiers */
6296 ot->name = "Degenerate Dissolve";
6297 ot->idname = "MESH_OT_dissolve_degenerate";
6298 ot->description = "Dissolve zero area faces and zero length edges";
6299
6300 /* API callbacks. */
6302 ot->poll = ED_operator_editmesh;
6303
6304 /* flags */
6305 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6306
6308 "threshold",
6309 1e-4f,
6310 1e-6f,
6311 50.0f,
6312 "Merge Distance",
6313 "Maximum distance between elements to merge",
6314 1e-5f,
6315 10.0f);
6316}
6317
6319
6320/* -------------------------------------------------------------------- */
6323
6324/* internally uses dissolve */
6326{
6327 const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split");
6328 const Scene *scene = CTX_data_scene(C);
6329 ViewLayer *view_layer = CTX_data_view_layer(C);
6330
6332 scene, view_layer, CTX_wm_view3d(C));
6333 for (Object *obedit : objects) {
6335
6336 if (em->bm->totedgesel == 0) {
6337 continue;
6338 }
6339
6340 /* deal with selection */
6341 {
6342 BMEdge *e;
6343 BMIter iter;
6344
6346
6347 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
6348 if (BM_elem_flag_test(e, BM_ELEM_SELECT) && e->l) {
6349 BMLoop *l_iter = e->l;
6350 do {
6352 } while ((l_iter = l_iter->radial_next) != e->l);
6353 }
6354 }
6355 }
6356
6357 if (!EDBM_op_callf(
6358 em,
6359 op,
6360 "dissolve_edges edges=%he use_verts=%b use_face_split=%b angle_threshold=%f",
6362 true,
6363 use_face_split,
6364 M_PI))
6365 {
6366 continue;
6367 }
6368
6370
6372
6374 params.calc_looptris = true;
6375 params.calc_normals = false;
6376 params.is_destructive = true;
6377 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
6378 }
6379
6380 return OPERATOR_FINISHED;
6381}
6382
6384{
6385 /* identifiers */
6386 ot->name = "Delete Edge Loop";
6387 ot->description = "Delete an edge loop by merging the faces on each side";
6388 ot->idname = "MESH_OT_delete_edgeloop";
6389
6390 /* API callbacks. */
6392 ot->poll = ED_operator_editmesh;
6393
6394 /* flags */
6395 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6396
6397 RNA_def_boolean(ot->srna,
6398 "use_face_split",
6399 true,
6400 "Face Split",
6401 "Split off face corners to maintain surrounding geometry");
6402}
6403
6405
6406/* -------------------------------------------------------------------- */
6409
6411{
6412 const Scene *scene = CTX_data_scene(C);
6413 ViewLayer *view_layer = CTX_data_view_layer(C);
6415 scene, view_layer, CTX_wm_view3d(C));
6416 for (Object *obedit : objects) {
6418 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
6419 continue;
6420 }
6422
6423 BMOperator bmop;
6424 EDBM_op_init(em, &bmop, op, "split geom=%hvef use_only_faces=%b", BM_ELEM_SELECT, false);
6425 BMO_op_exec(em->bm, &bmop);
6428 em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
6429
6431
6432 if (!EDBM_op_finish(em, &bmop, op, true)) {
6433 continue;
6434 }
6435
6436 /* Geometry has changed, need to recalculate normals and tessellation. */
6438 params.calc_looptris = true;
6439 params.calc_normals = true;
6440 params.is_destructive = true;
6441 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
6442 }
6443
6444 return OPERATOR_FINISHED;
6445}
6446
6448{
6449 /* identifiers */
6450 ot->name = "Split";
6451 ot->idname = "MESH_OT_split";
6452 ot->description = "Split off selected geometry from connected unselected geometry";
6453
6454 /* API callbacks. */
6455 ot->exec = edbm_split_exec;
6456 ot->poll = ED_operator_editmesh;
6457
6458 /* flags */
6459 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6460}
6461
6463
6464/* -------------------------------------------------------------------- */
6470
6471enum {
6487};
6488
6491 float srt;
6494};
6495
6496static int bmelemsort_comp(const void *v1, const void *v2)
6497{
6498 const BMElemSort *x1 = static_cast<const BMElemSort *>(v1);
6499 const BMElemSort *x2 = static_cast<const BMElemSort *>(v2);
6500
6501 return (x1->srt > x2->srt) - (x1->srt < x2->srt);
6502}
6503
6504/* Reorders vertices/edges/faces using a given methods. Loops are not supported. */
6506 Scene *scene,
6507 Object *ob,
6508 RegionView3D *rv3d,
6509 const int types,
6510 const int flag,
6511 const int action,
6512 const int reverse,
6513 const uint seed)
6514{
6516
6517 BMVert *ve;
6518 BMEdge *ed;
6519 BMFace *fa;
6520 BMIter iter;
6521
6522 /* In all five elements below, 0 = vertices, 1 = edges, 2 = faces. */
6523 /* Just to mark protected elements. */
6524 char *pblock[3] = {nullptr, nullptr, nullptr}, *pb;
6525 BMElemSort *sblock[3] = {nullptr, nullptr, nullptr}, *sb;
6526 uint *map[3] = {nullptr, nullptr, nullptr}, *mp;
6527 int totelem[3] = {0, 0, 0};
6528 int affected[3] = {0, 0, 0};
6529 int i, j;
6530
6531 if (!(types && flag && action)) {
6532 return;
6533 }
6534
6535 if (types & BM_VERT) {
6536 totelem[0] = em->bm->totvert;
6537 }
6538 if (types & BM_EDGE) {
6539 totelem[1] = em->bm->totedge;
6540 }
6541 if (types & BM_FACE) {
6542 totelem[2] = em->bm->totface;
6543 }
6544
6545 if (ELEM(action, SRT_VIEW_ZAXIS, SRT_VIEW_XAXIS)) {
6546 float mat[4][4];
6547 float fact = reverse ? -1.0 : 1.0;
6548 int coidx = (action == SRT_VIEW_ZAXIS) ? 2 : 0;
6549
6550 /* Apply the view matrix to the object matrix. */
6551 mul_m4_m4m4(mat, rv3d->viewmat, ob->object_to_world().ptr());
6552
6553 if (totelem[0]) {
6554 pb = pblock[0] = MEM_calloc_arrayN<char>(totelem[0], __func__);
6555 sb = sblock[0] = MEM_calloc_arrayN<BMElemSort>(totelem[0], __func__);
6556
6557 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6558 if (BM_elem_flag_test(ve, flag)) {
6559 float co[3];
6560 mul_v3_m4v3(co, mat, ve->co);
6561
6562 pb[i] = false;
6563 sb[affected[0]].org_idx = i;
6564 sb[affected[0]++].srt = co[coidx] * fact;
6565 }
6566 else {
6567 pb[i] = true;
6568 }
6569 }
6570 }
6571
6572 if (totelem[1]) {
6573 pb = pblock[1] = MEM_calloc_arrayN<char>(totelem[1], __func__);
6574 sb = sblock[1] = MEM_calloc_arrayN<BMElemSort>(totelem[1], __func__);
6575
6576 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6577 if (BM_elem_flag_test(ed, flag)) {
6578 float co[3];
6579 mid_v3_v3v3(co, ed->v1->co, ed->v2->co);
6580 mul_m4_v3(mat, co);
6581
6582 pb[i] = false;
6583 sb[affected[1]].org_idx = i;
6584 sb[affected[1]++].srt = co[coidx] * fact;
6585 }
6586 else {
6587 pb[i] = true;
6588 }
6589 }
6590 }
6591
6592 if (totelem[2]) {
6593 pb = pblock[2] = MEM_calloc_arrayN<char>(totelem[2], __func__);
6594 sb = sblock[2] = MEM_calloc_arrayN<BMElemSort>(totelem[2], __func__);
6595
6596 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6597 if (BM_elem_flag_test(fa, flag)) {
6598 float co[3];
6600 mul_m4_v3(mat, co);
6601
6602 pb[i] = false;
6603 sb[affected[2]].org_idx = i;
6604 sb[affected[2]++].srt = co[coidx] * fact;
6605 }
6606 else {
6607 pb[i] = true;
6608 }
6609 }
6610 }
6611 }
6612
6613 else if (action == SRT_CURSOR_DISTANCE) {
6614 float cur[3];
6615 float mat[4][4];
6616 float fact = reverse ? -1.0 : 1.0;
6617
6618 copy_v3_v3(cur, scene->cursor.location);
6619
6620 invert_m4_m4(mat, ob->object_to_world().ptr());
6621 mul_m4_v3(mat, cur);
6622
6623 if (totelem[0]) {
6624 pb = pblock[0] = MEM_calloc_arrayN<char>(totelem[0], __func__);
6625 sb = sblock[0] = MEM_calloc_arrayN<BMElemSort>(totelem[0], __func__);
6626
6627 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6628 if (BM_elem_flag_test(ve, flag)) {
6629 pb[i] = false;
6630 sb[affected[0]].org_idx = i;
6631 sb[affected[0]++].srt = len_squared_v3v3(cur, ve->co) * fact;
6632 }
6633 else {
6634 pb[i] = true;
6635 }
6636 }
6637 }
6638
6639 if (totelem[1]) {
6640 pb = pblock[1] = MEM_calloc_arrayN<char>(totelem[1], __func__);
6641 sb = sblock[1] = MEM_calloc_arrayN<BMElemSort>(totelem[1], __func__);
6642
6643 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6644 if (BM_elem_flag_test(ed, flag)) {
6645 float co[3];
6646 mid_v3_v3v3(co, ed->v1->co, ed->v2->co);
6647
6648 pb[i] = false;
6649 sb[affected[1]].org_idx = i;
6650 sb[affected[1]++].srt = len_squared_v3v3(cur, co) * fact;
6651 }
6652 else {
6653 pb[i] = true;
6654 }
6655 }
6656 }
6657
6658 if (totelem[2]) {
6659 pb = pblock[2] = MEM_calloc_arrayN<char>(totelem[2], __func__);
6660 sb = sblock[2] = MEM_calloc_arrayN<BMElemSort>(totelem[2], __func__);
6661
6662 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6663 if (BM_elem_flag_test(fa, flag)) {
6664 float co[3];
6666
6667 pb[i] = false;
6668 sb[affected[2]].org_idx = i;
6669 sb[affected[2]++].srt = len_squared_v3v3(cur, co) * fact;
6670 }
6671 else {
6672 pb[i] = true;
6673 }
6674 }
6675 }
6676 }
6677
6678 /* Faces only! */
6679 else if (action == SRT_MATERIAL && totelem[2]) {
6680 pb = pblock[2] = MEM_calloc_arrayN<char>(totelem[2], __func__);
6681 sb = sblock[2] = MEM_calloc_arrayN<BMElemSort>(totelem[2], __func__);
6682
6683 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6684 if (BM_elem_flag_test(fa, flag)) {
6685 /* Reverse materials' order, not order of faces inside each mat! */
6686 /* NOTE: cannot use totcol, as mat_nr may sometimes be greater... */
6687 float srt = reverse ? float(MAXMAT - fa->mat_nr) : float(fa->mat_nr);
6688 pb[i] = false;
6689 sb[affected[2]].org_idx = i;
6690 /* Multiplying with totface and adding i ensures us
6691 * we keep current order for all faces of same mat. */
6692 sb[affected[2]++].srt = srt * float(totelem[2]) + float(i);
6693 // printf("e: %d; srt: %f; final: %f\n", i, srt, srt * float(totface) + float(i));
6694 }
6695 else {
6696 pb[i] = true;
6697 }
6698 }
6699 }
6700
6701 else if (action == SRT_SELECTED) {
6702 uint *tbuf[3] = {nullptr, nullptr, nullptr}, *tb;
6703
6704 if (totelem[0]) {
6705 tb = tbuf[0] = MEM_calloc_arrayN<uint>(totelem[0], __func__);
6706 mp = map[0] = MEM_calloc_arrayN<uint>(totelem[0], __func__);
6707
6708 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6709 if (BM_elem_flag_test(ve, flag)) {
6710 mp[affected[0]++] = i;
6711 }
6712 else {
6713 *tb = i;
6714 tb++;
6715 }
6716 }
6717 }
6718
6719 if (totelem[1]) {
6720 tb = tbuf[1] = MEM_calloc_arrayN<uint>(totelem[1], __func__);
6721 mp = map[1] = MEM_calloc_arrayN<uint>(totelem[1], __func__);
6722
6723 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6724 if (BM_elem_flag_test(ed, flag)) {
6725 mp[affected[1]++] = i;
6726 }
6727 else {
6728 *tb = i;
6729 tb++;
6730 }
6731 }
6732 }
6733
6734 if (totelem[2]) {
6735 tb = tbuf[2] = MEM_calloc_arrayN<uint>(totelem[2], __func__);
6736 mp = map[2] = MEM_calloc_arrayN<uint>(totelem[2], __func__);
6737
6738 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6739 if (BM_elem_flag_test(fa, flag)) {
6740 mp[affected[2]++] = i;
6741 }
6742 else {
6743 *tb = i;
6744 tb++;
6745 }
6746 }
6747 }
6748
6749 for (j = 3; j--;) {
6750 int tot = totelem[j];
6751 int aff = affected[j];
6752 tb = tbuf[j];
6753 mp = map[j];
6754 if (!(tb && mp)) {
6755 continue;
6756 }
6757 if (ELEM(aff, 0, tot)) {
6758 MEM_freeN(tb);
6759 MEM_freeN(mp);
6760 map[j] = nullptr;
6761 continue;
6762 }
6763 if (reverse) {
6764 memcpy(tb + (tot - aff), mp, aff * sizeof(int));
6765 }
6766 else {
6767 memcpy(mp + aff, tb, (tot - aff) * sizeof(int));
6768 tb = mp;
6769 mp = map[j] = tbuf[j];
6770 tbuf[j] = tb;
6771 }
6772
6773 /* Reverse mapping, we want an org2new one! */
6774 for (i = tot, tb = tbuf[j] + tot - 1; i--; tb--) {
6775 mp[*tb] = i;
6776 }
6777 MEM_freeN(tbuf[j]);
6778 }
6779 }
6780
6781 else if (action == SRT_RANDOMIZE) {
6782 if (totelem[0]) {
6783 /* Re-init random generator for each element type, to get consistent random when
6784 * enabling/disabling an element type. */
6786 pb = pblock[0] = MEM_calloc_arrayN<char>(totelem[0], __func__);
6787 sb = sblock[0] = MEM_calloc_arrayN<BMElemSort>(totelem[0], __func__);
6788
6789 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6790 if (BM_elem_flag_test(ve, flag)) {
6791 pb[i] = false;
6792 sb[affected[0]].org_idx = i;
6793 sb[affected[0]++].srt = BLI_rng_get_float(rng);
6794 }
6795 else {
6796 pb[i] = true;
6797 }
6798 }
6799
6800 BLI_rng_free(rng);
6801 }
6802
6803 if (totelem[1]) {
6805 pb = pblock[1] = MEM_calloc_arrayN<char>(totelem[1], __func__);
6806 sb = sblock[1] = MEM_calloc_arrayN<BMElemSort>(totelem[1], __func__);
6807
6808 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6809 if (BM_elem_flag_test(ed, flag)) {
6810 pb[i] = false;
6811 sb[affected[1]].org_idx = i;
6812 sb[affected[1]++].srt = BLI_rng_get_float(rng);
6813 }
6814 else {
6815 pb[i] = true;
6816 }
6817 }
6818
6819 BLI_rng_free(rng);
6820 }
6821
6822 if (totelem[2]) {
6824 pb = pblock[2] = MEM_calloc_arrayN<char>(totelem[2], __func__);
6825 sb = sblock[2] = MEM_calloc_arrayN<BMElemSort>(totelem[2], __func__);
6826
6827 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6828 if (BM_elem_flag_test(fa, flag)) {
6829 pb[i] = false;
6830 sb[affected[2]].org_idx = i;
6831 sb[affected[2]++].srt = BLI_rng_get_float(rng);
6832 }
6833 else {
6834 pb[i] = true;
6835 }
6836 }
6837
6838 BLI_rng_free(rng);
6839 }
6840 }
6841
6842 else if (action == SRT_REVERSE) {
6843 if (totelem[0]) {
6844 pb = pblock[0] = MEM_calloc_arrayN<char>(totelem[0], __func__);
6845 sb = sblock[0] = MEM_calloc_arrayN<BMElemSort>(totelem[0], __func__);
6846
6847 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6848 if (BM_elem_flag_test(ve, flag)) {
6849 pb[i] = false;
6850 sb[affected[0]].org_idx = i;
6851 sb[affected[0]++].srt = float(-i);
6852 }
6853 else {
6854 pb[i] = true;
6855 }
6856 }
6857 }
6858
6859 if (totelem[1]) {
6860 pb = pblock[1] = MEM_calloc_arrayN<char>(totelem[1], __func__);
6861 sb = sblock[1] = MEM_calloc_arrayN<BMElemSort>(totelem[1], __func__);
6862
6863 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6864 if (BM_elem_flag_test(ed, flag)) {
6865 pb[i] = false;
6866 sb[affected[1]].org_idx = i;
6867 sb[affected[1]++].srt = float(-i);
6868 }
6869 else {
6870 pb[i] = true;
6871 }
6872 }
6873 }
6874
6875 if (totelem[2]) {
6876 pb = pblock[2] = MEM_calloc_arrayN<char>(totelem[2], __func__);
6877 sb = sblock[2] = MEM_calloc_arrayN<BMElemSort>(totelem[2], __func__);
6878
6879 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6880 if (BM_elem_flag_test(fa, flag)) {
6881 pb[i] = false;
6882 sb[affected[2]].org_idx = i;
6883 sb[affected[2]++].srt = float(-i);
6884 }
6885 else {
6886 pb[i] = true;
6887 }
6888 }
6889 }
6890 }
6891
6892 // printf("%d vertices: %d to be affected...\n", totelem[0], affected[0]);
6893 // printf("%d edges: %d to be affected...\n", totelem[1], affected[1]);
6894 // printf("%d faces: %d to be affected...\n", totelem[2], affected[2]);
6895 if (affected[0] == 0 && affected[1] == 0 && affected[2] == 0) {
6896 for (j = 3; j--;) {
6897 if (pblock[j]) {
6898 MEM_freeN(pblock[j]);
6899 }
6900 if (sblock[j]) {
6901 MEM_freeN(sblock[j]);
6902 }
6903 if (map[j]) {
6904 MEM_freeN(map[j]);
6905 }
6906 }
6907 return;
6908 }
6909
6910 /* Sort affected elements, and populate mapping arrays, if needed. */
6911 for (j = 3; j--;) {
6912 pb = pblock[j];
6913 sb = sblock[j];
6914 if (pb && sb && !map[j]) {
6915 const char *p_blk;
6916 BMElemSort *s_blk;
6917 int tot = totelem[j];
6918 int aff = affected[j];
6919
6920 qsort(sb, aff, sizeof(BMElemSort), bmelemsort_comp);
6921
6922 mp = map[j] = MEM_malloc_arrayN<uint>(tot, __func__);
6923 p_blk = pb + tot - 1;
6924 s_blk = sb + aff - 1;
6925 for (i = tot; i--; p_blk--) {
6926 if (*p_blk) { /* Protected! */
6927 mp[i] = i;
6928 }
6929 else {
6930 mp[s_blk->org_idx] = i;
6931 s_blk--;
6932 }
6933 }
6934 }
6935 if (pb) {
6936 MEM_freeN(pb);
6937 }
6938 if (sb) {
6939 MEM_freeN(sb);
6940 }
6941 }
6942
6943 BM_mesh_remap(em->bm, map[0], map[1], map[2]);
6944
6946 params.calc_looptris = (totelem[2] != 0);
6947 params.calc_normals = false;
6948 params.is_destructive = true;
6949 EDBM_update(static_cast<Mesh *>(ob->data), &params);
6950
6951 DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_GEOMETRY);
6953
6954 for (j = 3; j--;) {
6955 if (map[j]) {
6956 MEM_freeN(map[j]);
6957 }
6958 }
6959}
6960
6962{
6963 Scene *scene = CTX_data_scene(C);
6964 ViewLayer *view_layer = CTX_data_view_layer(C);
6965 Object *ob_active = CTX_data_edit_object(C);
6966
6967 /* may be nullptr */
6969
6970 const int action = RNA_enum_get(op->ptr, "type");
6971 PropertyRNA *prop_elem_types = RNA_struct_find_property(op->ptr, "elements");
6972 const bool use_reverse = RNA_boolean_get(op->ptr, "reverse");
6973 uint seed = RNA_int_get(op->ptr, "seed");
6974 int elem_types = 0;
6975
6976 if (ELEM(action, SRT_VIEW_ZAXIS, SRT_VIEW_XAXIS)) {
6977 if (rv3d == nullptr) {
6978 BKE_report(op->reports, RPT_ERROR, "View not found, cannot sort by view axis");
6979 return OPERATOR_CANCELLED;
6980 }
6981 }
6982
6983 /* If no elem_types set, use current selection mode to set it! */
6984 if (RNA_property_is_set(op->ptr, prop_elem_types)) {
6985 elem_types = RNA_property_enum_get(op->ptr, prop_elem_types);
6986 }
6987 else {
6988 BMEditMesh *em = BKE_editmesh_from_object(ob_active);
6989 if (em->selectmode & SCE_SELECT_VERTEX) {
6990 elem_types |= BM_VERT;
6991 }
6992 if (em->selectmode & SCE_SELECT_EDGE) {
6993 elem_types |= BM_EDGE;
6994 }
6995 if (em->selectmode & SCE_SELECT_FACE) {
6996 elem_types |= BM_FACE;
6997 }
6998 RNA_enum_set(op->ptr, "elements", elem_types);
6999 }
7000
7002 scene, view_layer, CTX_wm_view3d(C));
7003
7004 for (uint ob_index = 0; ob_index < objects.size(); ob_index++) {
7005 Object *ob = objects[ob_index];
7007 BMesh *bm = em->bm;
7008
7009 if (!((elem_types & BM_VERT && bm->totvertsel > 0) ||
7010 (elem_types & BM_EDGE && bm->totedgesel > 0) ||
7011 (elem_types & BM_FACE && bm->totfacesel > 0)))
7012 {
7013 continue;
7014 }
7015
7016 int seed_iter = seed;
7017
7018 /* This gives a consistent result regardless of object order */
7019 if (ob_index) {
7020 seed_iter += BLI_ghashutil_strhash_p(ob->id.name);
7021 }
7022
7024 C, scene, ob, rv3d, elem_types, BM_ELEM_SELECT, action, use_reverse, seed_iter);
7025 }
7026 return OPERATOR_FINISHED;
7027}
7028
7030 wmOperator *op,
7031 const PropertyRNA *prop)
7032{
7033 const char *prop_id = RNA_property_identifier(prop);
7034 const int action = RNA_enum_get(op->ptr, "type");
7035
7036 /* Only show seed for randomize action! */
7037 if (STREQ(prop_id, "seed")) {
7038 if (action == SRT_RANDOMIZE) {
7039 return true;
7040 }
7041 return false;
7042 }
7043
7044 /* Hide seed for reverse and randomize actions! */
7045 if (STREQ(prop_id, "reverse")) {
7046 if (ELEM(action, SRT_RANDOMIZE, SRT_REVERSE)) {
7047 return false;
7048 }
7049 return true;
7050 }
7051
7052 return true;
7053}
7054
7056{
7057 static const EnumPropertyItem type_items[] = {
7059 "VIEW_ZAXIS",
7060 0,
7061 "View Z Axis",
7062 "Sort selected elements from farthest to nearest one in current view"},
7064 "VIEW_XAXIS",
7065 0,
7066 "View X Axis",
7067 "Sort selected elements from left to right one in current view"},
7069 "CURSOR_DISTANCE",
7070 0,
7071 "Cursor Distance",
7072 "Sort selected elements from nearest to farthest from 3D cursor"},
7073 {SRT_MATERIAL,
7074 "MATERIAL",
7075 0,
7076 "Material",
7077 "Sort selected faces from smallest to greatest material index"},
7078 {SRT_SELECTED,
7079 "SELECTED",
7080 0,
7081 "Selected",
7082 "Move all selected elements in first places, preserving their relative order.\n"
7083 "Warning: This will affect unselected elements' indices as well"},
7084 {SRT_RANDOMIZE, "RANDOMIZE", 0, "Randomize", "Randomize order of selected elements"},
7085 {SRT_REVERSE, "REVERSE", 0, "Reverse", "Reverse current order of selected elements"},
7086 {0, nullptr, 0, nullptr, nullptr},
7087 };
7088
7089 static const EnumPropertyItem elem_items[] = {
7090 {BM_VERT, "VERT", 0, "Vertices", ""},
7091 {BM_EDGE, "EDGE", 0, "Edges", ""},
7092 {BM_FACE, "FACE", 0, "Faces", ""},
7093 {0, nullptr, 0, nullptr, nullptr},
7094 };
7095
7096 /* identifiers */
7097 ot->name = "Sort Mesh Elements";
7098 ot->description =
7099 "The order of selected vertices/edges/faces is modified, based on a given method";
7100 ot->idname = "MESH_OT_sort_elements";
7101
7102 /* API callbacks. */
7103 ot->invoke = WM_menu_invoke;
7105 ot->poll = ED_operator_editmesh;
7106 ot->poll_property = edbm_sort_elements_poll_property;
7107
7108 /* flags */
7109 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
7110
7111 /* properties */
7112 ot->prop = RNA_def_enum(ot->srna,
7113 "type",
7114 type_items,
7116 "Type",
7117 "Type of reordering operation to apply");
7118 RNA_def_enum_flag(ot->srna,
7119 "elements",
7120 elem_items,
7121 BM_VERT,
7122 "Elements",
7123 "Which elements to affect (vertices, edges and/or faces)");
7124 RNA_def_boolean(ot->srna, "reverse", false, "Reverse", "Reverse the sorting effect");
7125 RNA_def_int(ot->srna, "seed", 0, 0, INT_MAX, "Seed", "Seed for random-based operations", 0, 255);
7126}
7127
7129
7130/* -------------------------------------------------------------------- */
7133
7134enum {
7138};
7139
7141{
7142 /* tags boundary edges from a face selection */
7143 BMIter iter;
7144 BMFace *f;
7145 BMEdge *e;
7146 int totface_del = 0;
7147
7149
7150 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
7154 }
7155 else {
7156 BMIter fiter;
7157 bool is_all_sel = true;
7158 /* check if its only used by selected faces */
7159 BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) {
7161 /* Tag face for removal. */
7162 if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
7164 totface_del++;
7165 }
7166 }
7167 else {
7168 is_all_sel = false;
7169 }
7170 }
7171
7172 if (is_all_sel == false) {
7174 }
7175 }
7176 }
7177 }
7178
7179 return totface_del;
7180}
7181
7183 BMEditMesh *em,
7184 Mesh *mesh,
7185 const bool use_pairs,
7186 const bool use_cyclic,
7187 const bool use_merge,
7188 const float merge_factor,
7189 const int twist_offset)
7190{
7191 BMOperator bmop;
7192 char edge_hflag;
7193 int totface_del = 0;
7194 BMFace **totface_del_arr = nullptr;
7195 const bool use_faces = (em->bm->totfacesel != 0);
7196 bool changed = false;
7197
7198 if (use_faces) {
7199 /* NOTE: When all faces are selected, all faces will be deleted with no edge-loops remaining.
7200 * In this case bridge will fail with a waning and delete all faces.
7201 * Ideally it's possible to detect cases when deleting faces leaves remaining edge-loops.
7202 * While this can be done in trivial cases - by checking the number of selected faces matches
7203 * the number of faces, that won't work for more involved cases involving hidden faces
7204 * and wire edges. One option could be to copy & restore the edit-mesh however
7205 * this is quite an expensive operation - to properly handle clearly invalid input.
7206 * Accept this limitation, the user must undo to restore the previous state, see: #123405. */
7207
7208 BMIter iter;
7209 BMFace *f;
7210 int i;
7211
7212 totface_del = edbm_bridge_tag_boundary_edges(em->bm);
7213 totface_del_arr = static_cast<BMFace **>(
7214 MEM_mallocN(sizeof(*totface_del_arr) * totface_del, __func__));
7215
7216 i = 0;
7217 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
7219 totface_del_arr[i++] = f;
7220 }
7221 }
7222 edge_hflag = BM_ELEM_TAG;
7223 }
7224 else {
7225 edge_hflag = BM_ELEM_SELECT;
7226 }
7227
7228 EDBM_op_init(em,
7229 &bmop,
7230 op,
7231 "bridge_loops edges=%he use_pairs=%b use_cyclic=%b use_merge=%b merge_factor=%f "
7232 "twist_offset=%i",
7233 edge_hflag,
7234 use_pairs,
7235 use_cyclic,
7236 use_merge,
7237 merge_factor,
7238 twist_offset);
7239
7240 if (use_faces && totface_del) {
7241 int i;
7243 for (i = 0; i < totface_del; i++) {
7244 BM_elem_flag_enable(totface_del_arr[i], BM_ELEM_TAG);
7245 }
7246 BMO_op_callf(em->bm,
7248 "delete geom=%hf context=%i",
7251 changed = true;
7252 }
7253
7254 BMO_op_exec(em->bm, &bmop);
7255
7257 /* when merge is used the edges are joined and remain selected */
7258 if (use_merge == false) {
7261 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
7262
7263 changed = true;
7264 }
7265
7266 if (use_merge == false) {
7267 EdgeRingOpSubdProps op_props;
7268 mesh_operator_edgering_props_get(op, &op_props);
7269
7270 if (op_props.cuts) {
7271 BMOperator bmop_subd;
7272 /* we only need face normals updated */
7274
7275 BMO_op_initf(em->bm,
7276 &bmop_subd,
7277 0,
7278 "subdivide_edgering edges=%S interp_mode=%i cuts=%i smooth=%f "
7279 "profile_shape=%i profile_shape_factor=%f",
7280 &bmop,
7281 "edges.out",
7282 op_props.interp_mode,
7283 op_props.cuts,
7284 op_props.smooth,
7285 op_props.profile_shape,
7286 op_props.profile_shape_factor);
7287 BMO_op_exec(em->bm, &bmop_subd);
7289 em->bm, bmop_subd.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
7290 BMO_op_finish(em->bm, &bmop_subd);
7291
7292 changed = true;
7293 }
7294 }
7295 }
7296
7297 if (totface_del_arr) {
7298 MEM_freeN(totface_del_arr);
7299 }
7300
7301 if (EDBM_op_finish(em, &bmop, op, true)) {
7302 changed = true;
7303 }
7304
7305 if (changed) {
7307 params.calc_looptris = true;
7308 params.calc_normals = false;
7309 params.is_destructive = true;
7310 EDBM_update(mesh, &params);
7311 }
7312
7313 /* Always return finished so the user can select different options. */
7314 return OPERATOR_FINISHED;
7315}
7316
7318{
7319 const int type = RNA_enum_get(op->ptr, "type");
7320 const bool use_pairs = (type == MESH_BRIDGELOOP_PAIRS);
7321 const bool use_cyclic = (type == MESH_BRIDGELOOP_CLOSED);
7322 const bool use_merge = RNA_boolean_get(op->ptr, "use_merge");
7323 const float merge_factor = RNA_float_get(op->ptr, "merge_factor");
7324 const int twist_offset = RNA_int_get(op->ptr, "twist_offset");
7325 const Scene *scene = CTX_data_scene(C);
7326 ViewLayer *view_layer = CTX_data_view_layer(C);
7327
7329 scene, view_layer, CTX_wm_view3d(C));
7330 for (Object *obedit : objects) {
7332
7333 if (em->bm->totvertsel == 0) {
7334 continue;
7335 }
7336
7338 em,
7339 static_cast<Mesh *>(obedit->data),
7340 use_pairs,
7341 use_cyclic,
7342 use_merge,
7343 merge_factor,
7344 twist_offset);
7345 }
7346 return OPERATOR_FINISHED;
7347}
7348
7350{
7351 static const EnumPropertyItem type_items[] = {
7352 {MESH_BRIDGELOOP_SINGLE, "SINGLE", 0, "Open Loop", ""},
7353 {MESH_BRIDGELOOP_CLOSED, "CLOSED", 0, "Closed Loop", ""},
7354 {MESH_BRIDGELOOP_PAIRS, "PAIRS", 0, "Loop Pairs", ""},
7355 {0, nullptr, 0, nullptr, nullptr},
7356 };
7357
7358 /* identifiers */
7359 ot->name = "Bridge Edge Loops";
7360 ot->description = "Create a bridge of faces between two or more selected edge loops";
7361 ot->idname = "MESH_OT_bridge_edge_loops";
7362
7363 /* API callbacks. */
7365 ot->poll = ED_operator_editmesh;
7366
7367 /* flags */
7368 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
7369
7370 ot->prop = RNA_def_enum(ot->srna,
7371 "type",
7372 type_items,
7374 "Connect Loops",
7375 "Method of bridging multiple loops");
7376
7377 RNA_def_boolean(ot->srna, "use_merge", false, "Merge", "Merge rather than creating faces");
7378 RNA_def_float(ot->srna, "merge_factor", 0.5f, 0.0f, 1.0f, "Merge Factor", "", 0.0f, 1.0f);
7379 RNA_def_int(ot->srna,
7380 "twist_offset",
7381 0,
7382 -1000,
7383 1000,
7384 "Twist",
7385 "Twist offset for closed loops",
7386 -1000,
7387 1000);
7388
7390}
7391
7393
7394/* -------------------------------------------------------------------- */
7397
7399{
7400 const bool use_boundary = RNA_boolean_get(op->ptr, "use_boundary");
7401 const bool use_even_offset = RNA_boolean_get(op->ptr, "use_even_offset");
7402 const bool use_replace = RNA_boolean_get(op->ptr, "use_replace");
7403 const bool use_relative_offset = RNA_boolean_get(op->ptr, "use_relative_offset");
7404 const bool use_crease = RNA_boolean_get(op->ptr, "use_crease");
7405 const float crease_weight = RNA_float_get(op->ptr, "crease_weight");
7406 const float thickness = RNA_float_get(op->ptr, "thickness");
7407 const float offset = RNA_float_get(op->ptr, "offset");
7408
7409 const Scene *scene = CTX_data_scene(C);
7410 ViewLayer *view_layer = CTX_data_view_layer(C);
7412 scene, view_layer, CTX_wm_view3d(C));
7413 for (Object *obedit : objects) {
7415
7416 if (em->bm->totfacesel == 0) {
7417 continue;
7418 }
7419
7420 BMOperator bmop;
7421
7422 EDBM_op_init(em,
7423 &bmop,
7424 op,
7425 "wireframe faces=%hf use_replace=%b use_boundary=%b use_even_offset=%b "
7426 "use_relative_offset=%b "
7427 "use_crease=%b crease_weight=%f thickness=%f offset=%f",
7429 use_replace,
7430 use_boundary,
7431 use_even_offset,
7432 use_relative_offset,
7433 use_crease,
7434 crease_weight,
7435 thickness,
7436 offset);
7437
7438 BMO_op_exec(em->bm, &bmop);
7439
7442 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
7443
7444 if (!EDBM_op_finish(em, &bmop, op, true)) {
7445 continue;
7446 }
7447
7449 params.calc_looptris = true;
7450 params.calc_normals = false;
7451 params.is_destructive = true;
7452 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7453 }
7454
7455 return OPERATOR_FINISHED;
7456}
7457
7459{
7460 PropertyRNA *prop;
7461
7462 /* identifiers */
7463 ot->name = "Wireframe";
7464 ot->idname = "MESH_OT_wireframe";
7465 ot->description = "Create a solid wireframe from faces";
7466
7467 /* API callbacks. */
7468 ot->exec = edbm_wireframe_exec;
7469 ot->poll = ED_operator_editmesh;
7470
7471 /* flags */
7472 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
7473
7474 /* properties */
7475 RNA_def_boolean(ot->srna, "use_boundary", true, "Boundary", "Inset face boundaries");
7476 RNA_def_boolean(ot->srna,
7477 "use_even_offset",
7478 true,
7479 "Offset Even",
7480 "Scale the offset to give more even thickness");
7481 RNA_def_boolean(ot->srna,
7482 "use_relative_offset",
7483 false,
7484 "Offset Relative",
7485 "Scale the offset by surrounding geometry");
7486 RNA_def_boolean(ot->srna, "use_replace", true, "Replace", "Remove original faces");
7488 ot->srna, "thickness", 0.01f, 0.0f, 1e4f, "Thickness", "", 0.0f, 10.0f);
7489 /* use 1 rather than 10 for max else dragging the button moves too far */
7490 RNA_def_property_ui_range(prop, 0.0, 1.0, 0.01, 4);
7491 RNA_def_float_distance(ot->srna, "offset", 0.01f, 0.0f, 1e4f, "Offset", "", 0.0f, 10.0f);
7492 RNA_def_boolean(ot->srna,
7493 "use_crease",
7494 false,
7495 "Crease",
7496 "Crease hub edges for an improved subdivision surface");
7497 prop = RNA_def_float(
7498 ot->srna, "crease_weight", 0.01f, 0.0f, 1e3f, "Crease Weight", "", 0.0f, 1.0f);
7499 RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 2);
7500}
7501
7503
7504/* -------------------------------------------------------------------- */
7507
7509{
7510 const bool use_cap_endpoint = RNA_boolean_get(op->ptr, "use_cap_endpoint");
7511 bool changed_multi = false;
7512 Scene *scene = CTX_data_scene(C);
7513 ViewLayer *view_layer = CTX_data_view_layer(C);
7515 scene, view_layer, CTX_wm_view3d(C));
7516 for (Base *base : bases) {
7517 Object *obedit = base->object;
7519
7520 if (em->bm->totedgesel == 0) {
7521 continue;
7522 }
7523
7524 BMOperator bmop;
7525 EDBM_op_init(em,
7526 &bmop,
7527 op,
7528 "offset_edgeloops edges=%he use_cap_endpoint=%b",
7530 use_cap_endpoint);
7531
7532 BMO_op_exec(em->bm, &bmop);
7533
7535
7537 em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true);
7538
7539 if (EDBM_op_finish(em, &bmop, op, true)) {
7541 params.calc_looptris = true;
7542 params.calc_normals = false;
7543 params.is_destructive = true;
7544 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7545 changed_multi = true;
7546 }
7547 }
7548
7549 if (changed_multi) {
7555 if (scene->toolsettings->selectmode == SCE_SELECT_FACE) {
7557 }
7558 }
7559
7560 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
7561}
7562
7564{
7565 /* identifiers */
7566 ot->name = "Offset Edge Loop";
7567 ot->idname = "MESH_OT_offset_edge_loops";
7568 ot->description = "Create offset edge loop from the current selection";
7569
7570 /* API callbacks. */
7572 ot->poll = ED_operator_editmesh;
7573
7574 /* Keep internal, since this is only meant to be accessed via
7575 * `MESH_OT_offset_edge_loops_slide`. */
7576
7577 /* flags */
7579
7581 ot->srna, "use_cap_endpoint", false, "Cap Endpoint", "Extend loop around end-points");
7582}
7583
7585
7586/* -------------------------------------------------------------------- */
7589
7590#ifdef WITH_BULLET
7591static wmOperatorStatus edbm_convex_hull_exec(bContext *C, wmOperator *op)
7592{
7593 const bool use_existing_faces = RNA_boolean_get(op->ptr, "use_existing_faces");
7594 const bool delete_unused = RNA_boolean_get(op->ptr, "delete_unused");
7595 const bool make_holes = RNA_boolean_get(op->ptr, "make_holes");
7596 const bool join_triangles = RNA_boolean_get(op->ptr, "join_triangles");
7597
7598 float angle_face_threshold = RNA_float_get(op->ptr, "face_threshold");
7599 float angle_shape_threshold = RNA_float_get(op->ptr, "shape_threshold");
7600
7601 const Scene *scene = CTX_data_scene(C);
7602 ViewLayer *view_layer = CTX_data_view_layer(C);
7604 scene, view_layer, CTX_wm_view3d(C));
7605 for (Object *obedit : objects) {
7607
7608 if (em->bm->totvertsel == 0) {
7609 continue;
7610 }
7611
7612 BMOperator bmop;
7613
7614 EDBM_op_init(em,
7615 &bmop,
7616 op,
7617 "convex_hull input=%hvef "
7618 "use_existing_faces=%b",
7620 use_existing_faces);
7621 BMO_op_exec(em->bm, &bmop);
7622
7623 /* Hull fails if input is coplanar */
7625 EDBM_op_finish(em, &bmop, op, true);
7626 continue;
7627 }
7628
7630 em->bm, bmop.slots_out, "geom.out", BM_FACE, BM_ELEM_SELECT, true);
7631
7632 /* Delete unused vertices, edges, and faces */
7633 if (delete_unused) {
7634 if (!EDBM_op_callf(
7635 em, op, "delete geom=%S context=%i", &bmop, "geom_unused.out", DEL_ONLYTAGGED))
7636 {
7637 EDBM_op_finish(em, &bmop, op, true);
7638 continue;
7639 }
7640 }
7641
7642 /* Delete hole edges/faces */
7643 if (make_holes) {
7644 if (!EDBM_op_callf(
7645 em, op, "delete geom=%S context=%i", &bmop, "geom_holes.out", DEL_ONLYTAGGED))
7646 {
7647 EDBM_op_finish(em, &bmop, op, true);
7648 continue;
7649 }
7650 }
7651
7652 /* Merge adjacent triangles */
7653 if (join_triangles) {
7655 op,
7656 "faces.out",
7657 true,
7658 "join_triangles faces=%S "
7659 "angle_face_threshold=%f angle_shape_threshold=%f",
7660 &bmop,
7661 "geom.out",
7662 angle_face_threshold,
7663 angle_shape_threshold))
7664 {
7665 EDBM_op_finish(em, &bmop, op, true);
7666 continue;
7667 }
7668 }
7669
7670 if (!EDBM_op_finish(em, &bmop, op, true)) {
7671 continue;
7672 }
7673
7675 params.calc_looptris = true;
7676 params.calc_normals = false;
7677 params.is_destructive = true;
7678 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7680 }
7681
7682 return OPERATOR_FINISHED;
7683}
7684
7686{
7687 /* identifiers */
7688 ot->name = "Convex Hull";
7689 ot->description = "Enclose selected vertices in a convex polyhedron";
7690 ot->idname = "MESH_OT_convex_hull";
7691
7692 /* API callbacks. */
7693 ot->exec = edbm_convex_hull_exec;
7694 ot->poll = ED_operator_editmesh;
7695
7696 /* flags */
7697 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
7698
7699 /* props */
7700 RNA_def_boolean(ot->srna,
7701 "delete_unused",
7702 true,
7703 "Delete Unused",
7704 "Delete selected elements that are not used by the hull");
7705
7706 RNA_def_boolean(ot->srna,
7707 "use_existing_faces",
7708 true,
7709 "Use Existing Faces",
7710 "Skip hull triangles that are covered by a pre-existing face");
7711
7712 RNA_def_boolean(ot->srna,
7713 "make_holes",
7714 false,
7715 "Make Holes",
7716 "Delete selected faces that are used by the hull");
7717
7719 ot->srna, "join_triangles", true, "Join Triangles", "Merge adjacent triangles into quads");
7720
7722}
7723#endif /* WITH_BULLET */
7724
7726
7727/* -------------------------------------------------------------------- */
7730
7732{
7733 const float thresh = RNA_float_get(op->ptr, "threshold");
7734 const Scene *scene = CTX_data_scene(C);
7735 ViewLayer *view_layer = CTX_data_view_layer(C);
7737 scene, view_layer, CTX_wm_view3d(C));
7738
7739 for (Object *obedit : objects) {
7741
7742 if (em->bm->totvertsel == 0) {
7743 continue;
7744 }
7745
7746 BMOperator bmop;
7747 EDBM_op_init(em,
7748 &bmop,
7749 op,
7750 "symmetrize input=%hvef direction=%i dist=%f",
7752 RNA_enum_get(op->ptr, "direction"),
7753 thresh);
7754 BMO_op_exec(em->bm, &bmop);
7755
7757
7759 em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
7760
7761 if (!EDBM_op_finish(em, &bmop, op, true)) {
7762 continue;
7763 }
7765 params.calc_looptris = true;
7766 params.calc_normals = false;
7767 params.is_destructive = true;
7768 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7770 }
7771
7772 return OPERATOR_FINISHED;
7773}
7774
7776{
7777 /* identifiers */
7778 ot->name = "Symmetrize";
7779 ot->description = "Enforce symmetry (both form and topological) across an axis";
7780 ot->idname = "MESH_OT_symmetrize";
7781
7782 /* API callbacks. */
7783 ot->exec = mesh_symmetrize_exec;
7784 ot->poll = ED_operator_editmesh;
7785
7786 /* flags */
7787 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
7788
7789 ot->prop = RNA_def_enum(ot->srna,
7790 "direction",
7793 "Direction",
7794 "Which sides to copy from and to");
7795 RNA_def_float(ot->srna,
7796 "threshold",
7797 1e-4f,
7798 0.0f,
7799 10.0f,
7800 "Threshold",
7801 "Limit for snap middle vertices to the axis center",
7802 1e-5f,
7803 0.1f);
7804}
7805
7807
7808/* -------------------------------------------------------------------- */
7811
7813{
7814 const float eps = 0.00001f;
7815 const float eps_sq = eps * eps;
7816 const bool use_topology = false;
7817
7818 const float thresh = RNA_float_get(op->ptr, "threshold");
7819 const float fac = RNA_float_get(op->ptr, "factor");
7820 const bool use_center = RNA_boolean_get(op->ptr, "use_center");
7821 const int axis_dir = RNA_enum_get(op->ptr, "direction");
7822
7823 /* Vertices stats (total over all selected objects). */
7824 int totvertfound = 0, totvertmirr = 0, totvertfail = 0, totobjects = 0;
7825
7826 /* Axis. */
7827 int axis = axis_dir % 3;
7828 bool axis_sign = axis != axis_dir;
7829
7830 const Scene *scene = CTX_data_scene(C);
7831 ViewLayer *view_layer = CTX_data_view_layer(C);
7833 scene, view_layer, CTX_wm_view3d(C));
7834
7835 for (Object *obedit : objects) {
7837 BMesh *bm = em->bm;
7838
7839 if (em->bm->totvertsel == 0) {
7840 continue;
7841 }
7842
7844 continue;
7845 }
7846
7847 totobjects++;
7848
7849 /* Only allocate memory after checking whether to skip object. */
7850 int *index = MEM_malloc_arrayN<int>(bm->totvert, __func__);
7851
7852 /* Vertex iter. */
7853 BMIter iter;
7854 BMVert *v;
7855 int i;
7856
7857 EDBM_verts_mirror_cache_begin_ex(em, axis, true, true, false, use_topology, thresh, index);
7858
7860
7862
7864 if ((BM_elem_flag_test(v, BM_ELEM_SELECT) != false) &&
7865 (BM_elem_flag_test(v, BM_ELEM_TAG) == false))
7866 {
7867 int i_mirr = index[i];
7868 if (i_mirr != -1) {
7869
7870 BMVert *v_mirr = BM_vert_at_index(bm, index[i]);
7871
7872 if (v != v_mirr) {
7873 float co[3], co_mirr[3];
7874
7875 if ((v->co[axis] > v_mirr->co[axis]) == axis_sign) {
7876 std::swap(v, v_mirr);
7877 }
7878
7879 copy_v3_v3(co_mirr, v_mirr->co);
7880 co_mirr[axis] *= -1.0f;
7881
7882 if (len_squared_v3v3(v->co, co_mirr) > eps_sq) {
7883 totvertmirr++;
7884 }
7885
7886 interp_v3_v3v3(co, v->co, co_mirr, fac);
7887
7888 copy_v3_v3(v->co, co);
7889
7890 co[axis] *= -1.0f;
7891 copy_v3_v3(v_mirr->co, co);
7892
7895 totvertfound++;
7896 }
7897 else {
7898 if (use_center) {
7899
7900 if (fabsf(v->co[axis]) > eps) {
7901 totvertmirr++;
7902 }
7903
7904 v->co[axis] = 0.0f;
7905 }
7907 totvertfound++;
7908 }
7909 }
7910 else {
7911 totvertfail++;
7912 }
7913 }
7914 }
7916 params.calc_looptris = false;
7917 params.calc_normals = false;
7918 params.is_destructive = false;
7919 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7920
7921 /* No need to end cache, just free the array. */
7922 MEM_freeN(index);
7923 }
7924
7925 if (totvertfail) {
7926 BKE_reportf(op->reports,
7928 "%d already symmetrical, %d pairs mirrored, %d failed",
7929 totvertfound - totvertmirr,
7930 totvertmirr,
7931 totvertfail);
7932 }
7933 else if (totobjects) {
7934 BKE_reportf(op->reports,
7935 RPT_INFO,
7936 "%d already symmetrical, %d pairs mirrored",
7937 totvertfound - totvertmirr,
7938 totvertmirr);
7939 }
7940
7941 return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
7942}
7943
7945{
7946 /* identifiers */
7947 ot->name = "Snap to Symmetry";
7948 ot->description = "Snap vertex pairs to their mirrored locations";
7949 ot->idname = "MESH_OT_symmetry_snap";
7950
7951 /* API callbacks. */
7953 ot->poll = ED_operator_editmesh;
7954
7955 /* flags */
7956 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
7957
7958 ot->prop = RNA_def_enum(ot->srna,
7959 "direction",
7962 "Direction",
7963 "Which sides to copy from and to");
7965 "threshold",
7966 0.05f,
7967 0.0f,
7968 10.0f,
7969 "Threshold",
7970 "Distance within which matching vertices are searched",
7971 1e-4f,
7972 1.0f);
7973 RNA_def_float(ot->srna,
7974 "factor",
7975 0.5f,
7976 0.0f,
7977 1.0f,
7978 "Factor",
7979 "Mix factor of the locations of the vertices",
7980 0.0f,
7981 1.0f);
7983 ot->srna, "use_center", true, "Center", "Snap middle vertices to the axis center");
7984}
7985
7987
7988#if defined(WITH_FREESTYLE)
7989
7990/* -------------------------------------------------------------------- */
7993
7994static wmOperatorStatus edbm_mark_freestyle_edge_exec(bContext *C, wmOperator *op)
7995{
7996 BMEdge *eed;
7997 BMIter iter;
7998 FreestyleEdge *fed;
7999 const bool clear = RNA_boolean_get(op->ptr, "clear");
8000 const Scene *scene = CTX_data_scene(C);
8001 ViewLayer *view_layer = CTX_data_view_layer(C);
8002
8004 scene, view_layer, CTX_wm_view3d(C));
8005 for (Object *obedit : objects) {
8007
8008 if (em == nullptr) {
8009 continue;
8010 }
8011
8012 BMesh *bm = em->bm;
8013
8014 if (bm->totedgesel == 0) {
8015 continue;
8016 }
8017
8020 }
8021
8022 if (clear) {
8023 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
8025 fed = static_cast<FreestyleEdge *>(
8027 fed->flag &= ~FREESTYLE_EDGE_MARK;
8028 }
8029 }
8030 }
8031 else {
8032 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
8034 fed = static_cast<FreestyleEdge *>(
8036 fed->flag |= FREESTYLE_EDGE_MARK;
8037 }
8038 }
8039 }
8040
8041 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
8042 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
8043 }
8044
8045 return OPERATOR_FINISHED;
8046}
8047
8048void MESH_OT_mark_freestyle_edge(wmOperatorType *ot)
8049{
8050 PropertyRNA *prop;
8051
8052 /* identifiers */
8053 ot->name = "Mark Freestyle Edge";
8054 ot->description = "(Un)mark selected edges as Freestyle feature edges";
8055 ot->idname = "MESH_OT_mark_freestyle_edge";
8056
8057 /* API callbacks. */
8058 ot->exec = edbm_mark_freestyle_edge_exec;
8059 ot->poll = ED_operator_editmesh;
8060
8061 /* flags */
8062 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
8063
8064 prop = RNA_def_boolean(ot->srna, "clear", false, "Clear", "");
8066}
8067
8069
8070/* -------------------------------------------------------------------- */
8073
8074static wmOperatorStatus edbm_mark_freestyle_face_exec(bContext *C, wmOperator *op)
8075{
8076 BMFace *efa;
8077 BMIter iter;
8078 FreestyleFace *ffa;
8079 const bool clear = RNA_boolean_get(op->ptr, "clear");
8080 const Scene *scene = CTX_data_scene(C);
8081 ViewLayer *view_layer = CTX_data_view_layer(C);
8082
8084 scene, view_layer, CTX_wm_view3d(C));
8085 for (Object *obedit : objects) {
8087
8088 if (em == nullptr) {
8089 continue;
8090 }
8091
8092 if (em->bm->totfacesel == 0) {
8093 continue;
8094 }
8095
8098 }
8099
8100 if (clear) {
8101 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
8103 ffa = static_cast<FreestyleFace *>(
8105 ffa->flag &= ~FREESTYLE_FACE_MARK;
8106 }
8107 }
8108 }
8109 else {
8110 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
8112 ffa = static_cast<FreestyleFace *>(
8114 ffa->flag |= FREESTYLE_FACE_MARK;
8115 }
8116 }
8117 }
8118
8119 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
8120 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
8121 }
8122
8123 return OPERATOR_FINISHED;
8124}
8125
8126void MESH_OT_mark_freestyle_face(wmOperatorType *ot)
8127{
8128 PropertyRNA *prop;
8129
8130 /* identifiers */
8131 ot->name = "Mark Freestyle Face";
8132 ot->description = "(Un)mark selected faces for exclusion from Freestyle feature edge detection";
8133 ot->idname = "MESH_OT_mark_freestyle_face";
8134
8135 /* API callbacks. */
8136 ot->exec = edbm_mark_freestyle_face_exec;
8137 ot->poll = ED_operator_editmesh;
8138
8139 /* flags */
8140 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
8141
8142 prop = RNA_def_boolean(ot->srna, "clear", false, "Clear", "");
8144}
8145
8147
8148#endif /* WITH_FREESTYLE */
8149
8150/* -------------------------------------------------------------------- */
8153
8154/* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
8155/* NOTE: We could add more here, like e.g. a switch between local or global coordinates of target,
8156 * use number-input to type in explicit vector values. */
8157enum {
8158 /* Generic commands. */
8161
8162 /* Point To operator. */
8167
8173};
8174
8176{
8177 static const EnumPropertyItem modal_items[] = {
8178 {EDBM_CLNOR_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
8179 {EDBM_CLNOR_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
8180
8181 /* Point To operator. */
8182 {EDBM_CLNOR_MODAL_POINTTO_RESET, "RESET", 0, "Reset", "Reset normals to initial ones"},
8184 "INVERT",
8185 0,
8186 "Invert",
8187 "Toggle inversion of affected normals"},
8189 "SPHERIZE",
8190 0,
8191 "Spherize",
8192 "Interpolate between new and original normals"},
8193 {EDBM_CLNOR_MODAL_POINTTO_ALIGN, "ALIGN", 0, "Align", "Make all affected normals parallel"},
8194
8196 "USE_MOUSE",
8197 0,
8198 "Use Mouse",
8199 "Follow mouse cursor position"},
8201 "USE_PIVOT",
8202 0,
8203 "Use Pivot",
8204 "Use current rotation/scaling pivot point coordinates"},
8206 "USE_OBJECT",
8207 0,
8208 "Use Object",
8209 "Use current edited object's location"},
8211 "SET_USE_3DCURSOR",
8212 0,
8213 "Set and Use 3D Cursor",
8214 "Set new 3D cursor position and use it"},
8216 "SET_USE_SELECTED",
8217 0,
8218 "Select and Use Mesh Item",
8219 "Select new active mesh element and use its location"},
8220 {0, nullptr, 0, nullptr, nullptr},
8221 };
8222 static const char *keymap_name = "Custom Normals Modal Map";
8223
8224 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, keymap_name);
8225
8226 /* We only need to add map once */
8227 if (keymap && keymap->modal_items) {
8228 return nullptr;
8229 }
8230
8231 keymap = WM_modalkeymap_ensure(keyconf, keymap_name, modal_items);
8232
8233 WM_modalkeymap_assign(keymap, "MESH_OT_point_normals");
8234
8235 return keymap;
8236}
8237
8238#define CLNORS_VALID_VEC_LEN (1e-4f)
8239
8241
8242/* -------------------------------------------------------------------- */
8245
8246enum {
8249};
8250
8253 "COORDINATES",
8254 0,
8255 "Coordinates",
8256 "Use static coordinates (defined by various means)"},
8257 {EDBM_CLNOR_POINTTO_MODE_MOUSE, "MOUSE", 0, "Mouse", "Follow mouse cursor"},
8258 {0, nullptr, 0, nullptr, nullptr},
8259};
8260
8261/* Initialize loop normal data */
8263{
8264 Object *obedit = CTX_data_edit_object(C);
8266 BMesh *bm = em->bm;
8267
8270
8271 op->customdata = lnors_ed_arr;
8272
8273 return (lnors_ed_arr->totloop != 0);
8274}
8275
8277{
8278 if (op->customdata != nullptr) {
8279 return true;
8280 }
8281 return point_normals_init(C, op);
8282}
8283
8285{
8286 if (op->customdata != nullptr) {
8287 BMLoopNorEditDataArray *lnors_ed_arr = static_cast<BMLoopNorEditDataArray *>(op->customdata);
8289 op->customdata = nullptr;
8290 }
8291}
8292
8294{
8296 ED_workspace_status_text(C, nullptr);
8297}
8298
8300{
8301 WorkspaceStatus status(C);
8302
8303 status.opmodal(IFACE_("Confirm"), op->type, EDBM_CLNOR_MODAL_CONFIRM);
8304 status.opmodal(IFACE_("Cancel"), op->type, EDBM_CLNOR_MODAL_CANCEL);
8305 status.opmodal(IFACE_("Reset"), op->type, EDBM_CLNOR_MODAL_POINTTO_RESET);
8306
8307 status.opmodal(IFACE_("Invert"),
8308 op->type,
8310 RNA_boolean_get(op->ptr, "invert"));
8311 status.opmodal(IFACE_("Spherize"),
8312 op->type,
8314 RNA_boolean_get(op->ptr, "spherize"));
8315 status.opmodal(IFACE_("Align"),
8316 op->type,
8318 RNA_boolean_get(op->ptr, "align"));
8319
8320 status.opmodal(IFACE_("Use mouse"),
8321 op->type,
8324
8325 status.opmodal(IFACE_("Use Pivot"), op->type, EDBM_CLNOR_MODAL_POINTTO_USE_PIVOT);
8326 status.opmodal(IFACE_("Use Object"), op->type, EDBM_CLNOR_MODAL_POINTTO_USE_OBJECT);
8327 status.opmodal(
8328 IFACE_("Set and use 3D cursor"), op->type, EDBM_CLNOR_MODAL_POINTTO_SET_USE_3DCURSOR);
8329 status.opmodal(
8330 IFACE_("Select and use mesh item"), op->type, EDBM_CLNOR_MODAL_POINTTO_SET_USE_SELECTED);
8331}
8332
8333/* TODO: move that to generic function in BMesh? */
8334static void bmesh_selected_verts_center_calc(BMesh *bm, float *r_center)
8335{
8336 BMVert *v;
8337 BMIter viter;
8338 int i = 0;
8339
8340 zero_v3(r_center);
8341 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
8343 add_v3_v3(r_center, v->co);
8344 i++;
8345 }
8346 }
8347 mul_v3_fl(r_center, 1.0f / float(i));
8348}
8349
8350static void point_normals_apply(bContext *C, wmOperator *op, float target[3], const bool do_reset)
8351{
8352 Object *obedit = CTX_data_edit_object(C);
8353 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
8354 BMLoopNorEditDataArray *lnors_ed_arr = static_cast<BMLoopNorEditDataArray *>(op->customdata);
8355
8356 const bool do_invert = RNA_boolean_get(op->ptr, "invert");
8357 const bool do_spherize = RNA_boolean_get(op->ptr, "spherize");
8358 const bool do_align = RNA_boolean_get(op->ptr, "align");
8359 float center[3];
8360
8361 if (do_align && !do_reset) {
8363 }
8364
8365 sub_v3_v3(target, obedit->loc); /* Move target to local coordinates. */
8366
8367 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
8368 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
8369 if (do_reset) {
8370 copy_v3_v3(lnor_ed->nloc, lnor_ed->niloc);
8371 }
8372 else if (do_spherize) {
8373 /* Note that this is *not* real spherical interpolation.
8374 * Probably good enough in this case though? */
8375 const float strength = RNA_float_get(op->ptr, "spherize_strength");
8376 float spherized_normal[3];
8377
8378 sub_v3_v3v3(spherized_normal, target, lnor_ed->loc);
8379
8380 /* otherwise, multiplication by strength is meaningless... */
8381 normalize_v3(spherized_normal);
8382
8383 mul_v3_fl(spherized_normal, strength);
8384 mul_v3_v3fl(lnor_ed->nloc, lnor_ed->niloc, 1.0f - strength);
8385 add_v3_v3(lnor_ed->nloc, spherized_normal);
8386 }
8387 else if (do_align) {
8388 sub_v3_v3v3(lnor_ed->nloc, target, center);
8389 }
8390 else {
8391 sub_v3_v3v3(lnor_ed->nloc, target, lnor_ed->loc);
8392 }
8393
8394 if (do_invert && !do_reset) {
8395 negate_v3(lnor_ed->nloc);
8396 }
8397 if (normalize_v3(lnor_ed->nloc) >= CLNORS_VALID_VEC_LEN) {
8399 bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->nloc, lnor_ed->clnors_data);
8400 }
8401 }
8402}
8403
8405{
8406 /* As this operator passes events through, we can't be sure the user didn't exit edit-mode.
8407 * or performed some other operation. */
8408 if (!WM_operator_poll(C, op->type)) {
8410 return OPERATOR_CANCELLED;
8411 }
8412
8413 View3D *v3d = CTX_wm_view3d(C);
8414 Scene *scene = CTX_data_scene(C);
8415 Object *obedit = CTX_data_edit_object(C);
8417 BMesh *bm = em->bm;
8418
8419 float target[3];
8420
8422 int mode = RNA_enum_get(op->ptr, "mode");
8423 int new_mode = mode;
8424 bool force_mousemove = false;
8425 bool do_reset = false;
8426
8427 PropertyRNA *prop_target = RNA_struct_find_property(op->ptr, "target_location");
8428
8429 if (event->type == EVT_MODAL_MAP) {
8430 switch (event->val) {
8432 RNA_property_float_get_array(op->ptr, prop_target, target);
8434 break;
8435
8437 do_reset = true;
8439 break;
8440
8442 do_reset = true;
8444 break;
8445
8447 PropertyRNA *prop_invert = RNA_struct_find_property(op->ptr, "invert");
8449 op->ptr, prop_invert, !RNA_property_boolean_get(op->ptr, prop_invert));
8450 RNA_property_float_get_array(op->ptr, prop_target, target);
8452 break;
8453 }
8454
8456 PropertyRNA *prop_spherize = RNA_struct_find_property(op->ptr, "spherize");
8458 op->ptr, prop_spherize, !RNA_property_boolean_get(op->ptr, prop_spherize));
8459 RNA_property_float_get_array(op->ptr, prop_target, target);
8461 break;
8462 }
8463
8465 PropertyRNA *prop_align = RNA_struct_find_property(op->ptr, "align");
8467 op->ptr, prop_align, !RNA_property_boolean_get(op->ptr, prop_align));
8468 RNA_property_float_get_array(op->ptr, prop_target, target);
8470 break;
8471 }
8472
8475 /* We want to immediately update to mouse cursor position... */
8476 force_mousemove = true;
8478 break;
8479
8482 copy_v3_v3(target, obedit->loc);
8484 break;
8485
8489 copy_v3_v3(target, scene->cursor.location);
8491 break;
8492
8497 params.sel_op = SEL_OP_SET;
8498 if (EDBM_select_pick(C, event->mval, params)) {
8499 /* Point to newly selected active. */
8501
8502 add_v3_v3(target, obedit->loc);
8504 }
8505 break;
8506 }
8509 switch (scene->toolsettings->transform_pivot_point) {
8510 case V3D_AROUND_CENTER_BOUNDS: /* calculateCenterBound */
8511 {
8512 BMVert *v;
8513 BMIter viter;
8514 float min[3], max[3];
8515 int i = 0;
8516
8517 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
8519 if (i) {
8520 minmax_v3v3_v3(min, max, v->co);
8521 }
8522 else {
8523 copy_v3_v3(min, v->co);
8524 copy_v3_v3(max, v->co);
8525 }
8526 i++;
8527 }
8528 }
8529 mid_v3_v3v3(target, min, max);
8530 add_v3_v3(target, obedit->loc);
8531 break;
8532 }
8533
8536 add_v3_v3(target, obedit->loc);
8537 break;
8538 }
8539
8540 case V3D_AROUND_CURSOR:
8541 copy_v3_v3(target, scene->cursor.location);
8542 break;
8543
8544 case V3D_AROUND_ACTIVE:
8545 if (!blender::ed::object::calc_active_center_for_editmode(obedit, false, target)) {
8546 zero_v3(target);
8547 }
8548 add_v3_v3(target, obedit->loc);
8549 break;
8550
8551 default:
8552 BKE_report(op->reports, RPT_WARNING, "Does not support Individual Origins as pivot");
8553 copy_v3_v3(target, obedit->loc);
8554 }
8556 break;
8557 }
8558 default:
8559 break;
8560 }
8561 }
8562
8563 if (new_mode != mode) {
8564 mode = new_mode;
8565 RNA_enum_set(op->ptr, "mode", mode);
8566 }
8567
8568 /* Only handle mouse-move event in case we are in mouse mode. */
8569 if (event->type == MOUSEMOVE || force_mousemove) {
8570 if (mode == EDBM_CLNOR_POINTTO_MODE_MOUSE) {
8571 ARegion *region = CTX_wm_region(C);
8572 float center[3];
8573
8575
8576 ED_view3d_win_to_3d_int(v3d, region, center, event->mval, target);
8577
8579 }
8580 }
8581
8582 if (ret != OPERATOR_PASS_THROUGH) {
8584 RNA_property_float_set_array(op->ptr, prop_target, target);
8585 }
8586
8587 if (point_normals_ensure(C, op)) {
8588 point_normals_apply(C, op, target, do_reset);
8590 params.calc_looptris = true;
8591 params.calc_normals = false;
8592 params.is_destructive = false;
8593 /* Recheck booleans. */
8594 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
8595
8597 }
8598 else {
8600 }
8601 }
8602
8605 }
8606
8607 /* If we allow other tools to run, we can't be sure if they will re-allocate
8608 * the data this operator uses, see: #68159.
8609 * Free the data here, then use #point_normals_ensure to add it back on demand. */
8610 if (ret == OPERATOR_PASS_THROUGH) {
8611 /* Don't free on mouse-move, causes creation/freeing of the loop data in an inefficient way. */
8612 if (!ISMOUSE_MOTION(event->type)) {
8614 }
8615 }
8616 return ret;
8617}
8618
8620 wmOperator *op,
8621 const wmEvent * /*event*/)
8622{
8623 if (!point_normals_init(C, op)) {
8625 return OPERATOR_CANCELLED;
8626 }
8627
8629
8631
8634}
8635
8636/* TODO: make this work on multiple objects at once */
8638{
8639 Object *obedit = CTX_data_edit_object(C);
8640
8641 if (!point_normals_init(C, op)) {
8643 return OPERATOR_CANCELLED;
8644 }
8645
8646 /* Note that 'mode' is ignored in exec case,
8647 * we directly use vector stored in target_location, whatever that is. */
8648
8649 float target[3];
8650 RNA_float_get_array(op->ptr, "target_location", target);
8651
8652 point_normals_apply(C, op, target, false);
8653
8655 params.calc_looptris = true;
8656 params.calc_normals = false;
8657 params.is_destructive = false;
8658 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
8660
8661 return OPERATOR_FINISHED;
8662}
8663
8664static bool point_normals_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop, void * /*user_data*/)
8665{
8666 const char *prop_id = RNA_property_identifier(prop);
8667
8668 /* Only show strength option if spherize is enabled. */
8669 if (STREQ(prop_id, "spherize_strength")) {
8670 return RNA_boolean_get(ptr, "spherize");
8671 }
8672
8673 /* Else, show it! */
8674 return true;
8675}
8676
8678{
8679 uiLayout *layout = op->layout;
8681
8683
8684 uiLayoutSetPropSep(layout, true);
8685
8686 /* Main auto-draw call */
8687 uiDefAutoButsRNA(layout,
8688 &ptr,
8690 nullptr,
8691 nullptr,
8693 false);
8694}
8695
8697{
8698 /* identifiers */
8699 ot->name = "Point Normals to Target";
8700 ot->description = "Point selected custom normals to specified Target";
8701 ot->idname = "MESH_OT_point_normals";
8702
8703 /* API callbacks. */
8705 ot->invoke = edbm_point_normals_invoke;
8707 ot->poll = ED_operator_editmesh;
8709 ot->cancel = point_normals_cancel;
8710
8711 /* flags */
8713
8714 ot->prop = RNA_def_enum(ot->srna,
8715 "mode",
8718 "Mode",
8719 "How to define coordinates to point custom normals to");
8721
8722 RNA_def_boolean(ot->srna, "invert", false, "Invert", "Invert affected normals");
8723
8724 RNA_def_boolean(ot->srna, "align", false, "Align", "Make all affected normals parallel");
8725
8727 "target_location",
8728 3,
8729 nullptr,
8730 -FLT_MAX,
8731 FLT_MAX,
8732 "Target",
8733 "Target location to which normals will point",
8734 -1000.0f,
8735 1000.0f);
8736
8738 ot->srna, "spherize", false, "Spherize", "Interpolate between original and new normals");
8739
8740 RNA_def_float(ot->srna,
8741 "spherize_strength",
8742 0.1,
8743 0.0f,
8744 1.0f,
8745 "Spherize Strength",
8746 "Ratio of spherized normal to original normal",
8747 0.0f,
8748 1.0f);
8749}
8750
8752
8753/* -------------------------------------------------------------------- */
8756
8757static void normals_merge(BMesh *bm, BMLoopNorEditDataArray *lnors_ed_arr)
8758{
8759 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
8760
8761 BLI_SMALLSTACK_DECLARE(clnors, short *);
8762
8763 BLI_assert(bm->lnor_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
8764
8766
8767 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
8769
8770 if (BM_elem_flag_test(lnor_ed->loop, BM_ELEM_TAG)) {
8771 continue;
8772 }
8773
8774 MLoopNorSpace *lnor_space = bm->lnor_spacearr->lspacearr[lnor_ed->loop_index];
8775
8776 if ((lnor_space->flags & MLNOR_SPACE_IS_SINGLE) == 0) {
8777 LinkNode *loops = lnor_space->loops;
8778 float avg_normal[3] = {0.0f, 0.0f, 0.0f};
8779 short *clnors_data;
8780
8781 for (; loops; loops = loops->next) {
8782 BMLoop *l = static_cast<BMLoop *>(loops->link);
8783 const int loop_index = BM_elem_index_get(l);
8784
8785 BMLoopNorEditData *lnor_ed_tmp = lnors_ed_arr->lidx_to_lnor_editdata[loop_index];
8786 BLI_assert(lnor_ed_tmp->loop_index == loop_index && lnor_ed_tmp->loop == l);
8787 add_v3_v3(avg_normal, lnor_ed_tmp->nloc);
8788 BLI_SMALLSTACK_PUSH(clnors, lnor_ed_tmp->clnors_data);
8790 }
8791 if (normalize_v3(avg_normal) < CLNORS_VALID_VEC_LEN) {
8792 /* If avg normal is nearly 0, set clnor to default value. */
8793 zero_v3(avg_normal);
8794 }
8795 while ((clnors_data = static_cast<short *>(BLI_SMALLSTACK_POP(clnors)))) {
8796 BKE_lnor_space_custom_normal_to_data(lnor_space, avg_normal, clnors_data);
8797 }
8798 }
8799 }
8800}
8801
8803{
8804 BMFace *f;
8805 BMLoop *l, *l_curr, *l_first;
8806 BMIter fiter;
8807
8808 BLI_assert(bm->lnor_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
8809
8811
8812 BLI_SMALLSTACK_DECLARE(loop_stack, BMLoop *);
8813
8814 const int cd_clnors_offset = CustomData_get_offset_named(
8815 &bm->ldata, CD_PROP_INT16_2D, "custom_normal");
8816 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
8818
8819 l_curr = l_first = BM_FACE_FIRST_LOOP(f);
8820 do {
8821 if (BM_elem_flag_test(l_curr->v, BM_ELEM_SELECT) &&
8822 (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) ||
8824 {
8825 if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
8826 !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG))
8827 {
8828 const int loop_index = BM_elem_index_get(l_curr);
8829 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l_curr, cd_clnors_offset));
8831 bm->lnor_spacearr->lspacearr[loop_index], f->no, clnors);
8832 }
8833 else {
8834 BMVert *v_pivot = l_curr->v;
8835 UNUSED_VARS_NDEBUG(v_pivot);
8836 BMEdge *e_next;
8837 const BMEdge *e_org = l_curr->e;
8838 BMLoop *lfan_pivot, *lfan_pivot_next;
8839
8840 lfan_pivot = l_curr;
8841 e_next = lfan_pivot->e;
8842 float avg_normal[3] = {0.0f};
8843
8844 while (true) {
8845 lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next);
8846 if (lfan_pivot_next) {
8847 BLI_assert(lfan_pivot_next->v == v_pivot);
8848 }
8849 else {
8850 e_next = (lfan_pivot->e == e_next) ? lfan_pivot->prev->e : lfan_pivot->e;
8851 }
8852
8853 BLI_SMALLSTACK_PUSH(loop_stack, lfan_pivot);
8854 add_v3_v3(avg_normal, lfan_pivot->f->no);
8855
8856 if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) {
8857 break;
8858 }
8859 lfan_pivot = lfan_pivot_next;
8860 }
8861 if (normalize_v3(avg_normal) < CLNORS_VALID_VEC_LEN) {
8862 /* If avg normal is nearly 0, set clnor to default value. */
8863 zero_v3(avg_normal);
8864 }
8865 while ((l = static_cast<BMLoop *>(BLI_SMALLSTACK_POP(loop_stack)))) {
8866 const int l_index = BM_elem_index_get(l);
8867 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
8869 bm->lnor_spacearr->lspacearr[l_index], avg_normal, clnors);
8870 }
8871 }
8872 }
8873 } while ((l_curr = l_curr->next) != l_first);
8874 }
8875}
8876
8877static wmOperatorStatus normals_split_merge(bContext *C, const bool do_merge)
8878{
8879 const Scene *scene = CTX_data_scene(C);
8880 ViewLayer *view_layer = CTX_data_view_layer(C);
8882 scene, view_layer, CTX_wm_view3d(C));
8883
8884 for (Object *obedit : objects) {
8886 BMesh *bm = em->bm;
8887 BMEdge *e;
8888 BMIter eiter;
8889
8891
8892 /* Note that we need temp lnor editing data for all loops of all affected vertices, since by
8893 * setting some faces/edges as smooth we are going to change clnors spaces... See also #65809.
8894 */
8895 BMLoopNorEditDataArray *lnors_ed_arr = do_merge ?
8897 nullptr;
8898
8899 mesh_set_smooth_faces(em, do_merge);
8900
8901 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
8903 BM_elem_flag_set(e, BM_ELEM_SMOOTH, do_merge);
8904 }
8905 }
8906
8907 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
8909
8910 if (do_merge) {
8911 normals_merge(bm, lnors_ed_arr);
8912 }
8913 else {
8915 }
8916
8917 if (lnors_ed_arr) {
8919 }
8920
8922 params.calc_looptris = true;
8923 params.calc_normals = false;
8924 params.is_destructive = false;
8925 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
8926 }
8927
8928 return OPERATOR_FINISHED;
8929}
8930
8932{
8933 return normals_split_merge(C, true);
8934}
8935
8937{
8938 /* identifiers */
8939 ot->name = "Merge Normals";
8940 ot->description = "Merge custom normals of selected vertices";
8941 ot->idname = "MESH_OT_merge_normals";
8942
8943 /* API callbacks. */
8945 ot->poll = ED_operator_editmesh;
8946
8947 /* flags */
8948 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
8949}
8950
8952{
8953 return normals_split_merge(C, false);
8954}
8955
8957{
8958 /* identifiers */
8959 ot->name = "Split Normals";
8960 ot->description = "Split custom normals of selected vertices";
8961 ot->idname = "MESH_OT_split_normals";
8962
8963 /* API callbacks. */
8965 ot->poll = ED_operator_editmesh;
8966
8967 /* flags */
8968 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
8969}
8970
8972
8973/* -------------------------------------------------------------------- */
8976
8977enum {
8981};
8982
8985 "CUSTOM_NORMAL",
8986 0,
8987 "Custom Normal",
8988 "Take average of vertex normals"},
8990 "FACE_AREA",
8991 0,
8992 "Face Area",
8993 "Set all vertex normals by face area"},
8995 "CORNER_ANGLE",
8996 0,
8997 "Corner Angle",
8998 "Set all vertex normals by corner angle"},
8999 {0, nullptr, 0, nullptr, nullptr},
9000};
9001
9003{
9004 const Scene *scene = CTX_data_scene(C);
9005 ViewLayer *view_layer = CTX_data_view_layer(C);
9007 scene, view_layer, CTX_wm_view3d(C));
9008 const int average_type = RNA_enum_get(op->ptr, "average_type");
9009 const float absweight = float(RNA_int_get(op->ptr, "weight"));
9010 const float threshold = RNA_float_get(op->ptr, "threshold");
9011
9012 HeapSimple *loop_weight = BLI_heapsimple_new();
9013 BLI_SMALLSTACK_DECLARE(loop_stack, BMLoop *);
9014
9015 for (uint ob_index = 0; ob_index < objects.size(); ob_index++) {
9017 BLI_assert(BLI_heapsimple_is_empty(loop_weight));
9018
9019 Object *obedit = objects[ob_index];
9021 BMesh *bm = em->bm;
9022 BMFace *f;
9023 BMLoop *l, *l_curr, *l_first;
9024 BMIter fiter;
9025
9026 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
9028
9029 const int cd_clnors_offset = CustomData_get_offset_named(
9030 &bm->ldata, CD_PROP_INT16_2D, "custom_normal");
9031
9032 float weight = absweight / 50.0f;
9033 if (absweight == 100.0f) {
9034 weight = float(SHRT_MAX);
9035 }
9036 else if (absweight == 1.0f) {
9037 weight = 1 / float(SHRT_MAX);
9038 }
9039 else if ((weight - 1) * 25 > 1) {
9040 weight = (weight - 1) * 25;
9041 }
9042
9044
9045 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
9046 l_curr = l_first = BM_FACE_FIRST_LOOP(f);
9047 do {
9048 if (BM_elem_flag_test(l_curr->v, BM_ELEM_SELECT) &&
9049 (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) ||
9051 {
9052 if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
9053 !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG))
9054 {
9055 const int loop_index = BM_elem_index_get(l_curr);
9056 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l_curr, cd_clnors_offset));
9058 bm->lnor_spacearr->lspacearr[loop_index], f->no, clnors);
9059 }
9060 else {
9061 BMVert *v_pivot = l_curr->v;
9062 UNUSED_VARS_NDEBUG(v_pivot);
9063 BMEdge *e_next;
9064 const BMEdge *e_org = l_curr->e;
9065 BMLoop *lfan_pivot, *lfan_pivot_next;
9066
9067 lfan_pivot = l_curr;
9068 e_next = lfan_pivot->e;
9069
9070 while (true) {
9071 lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next);
9072 if (lfan_pivot_next) {
9073 BLI_assert(lfan_pivot_next->v == v_pivot);
9074 }
9075 else {
9076 e_next = (lfan_pivot->e == e_next) ? lfan_pivot->prev->e : lfan_pivot->e;
9077 }
9078
9079 float val = 1.0f;
9080 if (average_type == EDBM_CLNOR_AVERAGE_FACE_AREA) {
9081 val = 1.0f / BM_face_calc_area(lfan_pivot->f);
9082 }
9083 else if (average_type == EDBM_CLNOR_AVERAGE_ANGLE) {
9084 val = 1.0f / BM_loop_calc_face_angle(lfan_pivot);
9085 }
9086
9087 BLI_heapsimple_insert(loop_weight, val, lfan_pivot);
9088
9089 if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) {
9090 break;
9091 }
9092 lfan_pivot = lfan_pivot_next;
9093 }
9094
9095 float wnor[3], avg_normal[3] = {0.0f}, count = 0;
9096 float val = BLI_heapsimple_top_value(loop_weight);
9097
9098 while (!BLI_heapsimple_is_empty(loop_weight)) {
9099 const float cur_val = BLI_heapsimple_top_value(loop_weight);
9100 if (!compare_ff(val, cur_val, threshold)) {
9101 count++;
9102 val = cur_val;
9103 }
9104 l = static_cast<BMLoop *>(BLI_heapsimple_pop_min(loop_weight));
9105 BLI_SMALLSTACK_PUSH(loop_stack, l);
9106
9107 const float n_weight = pow(weight, count);
9108
9109 if (average_type == EDBM_CLNOR_AVERAGE_LOOP) {
9110 const int l_index = BM_elem_index_get(l);
9111 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
9113 bm->lnor_spacearr->lspacearr[l_index], clnors, wnor);
9114 }
9115 else {
9116 copy_v3_v3(wnor, l->f->no);
9117 }
9118 mul_v3_fl(wnor, (1.0f / cur_val) * (1.0f / n_weight));
9119 add_v3_v3(avg_normal, wnor);
9120 }
9121
9122 if (normalize_v3(avg_normal) < CLNORS_VALID_VEC_LEN) {
9123 /* If avg normal is nearly 0, set clnor to default value. */
9124 zero_v3(avg_normal);
9125 }
9126 while ((l = static_cast<BMLoop *>(BLI_SMALLSTACK_POP(loop_stack)))) {
9127 const int l_index = BM_elem_index_get(l);
9128 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
9130 bm->lnor_spacearr->lspacearr[l_index], avg_normal, clnors);
9131 }
9132 }
9133 }
9134 } while ((l_curr = l_curr->next) != l_first);
9135 }
9136
9138 params.calc_looptris = true;
9139 params.calc_normals = false;
9140 params.is_destructive = false;
9141 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
9142 }
9143
9144 BLI_heapsimple_free(loop_weight, nullptr);
9145
9146 return OPERATOR_FINISHED;
9147}
9148
9150 PropertyRNA *prop,
9151 void * /*user_data*/)
9152{
9153 const char *prop_id = RNA_property_identifier(prop);
9154 const int average_type = RNA_enum_get(ptr, "average_type");
9155
9156 /* Only show weight/threshold options when not in loop average type. */
9157 const bool is_clor_average_loop = average_type == EDBM_CLNOR_AVERAGE_LOOP;
9158 if (STREQ(prop_id, "weight")) {
9159 return !is_clor_average_loop;
9160 }
9161 if (STREQ(prop_id, "threshold")) {
9162 return !is_clor_average_loop;
9163 }
9164
9165 /* Else, show it! */
9166 return true;
9167}
9168
9170{
9171 uiLayout *layout = op->layout;
9173
9175
9176 uiLayoutSetPropSep(layout, true);
9177
9178 /* Main auto-draw call */
9179 uiDefAutoButsRNA(layout,
9180 &ptr,
9182 nullptr,
9183 nullptr,
9185 false);
9186}
9187
9189{
9190 /* identifiers */
9191 ot->name = "Average Normals";
9192 ot->description = "Average custom normals of selected vertices";
9193 ot->idname = "MESH_OT_average_normals";
9194
9195 /* API callbacks. */
9197 ot->poll = ED_operator_editmesh;
9199
9200 /* flags */
9201 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9202
9203 ot->prop = RNA_def_enum(ot->srna,
9204 "average_type",
9207 "Type",
9208 "Averaging method");
9209
9210 RNA_def_int(ot->srna, "weight", 50, 1, 100, "Weight", "Weight applied per face", 1, 100);
9211
9212 RNA_def_float(ot->srna,
9213 "threshold",
9214 0.01f,
9215 0,
9216 10,
9217 "Threshold",
9218 "Threshold value for different weights to be considered equal",
9219 0,
9220 5);
9221}
9222
9224
9225/* -------------------------------------------------------------------- */
9228
9229enum {
9235};
9236
9238 {EDBM_CLNOR_TOOLS_COPY, "COPY", 0, "Copy Normal", "Copy normal to the internal clipboard"},
9240 "PASTE",
9241 0,
9242 "Paste Normal",
9243 "Paste normal from the internal clipboard"},
9244 {EDBM_CLNOR_TOOLS_ADD, "ADD", 0, "Add Normal", "Add normal vector with selection"},
9246 "MULTIPLY",
9247 0,
9248 "Multiply Normal",
9249 "Multiply normal vector with selection"},
9251 "RESET",
9252 0,
9253 "Reset Normal",
9254 "Reset the internal clipboard and/or normal of selected element"},
9255 {0, nullptr, 0, nullptr, nullptr},
9256};
9257
9259{
9260 Scene *scene = CTX_data_scene(C);
9261 ViewLayer *view_layer = CTX_data_view_layer(C);
9263 scene, view_layer, CTX_wm_view3d(C));
9264 const int mode = RNA_enum_get(op->ptr, "mode");
9265 const bool absolute = RNA_boolean_get(op->ptr, "absolute");
9266 float *normal_vector = scene->toolsettings->normal_vector;
9267 bool done_copy = false;
9268
9269 for (Object *obedit : objects) {
9271 BMesh *bm = em->bm;
9272
9273 if (bm->totloop == 0) {
9274 continue;
9275 }
9276
9279 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
9280
9281 switch (mode) {
9283 if (bm->totfacesel == 0 && bm->totvertsel == 0) {
9285 continue;
9286 }
9287
9288 if (done_copy ||
9289 (bm->totfacesel != 1 && lnors_ed_arr->totloop != 1 && bm->totvertsel != 1))
9290 {
9291 BKE_report(op->reports,
9292 RPT_ERROR,
9293 "Can only copy one custom normal, vertex normal or face normal");
9295 continue;
9296 }
9297 if (lnors_ed_arr->totloop == 1) {
9298 copy_v3_v3(scene->toolsettings->normal_vector, lnors_ed_arr->lnor_editdata->nloc);
9299 }
9300 else if (bm->totfacesel == 1) {
9301 BMFace *f;
9302 BMIter fiter;
9303 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
9306 }
9307 }
9308 }
9309 else {
9310 /* 'Vertex' normal, i.e. common set of loop normals on the same vertex,
9311 * only if they are all the same. */
9312 bool are_same_lnors = true;
9313 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9314 if (!compare_v3v3(lnors_ed_arr->lnor_editdata->nloc, lnor_ed->nloc, 1e-4f)) {
9315 are_same_lnors = false;
9316 }
9317 }
9318 if (are_same_lnors) {
9319 copy_v3_v3(scene->toolsettings->normal_vector, lnors_ed_arr->lnor_editdata->nloc);
9320 }
9321 }
9322 done_copy = true;
9323 break;
9324
9326 if (!absolute) {
9327 if (normalize_v3(normal_vector) < CLNORS_VALID_VEC_LEN) {
9328 /* If normal is nearly 0, do nothing. */
9329 break;
9330 }
9331 }
9332 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9333 if (absolute) {
9334 float abs_normal[3];
9335 copy_v3_v3(abs_normal, lnor_ed->loc);
9336 negate_v3(abs_normal);
9337 add_v3_v3(abs_normal, normal_vector);
9338
9339 if (normalize_v3(abs_normal) < CLNORS_VALID_VEC_LEN) {
9340 /* If abs normal is nearly 0, set clnor to initial value. */
9341 copy_v3_v3(abs_normal, lnor_ed->niloc);
9342 }
9343 BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index],
9344 abs_normal,
9345 lnor_ed->clnors_data);
9346 }
9347 else {
9348 BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index],
9349 normal_vector,
9350 lnor_ed->clnors_data);
9351 }
9352 }
9353 break;
9354
9356 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9357 mul_v3_v3(lnor_ed->nloc, normal_vector);
9358
9359 if (normalize_v3(lnor_ed->nloc) < CLNORS_VALID_VEC_LEN) {
9360 /* If abs normal is nearly 0, set clnor to initial value. */
9361 copy_v3_v3(lnor_ed->nloc, lnor_ed->niloc);
9362 }
9363 BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index],
9364 lnor_ed->nloc,
9365 lnor_ed->clnors_data);
9366 }
9367 break;
9368
9370 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9371 add_v3_v3(lnor_ed->nloc, normal_vector);
9372
9373 if (normalize_v3(lnor_ed->nloc) < CLNORS_VALID_VEC_LEN) {
9374 /* If abs normal is nearly 0, set clnor to initial value. */
9375 copy_v3_v3(lnor_ed->nloc, lnor_ed->niloc);
9376 }
9377 BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index],
9378 lnor_ed->nloc,
9379 lnor_ed->clnors_data);
9380 }
9381 break;
9382
9384 zero_v3(normal_vector);
9385 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9386 BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index],
9387 normal_vector,
9388 lnor_ed->clnors_data);
9389 }
9390 break;
9391
9392 default:
9393 BLI_assert(0);
9394 break;
9395 }
9396
9398
9400 params.calc_looptris = true;
9401 params.calc_normals = false;
9402 params.is_destructive = false;
9403 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
9404 }
9405
9406 return OPERATOR_FINISHED;
9407}
9408
9409static bool normals_tools_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop, void * /*user_data*/)
9410{
9411 const char *prop_id = RNA_property_identifier(prop);
9412 const int mode = RNA_enum_get(ptr, "mode");
9413
9414 /* Only show absolute option in paste mode. */
9415 if (STREQ(prop_id, "absolute")) {
9416 return (mode == EDBM_CLNOR_TOOLS_PASTE);
9417 }
9418
9419 /* Else, show it! */
9420 return true;
9421}
9422
9424{
9425 uiLayout *layout = op->layout;
9427
9429
9430 /* Main auto-draw call */
9431 uiDefAutoButsRNA(layout,
9432 &ptr,
9434 nullptr,
9435 nullptr,
9437 false);
9438}
9439
9441{
9442 /* identifiers */
9443 ot->name = "Normals Vector Tools";
9444 ot->description = "Custom normals tools using Normal Vector of UI";
9445 ot->idname = "MESH_OT_normals_tools";
9446
9447 /* API callbacks. */
9449 ot->poll = ED_operator_editmesh;
9451
9452 /* flags */
9453 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9454
9455 ot->prop = RNA_def_enum(ot->srna,
9456 "mode",
9459 "Mode",
9460 "Mode of tools taking input from interface");
9462
9463 RNA_def_boolean(ot->srna,
9464 "absolute",
9465 false,
9466 "Absolute Coordinates",
9467 "Copy Absolute coordinates of Normal vector");
9468}
9469
9471
9472/* -------------------------------------------------------------------- */
9475
9477{
9478 const Scene *scene = CTX_data_scene(C);
9479 ViewLayer *view_layer = CTX_data_view_layer(C);
9481 scene, view_layer, CTX_wm_view3d(C));
9482
9483 for (Object *obedit : objects) {
9485 BMesh *bm = em->bm;
9486 if (bm->totfacesel == 0) {
9487 continue;
9488 }
9489
9490 BMFace *f;
9491 BMVert *v;
9492 BMEdge *e;
9493 BMLoop *l;
9494 BMIter fiter, viter, eiter, liter;
9495
9496 const bool keep_sharp = RNA_boolean_get(op->ptr, "keep_sharp");
9497
9499
9500 float(*vert_normals)[3] = static_cast<float(*)[3]>(
9501 MEM_mallocN(sizeof(*vert_normals) * bm->totvert, __func__));
9502 {
9503 int v_index;
9504 BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, v_index) {
9505 BM_vert_calc_normal_ex(v, BM_ELEM_SELECT, vert_normals[v_index]);
9506 }
9507 }
9508
9509 BLI_bitmap *loop_set = BLI_BITMAP_NEW(bm->totloop, __func__);
9510 const int cd_clnors_offset = CustomData_get_offset_named(
9511 &bm->ldata, CD_PROP_INT16_2D, "custom_normal");
9512
9513 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
9514 BM_ITER_ELEM (e, &eiter, f, BM_EDGES_OF_FACE) {
9515 if (!keep_sharp ||
9517 {
9518 BM_ITER_ELEM (v, &viter, e, BM_VERTS_OF_EDGE) {
9520 const int l_index = BM_elem_index_get(l);
9521 const int v_index = BM_elem_index_get(l->v);
9522
9523 if (!is_zero_v3(vert_normals[v_index])) {
9524 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
9526 bm->lnor_spacearr->lspacearr[l_index], vert_normals[v_index], clnors);
9527
9528 if (bm->lnor_spacearr->lspacearr[l_index]->flags & MLNOR_SPACE_IS_SINGLE) {
9529 BLI_BITMAP_ENABLE(loop_set, l_index);
9530 }
9531 else {
9532 LinkNode *loops = bm->lnor_spacearr->lspacearr[l_index]->loops;
9533 for (; loops; loops = loops->next) {
9534 BLI_BITMAP_ENABLE(loop_set, BM_elem_index_get((BMLoop *)loops->link));
9535 }
9536 }
9537 }
9538 }
9539 }
9540 }
9541 }
9542
9543 int v_index;
9544 BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, v_index) {
9545 BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
9546 if (BLI_BITMAP_TEST(loop_set, BM_elem_index_get(l))) {
9547 const int loop_index = BM_elem_index_get(l);
9548 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
9550 bm->lnor_spacearr->lspacearr[loop_index], vert_normals[v_index], clnors);
9551 }
9552 }
9553 }
9554
9555 MEM_freeN(loop_set);
9556 MEM_freeN(vert_normals);
9558 params.calc_looptris = true;
9559 params.calc_normals = false;
9560 params.is_destructive = false;
9561 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
9562 }
9563
9564 return OPERATOR_FINISHED;
9565}
9566
9568{
9569 /* identifiers */
9570 ot->name = "Set Normals from Faces";
9571 ot->description = "Set the custom normals from the selected faces ones";
9572 ot->idname = "MESH_OT_set_normals_from_faces";
9573
9574 /* API callbacks. */
9576 ot->poll = ED_operator_editmesh;
9577
9578 /* flags */
9579 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9580
9582 ot->srna, "keep_sharp", false, "Keep Sharp Edges", "Do not set sharp edges to face");
9583}
9584
9586
9587/* -------------------------------------------------------------------- */
9590
9592{
9593 const Scene *scene = CTX_data_scene(C);
9594 ViewLayer *view_layer = CTX_data_view_layer(C);
9596 scene, view_layer, CTX_wm_view3d(C));
9597
9598 for (Object *obedit : objects) {
9600 BMesh *bm = em->bm;
9601 BMFace *f;
9602 BMLoop *l;
9603 BMIter fiter, liter;
9604
9607
9608 float(*smooth_normal)[3] = static_cast<float(*)[3]>(
9609 MEM_callocN(sizeof(*smooth_normal) * lnors_ed_arr->totloop, __func__));
9610
9611 /* NOTE(@mont29): This is weird choice of operation, taking all loops of faces of current
9612 * vertex. Could lead to some rather far away loops weighting as much as very close ones
9613 * (topologically speaking), with complex polygons.
9614 * Using topological distance here (rather than geometrical one)
9615 * makes sense IMHO, but would rather go with a more consistent and flexible code,
9616 * we could even add max topological distance to take into account, and a weighting curve.
9617 * Would do that later though, think for now we can live with that choice. */
9618 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
9619 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9620 l = lnor_ed->loop;
9621 float loop_normal[3];
9622
9623 BM_ITER_ELEM (f, &fiter, l->v, BM_FACES_OF_VERT) {
9624 BMLoop *l_other;
9625 BM_ITER_ELEM (l_other, &liter, f, BM_LOOPS_OF_FACE) {
9626 const int l_index_other = BM_elem_index_get(l_other);
9627 short *clnors = static_cast<short *>(
9628 BM_ELEM_CD_GET_VOID_P(l_other, lnors_ed_arr->cd_custom_normal_offset));
9630 bm->lnor_spacearr->lspacearr[l_index_other], clnors, loop_normal);
9631 add_v3_v3(smooth_normal[i], loop_normal);
9632 }
9633 }
9634 }
9635
9636 const float factor = RNA_float_get(op->ptr, "factor");
9637
9638 lnor_ed = lnors_ed_arr->lnor_editdata;
9639 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9640 float current_normal[3];
9641
9642 if (normalize_v3(smooth_normal[i]) < CLNORS_VALID_VEC_LEN) {
9643 /* Skip in case the smooth normal is invalid. */
9644 continue;
9645 }
9646
9648 bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->clnors_data, current_normal);
9649
9650 /* NOTE: again, this is not true spherical interpolation that normals would need...
9651 * But it's probably good enough for now. */
9652 mul_v3_fl(current_normal, 1.0f - factor);
9653 mul_v3_fl(smooth_normal[i], factor);
9654 add_v3_v3(current_normal, smooth_normal[i]);
9655
9656 if (normalize_v3(current_normal) < CLNORS_VALID_VEC_LEN) {
9657 /* Skip in case the smoothed normal is invalid. */
9658 continue;
9659 }
9660
9662 bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], current_normal, lnor_ed->clnors_data);
9663 }
9664
9666 MEM_freeN(smooth_normal);
9667
9669 params.calc_looptris = true;
9670 params.calc_normals = false;
9671 params.is_destructive = false;
9672 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
9673 }
9674
9675 return OPERATOR_FINISHED;
9676}
9677
9679{
9680 /* identifiers */
9681 ot->name = "Smooth Normals Vectors";
9682 ot->description = "Smooth custom normals based on adjacent vertex normals";
9683 ot->idname = "MESH_OT_smooth_normals";
9684
9685 /* API callbacks. */
9687 ot->poll = ED_operator_editmesh;
9688
9689 /* flags */
9690 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9691
9692 RNA_def_float(ot->srna,
9693 "factor",
9694 0.5f,
9695 0.0f,
9696 1.0f,
9697 "Factor",
9698 "Specifies weight of smooth vs original normal",
9699 0.0f,
9700 1.0f);
9701}
9702
9704
9705/* -------------------------------------------------------------------- */
9708
9710{
9711 const Scene *scene = CTX_data_scene(C);
9712 ViewLayer *view_layer = CTX_data_view_layer(C);
9714 scene, view_layer, CTX_wm_view3d(C));
9715
9716 for (Object *obedit : objects) {
9718 BMesh *bm = em->bm;
9719 BMFace *f;
9720 BMIter fiter;
9721 const int face_strength = RNA_enum_get(op->ptr, "face_strength");
9722 const bool set = RNA_boolean_get(op->ptr, "set");
9723
9725
9726 const char *layer_id = MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID;
9727 int cd_prop_int_index = CustomData_get_named_layer_index(&bm->pdata, CD_PROP_INT32, layer_id);
9728 if (cd_prop_int_index == -1) {
9729 BM_data_layer_add_named(bm, &bm->pdata, CD_PROP_INT32, layer_id);
9730 cd_prop_int_index = CustomData_get_named_layer_index(&bm->pdata, CD_PROP_INT32, layer_id);
9731 }
9732 cd_prop_int_index -= CustomData_get_layer_index(&bm->pdata, CD_PROP_INT32);
9733 const int cd_prop_int_offset = CustomData_get_n_offset(
9734 &bm->pdata, CD_PROP_INT32, cd_prop_int_index);
9735
9737
9738 if (set) {
9739 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
9741 int *strength = static_cast<int *>(BM_ELEM_CD_GET_VOID_P(f, cd_prop_int_offset));
9742 *strength = face_strength;
9743 }
9744 }
9745 }
9746 else {
9747 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
9748 const int *strength = static_cast<int *>(BM_ELEM_CD_GET_VOID_P(f, cd_prop_int_offset));
9749 if (*strength == face_strength) {
9750 BM_face_select_set(bm, f, true);
9752 }
9753 else {
9754 BM_face_select_set(bm, f, false);
9755 }
9756 }
9757 }
9758
9760 params.calc_looptris = false;
9761 params.calc_normals = false;
9762 params.is_destructive = false;
9763 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
9764 }
9765
9766 return OPERATOR_FINISHED;
9767}
9768
9770 {FACE_STRENGTH_WEAK, "WEAK", 0, "Weak", ""},
9771 {FACE_STRENGTH_MEDIUM, "MEDIUM", 0, "Medium", ""},
9772 {FACE_STRENGTH_STRONG, "STRONG", 0, "Strong", ""},
9773 {0, nullptr, 0, nullptr, nullptr},
9774};
9775
9777{
9778 /* identifiers */
9779 ot->name = "Face Normals Strength";
9780 ot->description = "Set/Get strength of face (used in Weighted Normal modifier)";
9781 ot->idname = "MESH_OT_mod_weighted_strength";
9782
9783 /* API callbacks. */
9785 ot->poll = ED_operator_editmesh;
9786
9787 /* flags */
9788 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9789
9790 ot->prop = RNA_def_boolean(ot->srna, "set", false, "Set Value", "Set value of faces");
9791
9792 ot->prop = RNA_def_enum(
9793 ot->srna,
9794 "face_strength",
9797 "Face Strength",
9798 "Strength to use for assigning or selecting face influence for weighted normal modifier");
9799}
9800
9802{
9803 /* identifiers */
9804 ot->name = "Flip Quad Tessellation";
9805 ot->description = "Flips the tessellation of selected quads";
9806 ot->idname = "MESH_OT_flip_quad_tessellation";
9807
9809 ot->poll = ED_operator_editmesh;
9810
9811 /* flags */
9812 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
9813}
9814
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])
void BKE_lnor_space_custom_normal_to_data(const MLoopNorSpace *lnor_space, const float custom_lnor[3], short r_clnor_data[2])
@ MLNOR_SPACEARR_BMLOOP_PTR
Definition BKE_mesh.h:293
@ 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_FACE_MARK
@ FREESTYLE_EDGE_MARK
@ MOD_TRIANGULATE_QUAD_BEAUTY
@ eModifierMode_Realtime
@ MOD_TRIANGULATE_NGON_BEAUTY
@ MOD_MIR_AXIS_Z
@ MOD_MIR_CLIPPING
@ MOD_MIR_AXIS_X
@ MOD_MIR_AXIS_Y
@ eModifierType_Mirror
#define MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID
Object is a sort of wrapper for general info.
@ OB_MESH
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ PROP_SMOOTH
eDupli_ID_Flags
@ USER_DUP_MESH
@ USER_DUP_ACT
@ V3D_AROUND_ACTIVE
@ V3D_AROUND_CENTER_BOUNDS
@ V3D_AROUND_CURSOR
@ V3D_AROUND_CENTER_MEDIAN
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
@ OP_IS_REPEAT_LAST
@ OP_IS_MODAL_GRAB_CURSOR
void EDBM_flag_disable_all(BMEditMesh *em, char hflag)
void 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
@ 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
ReportList * reports
Definition WM_types.hh:1025
#define ND_SELECT
Definition WM_types.hh:505
#define NC_OBJECT
Definition WM_types.hh:376
#define U
#define BM_elem_cb_check_hflag_disabled_simple(type, hflag_n)
#define BM_DISK_EDGE_NEXT(e, v)
#define BM_ALL_NOLOOP
#define BM_ALL
#define BM_FACE_FIRST_LOOP(p)
@ BM_SPACEARR_DIRTY_ALL
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SELECT
@ BM_ELEM_SMOOTH
@ BM_ELEM_TAG
#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
@ DEL_ONLYTAGGED
@ DEL_FACES_KEEP_BOUNDARY
@ DEL_EDGESFACES
@ DEL_ONLYFACES
void BMO_slot_buffer_hflag_enable(BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, char hflag, bool do_flush)
BMO_FLAG_BUFFER.
void * BMO_iter_map_value_ptr(BMOIter *iter)
void BMO_slot_buffer_hflag_disable(BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, char hflag, bool do_flush)
BMO_FLAG_BUFFER.
void BMO_slot_buffer_from_enabled_hflag(BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, char hflag)
BMOpSlot * BMO_slot_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *identifier)
BMESH OPSTACK GET SLOT.
void BMO_op_exec(BMesh *bm, BMOperator *op)
BMESH OPSTACK EXEC OP.
void * BMO_slot_buffer_get_first(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
void BMO_slot_int_set(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, int i)
void BMO_op_init(BMesh *bm, BMOperator *op, int flag, const char *opname)
BMESH OPSTACK INIT OP.
#define BMO_ITER(ele, iter, slot_args, slot_name, restrict_flag)
bool BMO_op_initf(BMesh *bm, BMOperator *op, int flag, const char *fmt,...)
void BMO_op_finish(BMesh *bm, BMOperator *op)
BMESH OPSTACK FINISH OP.
int BMO_slot_buffer_len(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
bool BMO_op_callf(BMesh *bm, int flag, const char *fmt,...)
#define BMO_FLAG_DEFAULTS
@ BMO_FLAG_RESPECT_HIDE
@ BMO_DELIM_NORMAL
BLI_INLINE void BMO_slot_map_elem_insert(BMOperator *op, BMOpSlot *slot, const void *element, void *val)
@ SUBDIV_SELECT_ORIG
@ 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)
@ SUBD_FALLOFF_LIN
@ SUBD_RING_INTERP_SURF
@ SUBD_RING_INTERP_PATH
@ SUBD_RING_INTERP_LINEAR
@ BMOP_POKE_MEDIAN_WEIGHTED
@ BMOP_POKE_BOUNDS
@ BMOP_POKE_MEDIAN
@ SUBD_CORNER_FAN
@ SUBD_CORNER_STRAIGHT_CUT
@ SUBD_CORNER_PATH
@ SUBD_CORNER_INNERVERT
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)
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)
@ EDBM_CLNOR_TOOLS_RESET
@ EDBM_CLNOR_TOOLS_COPY
@ EDBM_CLNOR_TOOLS_ADD
@ EDBM_CLNOR_TOOLS_MULTIPLY
@ EDBM_CLNOR_TOOLS_PASTE
static wmOperatorStatus edbm_dissolve_limited_exec(bContext *C, wmOperator *op)
static bool edbm_connect_vert_pair(BMEditMesh *em, Mesh *mesh, wmOperator *op)
static void mesh_separate_material_assign_mat_nr(Main *bmain, Object *ob, const short mat_nr)
void MESH_OT_set_normals_from_faces(wmOperatorType *ot)
void MESH_OT_symmetrize(wmOperatorType *ot)
static void normals_merge(BMesh *bm, BMLoopNorEditDataArray *lnors_ed_arr)
static const EnumPropertyItem prop_mesh_cornervert_types[]
void MESH_OT_edge_split(wmOperatorType *ot)
static wmOperatorStatus edbm_dissolve_degenerate_exec(bContext *C, wmOperator *op)
static bool bm_vert_connect_select_history(BMesh *bm)
static bool merge_firstlast(BMEditMesh *em, const bool use_first, const bool use_uvmerge, wmOperator *wmop)
void MESH_OT_delete(wmOperatorType *ot)
static wmOperatorStatus edbm_mod_weighted_strength_exec(bContext *C, wmOperator *op)
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)
@ EDBM_CLNOR_AVERAGE_LOOP
@ EDBM_CLNOR_AVERAGE_FACE_AREA
@ EDBM_CLNOR_AVERAGE_ANGLE
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)
@ MESH_SEPARATE_LOOSE
@ MESH_SEPARATE_MATERIAL
@ MESH_SEPARATE_SELECTED
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)
@ 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 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)
@ MESH_BRIDGELOOP_PAIRS
@ MESH_BRIDGELOOP_SINGLE
@ MESH_BRIDGELOOP_CLOSED
static wmOperatorStatus edbm_faces_shade_flat_exec(bContext *C, wmOperator *)
@ EDBM_CLNOR_POINTTO_MODE_MOUSE
@ EDBM_CLNOR_POINTTO_MODE_COORDINATES
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)
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 *)
static void edbm_add_edge_face_exec__tricky_finalize_sel(BMesh *bm, BMElem *ele_desel, BMFace *f)
static wmOperatorStatus edbm_blend_from_shape_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_vert_connect_nonplaner_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_edge_rotate_selected_exec(bContext *C, wmOperator *op)
static FillGridSplitJoin * edbm_fill_grid_split_join_init(BMEditMesh *em)
void MESH_OT_dissolve_mode(wmOperatorType *ot)
static void mesh_set_smooth_faces(BMEditMesh *em, short smooth)
void MESH_OT_bridge_edge_loops(wmOperatorType *ot)
static wmOperatorStatus edbm_fill_holes_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_hide_exec(bContext *C, wmOperator *op)
static bool bm_vert_is_select_history_open(BMesh *bm)
static Base * mesh_separate_tagged(Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
void MESH_OT_subdivide(wmOperatorType *ot)
void MESH_OT_unsubdivide(wmOperatorType *ot)
void MESH_OT_duplicate(wmOperatorType *ot)
static bool shape_propagate(BMEditMesh *em, bool use_symmetry)
wmKeyMap * point_normals_modal_keymap(wmKeyConfig *keyconf)
static wmOperatorStatus edbm_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *)
void MESH_OT_normals_make_consistent(wmOperatorType *ot)
static wmOperatorStatus edbm_quads_convert_to_tris_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_sort_elements_exec(bContext *C, wmOperator *op)
void MESH_OT_tris_convert_to_quads(wmOperatorType *ot)
static const EnumPropertyItem prop_mesh_face_strength_types[]
static wmOperatorStatus edbm_poke_face_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_offset_edgeloop_exec(bContext *C, wmOperator *op)
void MESH_OT_flip_normals(wmOperatorType *ot)
static wmOperatorStatus edbm_reveal_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_normals_tools_exec(bContext *C, wmOperator *op)
static void edbm_report_delete_info(ReportList *reports, const int totelem_old[3], const int totelem_new[3])
static bool edbm_fill_grid_prepare(BMesh *bm, int offset, int *span_p, const bool span_calc)
@ SRT_VIEW_XAXIS
@ SRT_SELECTED
@ SRT_RANDOMIZE
@ SRT_MATERIAL
@ SRT_CURSOR_DISTANCE
@ SRT_REVERSE
@ SRT_VIEW_ZAXIS
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
@ MESH_MERGE_LAST
@ MESH_MERGE_CENTER
@ MESH_MERGE_CURSOR
@ MESH_MERGE_FIRST
@ MESH_MERGE_COLLAPSE
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)
@ MESH_DELETE_EDGE_FACE
@ MESH_DELETE_EDGE
@ MESH_DELETE_FACE
@ MESH_DELETE_VERT
@ MESH_DELETE_ONLY_FACE
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 *)
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:4226
wmOperatorType * ot
Definition wm_files.cc:4225
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