Blender V4.3
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
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.hh"
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.h"
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/* -------------------------------------------------------------------- */
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 */
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
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
188 "fractal",
189 0.0f,
190 0.0f,
191 1e6f,
192 "Fractal",
193 "Fractal randomness factor",
194 0.0f,
195 1000.0f);
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);
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
218/* -------------------------------------------------------------------- */
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
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 */
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 */
342
343 /* flags */
345
346 /* properties */
348}
349
352/* -------------------------------------------------------------------- */
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 */
404
405 /* flags */
407
408 /* props */
410 ot->srna, "iterations", 2, 1, 1000, "Iterations", "Number of times to un-subdivide", 1, 100);
411}
412
415/* -------------------------------------------------------------------- */
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{
432 BKE_reportf(reports,
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 */
543
545
546 /* flags */
548
549 /* props */
551 "type",
552 prop_mesh_delete_types,
554 "Type",
555 "Method used for deleting mesh data");
557}
558
561/* -------------------------------------------------------------------- */
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
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
679/* -------------------------------------------------------------------- */
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 */
722
723 /* flags */
725}
726
729/* -------------------------------------------------------------------- */
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);
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 */
1017
1018 /* flags */
1020}
1021
1024/* -------------------------------------------------------------------- */
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 */
1090
1091 /* flags */
1093
1094 prop = RNA_def_boolean(ot->srna, "clear", false, "Clear", "");
1096
1098}
1099
1102/* -------------------------------------------------------------------- */
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 */
1162
1163 /* flags */
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
1179/* -------------------------------------------------------------------- */
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 = static_cast<BMVert **>(MEM_mallocN(sizeof(*verts) * 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 */
1319
1320 /* flags */
1322}
1323
1326/* -------------------------------------------------------------------- */
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 */
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 */
1619
1620 /* flags */
1622}
1623
1626/* -------------------------------------------------------------------- */
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 */
1668
1669 /* flags */
1671}
1672
1675/* -------------------------------------------------------------------- */
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 */
1727
1728 /* flags */
1730
1731 /* props */
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
1747/* -------------------------------------------------------------------- */
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 */
1802
1803 /* flags */
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
1813/* -------------------------------------------------------------------- */
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 */
1959
1960 /* flags */
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
1980/* -------------------------------------------------------------------- */
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
2036static int edbm_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
2037{
2038 WM_cursor_wait(true);
2039 const int retval = edbm_duplicate_exec(C, op);
2040 WM_cursor_wait(false);
2041
2042 return retval;
2043}
2044
2046{
2047 /* identifiers */
2048 ot->name = "Duplicate";
2049 ot->description = "Duplicate selected vertices, edges or faces";
2050 ot->idname = "MESH_OT_duplicate";
2051
2052 /* api callbacks */
2055
2057
2058 /* to give to transform */
2059 RNA_def_int(ot->srna, "mode", TFM_TRANSLATION, 0, INT_MAX, "Mode", "", 0, INT_MAX);
2060}
2061
2063{
2064 BMLoopNorEditDataArray *lnors_ed_arr = nullptr;
2066 /* The mesh has custom normal data, update these too.
2067 * Otherwise they will be left in a mangled state.
2068 */
2070 lnors_ed_arr = BM_loop_normal_editdata_array_init(bm, true);
2071 }
2072
2073 return lnors_ed_arr;
2074}
2075
2077{
2078 if (!lnors_ed_arr) {
2079 return false;
2080 }
2081
2082 if (lnors_ed_arr->totloop == 0) {
2083 /* No loops normals to flip, exit early! */
2084 return false;
2085 }
2086
2089
2090 /* We need to recreate the custom normal array because the clnors_data will
2091 * be mangled because we swapped the loops around when we flipped the faces. */
2092 BMLoopNorEditDataArray *lnors_ed_arr_new_full = BM_loop_normal_editdata_array_init(bm, true);
2093
2094 {
2095 /* We need to recalculate all loop normals in the affected area. Even the ones that are not
2096 * going to be flipped because the clnors data is mangled. */
2097
2098 BMLoopNorEditData *lnor_ed_new_full = lnors_ed_arr_new_full->lnor_editdata;
2099 for (int i = 0; i < lnors_ed_arr_new_full->totloop; i++, lnor_ed_new_full++) {
2100
2101 BMLoopNorEditData *lnor_ed =
2102 lnors_ed_arr->lidx_to_lnor_editdata[lnor_ed_new_full->loop_index];
2103
2104 BLI_assert(lnor_ed != nullptr);
2105
2107 bm->lnor_spacearr->lspacearr[lnor_ed_new_full->loop_index],
2108 lnor_ed->nloc,
2109 lnor_ed_new_full->clnors_data);
2110 }
2111 }
2112
2113 BMFace *f;
2114 BMLoop *l, *l_start;
2115 BMIter iter_f;
2116 BM_ITER_MESH (f, &iter_f, bm, BM_FACES_OF_MESH) {
2117 /* Flip all the custom loop normals on the selected faces. */
2119 continue;
2120 }
2121
2122 /* Because the winding has changed, we need to go the reverse way around the face to get the
2123 * correct placement of the normals. However we need to derive the old loop index to get the
2124 * correct data. Note that the first loop index is the same though. So the loop starts and ends
2125 * in the same place as before the flip.
2126 */
2127
2128 l_start = l = BM_FACE_FIRST_LOOP(f);
2129 int old_index = BM_elem_index_get(l);
2130 do {
2131 int loop_index = BM_elem_index_get(l);
2132
2133 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lidx_to_lnor_editdata[old_index];
2134 BMLoopNorEditData *lnor_ed_new = lnors_ed_arr_new_full->lidx_to_lnor_editdata[loop_index];
2135 BLI_assert(lnor_ed != nullptr && lnor_ed_new != nullptr);
2136
2137 negate_v3(lnor_ed->nloc);
2138
2140 bm->lnor_spacearr->lspacearr[loop_index], lnor_ed->nloc, lnor_ed_new->clnors_data);
2141
2142 old_index++;
2143 l = l->prev;
2144 } while (l != l_start);
2145 }
2146 BM_loop_normal_editdata_array_free(lnors_ed_arr_new_full);
2147 return true;
2148}
2149
2152/* -------------------------------------------------------------------- */
2157{
2159 return;
2160 }
2161
2162 /* The mesh has custom normal data, flip them. */
2163 BMesh *bm = em->bm;
2164
2167 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
2168
2169 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
2170 negate_v3(lnor_ed->nloc);
2171
2173 bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->nloc, lnor_ed->clnors_data);
2174 }
2177 params.calc_looptris = true;
2178 params.calc_normals = false;
2179 params.is_destructive = false;
2180 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2181}
2182
2184{
2185 if (EDBM_op_callf(em, op, "flip_quad_tessellation faces=%hf", BM_ELEM_SELECT)) {
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}
2193
2195{
2196
2197 bool has_flipped_faces = false;
2198
2199 /* See if we have any custom normals to flip. */
2201
2202 if (EDBM_op_callf(em, op, "reverse_faces faces=%hf flip_multires=%b", BM_ELEM_SELECT, true)) {
2203 has_flipped_faces = true;
2204 }
2205
2206 if (flip_custom_normals(em->bm, lnors_ed_arr) || has_flipped_faces) {
2208 params.calc_looptris = true;
2209 params.calc_normals = false;
2210 params.is_destructive = false;
2211 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2212 }
2213
2214 if (lnors_ed_arr != nullptr) {
2216 }
2217}
2218
2220{
2221 const Scene *scene = CTX_data_scene(C);
2222 ViewLayer *view_layer = CTX_data_view_layer(C);
2224 scene, view_layer, CTX_wm_view3d(C));
2225
2226 for (Object *obedit : objects) {
2228 if (em->bm->totfacesel == 0) {
2229 continue;
2230 }
2231 edbm_flip_quad_tessellation(op, obedit, em);
2232 }
2233
2234 return OPERATOR_FINISHED;
2235}
2236
2238{
2239 const bool only_clnors = RNA_boolean_get(op->ptr, "only_clnors");
2240
2241 const Scene *scene = CTX_data_scene(C);
2242 ViewLayer *view_layer = CTX_data_view_layer(C);
2244 scene, view_layer, CTX_wm_view3d(C));
2245
2246 for (Object *obedit : objects) {
2248
2249 if (only_clnors) {
2250 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
2251 continue;
2252 }
2254 }
2255 else {
2256 if (em->bm->totfacesel == 0) {
2257 continue;
2258 }
2259 edbm_flip_normals_face_winding(op, obedit, em);
2260 }
2261 }
2262
2263 return OPERATOR_FINISHED;
2264}
2265
2267{
2268 /* identifiers */
2269 ot->name = "Flip Normals";
2270 ot->description = "Flip the direction of selected faces' normals (and of their vertices)";
2271 ot->idname = "MESH_OT_flip_normals";
2272
2273 /* api callbacks */
2276
2277 /* flags */
2279
2281 "only_clnors",
2282 false,
2283 "Custom Normals Only",
2284 "Only flip the custom loop normals of the selected elements");
2285}
2286
2289/* -------------------------------------------------------------------- */
2297{
2298 BMEdge *eed;
2299 BMIter iter;
2300 const bool use_ccw = RNA_boolean_get(op->ptr, "use_ccw");
2301
2302 int tot_failed_all = 0;
2303 bool no_selected_edges = true, invalid_selected_edges = true;
2304
2305 const Scene *scene = CTX_data_scene(C);
2306 ViewLayer *view_layer = CTX_data_view_layer(C);
2308 scene, view_layer, CTX_wm_view3d(C));
2309 for (Object *obedit : objects) {
2311 int tot = 0;
2312
2313 if (em->bm->totedgesel == 0) {
2314 continue;
2315 }
2316 no_selected_edges = false;
2317
2318 /* first see if we have two adjacent faces */
2319 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
2322 BMFace *fa, *fb;
2323 if (BM_edge_face_pair(eed, &fa, &fb)) {
2324 /* if both faces are selected we rotate between them,
2325 * otherwise - rotate between 2 unselected - but not mixed */
2328 tot++;
2329 }
2330 }
2331 }
2332 }
2333
2334 /* OK, we don't have two adjacent faces, but we do have two selected ones.
2335 * that's an error condition. */
2336 if (tot == 0) {
2337 continue;
2338 }
2339 invalid_selected_edges = false;
2340
2341 BMOperator bmop;
2342 EDBM_op_init(em, &bmop, op, "rotate_edges edges=%he use_ccw=%b", BM_ELEM_TAG, use_ccw);
2343
2344 /* avoids leaving old verts selected which can be a problem running multiple times,
2345 * since this means the edges become selected around the face
2346 * which then attempt to rotate */
2348
2349 BMO_op_exec(em->bm, &bmop);
2350 /* edges may rotate into hidden vertices, if this does _not_ run we get an illogical state */
2352 em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_HIDDEN, true);
2354 em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true);
2355
2356 const int tot_rotate = BMO_slot_buffer_len(bmop.slots_out, "edges.out");
2357 const int tot_failed = tot - tot_rotate;
2358
2359 tot_failed_all += tot_failed;
2360
2361 if (tot_failed != 0) {
2362 /* If some edges fail to rotate, we need to re-select them,
2363 * otherwise we can end up with invalid selection
2364 * (unselected edge between 2 selected faces). */
2366 }
2367
2369
2370 if (!EDBM_op_finish(em, &bmop, op, true)) {
2371 continue;
2372 }
2373
2375 params.calc_looptris = true;
2376 params.calc_normals = false;
2377 params.is_destructive = true;
2378 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2379 }
2380
2381 if (no_selected_edges) {
2382 BKE_report(
2383 op->reports, RPT_ERROR, "Select edges or face pairs for edge loops to rotate about");
2384 return OPERATOR_CANCELLED;
2385 }
2386
2387 /* Ok, we don't have two adjacent faces, but we do have two selected ones.
2388 * that's an error condition. */
2389 if (invalid_selected_edges) {
2390 BKE_report(op->reports, RPT_ERROR, "Could not find any selected edges that can be rotated");
2391 return OPERATOR_CANCELLED;
2392 }
2393
2394 if (tot_failed_all != 0) {
2395 BKE_reportf(op->reports, RPT_WARNING, "Unable to rotate %d edge(s)", tot_failed_all);
2396 }
2397
2398 return OPERATOR_FINISHED;
2399}
2400
2402{
2403 /* identifiers */
2404 ot->name = "Rotate Selected Edge";
2405 ot->description = "Rotate selected edge or adjoining faces";
2406 ot->idname = "MESH_OT_edge_rotate";
2407
2408 /* api callbacks */
2411
2412 /* flags */
2414
2415 /* props */
2416 RNA_def_boolean(ot->srna, "use_ccw", false, "Counter Clockwise", "");
2417}
2418
2421/* -------------------------------------------------------------------- */
2426{
2427 const bool unselected = RNA_boolean_get(op->ptr, "unselected");
2428 const Scene *scene = CTX_data_scene(C);
2429 ViewLayer *view_layer = CTX_data_view_layer(C);
2430 bool changed = false;
2431
2433 scene, view_layer, CTX_wm_view3d(C));
2434 for (Object *obedit : objects) {
2436 BMesh *bm = em->bm;
2437
2438 if (unselected) {
2439 if (em->selectmode & SCE_SELECT_VERTEX) {
2440 if (bm->totvertsel == bm->totvert) {
2441 continue;
2442 }
2443 }
2444 else if (em->selectmode & SCE_SELECT_EDGE) {
2445 if (bm->totedgesel == bm->totedge) {
2446 continue;
2447 }
2448 }
2449 else if (em->selectmode & SCE_SELECT_FACE) {
2450 if (bm->totfacesel == bm->totface) {
2451 continue;
2452 }
2453 }
2454 }
2455 else {
2456 if (bm->totvertsel == 0) {
2457 continue;
2458 }
2459 }
2460
2461 if (EDBM_mesh_hide(em, unselected)) {
2463 params.calc_looptris = true;
2464 params.calc_normals = false;
2465 params.is_destructive = false;
2466 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2467 changed = true;
2468 }
2469 }
2470
2471 if (!changed) {
2472 return OPERATOR_CANCELLED;
2473 }
2474
2475 return OPERATOR_FINISHED;
2476}
2477
2479{
2480 /* identifiers */
2481 ot->name = "Hide Selected";
2482 ot->idname = "MESH_OT_hide";
2483 ot->description = "Hide (un)selected vertices, edges or faces";
2484
2485 /* api callbacks */
2488
2489 /* flags */
2491
2492 /* props */
2494 ot->srna, "unselected", false, "Unselected", "Hide unselected rather than selected");
2495}
2496
2499/* -------------------------------------------------------------------- */
2504{
2505 const bool select = RNA_boolean_get(op->ptr, "select");
2506 const Scene *scene = CTX_data_scene(C);
2507 ViewLayer *view_layer = CTX_data_view_layer(C);
2508
2510 scene, view_layer, CTX_wm_view3d(C));
2511 for (Object *obedit : objects) {
2513
2514 if (EDBM_mesh_reveal(em, select)) {
2516 params.calc_looptris = true;
2517 params.calc_normals = false;
2518 params.is_destructive = false;
2519 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2520 }
2521 }
2522
2523 return OPERATOR_FINISHED;
2524}
2525
2527{
2528 /* identifiers */
2529 ot->name = "Reveal Hidden";
2530 ot->idname = "MESH_OT_reveal";
2531 ot->description = "Reveal all hidden vertices, edges and faces";
2532
2533 /* api callbacks */
2536
2537 /* flags */
2539
2540 RNA_def_boolean(ot->srna, "select", true, "Select", "");
2541}
2542
2545/* -------------------------------------------------------------------- */
2550{
2551 const Scene *scene = CTX_data_scene(C);
2552 ViewLayer *view_layer = CTX_data_view_layer(C);
2553 const bool inside = RNA_boolean_get(op->ptr, "inside");
2554
2556 scene, view_layer, CTX_wm_view3d(C));
2557 for (Object *obedit : objects) {
2559
2560 if (em->bm->totfacesel == 0) {
2561 continue;
2562 }
2563
2564 BMLoopNorEditDataArray *lnors_ed_arr = nullptr;
2565
2566 if (inside) {
2567 /* Save custom normal data for later so we can flip them correctly. */
2568 lnors_ed_arr = flip_custom_normals_init_data(em->bm);
2569 }
2570
2571 if (!EDBM_op_callf(em, op, "recalc_face_normals faces=%hf", BM_ELEM_SELECT)) {
2572 continue;
2573 }
2574
2575 if (inside) {
2576 EDBM_op_callf(em, op, "reverse_faces faces=%hf flip_multires=%b", BM_ELEM_SELECT, true);
2577 flip_custom_normals(em->bm, lnors_ed_arr);
2578 if (lnors_ed_arr != nullptr) {
2580 }
2581 }
2582
2584 params.calc_looptris = true;
2585 params.calc_normals = false;
2586 params.is_destructive = false;
2587 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2588 }
2589
2590 return OPERATOR_FINISHED;
2591}
2592
2594{
2595 /* identifiers */
2596 ot->name = "Recalculate Normals";
2597 ot->description = "Make face and vertex normals point either outside or inside the mesh";
2598 ot->idname = "MESH_OT_normals_make_consistent";
2599
2600 /* api callbacks */
2603
2604 /* flags */
2606
2607 RNA_def_boolean(ot->srna, "inside", false, "Inside", "");
2608}
2609
2612/* -------------------------------------------------------------------- */
2617{
2618 const float fac = RNA_float_get(op->ptr, "factor");
2619
2620 const bool xaxis = RNA_boolean_get(op->ptr, "xaxis");
2621 const bool yaxis = RNA_boolean_get(op->ptr, "yaxis");
2622 const bool zaxis = RNA_boolean_get(op->ptr, "zaxis");
2623 int repeat = RNA_int_get(op->ptr, "repeat");
2624
2625 if (!repeat) {
2626 repeat = 1;
2627 }
2628
2629 const Scene *scene = CTX_data_scene(C);
2630 ViewLayer *view_layer = CTX_data_view_layer(C);
2631 int tot_selected = 0, tot_locked = 0;
2633 scene, view_layer, CTX_wm_view3d(C));
2634 for (Object *obedit : objects) {
2635 Mesh *mesh = static_cast<Mesh *>(obedit->data);
2637 bool mirrx = false, mirry = false, mirrz = false;
2638 float clip_dist = 0.0f;
2639 const bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
2640
2641 if (em->bm->totvertsel == 0) {
2642 continue;
2643 }
2644
2646 tot_locked++;
2647 continue;
2648 }
2649
2650 tot_selected++;
2651
2652 /* mirror before smooth */
2653 if (((Mesh *)obedit->data)->symmetry & ME_SYMMETRY_X) {
2654 EDBM_verts_mirror_cache_begin(em, 0, false, true, false, use_topology);
2655 }
2656
2657 /* if there is a mirror modifier with clipping, flag the verts that
2658 * are within tolerance of the plane(s) of reflection
2659 */
2660 LISTBASE_FOREACH (ModifierData *, md, &obedit->modifiers) {
2661 if (md->type == eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) {
2663
2664 if (mmd->flag & MOD_MIR_CLIPPING) {
2665 if (mmd->flag & MOD_MIR_AXIS_X) {
2666 mirrx = true;
2667 }
2668 if (mmd->flag & MOD_MIR_AXIS_Y) {
2669 mirry = true;
2670 }
2671 if (mmd->flag & MOD_MIR_AXIS_Z) {
2672 mirrz = true;
2673 }
2674
2675 clip_dist = mmd->tolerance;
2676 }
2677 }
2678 }
2679
2680 for (int i = 0; i < repeat; i++) {
2681 if (!EDBM_op_callf(
2682 em,
2683 op,
2684 "smooth_vert verts=%hv factor=%f mirror_clip_x=%b mirror_clip_y=%b mirror_clip_z=%b "
2685 "clip_dist=%f use_axis_x=%b use_axis_y=%b use_axis_z=%b",
2687 fac,
2688 mirrx,
2689 mirry,
2690 mirrz,
2691 clip_dist,
2692 xaxis,
2693 yaxis,
2694 zaxis))
2695 {
2696 continue;
2697 }
2698 }
2699
2700 /* apply mirror */
2701 if (((Mesh *)obedit->data)->symmetry & ME_SYMMETRY_X) {
2704 }
2705
2707 params.calc_looptris = true;
2708 params.calc_normals = false;
2709 params.is_destructive = false;
2710 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2711 }
2712
2713 if (tot_selected == 0 && !tot_locked) {
2714 BKE_report(op->reports, RPT_WARNING, "No selected vertex");
2715 }
2716
2717 return tot_selected ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
2718}
2719
2721{
2722 /* identifiers */
2723 ot->name = "Smooth Vertices";
2724 ot->description = "Flatten angles of selected vertices";
2725 ot->idname = "MESH_OT_vertices_smooth";
2726
2727 /* api callbacks */
2730
2731 /* flags */
2733
2735 ot->srna, "factor", 0.0f, -10.0f, 10.0f, "Smoothing", "Smoothing factor", 0.0f, 1.0f);
2737 ot->srna, "repeat", 1, 1, 1000, "Repeat", "Number of times to smooth the mesh", 1, 100);
2738
2740
2741 RNA_def_boolean(ot->srna, "xaxis", true, "X-Axis", "Smooth along the X axis");
2742 RNA_def_boolean(ot->srna, "yaxis", true, "Y-Axis", "Smooth along the Y axis");
2743 RNA_def_boolean(ot->srna, "zaxis", true, "Z-Axis", "Smooth along the Z axis");
2744
2745 /* Set generic modal callbacks. */
2747}
2748
2751/* -------------------------------------------------------------------- */
2756{
2757 int tot_selected = 0, tot_locked = 0;
2758 const Scene *scene = CTX_data_scene(C);
2759 ViewLayer *view_layer = CTX_data_view_layer(C);
2760
2761 const float lambda_factor = RNA_float_get(op->ptr, "lambda_factor");
2762 const float lambda_border = RNA_float_get(op->ptr, "lambda_border");
2763 const bool usex = RNA_boolean_get(op->ptr, "use_x");
2764 const bool usey = RNA_boolean_get(op->ptr, "use_y");
2765 const bool usez = RNA_boolean_get(op->ptr, "use_z");
2766 const bool preserve_volume = RNA_boolean_get(op->ptr, "preserve_volume");
2767 int repeat = RNA_int_get(op->ptr, "repeat");
2768
2769 if (!repeat) {
2770 repeat = 1;
2771 }
2772
2774 scene, view_layer, CTX_wm_view3d(C));
2775 for (Object *obedit : objects) {
2777 Mesh *mesh = static_cast<Mesh *>(obedit->data);
2778 bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
2779
2780 if (em->bm->totvertsel == 0) {
2781 continue;
2782 }
2783
2785 tot_locked++;
2786 continue;
2787 }
2788
2789 tot_selected++;
2790
2791 /* Mirror before smooth. */
2792 if (((Mesh *)obedit->data)->symmetry & ME_SYMMETRY_X) {
2793 EDBM_verts_mirror_cache_begin(em, 0, false, true, false, use_topology);
2794 }
2795
2796 bool failed_repeat_loop = false;
2797 for (int i = 0; i < repeat; i++) {
2798 if (!EDBM_op_callf(em,
2799 op,
2800 "smooth_laplacian_vert verts=%hv lambda_factor=%f lambda_border=%f "
2801 "use_x=%b use_y=%b use_z=%b preserve_volume=%b",
2803 lambda_factor,
2804 lambda_border,
2805 usex,
2806 usey,
2807 usez,
2808 preserve_volume))
2809 {
2810 failed_repeat_loop = true;
2811 break;
2812 }
2813 }
2814 if (failed_repeat_loop) {
2815 continue;
2816 }
2817
2818 /* Apply mirror. */
2819 if (((Mesh *)obedit->data)->symmetry & ME_SYMMETRY_X) {
2822 }
2823
2825 params.calc_looptris = true;
2826 params.calc_normals = false;
2827 params.is_destructive = false;
2828 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2829 }
2830
2831 if (tot_selected == 0 && !tot_locked) {
2832 BKE_report(op->reports, RPT_WARNING, "No selected vertex");
2833 }
2834
2835 return tot_selected ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
2836}
2837
2839{
2840 /* identifiers */
2841 ot->name = "Laplacian Smooth Vertices";
2842 ot->description = "Laplacian smooth of selected vertices";
2843 ot->idname = "MESH_OT_vertices_smooth_laplacian";
2844
2845 /* api callbacks */
2848
2849 /* flags */
2851
2853 ot->srna, "repeat", 1, 1, 1000, "Number of iterations to smooth the mesh", "", 1, 200);
2855 ot->srna, "lambda_factor", 1.0f, 1e-7f, 1000.0f, "Lambda factor", "", 1e-7f, 1000.0f);
2857 "lambda_border",
2858 5e-5f,
2859 1e-7f,
2860 1000.0f,
2861 "Lambda factor in border",
2862 "",
2863 1e-7f,
2864 1000.0f);
2865
2867
2868 RNA_def_boolean(ot->srna, "use_x", true, "Smooth X Axis", "Smooth object along X axis");
2869 RNA_def_boolean(ot->srna, "use_y", true, "Smooth Y Axis", "Smooth object along Y axis");
2870 RNA_def_boolean(ot->srna, "use_z", true, "Smooth Z Axis", "Smooth object along Z axis");
2872 "preserve_volume",
2873 true,
2874 "Preserve Volume",
2875 "Apply volume preservation after smooth");
2876}
2877
2880/* -------------------------------------------------------------------- */
2884static void mesh_set_smooth_faces(BMEditMesh *em, short smooth)
2885{
2886 BMIter iter;
2887 BMFace *efa;
2888
2889 if (em == nullptr) {
2890 return;
2891 }
2892
2893 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2895 BM_elem_flag_set(efa, BM_ELEM_SMOOTH, smooth);
2896 }
2897 }
2898}
2899
2901{
2902 const Scene *scene = CTX_data_scene(C);
2903 ViewLayer *view_layer = CTX_data_view_layer(C);
2905 scene, view_layer, CTX_wm_view3d(C));
2906 for (Object *obedit : objects) {
2908
2909 if (em->bm->totfacesel == 0) {
2910 continue;
2911 }
2912
2913 mesh_set_smooth_faces(em, 1);
2915 params.calc_looptris = false;
2916 params.calc_normals = false;
2917 params.is_destructive = false;
2918 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2919 }
2920
2921 return OPERATOR_FINISHED;
2922}
2923
2925{
2926 /* identifiers */
2927 ot->name = "Shade Smooth";
2928 ot->description = "Display faces smooth (using vertex normals)";
2929 ot->idname = "MESH_OT_faces_shade_smooth";
2930
2931 /* api callbacks */
2934
2935 /* flags */
2937}
2938
2941/* -------------------------------------------------------------------- */
2946{
2947 const Scene *scene = CTX_data_scene(C);
2948 ViewLayer *view_layer = CTX_data_view_layer(C);
2950 scene, view_layer, CTX_wm_view3d(C));
2951 for (Object *obedit : objects) {
2953
2954 if (em->bm->totfacesel == 0) {
2955 continue;
2956 }
2957
2958 mesh_set_smooth_faces(em, 0);
2960 params.calc_looptris = false;
2961 params.calc_normals = false;
2962 params.is_destructive = false;
2963 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
2964 }
2965
2966 return OPERATOR_FINISHED;
2967}
2968
2970{
2971 /* identifiers */
2972 ot->name = "Shade Flat";
2973 ot->description = "Display faces flat";
2974 ot->idname = "MESH_OT_faces_shade_flat";
2975
2976 /* api callbacks */
2979
2980 /* flags */
2982}
2983
2986/* -------------------------------------------------------------------- */
2991{
2992 /* get the direction from RNA */
2993 const bool use_ccw = RNA_boolean_get(op->ptr, "use_ccw");
2994
2995 const Scene *scene = CTX_data_scene(C);
2996 ViewLayer *view_layer = CTX_data_view_layer(C);
2998 scene, view_layer, CTX_wm_view3d(C));
2999 for (Object *obedit : objects) {
3001
3002 if (em->bm->totfacesel == 0) {
3003 continue;
3004 }
3005
3006 BMOperator bmop;
3007
3008 EDBM_op_init(em, &bmop, op, "rotate_uvs faces=%hf use_ccw=%b", BM_ELEM_SELECT, use_ccw);
3009
3010 BMO_op_exec(em->bm, &bmop);
3011
3012 if (!EDBM_op_finish(em, &bmop, op, true)) {
3013 continue;
3014 }
3015
3017 params.calc_looptris = false;
3018 params.calc_normals = false;
3019 params.is_destructive = false;
3020 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
3021 }
3022
3023 return OPERATOR_FINISHED;
3024}
3025
3027{
3028 const Scene *scene = CTX_data_scene(C);
3029 ViewLayer *view_layer = CTX_data_view_layer(C);
3031 scene, view_layer, CTX_wm_view3d(C));
3032 for (Object *obedit : objects) {
3034
3035 if (em->bm->totfacesel == 0) {
3036 continue;
3037 }
3038
3039 BMOperator bmop;
3040
3041 EDBM_op_init(em, &bmop, op, "reverse_uvs faces=%hf", BM_ELEM_SELECT);
3042
3043 BMO_op_exec(em->bm, &bmop);
3044
3045 if (!EDBM_op_finish(em, &bmop, op, true)) {
3046 continue;
3047 }
3049 params.calc_looptris = false;
3050 params.calc_normals = false;
3051 params.is_destructive = false;
3052 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
3053 }
3054
3055 return OPERATOR_FINISHED;
3056}
3057
3059{
3060 /* get the direction from RNA */
3061 const bool use_ccw = RNA_boolean_get(op->ptr, "use_ccw");
3062
3063 const Scene *scene = CTX_data_scene(C);
3064 ViewLayer *view_layer = CTX_data_view_layer(C);
3066 scene, view_layer, CTX_wm_view3d(C));
3067
3068 for (uint ob_index = 0; ob_index < objects.size(); ob_index++) {
3069 Object *ob = objects[ob_index];
3071 if (em->bm->totfacesel == 0) {
3072 continue;
3073 }
3074
3075 BMOperator bmop;
3076
3078 AttributeOwner owner = AttributeOwner::from_id(&mesh->id);
3080 owner, mesh->active_color_attribute, CD_MASK_COLOR_ALL, ATTR_DOMAIN_MASK_CORNER);
3081 if (!layer) {
3082 continue;
3083 }
3084
3085 int color_index = BKE_attribute_to_index(
3087 EDBM_op_init(em,
3088 &bmop,
3089 op,
3090 "rotate_colors faces=%hf use_ccw=%b color_index=%i",
3092 use_ccw,
3093 color_index);
3094
3095 BMO_op_exec(em->bm, &bmop);
3096
3097 if (!EDBM_op_finish(em, &bmop, op, true)) {
3098 continue;
3099 }
3100
3101 /* dependencies graph and notification stuff */
3103 params.calc_looptris = false;
3104 params.calc_normals = false;
3105 params.is_destructive = false;
3106 EDBM_update(static_cast<Mesh *>(ob->data), &params);
3107 }
3108
3109 return OPERATOR_FINISHED;
3110}
3111
3113{
3114 const Scene *scene = CTX_data_scene(C);
3115 ViewLayer *view_layer = CTX_data_view_layer(C);
3117 scene, view_layer, CTX_wm_view3d(C));
3118
3119 for (Object *obedit : objects) {
3121
3122 if (em->bm->totfacesel == 0) {
3123 continue;
3124 }
3125
3126 Mesh *mesh = BKE_object_get_original_mesh(obedit);
3127 AttributeOwner owner = AttributeOwner::from_id(&mesh->id);
3129 owner, mesh->active_color_attribute, CD_MASK_COLOR_ALL, ATTR_DOMAIN_MASK_CORNER);
3130 if (!layer) {
3131 continue;
3132 }
3133
3134 BMOperator bmop;
3135
3136 int color_index = BKE_attribute_to_index(
3139 em, &bmop, op, "reverse_colors faces=%hf color_index=%i", BM_ELEM_SELECT, color_index);
3140
3141 BMO_op_exec(em->bm, &bmop);
3142
3143 if (!EDBM_op_finish(em, &bmop, op, true)) {
3144 continue;
3145 }
3146
3148 params.calc_looptris = false;
3149 params.calc_normals = false;
3150 params.is_destructive = false;
3151 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
3152 }
3153
3154 return OPERATOR_FINISHED;
3155}
3156
3158{
3159 /* identifiers */
3160 ot->name = "Rotate UVs";
3161 ot->idname = "MESH_OT_uvs_rotate";
3162 ot->description = "Rotate UV coordinates inside faces";
3163
3164 /* api callbacks */
3167
3168 /* flags */
3170
3171 /* props */
3172 RNA_def_boolean(ot->srna, "use_ccw", false, "Counter Clockwise", "");
3173}
3174
3176{
3177 /* identifiers */
3178 ot->name = "Reverse UVs";
3179 ot->idname = "MESH_OT_uvs_reverse";
3180 ot->description = "Flip direction of UV coordinates inside faces";
3181
3182 /* api callbacks */
3185
3186 /* flags */
3188
3189 /* props */
3190 // RNA_def_enum(ot->srna, "axis", axis_items, DIRECTION_CW, "Axis", "Axis to mirror UVs around");
3191}
3192
3194{
3195 /* identifiers */
3196 ot->name = "Rotate Colors";
3197 ot->idname = "MESH_OT_colors_rotate";
3198 ot->description = "Rotate face corner color attribute inside faces";
3199
3200 /* api callbacks */
3203
3204 /* flags */
3206
3207 /* props */
3208 RNA_def_boolean(ot->srna, "use_ccw", false, "Counter Clockwise", "");
3209}
3210
3212{
3213 /* identifiers */
3214 ot->name = "Reverse Colors";
3215 ot->idname = "MESH_OT_colors_reverse";
3216 ot->description = "Flip direction of face corner color attribute inside faces";
3217
3218 /* api callbacks */
3221
3222 /* flags */
3224
3225 /* props */
3226#if 0
3227 RNA_def_enum(ot->srna, "axis", axis_items, DIRECTION_CW, "Axis", "Axis to mirror colors around");
3228#endif
3229}
3230
3233/* -------------------------------------------------------------------- */
3237enum {
3243};
3244
3246 const bool use_first,
3247 const bool use_uvmerge,
3248 wmOperator *wmop)
3249{
3250 BMVert *mergevert;
3251 BMEditSelection *ese;
3252
3253 /* operator could be called directly from shortcut or python,
3254 * so do extra check for data here
3255 */
3256
3257 /* While #merge_type_itemf does a sanity check, this operation runs on all edit-mode objects.
3258 * Some of them may not have the expected selection state. */
3259 if (use_first == false) {
3260 if (!em->bm->selected.last || ((BMEditSelection *)em->bm->selected.last)->htype != BM_VERT) {
3261 return false;
3262 }
3263
3264 ese = static_cast<BMEditSelection *>(em->bm->selected.last);
3265 mergevert = (BMVert *)ese->ele;
3266 }
3267 else {
3268 if (!em->bm->selected.first || ((BMEditSelection *)em->bm->selected.first)->htype != BM_VERT) {
3269 return false;
3270 }
3271
3272 ese = static_cast<BMEditSelection *>(em->bm->selected.first);
3273 mergevert = (BMVert *)ese->ele;
3274 }
3275
3276 if (!BM_elem_flag_test(mergevert, BM_ELEM_SELECT)) {
3277 return false;
3278 }
3279
3280 if (use_uvmerge) {
3281 if (!EDBM_op_callf(
3282 em, wmop, "pointmerge_facedata verts=%hv vert_snap=%e", BM_ELEM_SELECT, mergevert))
3283 {
3284 return false;
3285 }
3286 }
3287
3288 if (!EDBM_op_callf(em, wmop, "pointmerge verts=%hv merge_co=%v", BM_ELEM_SELECT, mergevert->co))
3289 {
3290 return false;
3291 }
3292
3293 return true;
3294}
3295
3296static bool merge_target(BMEditMesh *em,
3297 Scene *scene,
3298 Object *ob,
3299 const bool use_cursor,
3300 const bool use_uvmerge,
3301 wmOperator *wmop)
3302{
3303 BMIter iter;
3304 BMVert *v;
3305 float co[3], cent[3] = {0.0f, 0.0f, 0.0f};
3306 const float *vco = nullptr;
3307
3308 if (use_cursor) {
3309 vco = scene->cursor.location;
3310 copy_v3_v3(co, vco);
3311 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
3312 mul_m4_v3(ob->world_to_object().ptr(), co);
3313 }
3314 else {
3315 float fac;
3316 int i = 0;
3317 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
3319 continue;
3320 }
3321 add_v3_v3(cent, v->co);
3322 i++;
3323 }
3324
3325 if (!i) {
3326 return false;
3327 }
3328
3329 fac = 1.0f / float(i);
3330 mul_v3_fl(cent, fac);
3331 copy_v3_v3(co, cent);
3332 vco = co;
3333 }
3334
3335 if (!vco) {
3336 return false;
3337 }
3338
3339 if (use_uvmerge) {
3340 if (!EDBM_op_callf(em, wmop, "average_vert_facedata verts=%hv", BM_ELEM_SELECT)) {
3341 return false;
3342 }
3343 }
3344
3345 if (!EDBM_op_callf(em, wmop, "pointmerge verts=%hv merge_co=%v", BM_ELEM_SELECT, co)) {
3346 return false;
3347 }
3348
3349 return true;
3350}
3351
3353{
3354 Scene *scene = CTX_data_scene(C);
3355 ViewLayer *view_layer = CTX_data_view_layer(C);
3357 scene, view_layer, CTX_wm_view3d(C));
3358 const int type = RNA_enum_get(op->ptr, "type");
3359 const bool uvs = RNA_boolean_get(op->ptr, "uvs");
3360
3361 for (Object *obedit : objects) {
3363
3364 if (em->bm->totvertsel == 0) {
3365 continue;
3366 }
3367
3369
3370 bool ok = false;
3371 switch (type) {
3372 case MESH_MERGE_CENTER:
3373 ok = merge_target(em, scene, obedit, false, uvs, op);
3374 break;
3375 case MESH_MERGE_CURSOR:
3376 ok = merge_target(em, scene, obedit, true, uvs, op);
3377 break;
3378 case MESH_MERGE_LAST:
3379 ok = merge_firstlast(em, false, uvs, op);
3380 break;
3381 case MESH_MERGE_FIRST:
3382 ok = merge_firstlast(em, true, uvs, op);
3383 break;
3385 ok = EDBM_op_callf(em, op, "collapse edges=%he uvs=%b", BM_ELEM_SELECT, uvs);
3386 break;
3387 default:
3388 BLI_assert(0);
3389 break;
3390 }
3391
3392 if (!ok) {
3393 continue;
3394 }
3395
3397
3399 params.calc_looptris = true;
3400 params.calc_normals = false;
3401 params.is_destructive = true;
3402 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
3403
3404 /* once collapsed, we can't have edge/face selection */
3405 if ((em->selectmode & SCE_SELECT_VERTEX) == 0) {
3407 }
3408 /* Only active object supported, see comment below. */
3410 break;
3411 }
3412 }
3413
3414 return OPERATOR_FINISHED;
3415}
3416
3418 {MESH_MERGE_CENTER, "CENTER", 0, "At Center", ""},
3419 {MESH_MERGE_CURSOR, "CURSOR", 0, "At Cursor", ""},
3420 {MESH_MERGE_COLLAPSE, "COLLAPSE", 0, "Collapse", ""},
3421 {MESH_MERGE_FIRST, "FIRST", 0, "At First", ""},
3422 {MESH_MERGE_LAST, "LAST", 0, "At Last", ""},
3423 {0, nullptr, 0, nullptr, nullptr},
3424};
3425
3427 PointerRNA * /*ptr*/,
3428 PropertyRNA * /*prop*/,
3429 bool *r_free)
3430{
3431 if (!C) { /* needed for docs */
3432 return merge_type_items;
3433 }
3434
3435 Object *obedit = CTX_data_edit_object(C);
3436 if (obedit && obedit->type == OB_MESH) {
3437 EnumPropertyItem *item = nullptr;
3438 int totitem = 0;
3440
3441 /* Keep these first so that their automatic shortcuts don't change. */
3445
3446 /* Only active object supported:
3447 * In practice it doesn't make sense to run this operation on non-active meshes
3448 * since selecting will activate - we could have a separate code-path for these but it's a
3449 * hassle for now just apply to the active (first) object. */
3450 if (em->selectmode & SCE_SELECT_VERTEX) {
3451 if (em->bm->selected.first && em->bm->selected.last &&
3452 ((BMEditSelection *)em->bm->selected.first)->htype == BM_VERT &&
3453 ((BMEditSelection *)em->bm->selected.last)->htype == BM_VERT)
3454 {
3457 }
3458 else if (em->bm->selected.first &&
3459 ((BMEditSelection *)em->bm->selected.first)->htype == BM_VERT)
3460 {
3462 }
3463 else if (em->bm->selected.last &&
3464 ((BMEditSelection *)em->bm->selected.last)->htype == BM_VERT)
3465 {
3467 }
3468 }
3469
3470 RNA_enum_item_end(&item, &totitem);
3471
3472 *r_free = true;
3473
3474 return item;
3475 }
3476
3477 /* Get all items e.g. when creating keymap item. */
3478 return merge_type_items;
3479}
3480
3482{
3483 /* identifiers */
3484 ot->name = "Merge";
3485 ot->description = "Merge selected vertices";
3486 ot->idname = "MESH_OT_merge";
3487
3488 /* api callbacks */
3492
3493 /* flags */
3495
3496 /* properties */
3497 ot->prop = RNA_def_enum(
3498 ot->srna, "type", merge_type_items, MESH_MERGE_CENTER, "Type", "Merge method to use");
3500
3502
3503 RNA_def_boolean(ot->srna, "uvs", false, "UVs", "Move UVs according to merge");
3504}
3505
3508/* -------------------------------------------------------------------- */
3513{
3514 const float threshold = RNA_float_get(op->ptr, "threshold");
3515 const bool use_unselected = RNA_boolean_get(op->ptr, "use_unselected");
3516 const bool use_sharp_edge_from_normals = RNA_boolean_get(op->ptr, "use_sharp_edge_from_normals");
3517
3518 int count_multi = 0;
3519
3520 const Scene *scene = CTX_data_scene(C);
3521 ViewLayer *view_layer = CTX_data_view_layer(C);
3523 scene, view_layer, CTX_wm_view3d(C));
3524
3525 for (Object *obedit : objects) {
3527
3528 /* Selection used as target with 'use_unselected'. */
3529 if (em->bm->totvertsel == 0) {
3530 continue;
3531 }
3532
3533 BMOperator bmop;
3534 const int totvert_orig = em->bm->totvert;
3535
3536 /* avoid losing selection state (select -> tags) */
3537 char htype_select;
3538 if (em->selectmode & SCE_SELECT_VERTEX) {
3539 htype_select = BM_VERT;
3540 }
3541 else if (em->selectmode & SCE_SELECT_EDGE) {
3542 htype_select = BM_EDGE;
3543 }
3544 else {
3545 htype_select = BM_FACE;
3546 }
3547
3549
3550 /* store selection as tags */
3551 BM_mesh_elem_hflag_enable_test(em->bm, htype_select, BM_ELEM_TAG, true, true, BM_ELEM_SELECT);
3552
3553 if (use_unselected) {
3554 EDBM_automerge(obedit, false, BM_ELEM_SELECT, threshold);
3555 }
3556 else {
3557 EDBM_op_init(em, &bmop, op, "find_doubles verts=%hv dist=%f", BM_ELEM_SELECT, threshold);
3558
3559 BMO_op_exec(em->bm, &bmop);
3560
3561 if (!EDBM_op_callf(em, op, "weld_verts targetmap=%S", &bmop, "targetmap.out")) {
3562 BMO_op_finish(em->bm, &bmop);
3563 continue;
3564 }
3565
3566 if (!EDBM_op_finish(em, &bmop, op, true)) {
3567 continue;
3568 }
3569 }
3570
3571 const int count = (totvert_orig - em->bm->totvert);
3572
3573 /* restore selection from tags */
3574 BM_mesh_elem_hflag_enable_test(em->bm, htype_select, BM_ELEM_SELECT, true, true, BM_ELEM_TAG);
3576
3577 BM_custom_loop_normals_from_vector_layer(em->bm, use_sharp_edge_from_normals);
3578
3579 if (count) {
3580 count_multi += count;
3582 params.calc_looptris = true;
3583 params.calc_normals = false;
3584 params.is_destructive = true;
3585 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
3586 }
3587 }
3588
3589 BKE_reportf(op->reports,
3590 RPT_INFO,
3591 count_multi == 1 ? "Removed %d vertex" : "Removed %d vertices",
3592 count_multi);
3593
3594 return OPERATOR_FINISHED;
3595}
3596
3598{
3599 /* identifiers */
3600 ot->name = "Merge by Distance";
3601 ot->description = "Merge vertices based on their proximity";
3602 ot->idname = "MESH_OT_remove_doubles";
3603
3604 /* api callbacks */
3607
3608 /* flags */
3610
3612 "threshold",
3613 1e-4f,
3614 1e-6f,
3615 50.0f,
3616 "Merge Distance",
3617 "Maximum distance between elements to merge",
3618 1e-5f,
3619 10.0f);
3621 "use_unselected",
3622 false,
3623 "Unselected",
3624 "Merge selected to other unselected vertices");
3625
3627 "use_sharp_edge_from_normals",
3628 false,
3629 "Sharp Edges",
3630 "Calculate sharp edges using custom normal data (when available)");
3631}
3632
3635/* -------------------------------------------------------------------- */
3639/* BMESH_TODO this should be properly encapsulated in a bmop. but later. */
3640static bool shape_propagate(BMEditMesh *em, bool use_symmetry)
3641{
3642 BMIter iter;
3643 BMVert *eve = nullptr;
3644 float *co;
3645 int totshape = CustomData_number_of_layers(&em->bm->vdata, CD_SHAPEKEY);
3646
3648 return false;
3649 }
3650
3651 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
3653 BMVert *mirr = use_symmetry ? EDBM_verts_mirror_get(em, eve) : nullptr;
3654
3655 if (!mirr || !BM_elem_flag_test(mirr, BM_ELEM_SELECT) ||
3657 {
3658 continue;
3659 }
3660 }
3661
3662 for (int i = 0; i < totshape; i++) {
3663 co = static_cast<float *>(
3665 copy_v3_v3(co, eve->co);
3666 }
3667 }
3668 return true;
3669}
3670
3672{
3673 const Scene *scene = CTX_data_scene(C);
3674 ViewLayer *view_layer = CTX_data_view_layer(C);
3675 int tot_shapekeys = 0;
3676 int tot_selected_verts_objects = 0;
3677 int tot_locked = 0;
3678
3680 scene, view_layer, CTX_wm_view3d(C));
3681 for (Object *obedit : objects) {
3682 Mesh *mesh = static_cast<Mesh *>(obedit->data);
3683 BMEditMesh *em = mesh->runtime->edit_mesh.get();
3684
3685 if (em->bm->totvertsel == 0) {
3686 continue;
3687 }
3688
3689 /* Check for locked shape keys. */
3691 tot_locked++;
3692 continue;
3693 }
3694
3695 tot_selected_verts_objects++;
3696
3697 const bool use_symmetry = (mesh->symmetry & ME_SYMMETRY_X) != 0;
3698
3699 if (use_symmetry) {
3700 const bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
3701
3702 EDBM_verts_mirror_cache_begin(em, 0, false, false, false, use_topology);
3703 }
3704
3705 if (shape_propagate(em, use_symmetry)) {
3706 tot_shapekeys++;
3707 }
3708
3709 if (use_symmetry) {
3711 }
3712
3714 params.calc_looptris = false;
3715 params.calc_normals = false;
3716 params.is_destructive = false;
3717 EDBM_update(mesh, &params);
3718 }
3719
3720 if (tot_selected_verts_objects == 0) {
3721 if (!tot_locked) {
3722 BKE_report(op->reports, RPT_ERROR, "No selected vertex");
3723 }
3724 return OPERATOR_CANCELLED;
3725 }
3726 if (tot_shapekeys == 0) {
3727 BKE_report(op->reports, RPT_ERROR, "Mesh(es) do not have shape keys");
3728 return OPERATOR_CANCELLED;
3729 }
3730
3731 return OPERATOR_FINISHED;
3732}
3733
3735{
3736 /* identifiers */
3737 ot->name = "Shape Propagate";
3738 ot->description = "Apply selected vertex locations to all other shape keys";
3739 ot->idname = "MESH_OT_shape_propagate_to_all";
3740
3741 /* api callbacks */
3744
3745 /* flags */
3747}
3748
3751/* -------------------------------------------------------------------- */
3755/* BMESH_TODO this should be properly encapsulated in a bmop. but later. */
3757{
3758 Object *obedit_ref = CTX_data_edit_object(C);
3759 Mesh *me_ref = static_cast<Mesh *>(obedit_ref->data);
3760 Key *key_ref = me_ref->key;
3761 KeyBlock *kb_ref = nullptr;
3762 BMEditMesh *em_ref = me_ref->runtime->edit_mesh.get();
3763 BMVert *eve;
3764 BMIter iter;
3765 const Scene *scene = CTX_data_scene(C);
3766 ViewLayer *view_layer = CTX_data_view_layer(C);
3767 float co[3], *sco;
3768 int totshape_ref = 0;
3769
3770 const float blend = RNA_float_get(op->ptr, "blend");
3771 int shape_ref = RNA_enum_get(op->ptr, "shape");
3772 const bool use_add = RNA_boolean_get(op->ptr, "add");
3773
3774 /* Sanity check. */
3775 totshape_ref = CustomData_number_of_layers(&em_ref->bm->vdata, CD_SHAPEKEY);
3776
3777 if (totshape_ref == 0 || shape_ref < 0) {
3778 BKE_report(op->reports, RPT_ERROR, "Active mesh does not have shape keys");
3779 return OPERATOR_CANCELLED;
3780 }
3781 if (shape_ref >= totshape_ref) {
3782 /* This case occurs if operator was used before on object with more keys than current one. */
3783 shape_ref = 0; /* default to basis */
3784 }
3785
3786 /* Get shape key - needed for finding reference shape (for add mode only). */
3787 if (key_ref) {
3788 kb_ref = static_cast<KeyBlock *>(BLI_findlink(&key_ref->block, shape_ref));
3789 }
3790
3791 int tot_selected_verts_objects = 0, tot_locked = 0;
3793 scene, view_layer, CTX_wm_view3d(C));
3794 for (Object *obedit : objects) {
3795 Mesh *mesh = static_cast<Mesh *>(obedit->data);
3796 Key *key = mesh->key;
3797 KeyBlock *kb = nullptr;
3798 BMEditMesh *em = mesh->runtime->edit_mesh.get();
3799 int shape;
3800
3801 if (em->bm->totvertsel == 0) {
3802 continue;
3803 }
3804
3806 tot_locked++;
3807 continue;
3808 }
3809
3810 tot_selected_verts_objects++;
3811
3812 if (!key) {
3813 continue;
3814 }
3815 kb = BKE_keyblock_find_name(key, kb_ref->name);
3816 shape = BLI_findindex(&key->block, kb);
3817
3818 if (kb) {
3819 const bool use_symmetry = (mesh->symmetry & ME_SYMMETRY_X) != 0;
3820
3821 if (use_symmetry) {
3822 const bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
3823
3824 EDBM_verts_mirror_cache_begin(em, 0, false, true, false, use_topology);
3825 }
3826
3827 /* Perform blending on selected vertices. */
3828 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
3830 continue;
3831 }
3832
3833 /* Get coordinates of shapekey we're blending from. */
3834 sco = static_cast<float *>(
3835 CustomData_bmesh_get_n(&em->bm->vdata, eve->head.data, CD_SHAPEKEY, shape));
3836 copy_v3_v3(co, sco);
3837
3838 if (use_add) {
3839 /* In add mode, we add relative shape key offset. */
3840 const float *rco = static_cast<const float *>(
3842 sub_v3_v3v3(co, co, rco);
3843
3844 madd_v3_v3fl(eve->co, co, blend);
3845 }
3846 else {
3847 /* In blend mode, we interpolate to the shape key. */
3848 interp_v3_v3v3(eve->co, eve->co, co, blend);
3849 }
3850 }
3851
3852 if (use_symmetry) {
3855 }
3856
3858 params.calc_looptris = true;
3859 params.calc_normals = true;
3860 params.is_destructive = false;
3861 EDBM_update(mesh, &params);
3862 }
3863 }
3864
3865 if (tot_selected_verts_objects == 0 && !tot_locked) {
3866 BKE_report(op->reports, RPT_ERROR, "No selected vertex");
3867 }
3868
3869 return tot_selected_verts_objects ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
3870}
3871
3873 PointerRNA * /*ptr*/,
3874 PropertyRNA * /*prop*/,
3875 bool *r_free)
3876{
3877 Object *obedit = CTX_data_edit_object(C);
3878 BMEditMesh *em;
3879 EnumPropertyItem *item = nullptr;
3880 int totitem = 0;
3881
3882 if ((obedit && obedit->type == OB_MESH) && (em = BKE_editmesh_from_object(obedit)) &&
3884 {
3885 EnumPropertyItem tmp = {0, "", 0, "", ""};
3886 int a;
3887
3888 for (a = 0; a < em->bm->vdata.totlayer; a++) {
3889 if (em->bm->vdata.layers[a].type != CD_SHAPEKEY) {
3890 continue;
3891 }
3892
3893 tmp.value = totitem;
3894 tmp.identifier = em->bm->vdata.layers[a].name;
3895 tmp.name = em->bm->vdata.layers[a].name;
3896 /* RNA_enum_item_add sets totitem itself! */
3897 RNA_enum_item_add(&item, &totitem, &tmp);
3898 }
3899 }
3900
3901 RNA_enum_item_end(&item, &totitem);
3902 *r_free = true;
3903
3904 return item;
3905}
3906
3908{
3909 uiLayout *layout = op->layout;
3910 Object *obedit = CTX_data_edit_object(C);
3911 Mesh *mesh = static_cast<Mesh *>(obedit->data);
3912
3913 PointerRNA ptr_key = RNA_id_pointer_create((ID *)mesh->key);
3914
3915 uiLayoutSetPropSep(layout, true);
3916 uiLayoutSetPropDecorate(layout, false);
3917
3918 uiItemPointerR(layout, op->ptr, "shape", &ptr_key, "key_blocks", nullptr, ICON_SHAPEKEY_DATA);
3919 uiItemR(layout, op->ptr, "blend", UI_ITEM_NONE, nullptr, ICON_NONE);
3920 uiItemR(layout, op->ptr, "add", UI_ITEM_NONE, nullptr, ICON_NONE);
3921}
3922
3924{
3925 PropertyRNA *prop;
3926
3927 /* identifiers */
3928 ot->name = "Blend from Shape";
3929 ot->description = "Blend in shape from a shape key";
3930 ot->idname = "MESH_OT_blend_from_shape";
3931
3932 /* api callbacks */
3934 /* disable because search popup closes too easily */
3935 // ot->invoke = WM_operator_props_popup_call;
3938
3939 /* flags */
3941
3942 /* properties */
3943 prop = RNA_def_enum(
3944 ot->srna, "shape", rna_enum_dummy_NULL_items, 0, "Shape", "Shape key to use for blending");
3947 RNA_def_float(ot->srna, "blend", 1.0f, -1e3f, 1e3f, "Blend", "Blending factor", -2.0f, 2.0f);
3948 RNA_def_boolean(ot->srna, "add", true, "Add", "Add rather than blend between shapes");
3949}
3950
3953/* -------------------------------------------------------------------- */
3958{
3959 const float thickness = RNA_float_get(op->ptr, "thickness");
3960
3961 const Scene *scene = CTX_data_scene(C);
3962 ViewLayer *view_layer = CTX_data_view_layer(C);
3964 scene, view_layer, CTX_wm_view3d(C));
3965 for (Object *obedit : objects) {
3967 BMesh *bm = em->bm;
3968
3969 if (em->bm->totfacesel == 0) {
3970 continue;
3971 }
3972
3973 BMOperator bmop;
3974
3975 if (!EDBM_op_init(em, &bmop, op, "solidify geom=%hf thickness=%f", BM_ELEM_SELECT, thickness))
3976 {
3977 continue;
3978 }
3979
3980 /* deselect only the faces in the region to be solidified (leave wire
3981 * edges and loose verts selected, as there will be no corresponding
3982 * geometry selected below) */
3984
3985 /* run the solidify operator */
3986 BMO_op_exec(bm, &bmop);
3987
3988 /* select the newly generated faces */
3990
3991 /* No need to flush the selection, any selection history is no longer valid. */
3993
3994 if (!EDBM_op_finish(em, &bmop, op, true)) {
3995 continue;
3996 }
3997
3999 params.calc_looptris = true;
4000 params.calc_normals = false;
4001 params.is_destructive = true;
4002 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
4003 }
4004
4005 return OPERATOR_FINISHED;
4006}
4007
4009{
4010 PropertyRNA *prop;
4011 /* identifiers */
4012 ot->name = "Solidify";
4013 ot->description = "Create a solid skin by extruding, compensating for sharp angles";
4014 ot->idname = "MESH_OT_solidify";
4015
4016 /* api callbacks */
4019
4020 /* flags */
4022
4024 ot->srna, "thickness", 0.01f, -1e4f, 1e4f, "Thickness", "", -10.0f, 10.0f);
4025 RNA_def_property_ui_range(prop, -10.0, 10.0, 0.1, 4);
4026}
4027
4030/* -------------------------------------------------------------------- */
4034enum {
4038};
4039
4042 Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
4043{
4044 Object *obedit = base_old->object;
4045 BMeshCreateParams create_params{};
4046 create_params.use_toolflags = true;
4047 BMesh *bm_new = BM_mesh_create(&bm_mesh_allocsize_default, &create_params);
4048 BM_mesh_elem_toolflags_ensure(bm_new); /* Needed for 'duplicate' BMO. */
4049
4051
4052 /* Take into account user preferences for duplicating actions. */
4053 const eDupli_ID_Flags dupflag = eDupli_ID_Flags(USER_DUP_MESH | (U.dupflag & USER_DUP_ACT));
4054 Base *base_new = blender::ed::object::add_duplicate(bmain, scene, view_layer, base_old, dupflag);
4055
4056 /* normally would call directly after but in this case delay recalc */
4057 // DAG_relations_tag_update(bmain);
4058
4059 /* new in 2.5 */
4061 base_new->object,
4064 false);
4065
4067
4068 BMO_op_callf(bm_old,
4070 "duplicate geom=%hvef dest=%p",
4072 bm_new);
4073 BMO_op_callf(bm_old,
4075 "delete geom=%hvef context=%i",
4077 DEL_FACES);
4078
4079 /* deselect loose data - this used to get deleted,
4080 * we could de-select edges and verts only, but this turns out to be less complicated
4081 * since de-selecting all skips selection flushing logic */
4083
4084 BM_mesh_normals_update(bm_new);
4085
4086 BMeshToMeshParams to_mesh_params{};
4087 BM_mesh_bm_to_me(bmain, bm_new, static_cast<Mesh *>(base_new->object->data), &to_mesh_params);
4088
4089 BM_mesh_free(bm_new);
4090 ((Mesh *)base_new->object->data)->runtime->edit_mesh = nullptr;
4091
4092 return base_new;
4093}
4094
4096 Scene *scene,
4097 ViewLayer *view_layer,
4098 Base *base_old,
4099 BMesh *bm_old,
4100 BMVert **verts,
4101 uint verts_len,
4102 BMEdge **edges,
4103 uint edges_len,
4104 BMFace **faces,
4105 uint faces_len)
4106{
4107 BMAllocTemplate bm_new_allocsize{};
4108 bm_new_allocsize.totvert = verts_len;
4109 bm_new_allocsize.totedge = edges_len;
4110 bm_new_allocsize.totloop = faces_len * 3;
4111 bm_new_allocsize.totface = faces_len;
4112
4113 const bool use_custom_normals = (bm_old->lnor_spacearr != nullptr);
4114
4115 Object *obedit = base_old->object;
4116
4117 BMeshCreateParams create_params{};
4118 BMesh *bm_new = BM_mesh_create(&bm_new_allocsize, &create_params);
4119
4120 if (use_custom_normals) {
4121 /* Needed so the temporary normal layer is copied too. */
4122 BM_mesh_copy_init_customdata_all_layers(bm_new, bm_old, BM_ALL, &bm_new_allocsize);
4123 }
4124 else {
4125 BM_mesh_copy_init_customdata(bm_new, bm_old, &bm_new_allocsize);
4126 }
4127
4128 /* Take into account user preferences for duplicating actions. */
4129 const eDupli_ID_Flags dupflag = eDupli_ID_Flags(USER_DUP_MESH | (U.dupflag & USER_DUP_ACT));
4130 Base *base_new = blender::ed::object::add_duplicate(bmain, scene, view_layer, base_old, dupflag);
4131
4132 /* normally would call directly after but in this case delay recalc */
4133 // DAG_relations_tag_update(bmain);
4134
4135 /* new in 2.5 */
4137 base_new->object,
4140 false);
4141
4143
4144 BM_mesh_copy_arrays(bm_old, bm_new, verts, verts_len, edges, edges_len, faces, faces_len);
4145
4146 if (use_custom_normals) {
4148 }
4149
4150 for (uint i = 0; i < verts_len; i++) {
4151 BM_vert_kill(bm_old, verts[i]);
4152 }
4153 BMeshToMeshParams to_mesh_params{};
4154 BM_mesh_bm_to_me(bmain, bm_new, static_cast<Mesh *>(base_new->object->data), &to_mesh_params);
4155
4156 BM_mesh_free(bm_new);
4157 ((Mesh *)base_new->object->data)->runtime->edit_mesh = nullptr;
4158
4159 return base_new;
4160}
4161
4163 Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
4164{
4165 /* we may have tags from previous operators */
4167
4168 /* sel -> tag */
4170 bm_old, BM_FACE | BM_EDGE | BM_VERT, BM_ELEM_TAG, true, false, BM_ELEM_SELECT);
4171
4172 return (mesh_separate_tagged(bmain, scene, view_layer, base_old, bm_old) != nullptr);
4173}
4174
4181static void mesh_separate_material_assign_mat_nr(Main *bmain, Object *ob, const short mat_nr)
4182{
4183 ID *obdata = static_cast<ID *>(ob->data);
4184
4185 const short *totcolp = BKE_id_material_len_p(obdata);
4186 Material ***matarar = BKE_id_material_array_p(obdata);
4187
4188 if ((totcolp && matarar) == 0) {
4189 BLI_assert(0);
4190 return;
4191 }
4192
4193 if (*totcolp) {
4194 Material *ma_ob;
4195 Material *ma_obdata;
4196 char matbit;
4197
4198 if (mat_nr < ob->totcol) {
4199 ma_ob = ob->mat[mat_nr];
4200 matbit = ob->matbits[mat_nr];
4201 }
4202 else {
4203 ma_ob = nullptr;
4204 matbit = 0;
4205 }
4206
4207 if (mat_nr < *totcolp) {
4208 ma_obdata = (*matarar)[mat_nr];
4209 }
4210 else {
4211 ma_obdata = nullptr;
4212 }
4213
4214 BKE_id_material_clear(bmain, obdata);
4215 BKE_object_material_resize(bmain, ob, 1, true);
4216 BKE_id_material_resize(bmain, obdata, 1, true);
4217
4218 ob->mat[0] = ma_ob;
4219 id_us_plus((ID *)ma_ob);
4220 ob->matbits[0] = matbit;
4221 (*matarar)[0] = ma_obdata;
4222 id_us_plus((ID *)ma_obdata);
4223 }
4224 else {
4225 BKE_id_material_clear(bmain, obdata);
4226 BKE_object_material_resize(bmain, ob, 0, true);
4227 BKE_id_material_resize(bmain, obdata, 0, true);
4228 }
4229}
4230
4232 Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
4233{
4234 BMFace *f_cmp, *f;
4235 BMIter iter;
4236 bool result = false;
4237
4238 while ((f_cmp = static_cast<BMFace *>(BM_iter_at_index(bm_old, BM_FACES_OF_MESH, nullptr, 0)))) {
4239 Base *base_new;
4240 const short mat_nr = f_cmp->mat_nr;
4241 int tot = 0;
4242
4244
4245 BM_ITER_MESH (f, &iter, bm_old, BM_FACES_OF_MESH) {
4246 if (f->mat_nr == mat_nr) {
4247 BMLoop *l_iter;
4248 BMLoop *l_first;
4249
4251 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
4252 do {
4255 } while ((l_iter = l_iter->next) != l_first);
4256
4257 tot++;
4258 }
4259 }
4260
4261 /* leave the current object with some materials */
4262 if (tot == bm_old->totface) {
4263 mesh_separate_material_assign_mat_nr(bmain, base_old->object, mat_nr);
4264
4265 /* since we're in editmode, must set faces here */
4266 BM_ITER_MESH (f, &iter, bm_old, BM_FACES_OF_MESH) {
4267 f->mat_nr = 0;
4268 }
4269 break;
4270 }
4271
4272 /* Move selection into a separate object */
4273 base_new = mesh_separate_tagged(bmain, scene, view_layer, base_old, bm_old);
4274 if (base_new) {
4275 mesh_separate_material_assign_mat_nr(bmain, base_new->object, mat_nr);
4276 }
4277
4278 result |= (base_new != nullptr);
4279 }
4280
4281 return result;
4282}
4283
4285 Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
4286{
4287 /* Without this, we duplicate the object mode mesh for each loose part.
4288 * This can get very slow especially for large meshes with many parts
4289 * which would duplicate the mesh on entering edit-mode. */
4290 const bool clear_object_data = true;
4291
4292 bool result = false;
4293
4294 blender::Array<BMVert *> vert_groups(bm_old->totvert);
4295 blender::Array<BMEdge *> edge_groups(bm_old->totedge);
4296 blender::Array<BMFace *> face_groups(bm_old->totface);
4297
4298 int(*groups)[3] = nullptr;
4299 int groups_len = BM_mesh_calc_edge_groups_as_arrays(
4300 bm_old, vert_groups.data(), edge_groups.data(), face_groups.data(), &groups);
4301 if (groups_len <= 1) {
4302 MEM_SAFE_FREE(groups);
4303 return false;
4304 }
4305
4306 if (clear_object_data) {
4307 ED_mesh_geometry_clear(static_cast<Mesh *>(base_old->object->data));
4308 }
4309
4311
4312 /* Separate out all groups except the first. */
4313 uint group_ofs[3] = {uint(groups[0][0]), uint(groups[0][1]), uint(groups[0][2])};
4314 for (int i = 1; i < groups_len; i++) {
4315 Base *base_new = mesh_separate_arrays(bmain,
4316 scene,
4317 view_layer,
4318 base_old,
4319 bm_old,
4320 vert_groups.data() + group_ofs[0],
4321 groups[i][0],
4322 edge_groups.data() + group_ofs[1],
4323 groups[i][1],
4324 face_groups.data() + group_ofs[2],
4325 groups[i][2]);
4326 result |= (base_new != nullptr);
4327
4328 group_ofs[0] += groups[i][0];
4329 group_ofs[1] += groups[i][1];
4330 group_ofs[2] += groups[i][2];
4331 }
4332
4333 Mesh *me_old = static_cast<Mesh *>(base_old->object->data);
4335
4336 if (clear_object_data) {
4337 BMeshToMeshParams to_mesh_params{};
4338 to_mesh_params.update_shapekey_indices = true;
4339 BM_mesh_bm_to_me(nullptr, bm_old, me_old, &to_mesh_params);
4340 }
4341
4342 MEM_freeN(groups);
4343 return result;
4344}
4345
4347{
4348 Main *bmain = CTX_data_main(C);
4349 Scene *scene = CTX_data_scene(C);
4350 ViewLayer *view_layer = CTX_data_view_layer(C);
4351 const int type = RNA_enum_get(op->ptr, "type");
4352 bool changed_multi = false;
4353
4354 if (ED_operator_editmesh(C)) {
4355 uint empty_selection_len = 0;
4357 scene, view_layer, CTX_wm_view3d(C));
4358 for (const int base_index : bases.index_range()) {
4359 Base *base = bases[base_index];
4361
4362 if (type == 0) {
4363 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
4364 /* when all objects has no selection */
4365 if (++empty_selection_len == bases.size()) {
4366 BKE_report(op->reports, RPT_ERROR, "Nothing selected");
4367 }
4368 continue;
4369 }
4370 }
4371
4372 /* editmode separate */
4373 bool changed = false;
4374 switch (type) {
4376 changed = mesh_separate_selected(bmain, scene, view_layer, base, em->bm);
4377 break;
4379 changed = mesh_separate_material(bmain, scene, view_layer, base, em->bm);
4380 break;
4382 changed = mesh_separate_loose(bmain, scene, view_layer, base, em->bm);
4383 break;
4384 default:
4385 BLI_assert(0);
4386 break;
4387 }
4388
4389 if (changed) {
4391 params.calc_looptris = true;
4392 params.calc_normals = false;
4393 params.is_destructive = true;
4394 EDBM_update(static_cast<Mesh *>(base->object->data), &params);
4395 }
4396 changed_multi |= changed;
4397 }
4398 }
4399 else {
4400 if (type == MESH_SEPARATE_SELECTED) {
4401 BKE_report(op->reports, RPT_ERROR, "Selection not supported in object mode");
4402 return OPERATOR_CANCELLED;
4403 }
4404
4405 /* object mode separate */
4406 CTX_DATA_BEGIN (C, Base *, base_iter, selected_editable_bases) {
4407 Object *ob = base_iter->object;
4408 if (ob->type != OB_MESH) {
4409 continue;
4410 }
4411 Mesh *mesh = static_cast<Mesh *>(ob->data);
4412 if (!BKE_id_is_editable(bmain, &mesh->id)) {
4413 continue;
4414 }
4415
4416 BMeshCreateParams create_params{};
4417 create_params.use_toolflags = true;
4418 BMesh *bm_old = BM_mesh_create(&bm_mesh_allocsize_default, &create_params);
4419
4420 BMeshFromMeshParams from_mesh_params{};
4421 BM_mesh_bm_from_me(bm_old, mesh, &from_mesh_params);
4422
4423 bool changed = false;
4424 switch (type) {
4426 changed = mesh_separate_material(bmain, scene, view_layer, base_iter, bm_old);
4427 break;
4429 changed = mesh_separate_loose(bmain, scene, view_layer, base_iter, bm_old);
4430 break;
4431 default:
4432 BLI_assert(0);
4433 break;
4434 }
4435
4436 if (changed) {
4437 BMeshToMeshParams to_mesh_params{};
4438 to_mesh_params.calc_object_remap = true;
4439 BM_mesh_bm_to_me(bmain, bm_old, mesh, &to_mesh_params);
4440
4443 }
4444
4445 BM_mesh_free(bm_old);
4446
4447 changed_multi |= changed;
4448 }
4450 }
4451
4452 if (changed_multi) {
4453 /* delay depsgraph recalc until all objects are duplicated */
4457
4458 return OPERATOR_FINISHED;
4459 }
4460
4461 return OPERATOR_CANCELLED;
4462}
4463
4465{
4466 static const EnumPropertyItem prop_separate_types[] = {
4467 {MESH_SEPARATE_SELECTED, "SELECTED", 0, "Selection", ""},
4468 {MESH_SEPARATE_MATERIAL, "MATERIAL", 0, "By Material", ""},
4469 {MESH_SEPARATE_LOOSE, "LOOSE", 0, "By Loose Parts", ""},
4470 {0, nullptr, 0, nullptr, nullptr},
4471 };
4472
4473 /* identifiers */
4474 ot->name = "Separate";
4475 ot->description = "Separate selected geometry into a new mesh";
4476 ot->idname = "MESH_OT_separate";
4477
4478 /* api callbacks */
4481 ot->poll = ED_operator_scene_editable; /* object and editmode */
4482
4483 /* flags */
4484 ot->flag = OPTYPE_UNDO;
4485
4486 ot->prop = RNA_def_enum(
4487 ot->srna, "type", prop_separate_types, MESH_SEPARATE_SELECTED, "Type", "");
4488}
4489
4492/* -------------------------------------------------------------------- */
4497{
4498 const bool use_beauty = RNA_boolean_get(op->ptr, "use_beauty");
4499
4500 bool has_selected_edges = false, has_faces_filled = false;
4501
4502 const Scene *scene = CTX_data_scene(C);
4503 ViewLayer *view_layer = CTX_data_view_layer(C);
4505 scene, view_layer, CTX_wm_view3d(C));
4506 for (Object *obedit : objects) {
4508
4509 const int totface_orig = em->bm->totface;
4510
4511 if (em->bm->totedgesel == 0) {
4512 continue;
4513 }
4514 has_selected_edges = true;
4515
4516 BMOperator bmop;
4517 if (!EDBM_op_init(
4518 em, &bmop, op, "triangle_fill edges=%he use_beauty=%b", BM_ELEM_SELECT, use_beauty))
4519 {
4520 continue;
4521 }
4522
4523 BMO_op_exec(em->bm, &bmop);
4524
4525 /* cancel if nothing was done */
4526 if (totface_orig == em->bm->totface) {
4527 EDBM_op_finish(em, &bmop, op, true);
4528 continue;
4529 }
4530 has_faces_filled = true;
4531
4532 /* select new geometry */
4534 em->bm, bmop.slots_out, "geom.out", BM_FACE | BM_EDGE, BM_ELEM_SELECT, true);
4535
4536 if (!EDBM_op_finish(em, &bmop, op, true)) {
4537 continue;
4538 }
4539
4541 params.calc_looptris = true;
4542 params.calc_normals = false;
4543 params.is_destructive = true;
4544 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
4545 }
4546
4547 if (!has_selected_edges) {
4548 BKE_report(op->reports, RPT_ERROR, "No edges selected");
4549 return OPERATOR_CANCELLED;
4550 }
4551
4552 if (!has_faces_filled) {
4553 BKE_report(op->reports, RPT_WARNING, "No faces filled");
4554 return OPERATOR_CANCELLED;
4555 }
4556
4557 return OPERATOR_FINISHED;
4558}
4559
4561{
4562 /* identifiers */
4563 ot->name = "Fill";
4564 ot->idname = "MESH_OT_fill";
4565 ot->description = "Fill a selected edge loop with faces";
4566
4567 /* api callbacks */
4570
4571 /* flags */
4573
4574 RNA_def_boolean(ot->srna, "use_beauty", true, "Beauty", "Use best triangulation division");
4575}
4576
4579/* -------------------------------------------------------------------- */
4583static bool bm_edge_test_fill_grid_cb(BMEdge *e, void * /*bm_v*/)
4584{
4586}
4587
4589{
4590 BMIter iter;
4591 BMEdge *e_iter;
4592 BMVert *v_pair[2];
4593 int i = 0;
4594 BM_ITER_ELEM (e_iter, &iter, v, BM_EDGES_OF_VERT) {
4595 if (BM_elem_flag_test(e_iter, BM_ELEM_TAG)) {
4596 v_pair[i++] = BM_edge_other_vert(e_iter, v);
4597 }
4598 }
4599 BLI_assert(i == 2);
4600
4601 return fabsf(float(M_PI) - angle_v3v3v3(v_pair[0]->co, v->co, v_pair[1]->co));
4602}
4603
4607static bool edbm_fill_grid_prepare(BMesh *bm, int offset, int *span_p, const bool span_calc)
4608{
4609 /* angle differences below this value are considered 'even'
4610 * in that they shouldn't be used to calculate corners used for the 'span' */
4611 const float eps_even = 1e-3f;
4612 BMEdge *e;
4613 BMIter iter;
4614 int count;
4615 int span = *span_p;
4616
4617 ListBase eloops = {nullptr};
4618 BMEdgeLoopStore *el_store;
4619 // LinkData *el_store;
4620
4622 el_store = static_cast<BMEdgeLoopStore *>(eloops.first);
4623
4624 if (count != 1) {
4625 /* Let the operator use the selection flags,
4626 * most likely failing with an error in this case. */
4627 BM_mesh_edgeloops_free(&eloops);
4628 return false;
4629 }
4630
4631 /* Only tag edges that are part of a loop. */
4632 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
4634 }
4635 const int verts_len = BM_edgeloop_length_get(el_store);
4636 const int edges_len = verts_len - (BM_edgeloop_is_closed(el_store) ? 0 : 1);
4637 BMEdge **edges = static_cast<BMEdge **>(MEM_mallocN(sizeof(*edges) * edges_len, __func__));
4638 BM_edgeloop_edges_get(el_store, edges);
4639 for (int i = 0; i < edges_len; i++) {
4641 }
4642
4643 if (span_calc) {
4644 span = verts_len / 4;
4645 }
4646 else {
4647 span = min_ii(span, (verts_len / 2) - 1);
4648 }
4649 offset = mod_i(offset, verts_len);
4650
4651 if ((count == 1) && ((verts_len & 1) == 0) && (verts_len == edges_len)) {
4652
4653 /* be clever! detect 2 edge loops from one closed edge loop */
4656 LinkData *v_act_link;
4657 int i;
4658
4659 if (v_act && (v_act_link = static_cast<LinkData *>(
4660 BLI_findptr(verts, v_act, offsetof(LinkData, data)))))
4661 {
4662 /* pass */
4663 }
4664 else {
4665 /* find the vertex with the best angle (a corner vertex) */
4666 LinkData *v_link_best = nullptr;
4667 float angle_best = -1.0f;
4668 LISTBASE_FOREACH (LinkData *, v_link, verts) {
4669 const float angle = edbm_fill_grid_vert_tag_angle(static_cast<BMVert *>(v_link->data));
4670 if ((angle > angle_best) || (v_link_best == nullptr)) {
4671 angle_best = angle;
4672 v_link_best = v_link;
4673 }
4674 }
4675
4676 v_act_link = v_link_best;
4677 v_act = static_cast<BMVert *>(v_act_link->data);
4678 }
4679
4680 /* set this vertex first */
4681 BLI_listbase_rotate_first(verts, v_act_link);
4682
4683 if (offset != 0) {
4684 v_act_link = static_cast<LinkData *>(BLI_findlink(verts, offset));
4685 v_act = static_cast<BMVert *>(v_act_link->data);
4686 BLI_listbase_rotate_first(verts, v_act_link);
4687 }
4688
4689 /* Run again to update the edge order from the rotated vertex list. */
4690 BM_edgeloop_edges_get(el_store, edges);
4691
4692 if (span_calc) {
4693 /* calculate the span by finding the next corner in 'verts'
4694 * we don't know what defines a corner exactly so find the 4 verts
4695 * in the loop with the greatest angle.
4696 * Tag them and use the first tagged vertex to calculate the span.
4697 *
4698 * NOTE: we may have already checked 'edbm_fill_grid_vert_tag_angle()' on each
4699 * vert, but advantage of de-duplicating is minimal. */
4700 SortPtrByFloat *ele_sort = static_cast<SortPtrByFloat *>(
4701 MEM_mallocN(sizeof(*ele_sort) * verts_len, __func__));
4702 LinkData *v_link;
4703 for (v_link = static_cast<LinkData *>(verts->first), i = 0; v_link;
4704 v_link = v_link->next, i++)
4705 {
4706 BMVert *v = static_cast<BMVert *>(v_link->data);
4707 const float angle = edbm_fill_grid_vert_tag_angle(v);
4708 ele_sort[i].sort_value = angle;
4709 ele_sort[i].data = v;
4710
4712 }
4713
4714 qsort(ele_sort, verts_len, sizeof(*ele_sort), BLI_sortutil_cmp_float_reverse);
4715
4716 /* check that we have at least 3 corners,
4717 * if the angle on the 3rd angle is roughly the same as the last,
4718 * then we can't calculate 3+ corners - fallback to the even span. */
4719 if ((ele_sort[2].sort_value - ele_sort[verts_len - 1].sort_value) > eps_even) {
4720 for (i = 0; i < 4; i++) {
4721 BMVert *v = static_cast<BMVert *>(ele_sort[i].data);
4723 }
4724
4725 /* now find the first... */
4726 for (v_link = static_cast<LinkData *>(verts->first), i = 0; i < verts_len / 2;
4727 v_link = v_link->next, i++)
4728 {
4729 BMVert *v = static_cast<BMVert *>(v_link->data);
4731 if (v != v_act) {
4732 span = i;
4733 break;
4734 }
4735 }
4736 }
4737 }
4738 MEM_freeN(ele_sort);
4739 }
4740 /* end span calc */
4741
4742 /* un-flag 'rails' */
4743 for (i = 0; i < span; i++) {
4745 BM_elem_flag_disable(edges[(verts_len / 2) + i], BM_ELEM_TAG);
4746 }
4747 }
4748 /* else let the bmesh-operator handle it */
4749
4750 BM_mesh_edgeloops_free(&eloops);
4751 MEM_freeN(edges);
4752
4753 *span_p = span;
4754
4755 return true;
4756}
4757
4759{
4760 const bool use_interp_simple = RNA_boolean_get(op->ptr, "use_interp_simple");
4761
4762 const Scene *scene = CTX_data_scene(C);
4763 ViewLayer *view_layer = CTX_data_view_layer(C);
4765 scene, view_layer, CTX_wm_view3d(C));
4766 for (uint ob_index = 0; ob_index < objects.size(); ob_index++) {
4767
4768 Object *obedit = objects[ob_index];
4770 if (em->bm->totedgesel == 0) {
4771 continue;
4772 }
4773
4774 bool use_prepare = true;
4775 const bool use_smooth = edbm_add_edge_face__smooth_get(em->bm);
4776 const int totedge_orig = em->bm->totedge;
4777 const int totface_orig = em->bm->totface;
4778
4779 if (use_prepare) {
4780 /* use when we have a single loop selected */
4781 PropertyRNA *prop_span = RNA_struct_find_property(op->ptr, "span");
4782 PropertyRNA *prop_offset = RNA_struct_find_property(op->ptr, "offset");
4783 bool calc_span;
4784
4785 int span;
4786 int offset;
4787
4788 /* Only reuse on redo because these settings need to match the current selection.
4789 * We never want to use them on other geometry, repeat last for eg, see: #60777. */
4790 if (((op->flag & OP_IS_INVOKE) || (op->flag & OP_IS_REPEAT_LAST) == 0) &&
4791 RNA_property_is_set(op->ptr, prop_span))
4792 {
4793 span = RNA_property_int_get(op->ptr, prop_span);
4794 calc_span = false;
4795 }
4796 else {
4797 /* Will be overwritten if possible. */
4798 span = 0;
4799 calc_span = true;
4800 }
4801
4802 offset = RNA_property_int_get(op->ptr, prop_offset);
4803
4804 /* in simple cases, move selection for tags, but also support more advanced cases */
4805 use_prepare = edbm_fill_grid_prepare(em->bm, offset, &span, calc_span);
4806
4807 RNA_property_int_set(op->ptr, prop_span, span);
4808 }
4809 /* end tricky prepare code */
4810
4811 BMOperator bmop;
4812 if (!EDBM_op_init(em,
4813 &bmop,
4814 op,
4815 "grid_fill edges=%he mat_nr=%i use_smooth=%b use_interp_simple=%b",
4816 use_prepare ? BM_ELEM_TAG : BM_ELEM_SELECT,
4817 em->mat_nr,
4818 use_smooth,
4819 use_interp_simple))
4820 {
4821 continue;
4822 }
4823
4824 BMO_op_exec(em->bm, &bmop);
4825
4826 /* NOTE: EDBM_op_finish() will change bmesh pointer inside of edit mesh,
4827 * so need to tell evaluated objects to sync new bmesh pointer to their
4828 * edit mesh structures.
4829 */
4830 DEG_id_tag_update(&obedit->id, 0);
4831
4832 /* cancel if nothing was done */
4833 if ((totedge_orig == em->bm->totedge) && (totface_orig == em->bm->totface)) {
4834 EDBM_op_finish(em, &bmop, op, true);
4835 continue;
4836 }
4837
4839 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
4840
4841 if (!EDBM_op_finish(em, &bmop, op, true)) {
4842 continue;
4843 }
4844
4846 params.calc_looptris = true;
4847 params.calc_normals = false;
4848 params.is_destructive = true;
4849 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
4850 }
4851
4852 return OPERATOR_FINISHED;
4853}
4854
4856{
4857 PropertyRNA *prop;
4858
4859 /* identifiers */
4860 ot->name = "Grid Fill";
4861 ot->description = "Fill grid from two loops";
4862 ot->idname = "MESH_OT_fill_grid";
4863
4864 /* api callbacks */
4867
4868 /* flags */
4870
4871 /* properties */
4872 prop = RNA_def_int(ot->srna, "span", 1, 1, 1000, "Span", "Number of grid columns", 1, 100);
4874 prop = RNA_def_int(ot->srna,
4875 "offset",
4876 0,
4877 -1000,
4878 1000,
4879 "Offset",
4880 "Vertex that is the corner of the grid",
4881 -100,
4882 100);
4885 "use_interp_simple",
4886 false,
4887 "Simple Blending",
4888 "Use simple interpolation of grid vertices");
4889}
4890
4893/* -------------------------------------------------------------------- */
4898{
4899 const int sides = RNA_int_get(op->ptr, "sides");
4900
4901 const Scene *scene = CTX_data_scene(C);
4902 ViewLayer *view_layer = CTX_data_view_layer(C);
4904 scene, view_layer, CTX_wm_view3d(C));
4905
4906 for (Object *obedit : objects) {
4908
4909 if (em->bm->totedgesel == 0) {
4910 continue;
4911 }
4912
4914 em, op, "faces.out", true, "holes_fill edges=%he sides=%i", BM_ELEM_SELECT, sides))
4915 {
4916 continue;
4917 }
4918
4920 params.calc_looptris = true;
4921 params.calc_normals = false;
4922 params.is_destructive = true;
4923 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
4924 }
4925
4926 return OPERATOR_FINISHED;
4927}
4928
4930{
4931 /* identifiers */
4932 ot->name = "Fill Holes";
4933 ot->idname = "MESH_OT_fill_holes";
4934 ot->description = "Fill in holes (boundary edge loops)";
4935
4936 /* api callbacks */
4939
4940 /* flags */
4942
4944 "sides",
4945 4,
4946 0,
4947 1000,
4948 "Sides",
4949 "Number of sides in hole required to fill (zero fills all holes)",
4950 0,
4951 100);
4952}
4953
4956/* -------------------------------------------------------------------- */
4961{
4962 const Scene *scene = CTX_data_scene(C);
4963 ViewLayer *view_layer = CTX_data_view_layer(C);
4965 scene, view_layer, CTX_wm_view3d(C));
4966
4967 const float angle_max = M_PI;
4968 const float angle_limit = RNA_float_get(op->ptr, "angle_limit");
4969 char hflag;
4970
4971 for (Object *obedit : objects) {
4973
4974 if (em->bm->totfacesel == 0) {
4975 continue;
4976 }
4977
4978 if (angle_limit >= angle_max) {
4979 hflag = BM_ELEM_SELECT;
4980 }
4981 else {
4982 BMIter iter;
4983 BMEdge *e;
4984
4985 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
4989 BM_edge_calc_face_angle_ex(e, angle_max) < angle_limit));
4990 }
4991 hflag = BM_ELEM_TAG;
4992 }
4993
4995 em, op, "geom.out", true, "beautify_fill faces=%hf edges=%he", BM_ELEM_SELECT, hflag))
4996 {
4997 continue;
4998 }
4999
5001 params.calc_looptris = true;
5002 params.calc_normals = false;
5003 params.is_destructive = true;
5004 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5005 }
5006
5007 return OPERATOR_FINISHED;
5008}
5009
5011{
5012 PropertyRNA *prop;
5013
5014 /* identifiers */
5015 ot->name = "Beautify Faces";
5016 ot->idname = "MESH_OT_beautify_fill";
5017 ot->description = "Rearrange some faces to try to get less degenerated geometry";
5018
5019 /* api callbacks */
5022
5023 /* flags */
5025
5026 /* props */
5028 "angle_limit",
5029 0,
5030 nullptr,
5031 0.0f,
5032 DEG2RADF(180.0f),
5033 "Max Angle",
5034 "Angle limit",
5035 0.0f,
5036 DEG2RADF(180.0f));
5038}
5039
5042/* -------------------------------------------------------------------- */
5047{
5048 const float offset = RNA_float_get(op->ptr, "offset");
5049 const bool use_relative_offset = RNA_boolean_get(op->ptr, "use_relative_offset");
5050 const int center_mode = RNA_enum_get(op->ptr, "center_mode");
5051
5052 const Scene *scene = CTX_data_scene(C);
5053 ViewLayer *view_layer = CTX_data_view_layer(C);
5055 scene, view_layer, CTX_wm_view3d(C));
5056 for (Object *obedit : objects) {
5058
5059 if (em->bm->totfacesel == 0) {
5060 continue;
5061 }
5062
5063 BMOperator bmop;
5064 EDBM_op_init(em,
5065 &bmop,
5066 op,
5067 "poke faces=%hf offset=%f use_relative_offset=%b center_mode=%i",
5069 offset,
5070 use_relative_offset,
5071 center_mode);
5072 BMO_op_exec(em->bm, &bmop);
5073
5075
5077 em->bm, bmop.slots_out, "verts.out", BM_VERT, BM_ELEM_SELECT, true);
5079 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
5080
5081 if (!EDBM_op_finish(em, &bmop, op, true)) {
5082 continue;
5083 }
5084
5086 params.calc_looptris = true;
5087 params.calc_normals = true;
5088 params.is_destructive = true;
5089 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5090 }
5091
5092 return OPERATOR_FINISHED;
5093}
5094
5096{
5097 static const EnumPropertyItem poke_center_modes[] = {
5099 "MEDIAN_WEIGHTED",
5100 0,
5101 "Weighted Median",
5102 "Weighted median face center"},
5103 {BMOP_POKE_MEDIAN, "MEDIAN", 0, "Median", "Median face center"},
5104 {BMOP_POKE_BOUNDS, "BOUNDS", 0, "Bounds", "Face bounds center"},
5105 {0, nullptr, 0, nullptr, nullptr},
5106 };
5107
5108 /* identifiers */
5109 ot->name = "Poke Faces";
5110 ot->idname = "MESH_OT_poke";
5111 ot->description = "Split a face into a fan";
5112
5113 /* api callbacks */
5116
5117 /* flags */
5119
5121 ot->srna, "offset", 0.0f, -1e3f, 1e3f, "Poke Offset", "Poke Offset", -1.0f, 1.0f);
5123 "use_relative_offset",
5124 false,
5125 "Offset Relative",
5126 "Scale the offset by surrounding geometry");
5128 "center_mode",
5129 poke_center_modes,
5131 "Poke Center",
5132 "Poke face center calculation");
5133}
5134
5137/* -------------------------------------------------------------------- */
5142{
5143 const int quad_method = RNA_enum_get(op->ptr, "quad_method");
5144 const int ngon_method = RNA_enum_get(op->ptr, "ngon_method");
5145 const Scene *scene = CTX_data_scene(C);
5146 ViewLayer *view_layer = CTX_data_view_layer(C);
5147
5149 scene, view_layer, CTX_wm_view3d(C));
5150 for (Object *obedit : objects) {
5152
5153 if (em->bm->totfacesel == 0) {
5154 continue;
5155 }
5156
5157 BMOperator bmop;
5158 BMOIter oiter;
5159 BMFace *f;
5160
5162
5163 EDBM_op_init(em,
5164 &bmop,
5165 op,
5166 "triangulate faces=%hf quad_method=%i ngon_method=%i",
5168 quad_method,
5169 ngon_method);
5170 BMO_op_exec(em->bm, &bmop);
5171
5172 /* select the output */
5174 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
5175
5176 /* remove the doubles */
5177 BMO_ITER (f, &oiter, bmop.slots_out, "face_map_double.out", BM_FACE) {
5178 BM_face_kill(em->bm, f);
5179 }
5180
5182
5183 if (!EDBM_op_finish(em, &bmop, op, true)) {
5184 continue;
5185 }
5186
5188
5190 params.calc_looptris = true;
5191 params.calc_normals = false;
5192 params.is_destructive = true;
5193 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5194 }
5195
5196 return OPERATOR_FINISHED;
5197}
5198
5200{
5201 /* identifiers */
5202 ot->name = "Triangulate Faces";
5203 ot->idname = "MESH_OT_quads_convert_to_tris";
5204 ot->description = "Triangulate selected faces";
5205
5206 /* api callbacks */
5209
5210 /* flags */
5212
5214 "quad_method",
5217 "Quad Method",
5218 "Method for splitting the quads into triangles");
5220 "ngon_method",
5223 "N-gon Method",
5224 "Method for splitting the n-gons into triangles");
5225}
5226
5229/* -------------------------------------------------------------------- */
5234{
5235 const Scene *scene = CTX_data_scene(C);
5236 ViewLayer *view_layer = CTX_data_view_layer(C);
5237
5239 scene, view_layer, CTX_wm_view3d(C));
5240
5241 const bool do_seam = RNA_boolean_get(op->ptr, "seam");
5242 const bool do_sharp = RNA_boolean_get(op->ptr, "sharp");
5243 const bool do_uvs = RNA_boolean_get(op->ptr, "uvs");
5244 const bool do_vcols = RNA_boolean_get(op->ptr, "vcols");
5245 const bool do_materials = RNA_boolean_get(op->ptr, "materials");
5246
5247 float angle_face_threshold, angle_shape_threshold;
5248 bool is_face_pair;
5249 {
5250 int totelem_sel[3];
5251 EDBM_mesh_stats_multi(objects, nullptr, totelem_sel);
5252 is_face_pair = (totelem_sel[2] == 2);
5253 }
5254
5255 /* When joining exactly 2 faces, no limit.
5256 * this is useful for one off joins while editing. */
5257 {
5258 PropertyRNA *prop;
5259 prop = RNA_struct_find_property(op->ptr, "face_threshold");
5260 if (is_face_pair && (RNA_property_is_set(op->ptr, prop) == false)) {
5261 angle_face_threshold = DEG2RADF(180.0f);
5262 }
5263 else {
5264 angle_face_threshold = RNA_property_float_get(op->ptr, prop);
5265 }
5266
5267 prop = RNA_struct_find_property(op->ptr, "shape_threshold");
5268 if (is_face_pair && (RNA_property_is_set(op->ptr, prop) == false)) {
5269 angle_shape_threshold = DEG2RADF(180.0f);
5270 }
5271 else {
5272 angle_shape_threshold = RNA_property_float_get(op->ptr, prop);
5273 }
5274 }
5275
5276 for (Object *obedit : objects) {
5278
5279 if (em->bm->totfacesel == 0) {
5280 continue;
5281 }
5282
5284
5286 em,
5287 op,
5288 "faces.out",
5289 true,
5290 "join_triangles faces=%hf angle_face_threshold=%f angle_shape_threshold=%f "
5291 "cmp_seam=%b cmp_sharp=%b cmp_uvs=%b cmp_vcols=%b cmp_materials=%b",
5293 angle_face_threshold,
5294 angle_shape_threshold,
5295 do_seam,
5296 do_sharp,
5297 do_uvs,
5298 do_vcols,
5299 do_materials))
5300 {
5301 continue;
5302 }
5303
5305
5307 params.calc_looptris = true;
5308 params.calc_normals = false;
5309 params.is_destructive = true;
5310 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5311 }
5312
5313 return OPERATOR_FINISHED;
5314}
5315
5317{
5318 PropertyRNA *prop;
5319
5321 "face_threshold",
5322 0,
5323 nullptr,
5324 0.0f,
5325 DEG2RADF(180.0f),
5326 "Max Face Angle",
5327 "Face angle limit",
5328 0.0f,
5329 DEG2RADF(180.0f));
5331
5333 "shape_threshold",
5334 0,
5335 nullptr,
5336 0.0f,
5337 DEG2RADF(180.0f),
5338 "Max Shape Angle",
5339 "Shape angle limit",
5340 0.0f,
5341 DEG2RADF(180.0f));
5343
5344 RNA_def_boolean(ot->srna, "uvs", false, "Compare UVs", "");
5345 RNA_def_boolean(ot->srna, "vcols", false, "Compare Color Attributes", "");
5346 RNA_def_boolean(ot->srna, "seam", false, "Compare Seam", "");
5347 RNA_def_boolean(ot->srna, "sharp", false, "Compare Sharp", "");
5348 RNA_def_boolean(ot->srna, "materials", false, "Compare Materials", "");
5349}
5350
5352{
5353 /* identifiers */
5354 ot->name = "Tris to Quads";
5355 ot->idname = "MESH_OT_tris_convert_to_quads";
5356 ot->description = "Join triangles into quads";
5357
5358 /* api callbacks */
5361
5362 /* flags */
5364
5366}
5367
5370/* -------------------------------------------------------------------- */
5380{
5381 const float ratio = RNA_float_get(op->ptr, "ratio");
5382 bool use_vertex_group = RNA_boolean_get(op->ptr, "use_vertex_group");
5383 const float vertex_group_factor = RNA_float_get(op->ptr, "vertex_group_factor");
5384 const bool invert_vertex_group = RNA_boolean_get(op->ptr, "invert_vertex_group");
5385 const bool use_symmetry = RNA_boolean_get(op->ptr, "use_symmetry");
5386 const float symmetry_eps = 0.00002f;
5387 const int symmetry_axis = use_symmetry ? RNA_enum_get(op->ptr, "symmetry_axis") : -1;
5388
5389 /* nop */
5390 if (ratio == 1.0f) {
5391 return OPERATOR_FINISHED;
5392 }
5393
5394 const Scene *scene = CTX_data_scene(C);
5395 ViewLayer *view_layer = CTX_data_view_layer(C);
5397 scene, view_layer, CTX_wm_view3d(C));
5398
5399 for (Object *obedit : objects) {
5401 BMesh *bm = em->bm;
5402 if (bm->totedgesel == 0) {
5403 continue;
5404 }
5405
5406 float *vweights = static_cast<float *>(MEM_mallocN(sizeof(*vweights) * bm->totvert, __func__));
5407 {
5408 const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
5409 const int defbase_act = BKE_object_defgroup_active_index_get(obedit) - 1;
5410
5411 if (use_vertex_group && (cd_dvert_offset == -1)) {
5412 BKE_report(op->reports, RPT_WARNING, "No active vertex group");
5413 use_vertex_group = false;
5414 }
5415
5416 BMIter iter;
5417 BMVert *v;
5418 int i;
5419 BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
5420 float weight = 0.0f;
5422 if (use_vertex_group) {
5423 const MDeformVert *dv = static_cast<const MDeformVert *>(
5424 BM_ELEM_CD_GET_VOID_P(v, cd_dvert_offset));
5425 weight = BKE_defvert_find_weight(dv, defbase_act);
5426 if (invert_vertex_group) {
5427 weight = 1.0f - weight;
5428 }
5429 }
5430 else {
5431 weight = 1.0f;
5432 }
5433 }
5434
5435 vweights[i] = weight;
5436 BM_elem_index_set(v, i); /* set_inline */
5437 }
5438 bm->elem_index_dirty &= ~BM_VERT;
5439 }
5440
5441 float ratio_adjust;
5442
5443 if ((bm->totface == bm->totfacesel) || (ratio == 0.0f)) {
5444 ratio_adjust = ratio;
5445 }
5446 else {
5455 int totface_basis = 0;
5456 int totface_adjacent = 0;
5457 BMIter iter;
5458 BMFace *f;
5459 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
5460 /* count faces during decimation, ngons are triangulated */
5461 const int f_len = f->len > 4 ? (f->len - 2) : 1;
5462 totface_basis += f_len;
5463
5464 BMLoop *l_iter, *l_first;
5465 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
5466 do {
5467 if (vweights[BM_elem_index_get(l_iter->v)] != 0.0f) {
5468 totface_adjacent += f_len;
5469 break;
5470 }
5471 } while ((l_iter = l_iter->next) != l_first);
5472 }
5473
5474 ratio_adjust = ratio;
5475 ratio_adjust = 1.0f - ratio_adjust;
5476 ratio_adjust *= float(totface_adjacent) / float(totface_basis);
5477 ratio_adjust = 1.0f - ratio_adjust;
5478 }
5479
5481 em->bm, ratio_adjust, vweights, vertex_group_factor, false, symmetry_axis, symmetry_eps);
5482
5483 MEM_freeN(vweights);
5484
5485 {
5486 short selectmode = em->selectmode;
5487 if ((selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) == 0) {
5488 /* ensure we flush edges -> faces */
5489 selectmode |= SCE_SELECT_EDGE;
5490 }
5491 EDBM_selectmode_flush_ex(em, selectmode);
5492 }
5494 params.calc_looptris = true;
5495 params.calc_normals = true;
5496 params.is_destructive = true;
5497 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5498 }
5499
5500 return OPERATOR_FINISHED;
5501}
5502
5503static bool edbm_decimate_check(bContext * /*C*/, wmOperator * /*op*/)
5504{
5505 return true;
5506}
5507
5508static void edbm_decimate_ui(bContext * /*C*/, wmOperator *op)
5509{
5510 uiLayout *layout = op->layout, *row, *col, *sub;
5511
5512 uiLayoutSetPropSep(layout, true);
5513
5514 uiItemR(layout, op->ptr, "ratio", UI_ITEM_NONE, nullptr, ICON_NONE);
5515
5516 uiItemR(layout, op->ptr, "use_vertex_group", UI_ITEM_NONE, nullptr, ICON_NONE);
5517 col = uiLayoutColumn(layout, false);
5518 uiLayoutSetActive(col, RNA_boolean_get(op->ptr, "use_vertex_group"));
5519 uiItemR(col, op->ptr, "vertex_group_factor", UI_ITEM_NONE, nullptr, ICON_NONE);
5520 uiItemR(col, op->ptr, "invert_vertex_group", UI_ITEM_NONE, nullptr, ICON_NONE);
5521
5522 row = uiLayoutRowWithHeading(layout, true, IFACE_("Symmetry"));
5523 uiItemR(row, op->ptr, "use_symmetry", UI_ITEM_NONE, "", ICON_NONE);
5524 sub = uiLayoutRow(row, true);
5525 uiLayoutSetActive(sub, RNA_boolean_get(op->ptr, "use_symmetry"));
5526 uiItemR(sub, op->ptr, "symmetry_axis", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
5527}
5528
5530{
5531 /* identifiers */
5532 ot->name = "Decimate Geometry";
5533 ot->idname = "MESH_OT_decimate";
5534 ot->description = "Simplify geometry by collapsing edges";
5535
5536 /* api callbacks */
5541
5542 /* flags */
5544
5545 /* NOTE: keep in sync with 'rna_def_modifier_decimate'. */
5546 RNA_def_float(ot->srna, "ratio", 1.0f, 0.0f, 1.0f, "Ratio", "", 0.0f, 1.0f);
5547
5549 "use_vertex_group",
5550 false,
5551 "Vertex Group",
5552 "Use active vertex group as an influence");
5554 "vertex_group_factor",
5555 1.0f,
5556 0.0f,
5557 1000.0f,
5558 "Weight",
5559 "Vertex group strength",
5560 0.0f,
5561 10.0f);
5563 ot->srna, "invert_vertex_group", false, "Invert", "Invert vertex group influence");
5564
5565 RNA_def_boolean(ot->srna, "use_symmetry", false, "Symmetry", "Maintain symmetry on an axis");
5566
5567 RNA_def_enum(ot->srna, "symmetry_axis", rna_enum_axis_xyz_items, 1, "Axis", "Axis of symmetry");
5568}
5569
5572/* -------------------------------------------------------------------- */
5577{
5578 PropertyRNA *prop;
5579
5580 prop = RNA_def_boolean(
5581 ot->srna, "use_verts", value, "Dissolve Vertices", "Dissolve remaining vertices");
5582
5583 if (flag) {
5585 }
5586}
5588{
5590 "use_face_split",
5591 false,
5592 "Face Split",
5593 "Split off face corners to maintain surrounding geometry");
5594}
5596{
5598 "use_boundary_tear",
5599 false,
5600 "Tear Boundary",
5601 "Split off face corners instead of merging faces");
5602}
5603
5605{
5606 const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split");
5607 const bool use_boundary_tear = RNA_boolean_get(op->ptr, "use_boundary_tear");
5608
5609 const Scene *scene = CTX_data_scene(C);
5610 ViewLayer *view_layer = CTX_data_view_layer(C);
5612 scene, view_layer, CTX_wm_view3d(C));
5613
5614 for (Object *obedit : objects) {
5616
5617 if (em->bm->totvertsel == 0) {
5618 continue;
5619 }
5620
5622
5623 if (!EDBM_op_callf(em,
5624 op,
5625 "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b",
5627 use_face_split,
5628 use_boundary_tear))
5629 {
5630 continue;
5631 }
5632
5634
5636 params.calc_looptris = true;
5637 params.calc_normals = false;
5638 params.is_destructive = true;
5639 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5640 }
5641
5642 return OPERATOR_FINISHED;
5643}
5644
5646{
5647 /* identifiers */
5648 ot->name = "Dissolve Vertices";
5649 ot->description = "Dissolve vertices, merge edges and faces";
5650 ot->idname = "MESH_OT_dissolve_verts";
5651
5652 /* api callbacks */
5655
5656 /* flags */
5658
5661}
5662
5665/* -------------------------------------------------------------------- */
5670{
5671 const bool use_verts = RNA_boolean_get(op->ptr, "use_verts");
5672 const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split");
5673
5674 const Scene *scene = CTX_data_scene(C);
5675 ViewLayer *view_layer = CTX_data_view_layer(C);
5677 scene, view_layer, CTX_wm_view3d(C));
5678 for (Object *obedit : objects) {
5680
5681 if (em->bm->totedgesel == 0) {
5682 continue;
5683 }
5684
5686
5687 if (!EDBM_op_callf(em,
5688 op,
5689 "dissolve_edges edges=%he use_verts=%b use_face_split=%b",
5691 use_verts,
5692 use_face_split))
5693 {
5694 continue;
5695 }
5696
5698
5700 params.calc_looptris = true;
5701 params.calc_normals = false;
5702 params.is_destructive = true;
5703 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5704 }
5705
5706 return OPERATOR_FINISHED;
5707}
5708
5710{
5711 /* identifiers */
5712 ot->name = "Dissolve Edges";
5713 ot->description = "Dissolve edges, merging faces";
5714 ot->idname = "MESH_OT_dissolve_edges";
5715
5716 /* api callbacks */
5719
5720 /* flags */
5722
5725}
5726
5729/* -------------------------------------------------------------------- */
5734{
5735 const bool use_verts = RNA_boolean_get(op->ptr, "use_verts");
5736 const Scene *scene = CTX_data_scene(C);
5737 ViewLayer *view_layer = CTX_data_view_layer(C);
5739 scene, view_layer, CTX_wm_view3d(C));
5740 for (Object *obedit : objects) {
5742
5743 if (em->bm->totfacesel == 0) {
5744 continue;
5745 }
5746
5748
5750 op,
5751 "region.out",
5752 true,
5753 "dissolve_faces faces=%hf use_verts=%b",
5755 use_verts))
5756 {
5757 continue;
5758 }
5759
5761
5763 params.calc_looptris = true;
5764 params.calc_normals = false;
5765 params.is_destructive = true;
5766 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5767 }
5768
5769 return OPERATOR_FINISHED;
5770}
5771
5773{
5774 /* identifiers */
5775 ot->name = "Dissolve Faces";
5776 ot->description = "Dissolve faces";
5777 ot->idname = "MESH_OT_dissolve_faces";
5778
5779 /* api callbacks */
5782
5783 /* flags */
5785
5787}
5788
5791/* -------------------------------------------------------------------- */
5796{
5797 Object *obedit = CTX_data_edit_object(C);
5799 PropertyRNA *prop;
5800
5801 prop = RNA_struct_find_property(op->ptr, "use_verts");
5802 if (!RNA_property_is_set(op->ptr, prop)) {
5803 /* always enable in edge-mode */
5804 if ((em->selectmode & SCE_SELECT_FACE) == 0) {
5805 RNA_property_boolean_set(op->ptr, prop, true);
5806 }
5807 }
5808
5809 if (em->selectmode & SCE_SELECT_VERTEX) {
5810 return edbm_dissolve_verts_exec(C, op);
5811 }
5812 if (em->selectmode & SCE_SELECT_EDGE) {
5813 return edbm_dissolve_edges_exec(C, op);
5814 }
5815 return edbm_dissolve_faces_exec(C, op);
5816}
5817
5819{
5820 /* identifiers */
5821 ot->name = "Dissolve Selection";
5822 ot->description = "Dissolve geometry based on the selection mode";
5823 ot->idname = "MESH_OT_dissolve_mode";
5824
5825 /* api callbacks */
5828
5829 /* flags */
5831
5835}
5836
5839/* -------------------------------------------------------------------- */
5844{
5845 const float angle_limit = RNA_float_get(op->ptr, "angle_limit");
5846 const bool use_dissolve_boundaries = RNA_boolean_get(op->ptr, "use_dissolve_boundaries");
5847 const int delimit = RNA_enum_get(op->ptr, "delimit");
5848 char dissolve_flag;
5849
5850 const Scene *scene = CTX_data_scene(C);
5851 ViewLayer *view_layer = CTX_data_view_layer(C);
5853 scene, view_layer, CTX_wm_view3d(C));
5854 for (Object *obedit : objects) {
5856 BMesh *bm = em->bm;
5857
5858 if ((bm->totvertsel == 0) && (bm->totedgesel == 0) && (bm->totfacesel == 0)) {
5859 continue;
5860 }
5861
5863
5864 if (em->selectmode == SCE_SELECT_FACE) {
5865 /* flush selection to tags and untag edges/verts with partially selected faces */
5866 BMIter iter;
5867 BMIter liter;
5868
5869 BMElem *ele;
5870 BMFace *f;
5871 BMLoop *l;
5872
5873 BM_ITER_MESH (ele, &iter, bm, BM_VERTS_OF_MESH) {
5875 }
5876 BM_ITER_MESH (ele, &iter, bm, BM_EDGES_OF_MESH) {
5878 }
5879
5880 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
5882 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
5885 }
5886 }
5887 }
5888
5889 dissolve_flag = BM_ELEM_TAG;
5890 }
5891 else {
5892 dissolve_flag = BM_ELEM_SELECT;
5893 }
5894
5896 em,
5897 op,
5898 "region.out",
5899 true,
5900 "dissolve_limit edges=%he verts=%hv angle_limit=%f use_dissolve_boundaries=%b delimit=%i",
5901 dissolve_flag,
5902 dissolve_flag,
5903 angle_limit,
5904 use_dissolve_boundaries,
5905 delimit);
5906
5908
5910 params.calc_looptris = true;
5911 params.calc_normals = false;
5912 params.is_destructive = true;
5913 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5914 }
5915
5916 return OPERATOR_FINISHED;
5917}
5918
5920{
5921 PropertyRNA *prop;
5922
5923 /* identifiers */
5924 ot->name = "Limited Dissolve";
5925 ot->idname = "MESH_OT_dissolve_limited";
5926 ot->description =
5927 "Dissolve selected edges and vertices, limited by the angle of surrounding geometry";
5928
5929 /* api callbacks */
5932
5933 /* flags */
5935
5937 "angle_limit",
5938 0,
5939 nullptr,
5940 0.0f,
5941 DEG2RADF(180.0f),
5942 "Max Angle",
5943 "Angle limit",
5944 0.0f,
5945 DEG2RADF(180.0f));
5948 "use_dissolve_boundaries",
5949 false,
5950 "All Boundaries",
5951 "Dissolve all vertices in between face boundaries");
5953 "delimit",
5956 "Delimit",
5957 "Delimit dissolve operation");
5958}
5959
5962/* -------------------------------------------------------------------- */
5967{
5968 const Scene *scene = CTX_data_scene(C);
5969 ViewLayer *view_layer = CTX_data_view_layer(C);
5970 int totelem_old[3] = {0, 0, 0};
5971 int totelem_new[3] = {0, 0, 0};
5972
5974 scene, view_layer, CTX_wm_view3d(C));
5975
5976 for (Object *obedit : objects) {
5978 BMesh *bm = em->bm;
5979 totelem_old[0] += bm->totvert;
5980 totelem_old[1] += bm->totedge;
5981 totelem_old[2] += bm->totface;
5982 } /* objects */
5983
5984 const float thresh = RNA_float_get(op->ptr, "threshold");
5985
5986 for (Object *obedit : objects) {
5988 BMesh *bm = em->bm;
5989
5990 if (!EDBM_op_callf(em, op, "dissolve_degenerate edges=%he dist=%f", BM_ELEM_SELECT, thresh)) {
5991 continue;
5992 }
5993
5994 /* tricky to maintain correct selection here, so just flush up from verts */
5996
5998 params.calc_looptris = true;
5999 params.calc_normals = false;
6000 params.is_destructive = true;
6001 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
6002
6003 totelem_new[0] += bm->totvert;
6004 totelem_new[1] += bm->totedge;
6005 totelem_new[2] += bm->totface;
6006 }
6007
6008 edbm_report_delete_info(op->reports, totelem_old, totelem_new);
6009
6010 return OPERATOR_FINISHED;
6011}
6012
6014{
6015 /* identifiers */
6016 ot->name = "Degenerate Dissolve";
6017 ot->idname = "MESH_OT_dissolve_degenerate";
6018 ot->description = "Dissolve zero area faces and zero length edges";
6019
6020 /* api callbacks */
6023
6024 /* flags */
6026
6028 "threshold",
6029 1e-4f,
6030 1e-6f,
6031 50.0f,
6032 "Merge Distance",
6033 "Maximum distance between elements to merge",
6034 1e-5f,
6035 10.0f);
6036}
6037
6040/* -------------------------------------------------------------------- */
6044/* internally uses dissolve */
6046{
6047 const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split");
6048 const Scene *scene = CTX_data_scene(C);
6049 ViewLayer *view_layer = CTX_data_view_layer(C);
6050
6052 scene, view_layer, CTX_wm_view3d(C));
6053 for (Object *obedit : objects) {
6055
6056 if (em->bm->totedgesel == 0) {
6057 continue;
6058 }
6059
6060 /* deal with selection */
6061 {
6062 BMEdge *e;
6063 BMIter iter;
6064
6066
6067 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
6068 if (BM_elem_flag_test(e, BM_ELEM_SELECT) && e->l) {
6069 BMLoop *l_iter = e->l;
6070 do {
6072 } while ((l_iter = l_iter->radial_next) != e->l);
6073 }
6074 }
6075 }
6076
6077 if (!EDBM_op_callf(em,
6078 op,
6079 "dissolve_edges edges=%he use_verts=%b use_face_split=%b",
6081 true,
6082 use_face_split))
6083 {
6084 continue;
6085 }
6086
6088
6090
6092 params.calc_looptris = true;
6093 params.calc_normals = false;
6094 params.is_destructive = true;
6095 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
6096 }
6097
6098 return OPERATOR_FINISHED;
6099}
6100
6102{
6103 /* identifiers */
6104 ot->name = "Delete Edge Loop";
6105 ot->description = "Delete an edge loop by merging the faces on each side";
6106 ot->idname = "MESH_OT_delete_edgeloop";
6107
6108 /* api callbacks */
6111
6112 /* flags */
6114
6116 "use_face_split",
6117 true,
6118 "Face Split",
6119 "Split off face corners to maintain surrounding geometry");
6120}
6121
6124/* -------------------------------------------------------------------- */
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 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
6137 continue;
6138 }
6140
6141 BMOperator bmop;
6142 EDBM_op_init(em, &bmop, op, "split geom=%hvef use_only_faces=%b", BM_ELEM_SELECT, false);
6143 BMO_op_exec(em->bm, &bmop);
6146 em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
6147
6149
6150 if (!EDBM_op_finish(em, &bmop, op, true)) {
6151 continue;
6152 }
6153
6154 /* Geometry has changed, need to recalculate normals and tessellation. */
6156 params.calc_looptris = true;
6157 params.calc_normals = true;
6158 params.is_destructive = true;
6159 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
6160 }
6161
6162 return OPERATOR_FINISHED;
6163}
6164
6166{
6167 /* identifiers */
6168 ot->name = "Split";
6169 ot->idname = "MESH_OT_split";
6170 ot->description = "Split off selected geometry from connected unselected geometry";
6171
6172 /* api callbacks */
6175
6176 /* flags */
6178}
6179
6182/* -------------------------------------------------------------------- */
6189enum {
6205};
6206
6209 float srt;
6212};
6213
6214static int bmelemsort_comp(const void *v1, const void *v2)
6215{
6216 const BMElemSort *x1 = static_cast<const BMElemSort *>(v1);
6217 const BMElemSort *x2 = static_cast<const BMElemSort *>(v2);
6218
6219 return (x1->srt > x2->srt) - (x1->srt < x2->srt);
6220}
6221
6222/* Reorders vertices/edges/faces using a given methods. Loops are not supported. */
6224 Scene *scene,
6225 Object *ob,
6226 RegionView3D *rv3d,
6227 const int types,
6228 const int flag,
6229 const int action,
6230 const int reverse,
6231 const uint seed)
6232{
6234
6235 BMVert *ve;
6236 BMEdge *ed;
6237 BMFace *fa;
6238 BMIter iter;
6239
6240 /* In all five elements below, 0 = vertices, 1 = edges, 2 = faces. */
6241 /* Just to mark protected elements. */
6242 char *pblock[3] = {nullptr, nullptr, nullptr}, *pb;
6243 BMElemSort *sblock[3] = {nullptr, nullptr, nullptr}, *sb;
6244 uint *map[3] = {nullptr, nullptr, nullptr}, *mp;
6245 int totelem[3] = {0, 0, 0};
6246 int affected[3] = {0, 0, 0};
6247 int i, j;
6248
6249 if (!(types && flag && action)) {
6250 return;
6251 }
6252
6253 if (types & BM_VERT) {
6254 totelem[0] = em->bm->totvert;
6255 }
6256 if (types & BM_EDGE) {
6257 totelem[1] = em->bm->totedge;
6258 }
6259 if (types & BM_FACE) {
6260 totelem[2] = em->bm->totface;
6261 }
6262
6263 if (ELEM(action, SRT_VIEW_ZAXIS, SRT_VIEW_XAXIS)) {
6264 float mat[4][4];
6265 float fact = reverse ? -1.0 : 1.0;
6266 int coidx = (action == SRT_VIEW_ZAXIS) ? 2 : 0;
6267
6268 /* Apply the view matrix to the object matrix. */
6269 mul_m4_m4m4(mat, rv3d->viewmat, ob->object_to_world().ptr());
6270
6271 if (totelem[0]) {
6272 pb = pblock[0] = static_cast<char *>(MEM_callocN(sizeof(char) * totelem[0], __func__));
6273 sb = sblock[0] = static_cast<BMElemSort *>(
6274 MEM_callocN(sizeof(BMElemSort) * totelem[0], __func__));
6275
6276 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6277 if (BM_elem_flag_test(ve, flag)) {
6278 float co[3];
6279 mul_v3_m4v3(co, mat, ve->co);
6280
6281 pb[i] = false;
6282 sb[affected[0]].org_idx = i;
6283 sb[affected[0]++].srt = co[coidx] * fact;
6284 }
6285 else {
6286 pb[i] = true;
6287 }
6288 }
6289 }
6290
6291 if (totelem[1]) {
6292 pb = pblock[1] = static_cast<char *>(MEM_callocN(sizeof(char) * totelem[1], __func__));
6293 sb = sblock[1] = static_cast<BMElemSort *>(
6294 MEM_callocN(sizeof(BMElemSort) * totelem[1], __func__));
6295
6296 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6297 if (BM_elem_flag_test(ed, flag)) {
6298 float co[3];
6299 mid_v3_v3v3(co, ed->v1->co, ed->v2->co);
6300 mul_m4_v3(mat, co);
6301
6302 pb[i] = false;
6303 sb[affected[1]].org_idx = i;
6304 sb[affected[1]++].srt = co[coidx] * fact;
6305 }
6306 else {
6307 pb[i] = true;
6308 }
6309 }
6310 }
6311
6312 if (totelem[2]) {
6313 pb = pblock[2] = static_cast<char *>(MEM_callocN(sizeof(char) * totelem[2], __func__));
6314 sb = sblock[2] = static_cast<BMElemSort *>(
6315 MEM_callocN(sizeof(BMElemSort) * totelem[2], __func__));
6316
6317 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6318 if (BM_elem_flag_test(fa, flag)) {
6319 float co[3];
6321 mul_m4_v3(mat, co);
6322
6323 pb[i] = false;
6324 sb[affected[2]].org_idx = i;
6325 sb[affected[2]++].srt = co[coidx] * fact;
6326 }
6327 else {
6328 pb[i] = true;
6329 }
6330 }
6331 }
6332 }
6333
6334 else if (action == SRT_CURSOR_DISTANCE) {
6335 float cur[3];
6336 float mat[4][4];
6337 float fact = reverse ? -1.0 : 1.0;
6338
6339 copy_v3_v3(cur, scene->cursor.location);
6340
6341 invert_m4_m4(mat, ob->object_to_world().ptr());
6342 mul_m4_v3(mat, cur);
6343
6344 if (totelem[0]) {
6345 pb = pblock[0] = static_cast<char *>(MEM_callocN(sizeof(char) * totelem[0], __func__));
6346 sb = sblock[0] = static_cast<BMElemSort *>(
6347 MEM_callocN(sizeof(BMElemSort) * totelem[0], __func__));
6348
6349 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6350 if (BM_elem_flag_test(ve, flag)) {
6351 pb[i] = false;
6352 sb[affected[0]].org_idx = i;
6353 sb[affected[0]++].srt = len_squared_v3v3(cur, ve->co) * fact;
6354 }
6355 else {
6356 pb[i] = true;
6357 }
6358 }
6359 }
6360
6361 if (totelem[1]) {
6362 pb = pblock[1] = static_cast<char *>(MEM_callocN(sizeof(char) * totelem[1], __func__));
6363 sb = sblock[1] = static_cast<BMElemSort *>(
6364 MEM_callocN(sizeof(BMElemSort) * totelem[1], __func__));
6365
6366 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6367 if (BM_elem_flag_test(ed, flag)) {
6368 float co[3];
6369 mid_v3_v3v3(co, ed->v1->co, ed->v2->co);
6370
6371 pb[i] = false;
6372 sb[affected[1]].org_idx = i;
6373 sb[affected[1]++].srt = len_squared_v3v3(cur, co) * fact;
6374 }
6375 else {
6376 pb[i] = true;
6377 }
6378 }
6379 }
6380
6381 if (totelem[2]) {
6382 pb = pblock[2] = static_cast<char *>(MEM_callocN(sizeof(char) * totelem[2], __func__));
6383 sb = sblock[2] = static_cast<BMElemSort *>(
6384 MEM_callocN(sizeof(BMElemSort) * totelem[2], __func__));
6385
6386 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6387 if (BM_elem_flag_test(fa, flag)) {
6388 float co[3];
6390
6391 pb[i] = false;
6392 sb[affected[2]].org_idx = i;
6393 sb[affected[2]++].srt = len_squared_v3v3(cur, co) * fact;
6394 }
6395 else {
6396 pb[i] = true;
6397 }
6398 }
6399 }
6400 }
6401
6402 /* Faces only! */
6403 else if (action == SRT_MATERIAL && totelem[2]) {
6404 pb = pblock[2] = static_cast<char *>(MEM_callocN(sizeof(char) * totelem[2], __func__));
6405 sb = sblock[2] = static_cast<BMElemSort *>(
6406 MEM_callocN(sizeof(BMElemSort) * totelem[2], __func__));
6407
6408 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6409 if (BM_elem_flag_test(fa, flag)) {
6410 /* Reverse materials' order, not order of faces inside each mat! */
6411 /* NOTE: cannot use totcol, as mat_nr may sometimes be greater... */
6412 float srt = reverse ? float(MAXMAT - fa->mat_nr) : float(fa->mat_nr);
6413 pb[i] = false;
6414 sb[affected[2]].org_idx = i;
6415 /* Multiplying with totface and adding i ensures us
6416 * we keep current order for all faces of same mat. */
6417 sb[affected[2]++].srt = srt * float(totelem[2]) + float(i);
6418 // printf("e: %d; srt: %f; final: %f\n", i, srt, srt * float(totface) + float(i));
6419 }
6420 else {
6421 pb[i] = true;
6422 }
6423 }
6424 }
6425
6426 else if (action == SRT_SELECTED) {
6427 uint *tbuf[3] = {nullptr, nullptr, nullptr}, *tb;
6428
6429 if (totelem[0]) {
6430 tb = tbuf[0] = static_cast<uint *>(MEM_callocN(sizeof(int) * totelem[0], __func__));
6431 mp = map[0] = static_cast<uint *>(MEM_callocN(sizeof(int) * totelem[0], __func__));
6432
6433 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6434 if (BM_elem_flag_test(ve, flag)) {
6435 mp[affected[0]++] = i;
6436 }
6437 else {
6438 *tb = i;
6439 tb++;
6440 }
6441 }
6442 }
6443
6444 if (totelem[1]) {
6445 tb = tbuf[1] = static_cast<uint *>(MEM_callocN(sizeof(int) * totelem[1], __func__));
6446 mp = map[1] = static_cast<uint *>(MEM_callocN(sizeof(int) * totelem[1], __func__));
6447
6448 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6449 if (BM_elem_flag_test(ed, flag)) {
6450 mp[affected[1]++] = i;
6451 }
6452 else {
6453 *tb = i;
6454 tb++;
6455 }
6456 }
6457 }
6458
6459 if (totelem[2]) {
6460 tb = tbuf[2] = static_cast<uint *>(MEM_callocN(sizeof(int) * totelem[2], __func__));
6461 mp = map[2] = static_cast<uint *>(MEM_callocN(sizeof(int) * totelem[2], __func__));
6462
6463 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6464 if (BM_elem_flag_test(fa, flag)) {
6465 mp[affected[2]++] = i;
6466 }
6467 else {
6468 *tb = i;
6469 tb++;
6470 }
6471 }
6472 }
6473
6474 for (j = 3; j--;) {
6475 int tot = totelem[j];
6476 int aff = affected[j];
6477 tb = tbuf[j];
6478 mp = map[j];
6479 if (!(tb && mp)) {
6480 continue;
6481 }
6482 if (ELEM(aff, 0, tot)) {
6483 MEM_freeN(tb);
6484 MEM_freeN(mp);
6485 map[j] = nullptr;
6486 continue;
6487 }
6488 if (reverse) {
6489 memcpy(tb + (tot - aff), mp, aff * sizeof(int));
6490 }
6491 else {
6492 memcpy(mp + aff, tb, (tot - aff) * sizeof(int));
6493 tb = mp;
6494 mp = map[j] = tbuf[j];
6495 tbuf[j] = tb;
6496 }
6497
6498 /* Reverse mapping, we want an org2new one! */
6499 for (i = tot, tb = tbuf[j] + tot - 1; i--; tb--) {
6500 mp[*tb] = i;
6501 }
6502 MEM_freeN(tbuf[j]);
6503 }
6504 }
6505
6506 else if (action == SRT_RANDOMIZE) {
6507 if (totelem[0]) {
6508 /* Re-init random generator for each element type, to get consistent random when
6509 * enabling/disabling an element type. */
6511 pb = pblock[0] = static_cast<char *>(MEM_callocN(sizeof(char) * totelem[0], __func__));
6512 sb = sblock[0] = static_cast<BMElemSort *>(
6513 MEM_callocN(sizeof(BMElemSort) * totelem[0], __func__));
6514
6515 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6516 if (BM_elem_flag_test(ve, flag)) {
6517 pb[i] = false;
6518 sb[affected[0]].org_idx = i;
6519 sb[affected[0]++].srt = BLI_rng_get_float(rng);
6520 }
6521 else {
6522 pb[i] = true;
6523 }
6524 }
6525
6526 BLI_rng_free(rng);
6527 }
6528
6529 if (totelem[1]) {
6531 pb = pblock[1] = static_cast<char *>(MEM_callocN(sizeof(char) * totelem[1], __func__));
6532 sb = sblock[1] = static_cast<BMElemSort *>(
6533 MEM_callocN(sizeof(BMElemSort) * totelem[1], __func__));
6534
6535 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6536 if (BM_elem_flag_test(ed, flag)) {
6537 pb[i] = false;
6538 sb[affected[1]].org_idx = i;
6539 sb[affected[1]++].srt = BLI_rng_get_float(rng);
6540 }
6541 else {
6542 pb[i] = true;
6543 }
6544 }
6545
6546 BLI_rng_free(rng);
6547 }
6548
6549 if (totelem[2]) {
6551 pb = pblock[2] = static_cast<char *>(MEM_callocN(sizeof(char) * totelem[2], __func__));
6552 sb = sblock[2] = static_cast<BMElemSort *>(
6553 MEM_callocN(sizeof(BMElemSort) * totelem[2], __func__));
6554
6555 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6556 if (BM_elem_flag_test(fa, flag)) {
6557 pb[i] = false;
6558 sb[affected[2]].org_idx = i;
6559 sb[affected[2]++].srt = BLI_rng_get_float(rng);
6560 }
6561 else {
6562 pb[i] = true;
6563 }
6564 }
6565
6566 BLI_rng_free(rng);
6567 }
6568 }
6569
6570 else if (action == SRT_REVERSE) {
6571 if (totelem[0]) {
6572 pb = pblock[0] = static_cast<char *>(MEM_callocN(sizeof(char) * totelem[0], __func__));
6573 sb = sblock[0] = static_cast<BMElemSort *>(
6574 MEM_callocN(sizeof(BMElemSort) * totelem[0], __func__));
6575
6576 BM_ITER_MESH_INDEX (ve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
6577 if (BM_elem_flag_test(ve, flag)) {
6578 pb[i] = false;
6579 sb[affected[0]].org_idx = i;
6580 sb[affected[0]++].srt = float(-i);
6581 }
6582 else {
6583 pb[i] = true;
6584 }
6585 }
6586 }
6587
6588 if (totelem[1]) {
6589 pb = pblock[1] = static_cast<char *>(MEM_callocN(sizeof(char) * totelem[1], __func__));
6590 sb = sblock[1] = static_cast<BMElemSort *>(
6591 MEM_callocN(sizeof(BMElemSort) * totelem[1], __func__));
6592
6593 BM_ITER_MESH_INDEX (ed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
6594 if (BM_elem_flag_test(ed, flag)) {
6595 pb[i] = false;
6596 sb[affected[1]].org_idx = i;
6597 sb[affected[1]++].srt = float(-i);
6598 }
6599 else {
6600 pb[i] = true;
6601 }
6602 }
6603 }
6604
6605 if (totelem[2]) {
6606 pb = pblock[2] = static_cast<char *>(MEM_callocN(sizeof(char) * totelem[2], __func__));
6607 sb = sblock[2] = static_cast<BMElemSort *>(
6608 MEM_callocN(sizeof(BMElemSort) * totelem[2], __func__));
6609
6610 BM_ITER_MESH_INDEX (fa, &iter, em->bm, BM_FACES_OF_MESH, i) {
6611 if (BM_elem_flag_test(fa, flag)) {
6612 pb[i] = false;
6613 sb[affected[2]].org_idx = i;
6614 sb[affected[2]++].srt = float(-i);
6615 }
6616 else {
6617 pb[i] = true;
6618 }
6619 }
6620 }
6621 }
6622
6623 // printf("%d vertices: %d to be affected...\n", totelem[0], affected[0]);
6624 // printf("%d edges: %d to be affected...\n", totelem[1], affected[1]);
6625 // printf("%d faces: %d to be affected...\n", totelem[2], affected[2]);
6626 if (affected[0] == 0 && affected[1] == 0 && affected[2] == 0) {
6627 for (j = 3; j--;) {
6628 if (pblock[j]) {
6629 MEM_freeN(pblock[j]);
6630 }
6631 if (sblock[j]) {
6632 MEM_freeN(sblock[j]);
6633 }
6634 if (map[j]) {
6635 MEM_freeN(map[j]);
6636 }
6637 }
6638 return;
6639 }
6640
6641 /* Sort affected elements, and populate mapping arrays, if needed. */
6642 for (j = 3; j--;) {
6643 pb = pblock[j];
6644 sb = sblock[j];
6645 if (pb && sb && !map[j]) {
6646 const char *p_blk;
6647 BMElemSort *s_blk;
6648 int tot = totelem[j];
6649 int aff = affected[j];
6650
6651 qsort(sb, aff, sizeof(BMElemSort), bmelemsort_comp);
6652
6653 mp = map[j] = static_cast<uint *>(MEM_mallocN(sizeof(int) * tot, __func__));
6654 p_blk = pb + tot - 1;
6655 s_blk = sb + aff - 1;
6656 for (i = tot; i--; p_blk--) {
6657 if (*p_blk) { /* Protected! */
6658 mp[i] = i;
6659 }
6660 else {
6661 mp[s_blk->org_idx] = i;
6662 s_blk--;
6663 }
6664 }
6665 }
6666 if (pb) {
6667 MEM_freeN(pb);
6668 }
6669 if (sb) {
6670 MEM_freeN(sb);
6671 }
6672 }
6673
6674 BM_mesh_remap(em->bm, map[0], map[1], map[2]);
6675
6677 params.calc_looptris = (totelem[2] != 0);
6678 params.calc_normals = false;
6679 params.is_destructive = true;
6680 EDBM_update(static_cast<Mesh *>(ob->data), &params);
6681
6682 DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_GEOMETRY);
6684
6685 for (j = 3; j--;) {
6686 if (map[j]) {
6687 MEM_freeN(map[j]);
6688 }
6689 }
6690}
6691
6693{
6694 Scene *scene = CTX_data_scene(C);
6695 ViewLayer *view_layer = CTX_data_view_layer(C);
6696 Object *ob_active = CTX_data_edit_object(C);
6697
6698 /* may be nullptr */
6700
6701 const int action = RNA_enum_get(op->ptr, "type");
6702 PropertyRNA *prop_elem_types = RNA_struct_find_property(op->ptr, "elements");
6703 const bool use_reverse = RNA_boolean_get(op->ptr, "reverse");
6704 uint seed = RNA_int_get(op->ptr, "seed");
6705 int elem_types = 0;
6706
6707 if (ELEM(action, SRT_VIEW_ZAXIS, SRT_VIEW_XAXIS)) {
6708 if (rv3d == nullptr) {
6709 BKE_report(op->reports, RPT_ERROR, "View not found, cannot sort by view axis");
6710 return OPERATOR_CANCELLED;
6711 }
6712 }
6713
6714 /* If no elem_types set, use current selection mode to set it! */
6715 if (RNA_property_is_set(op->ptr, prop_elem_types)) {
6716 elem_types = RNA_property_enum_get(op->ptr, prop_elem_types);
6717 }
6718 else {
6719 BMEditMesh *em = BKE_editmesh_from_object(ob_active);
6720 if (em->selectmode & SCE_SELECT_VERTEX) {
6721 elem_types |= BM_VERT;
6722 }
6723 if (em->selectmode & SCE_SELECT_EDGE) {
6724 elem_types |= BM_EDGE;
6725 }
6726 if (em->selectmode & SCE_SELECT_FACE) {
6727 elem_types |= BM_FACE;
6728 }
6729 RNA_enum_set(op->ptr, "elements", elem_types);
6730 }
6731
6733 scene, view_layer, CTX_wm_view3d(C));
6734
6735 for (uint ob_index = 0; ob_index < objects.size(); ob_index++) {
6736 Object *ob = objects[ob_index];
6738 BMesh *bm = em->bm;
6739
6740 if (!((elem_types & BM_VERT && bm->totvertsel > 0) ||
6741 (elem_types & BM_EDGE && bm->totedgesel > 0) ||
6742 (elem_types & BM_FACE && bm->totfacesel > 0)))
6743 {
6744 continue;
6745 }
6746
6747 int seed_iter = seed;
6748
6749 /* This gives a consistent result regardless of object order */
6750 if (ob_index) {
6751 seed_iter += BLI_ghashutil_strhash_p(ob->id.name);
6752 }
6753
6755 C, scene, ob, rv3d, elem_types, BM_ELEM_SELECT, action, use_reverse, seed_iter);
6756 }
6757 return OPERATOR_FINISHED;
6758}
6759
6761 wmOperator *op,
6762 const PropertyRNA *prop)
6763{
6764 const char *prop_id = RNA_property_identifier(prop);
6765 const int action = RNA_enum_get(op->ptr, "type");
6766
6767 /* Only show seed for randomize action! */
6768 if (STREQ(prop_id, "seed")) {
6769 if (action == SRT_RANDOMIZE) {
6770 return true;
6771 }
6772 return false;
6773 }
6774
6775 /* Hide seed for reverse and randomize actions! */
6776 if (STREQ(prop_id, "reverse")) {
6777 if (ELEM(action, SRT_RANDOMIZE, SRT_REVERSE)) {
6778 return false;
6779 }
6780 return true;
6781 }
6782
6783 return true;
6784}
6785
6787{
6788 static const EnumPropertyItem type_items[] = {
6790 "VIEW_ZAXIS",
6791 0,
6792 "View Z Axis",
6793 "Sort selected elements from farthest to nearest one in current view"},
6795 "VIEW_XAXIS",
6796 0,
6797 "View X Axis",
6798 "Sort selected elements from left to right one in current view"},
6800 "CURSOR_DISTANCE",
6801 0,
6802 "Cursor Distance",
6803 "Sort selected elements from nearest to farthest from 3D cursor"},
6804 {SRT_MATERIAL,
6805 "MATERIAL",
6806 0,
6807 "Material",
6808 "Sort selected faces from smallest to greatest material index"},
6809 {SRT_SELECTED,
6810 "SELECTED",
6811 0,
6812 "Selected",
6813 "Move all selected elements in first places, preserving their relative order.\n"
6814 "Warning: This will affect unselected elements' indices as well"},
6815 {SRT_RANDOMIZE, "RANDOMIZE", 0, "Randomize", "Randomize order of selected elements"},
6816 {SRT_REVERSE, "REVERSE", 0, "Reverse", "Reverse current order of selected elements"},
6817 {0, nullptr, 0, nullptr, nullptr},
6818 };
6819
6820 static const EnumPropertyItem elem_items[] = {
6821 {BM_VERT, "VERT", 0, "Vertices", ""},
6822 {BM_EDGE, "EDGE", 0, "Edges", ""},
6823 {BM_FACE, "FACE", 0, "Faces", ""},
6824 {0, nullptr, 0, nullptr, nullptr},
6825 };
6826
6827 /* identifiers */
6828 ot->name = "Sort Mesh Elements";
6829 ot->description =
6830 "The order of selected vertices/edges/faces is modified, based on a given method";
6831 ot->idname = "MESH_OT_sort_elements";
6832
6833 /* api callbacks */
6838
6839 /* flags */
6841
6842 /* properties */
6844 "type",
6845 type_items,
6847 "Type",
6848 "Type of reordering operation to apply");
6850 "elements",
6851 elem_items,
6852 BM_VERT,
6853 "Elements",
6854 "Which elements to affect (vertices, edges and/or faces)");
6855 RNA_def_boolean(ot->srna, "reverse", false, "Reverse", "Reverse the sorting effect");
6856 RNA_def_int(ot->srna, "seed", 0, 0, INT_MAX, "Seed", "Seed for random-based operations", 0, 255);
6857}
6858
6861/* -------------------------------------------------------------------- */
6865enum {
6869};
6870
6872{
6873 /* tags boundary edges from a face selection */
6874 BMIter iter;
6875 BMFace *f;
6876 BMEdge *e;
6877 int totface_del = 0;
6878
6880
6881 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
6885 }
6886 else {
6887 BMIter fiter;
6888 bool is_all_sel = true;
6889 /* check if its only used by selected faces */
6890 BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) {
6892 /* Tag face for removal. */
6893 if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
6895 totface_del++;
6896 }
6897 }
6898 else {
6899 is_all_sel = false;
6900 }
6901 }
6902
6903 if (is_all_sel == false) {
6905 }
6906 }
6907 }
6908 }
6909
6910 return totface_del;
6911}
6912
6914 BMEditMesh *em,
6915 Mesh *mesh,
6916 const bool use_pairs,
6917 const bool use_cyclic,
6918 const bool use_merge,
6919 const float merge_factor,
6920 const int twist_offset)
6921{
6922 BMOperator bmop;
6923 char edge_hflag;
6924 int totface_del = 0;
6925 BMFace **totface_del_arr = nullptr;
6926 const bool use_faces = (em->bm->totfacesel != 0);
6927 bool changed = false;
6928
6929 if (use_faces) {
6930 /* NOTE: When all faces are selected, all faces will be deleted with no edge-loops remaining.
6931 * In this case bridge will fail with a waning and delete all faces.
6932 * Ideally it's possible to detect cases when deleting faces leaves remaining edge-loops.
6933 * While this can be done in trivial cases - by checking the number of selected faces matches
6934 * the number of faces, that won't work for more involved cases involving hidden faces
6935 * and wire edges. One option could be to copy & restore the edit-mesh however
6936 * this is quite an expensive operation - to properly handle clearly invalid input.
6937 * Accept this limitation, the user must undo to restore the previous state, see: #123405. */
6938
6939 BMIter iter;
6940 BMFace *f;
6941 int i;
6942
6943 totface_del = edbm_bridge_tag_boundary_edges(em->bm);
6944 totface_del_arr = static_cast<BMFace **>(
6945 MEM_mallocN(sizeof(*totface_del_arr) * totface_del, __func__));
6946
6947 i = 0;
6948 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
6950 totface_del_arr[i++] = f;
6951 }
6952 }
6953 edge_hflag = BM_ELEM_TAG;
6954 }
6955 else {
6956 edge_hflag = BM_ELEM_SELECT;
6957 }
6958
6959 EDBM_op_init(em,
6960 &bmop,
6961 op,
6962 "bridge_loops edges=%he use_pairs=%b use_cyclic=%b use_merge=%b merge_factor=%f "
6963 "twist_offset=%i",
6964 edge_hflag,
6965 use_pairs,
6966 use_cyclic,
6967 use_merge,
6968 merge_factor,
6969 twist_offset);
6970
6971 if (use_faces && totface_del) {
6972 int i;
6974 for (i = 0; i < totface_del; i++) {
6975 BM_elem_flag_enable(totface_del_arr[i], BM_ELEM_TAG);
6976 }
6977 BMO_op_callf(em->bm,
6979 "delete geom=%hf context=%i",
6982 changed = true;
6983 }
6984
6985 BMO_op_exec(em->bm, &bmop);
6986
6988 /* when merge is used the edges are joined and remain selected */
6989 if (use_merge == false) {
6992 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
6993
6994 changed = true;
6995 }
6996
6997 if (use_merge == false) {
6998 EdgeRingOpSubdProps op_props;
6999 mesh_operator_edgering_props_get(op, &op_props);
7000
7001 if (op_props.cuts) {
7002 BMOperator bmop_subd;
7003 /* we only need face normals updated */
7005
7006 BMO_op_initf(em->bm,
7007 &bmop_subd,
7008 0,
7009 "subdivide_edgering edges=%S interp_mode=%i cuts=%i smooth=%f "
7010 "profile_shape=%i profile_shape_factor=%f",
7011 &bmop,
7012 "edges.out",
7013 op_props.interp_mode,
7014 op_props.cuts,
7015 op_props.smooth,
7016 op_props.profile_shape,
7017 op_props.profile_shape_factor);
7018 BMO_op_exec(em->bm, &bmop_subd);
7020 em->bm, bmop_subd.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
7021 BMO_op_finish(em->bm, &bmop_subd);
7022
7023 changed = true;
7024 }
7025 }
7026 }
7027
7028 if (totface_del_arr) {
7029 MEM_freeN(totface_del_arr);
7030 }
7031
7032 if (EDBM_op_finish(em, &bmop, op, true)) {
7033 changed = true;
7034 }
7035
7036 if (changed) {
7038 params.calc_looptris = true;
7039 params.calc_normals = false;
7040 params.is_destructive = true;
7041 EDBM_update(mesh, &params);
7042 }
7043
7044 /* Always return finished so the user can select different options. */
7045 return OPERATOR_FINISHED;
7046}
7047
7049{
7050 const int type = RNA_enum_get(op->ptr, "type");
7051 const bool use_pairs = (type == MESH_BRIDGELOOP_PAIRS);
7052 const bool use_cyclic = (type == MESH_BRIDGELOOP_CLOSED);
7053 const bool use_merge = RNA_boolean_get(op->ptr, "use_merge");
7054 const float merge_factor = RNA_float_get(op->ptr, "merge_factor");
7055 const int twist_offset = RNA_int_get(op->ptr, "twist_offset");
7056 const Scene *scene = CTX_data_scene(C);
7057 ViewLayer *view_layer = CTX_data_view_layer(C);
7058
7060 scene, view_layer, CTX_wm_view3d(C));
7061 for (Object *obedit : objects) {
7063
7064 if (em->bm->totvertsel == 0) {
7065 continue;
7066 }
7067
7069 em,
7070 static_cast<Mesh *>(obedit->data),
7071 use_pairs,
7072 use_cyclic,
7073 use_merge,
7074 merge_factor,
7075 twist_offset);
7076 }
7077 return OPERATOR_FINISHED;
7078}
7079
7081{
7082 static const EnumPropertyItem type_items[] = {
7083 {MESH_BRIDGELOOP_SINGLE, "SINGLE", 0, "Open Loop", ""},
7084 {MESH_BRIDGELOOP_CLOSED, "CLOSED", 0, "Closed Loop", ""},
7085 {MESH_BRIDGELOOP_PAIRS, "PAIRS", 0, "Loop Pairs", ""},
7086 {0, nullptr, 0, nullptr, nullptr},
7087 };
7088
7089 /* identifiers */
7090 ot->name = "Bridge Edge Loops";
7091 ot->description = "Create a bridge of faces between two or more selected edge loops";
7092 ot->idname = "MESH_OT_bridge_edge_loops";
7093
7094 /* api callbacks */
7097
7098 /* flags */
7100
7102 "type",
7103 type_items,
7105 "Connect Loops",
7106 "Method of bridging multiple loops");
7107
7108 RNA_def_boolean(ot->srna, "use_merge", false, "Merge", "Merge rather than creating faces");
7109 RNA_def_float(ot->srna, "merge_factor", 0.5f, 0.0f, 1.0f, "Merge Factor", "", 0.0f, 1.0f);
7111 "twist_offset",
7112 0,
7113 -1000,
7114 1000,
7115 "Twist",
7116 "Twist offset for closed loops",
7117 -1000,
7118 1000);
7119
7121}
7122
7125/* -------------------------------------------------------------------- */
7130{
7131 const bool use_boundary = RNA_boolean_get(op->ptr, "use_boundary");
7132 const bool use_even_offset = RNA_boolean_get(op->ptr, "use_even_offset");
7133 const bool use_replace = RNA_boolean_get(op->ptr, "use_replace");
7134 const bool use_relative_offset = RNA_boolean_get(op->ptr, "use_relative_offset");
7135 const bool use_crease = RNA_boolean_get(op->ptr, "use_crease");
7136 const float crease_weight = RNA_float_get(op->ptr, "crease_weight");
7137 const float thickness = RNA_float_get(op->ptr, "thickness");
7138 const float offset = RNA_float_get(op->ptr, "offset");
7139
7140 const Scene *scene = CTX_data_scene(C);
7141 ViewLayer *view_layer = CTX_data_view_layer(C);
7143 scene, view_layer, CTX_wm_view3d(C));
7144 for (Object *obedit : objects) {
7146
7147 if (em->bm->totfacesel == 0) {
7148 continue;
7149 }
7150
7151 BMOperator bmop;
7152
7153 EDBM_op_init(em,
7154 &bmop,
7155 op,
7156 "wireframe faces=%hf use_replace=%b use_boundary=%b use_even_offset=%b "
7157 "use_relative_offset=%b "
7158 "use_crease=%b crease_weight=%f thickness=%f offset=%f",
7160 use_replace,
7161 use_boundary,
7162 use_even_offset,
7163 use_relative_offset,
7164 use_crease,
7165 crease_weight,
7166 thickness,
7167 offset);
7168
7169 BMO_op_exec(em->bm, &bmop);
7170
7173 em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
7174
7175 if (!EDBM_op_finish(em, &bmop, op, true)) {
7176 continue;
7177 }
7178
7180 params.calc_looptris = true;
7181 params.calc_normals = false;
7182 params.is_destructive = true;
7183 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7184 }
7185
7186 return OPERATOR_FINISHED;
7187}
7188
7190{
7191 PropertyRNA *prop;
7192
7193 /* identifiers */
7194 ot->name = "Wireframe";
7195 ot->idname = "MESH_OT_wireframe";
7196 ot->description = "Create a solid wireframe from faces";
7197
7198 /* api callbacks */
7201
7202 /* flags */
7204
7205 /* properties */
7206 RNA_def_boolean(ot->srna, "use_boundary", true, "Boundary", "Inset face boundaries");
7208 "use_even_offset",
7209 true,
7210 "Offset Even",
7211 "Scale the offset to give more even thickness");
7213 "use_relative_offset",
7214 false,
7215 "Offset Relative",
7216 "Scale the offset by surrounding geometry");
7217 RNA_def_boolean(ot->srna, "use_replace", true, "Replace", "Remove original faces");
7219 ot->srna, "thickness", 0.01f, 0.0f, 1e4f, "Thickness", "", 0.0f, 10.0f);
7220 /* use 1 rather than 10 for max else dragging the button moves too far */
7221 RNA_def_property_ui_range(prop, 0.0, 1.0, 0.01, 4);
7222 RNA_def_float_distance(ot->srna, "offset", 0.01f, 0.0f, 1e4f, "Offset", "", 0.0f, 10.0f);
7224 "use_crease",
7225 false,
7226 "Crease",
7227 "Crease hub edges for an improved subdivision surface");
7228 prop = RNA_def_float(
7229 ot->srna, "crease_weight", 0.01f, 0.0f, 1e3f, "Crease Weight", "", 0.0f, 1.0f);
7230 RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 2);
7231}
7232
7235/* -------------------------------------------------------------------- */
7240{
7241 const bool use_cap_endpoint = RNA_boolean_get(op->ptr, "use_cap_endpoint");
7242 bool changed_multi = false;
7243 Scene *scene = CTX_data_scene(C);
7244 ViewLayer *view_layer = CTX_data_view_layer(C);
7246 scene, view_layer, CTX_wm_view3d(C));
7247 for (Base *base : bases) {
7248 Object *obedit = base->object;
7250
7251 if (em->bm->totedgesel == 0) {
7252 continue;
7253 }
7254
7255 BMOperator bmop;
7256 EDBM_op_init(em,
7257 &bmop,
7258 op,
7259 "offset_edgeloops edges=%he use_cap_endpoint=%b",
7261 use_cap_endpoint);
7262
7263 BMO_op_exec(em->bm, &bmop);
7264
7266
7268 em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true);
7269
7270 if (EDBM_op_finish(em, &bmop, op, true)) {
7272 params.calc_looptris = true;
7273 params.calc_normals = false;
7274 params.is_destructive = true;
7275 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7276 changed_multi = true;
7277 }
7278 }
7279
7280 if (changed_multi) {
7286 if (scene->toolsettings->selectmode == SCE_SELECT_FACE) {
7288 }
7289 }
7290
7291 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
7292}
7293
7295{
7296 /* identifiers */
7297 ot->name = "Offset Edge Loop";
7298 ot->idname = "MESH_OT_offset_edge_loops";
7299 ot->description = "Create offset edge loop from the current selection";
7300
7301 /* api callbacks */
7304
7305 /* Keep internal, since this is only meant to be accessed via
7306 * 'MESH_OT_offset_edge_loops_slide'. */
7307
7308 /* flags */
7310
7312 ot->srna, "use_cap_endpoint", false, "Cap Endpoint", "Extend loop around end-points");
7313}
7314
7317/* -------------------------------------------------------------------- */
7321#ifdef WITH_BULLET
7322static int edbm_convex_hull_exec(bContext *C, wmOperator *op)
7323{
7324 const bool use_existing_faces = RNA_boolean_get(op->ptr, "use_existing_faces");
7325 const bool delete_unused = RNA_boolean_get(op->ptr, "delete_unused");
7326 const bool make_holes = RNA_boolean_get(op->ptr, "make_holes");
7327 const bool join_triangles = RNA_boolean_get(op->ptr, "join_triangles");
7328
7329 float angle_face_threshold = RNA_float_get(op->ptr, "face_threshold");
7330 float angle_shape_threshold = RNA_float_get(op->ptr, "shape_threshold");
7331
7332 const Scene *scene = CTX_data_scene(C);
7333 ViewLayer *view_layer = CTX_data_view_layer(C);
7335 scene, view_layer, CTX_wm_view3d(C));
7336 for (Object *obedit : objects) {
7338
7339 if (em->bm->totvertsel == 0) {
7340 continue;
7341 }
7342
7343 BMOperator bmop;
7344
7345 EDBM_op_init(em,
7346 &bmop,
7347 op,
7348 "convex_hull input=%hvef "
7349 "use_existing_faces=%b",
7351 use_existing_faces);
7352 BMO_op_exec(em->bm, &bmop);
7353
7354 /* Hull fails if input is coplanar */
7356 EDBM_op_finish(em, &bmop, op, true);
7357 continue;
7358 }
7359
7361 em->bm, bmop.slots_out, "geom.out", BM_FACE, BM_ELEM_SELECT, true);
7362
7363 /* Delete unused vertices, edges, and faces */
7364 if (delete_unused) {
7365 if (!EDBM_op_callf(
7366 em, op, "delete geom=%S context=%i", &bmop, "geom_unused.out", DEL_ONLYTAGGED))
7367 {
7368 EDBM_op_finish(em, &bmop, op, true);
7369 continue;
7370 }
7371 }
7372
7373 /* Delete hole edges/faces */
7374 if (make_holes) {
7375 if (!EDBM_op_callf(
7376 em, op, "delete geom=%S context=%i", &bmop, "geom_holes.out", DEL_ONLYTAGGED))
7377 {
7378 EDBM_op_finish(em, &bmop, op, true);
7379 continue;
7380 }
7381 }
7382
7383 /* Merge adjacent triangles */
7384 if (join_triangles) {
7386 op,
7387 "faces.out",
7388 true,
7389 "join_triangles faces=%S "
7390 "angle_face_threshold=%f angle_shape_threshold=%f",
7391 &bmop,
7392 "geom.out",
7393 angle_face_threshold,
7394 angle_shape_threshold))
7395 {
7396 EDBM_op_finish(em, &bmop, op, true);
7397 continue;
7398 }
7399 }
7400
7401 if (!EDBM_op_finish(em, &bmop, op, true)) {
7402 continue;
7403 }
7404
7406 params.calc_looptris = true;
7407 params.calc_normals = false;
7408 params.is_destructive = true;
7409 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7411 }
7412
7413 return OPERATOR_FINISHED;
7414}
7415
7417{
7418 /* identifiers */
7419 ot->name = "Convex Hull";
7420 ot->description = "Enclose selected vertices in a convex polyhedron";
7421 ot->idname = "MESH_OT_convex_hull";
7422
7423 /* api callbacks */
7424 ot->exec = edbm_convex_hull_exec;
7426
7427 /* flags */
7429
7430 /* props */
7432 "delete_unused",
7433 true,
7434 "Delete Unused",
7435 "Delete selected elements that are not used by the hull");
7436
7438 "use_existing_faces",
7439 true,
7440 "Use Existing Faces",
7441 "Skip hull triangles that are covered by a pre-existing face");
7442
7444 "make_holes",
7445 false,
7446 "Make Holes",
7447 "Delete selected faces that are used by the hull");
7448
7450 ot->srna, "join_triangles", true, "Join Triangles", "Merge adjacent triangles into quads");
7451
7453}
7454#endif /* WITH_BULLET */
7455
7458/* -------------------------------------------------------------------- */
7463{
7464 const float thresh = RNA_float_get(op->ptr, "threshold");
7465 const Scene *scene = CTX_data_scene(C);
7466 ViewLayer *view_layer = CTX_data_view_layer(C);
7468 scene, view_layer, CTX_wm_view3d(C));
7469
7470 for (Object *obedit : objects) {
7472
7473 if (em->bm->totvertsel == 0) {
7474 continue;
7475 }
7476
7477 BMOperator bmop;
7478 EDBM_op_init(em,
7479 &bmop,
7480 op,
7481 "symmetrize input=%hvef direction=%i dist=%f",
7483 RNA_enum_get(op->ptr, "direction"),
7484 thresh);
7485 BMO_op_exec(em->bm, &bmop);
7486
7488
7490 em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
7491
7492 if (!EDBM_op_finish(em, &bmop, op, true)) {
7493 continue;
7494 }
7496 params.calc_looptris = true;
7497 params.calc_normals = false;
7498 params.is_destructive = true;
7499 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7501 }
7502
7503 return OPERATOR_FINISHED;
7504}
7505
7507{
7508 /* identifiers */
7509 ot->name = "Symmetrize";
7510 ot->description = "Enforce symmetry (both form and topological) across an axis";
7511 ot->idname = "MESH_OT_symmetrize";
7512
7513 /* api callbacks */
7516
7517 /* flags */
7519
7521 "direction",
7524 "Direction",
7525 "Which sides to copy from and to");
7527 "threshold",
7528 1e-4f,
7529 0.0f,
7530 10.0f,
7531 "Threshold",
7532 "Limit for snap middle vertices to the axis center",
7533 1e-5f,
7534 0.1f);
7535}
7536
7539/* -------------------------------------------------------------------- */
7544{
7545 const float eps = 0.00001f;
7546 const float eps_sq = eps * eps;
7547 const bool use_topology = false;
7548
7549 const float thresh = RNA_float_get(op->ptr, "threshold");
7550 const float fac = RNA_float_get(op->ptr, "factor");
7551 const bool use_center = RNA_boolean_get(op->ptr, "use_center");
7552 const int axis_dir = RNA_enum_get(op->ptr, "direction");
7553
7554 /* Vertices stats (total over all selected objects). */
7555 int totvertfound = 0, totvertmirr = 0, totvertfail = 0, totobjects = 0;
7556
7557 /* Axis. */
7558 int axis = axis_dir % 3;
7559 bool axis_sign = axis != axis_dir;
7560
7561 const Scene *scene = CTX_data_scene(C);
7562 ViewLayer *view_layer = CTX_data_view_layer(C);
7564 scene, view_layer, CTX_wm_view3d(C));
7565
7566 for (Object *obedit : objects) {
7568 BMesh *bm = em->bm;
7569
7570 if (em->bm->totvertsel == 0) {
7571 continue;
7572 }
7573
7575 continue;
7576 }
7577
7578 totobjects++;
7579
7580 /* Only allocate memory after checking whether to skip object. */
7581 int *index = static_cast<int *>(MEM_mallocN(bm->totvert * sizeof(*index), __func__));
7582
7583 /* Vertex iter. */
7584 BMIter iter;
7585 BMVert *v;
7586 int i;
7587
7588 EDBM_verts_mirror_cache_begin_ex(em, axis, true, true, false, use_topology, thresh, index);
7589
7591
7593
7594 BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
7595 if ((BM_elem_flag_test(v, BM_ELEM_SELECT) != false) &&
7596 (BM_elem_flag_test(v, BM_ELEM_TAG) == false))
7597 {
7598 int i_mirr = index[i];
7599 if (i_mirr != -1) {
7600
7601 BMVert *v_mirr = BM_vert_at_index(bm, index[i]);
7602
7603 if (v != v_mirr) {
7604 float co[3], co_mirr[3];
7605
7606 if ((v->co[axis] > v_mirr->co[axis]) == axis_sign) {
7607 std::swap(v, v_mirr);
7608 }
7609
7610 copy_v3_v3(co_mirr, v_mirr->co);
7611 co_mirr[axis] *= -1.0f;
7612
7613 if (len_squared_v3v3(v->co, co_mirr) > eps_sq) {
7614 totvertmirr++;
7615 }
7616
7617 interp_v3_v3v3(co, v->co, co_mirr, fac);
7618
7619 copy_v3_v3(v->co, co);
7620
7621 co[axis] *= -1.0f;
7622 copy_v3_v3(v_mirr->co, co);
7623
7626 totvertfound++;
7627 }
7628 else {
7629 if (use_center) {
7630
7631 if (fabsf(v->co[axis]) > eps) {
7632 totvertmirr++;
7633 }
7634
7635 v->co[axis] = 0.0f;
7636 }
7638 totvertfound++;
7639 }
7640 }
7641 else {
7642 totvertfail++;
7643 }
7644 }
7645 }
7647 params.calc_looptris = false;
7648 params.calc_normals = false;
7649 params.is_destructive = false;
7650 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
7651
7652 /* No need to end cache, just free the array. */
7653 MEM_freeN(index);
7654 }
7655
7656 if (totvertfail) {
7657 BKE_reportf(op->reports,
7659 "%d already symmetrical, %d pairs mirrored, %d failed",
7660 totvertfound - totvertmirr,
7661 totvertmirr,
7662 totvertfail);
7663 }
7664 else if (totobjects) {
7665 BKE_reportf(op->reports,
7666 RPT_INFO,
7667 "%d already symmetrical, %d pairs mirrored",
7668 totvertfound - totvertmirr,
7669 totvertmirr);
7670 }
7671
7672 return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
7673}
7674
7676{
7677 /* identifiers */
7678 ot->name = "Snap to Symmetry";
7679 ot->description = "Snap vertex pairs to their mirrored locations";
7680 ot->idname = "MESH_OT_symmetry_snap";
7681
7682 /* api callbacks */
7685
7686 /* flags */
7688
7690 "direction",
7693 "Direction",
7694 "Which sides to copy from and to");
7696 "threshold",
7697 0.05f,
7698 0.0f,
7699 10.0f,
7700 "Threshold",
7701 "Distance within which matching vertices are searched",
7702 1e-4f,
7703 1.0f);
7705 "factor",
7706 0.5f,
7707 0.0f,
7708 1.0f,
7709 "Factor",
7710 "Mix factor of the locations of the vertices",
7711 0.0f,
7712 1.0f);
7714 ot->srna, "use_center", true, "Center", "Snap middle vertices to the axis center");
7715}
7716
7719#if defined(WITH_FREESTYLE)
7720
7721/* -------------------------------------------------------------------- */
7725static int edbm_mark_freestyle_edge_exec(bContext *C, wmOperator *op)
7726{
7727 BMEdge *eed;
7728 BMIter iter;
7729 FreestyleEdge *fed;
7730 const bool clear = RNA_boolean_get(op->ptr, "clear");
7731 const Scene *scene = CTX_data_scene(C);
7732 ViewLayer *view_layer = CTX_data_view_layer(C);
7733
7735 scene, view_layer, CTX_wm_view3d(C));
7736 for (Object *obedit : objects) {
7738
7739 if (em == nullptr) {
7740 continue;
7741 }
7742
7743 BMesh *bm = em->bm;
7744
7745 if (bm->totedgesel == 0) {
7746 continue;
7747 }
7748
7751 }
7752
7753 if (clear) {
7754 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
7756 fed = static_cast<FreestyleEdge *>(
7758 fed->flag &= ~FREESTYLE_EDGE_MARK;
7759 }
7760 }
7761 }
7762 else {
7763 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
7765 fed = static_cast<FreestyleEdge *>(
7767 fed->flag |= FREESTYLE_EDGE_MARK;
7768 }
7769 }
7770 }
7771
7772 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
7773 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
7774 }
7775
7776 return OPERATOR_FINISHED;
7777}
7778
7779void MESH_OT_mark_freestyle_edge(wmOperatorType *ot)
7780{
7781 PropertyRNA *prop;
7782
7783 /* identifiers */
7784 ot->name = "Mark Freestyle Edge";
7785 ot->description = "(Un)mark selected edges as Freestyle feature edges";
7786 ot->idname = "MESH_OT_mark_freestyle_edge";
7787
7788 /* api callbacks */
7789 ot->exec = edbm_mark_freestyle_edge_exec;
7791
7792 /* flags */
7794
7795 prop = RNA_def_boolean(ot->srna, "clear", false, "Clear", "");
7797}
7798
7801/* -------------------------------------------------------------------- */
7805static int edbm_mark_freestyle_face_exec(bContext *C, wmOperator *op)
7806{
7807 BMFace *efa;
7808 BMIter iter;
7809 FreestyleFace *ffa;
7810 const bool clear = RNA_boolean_get(op->ptr, "clear");
7811 const Scene *scene = CTX_data_scene(C);
7812 ViewLayer *view_layer = CTX_data_view_layer(C);
7813
7815 scene, view_layer, CTX_wm_view3d(C));
7816 for (Object *obedit : objects) {
7818
7819 if (em == nullptr) {
7820 continue;
7821 }
7822
7823 if (em->bm->totfacesel == 0) {
7824 continue;
7825 }
7826
7829 }
7830
7831 if (clear) {
7832 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
7834 ffa = static_cast<FreestyleFace *>(
7836 ffa->flag &= ~FREESTYLE_FACE_MARK;
7837 }
7838 }
7839 }
7840 else {
7841 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
7843 ffa = static_cast<FreestyleFace *>(
7845 ffa->flag |= FREESTYLE_FACE_MARK;
7846 }
7847 }
7848 }
7849
7850 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
7851 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
7852 }
7853
7854 return OPERATOR_FINISHED;
7855}
7856
7857void MESH_OT_mark_freestyle_face(wmOperatorType *ot)
7858{
7859 PropertyRNA *prop;
7860
7861 /* identifiers */
7862 ot->name = "Mark Freestyle Face";
7863 ot->description = "(Un)mark selected faces for exclusion from Freestyle feature edge detection";
7864 ot->idname = "MESH_OT_mark_freestyle_face";
7865
7866 /* api callbacks */
7867 ot->exec = edbm_mark_freestyle_face_exec;
7869
7870 /* flags */
7872
7873 prop = RNA_def_boolean(ot->srna, "clear", false, "Clear", "");
7875}
7876
7879#endif /* WITH_FREESTYLE */
7880
7881/* -------------------------------------------------------------------- */
7885/* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
7886/* NOTE: We could add more here, like e.g. a switch between local or global coordinates of target,
7887 * use number-input to type in explicit vector values. */
7888enum {
7889 /* Generic commands. */
7892
7893 /* Point To operator. */
7898
7904};
7905
7907{
7908 static const EnumPropertyItem modal_items[] = {
7909 {EDBM_CLNOR_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
7910 {EDBM_CLNOR_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
7911
7912 /* Point To operator. */
7913 {EDBM_CLNOR_MODAL_POINTTO_RESET, "RESET", 0, "Reset", "Reset normals to initial ones"},
7915 "INVERT",
7916 0,
7917 "Invert",
7918 "Toggle inversion of affected normals"},
7920 "SPHERIZE",
7921 0,
7922 "Spherize",
7923 "Interpolate between new and original normals"},
7924 {EDBM_CLNOR_MODAL_POINTTO_ALIGN, "ALIGN", 0, "Align", "Make all affected normals parallel"},
7925
7927 "USE_MOUSE",
7928 0,
7929 "Use Mouse",
7930 "Follow mouse cursor position"},
7932 "USE_PIVOT",
7933 0,
7934 "Use Pivot",
7935 "Use current rotation/scaling pivot point coordinates"},
7937 "USE_OBJECT",
7938 0,
7939 "Use Object",
7940 "Use current edited object's location"},
7942 "SET_USE_3DCURSOR",
7943 0,
7944 "Set and Use 3D Cursor",
7945 "Set new 3D cursor position and use it"},
7947 "SET_USE_SELECTED",
7948 0,
7949 "Select and Use Mesh Item",
7950 "Select new active mesh element and use its location"},
7951 {0, nullptr, 0, nullptr, nullptr},
7952 };
7953 static const char *keymap_name = "Custom Normals Modal Map";
7954
7955 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, keymap_name);
7956
7957 /* We only need to add map once */
7958 if (keymap && keymap->modal_items) {
7959 return nullptr;
7960 }
7961
7962 keymap = WM_modalkeymap_ensure(keyconf, keymap_name, modal_items);
7963
7964 WM_modalkeymap_assign(keymap, "MESH_OT_point_normals");
7965
7966 return keymap;
7967}
7968
7969#define CLNORS_VALID_VEC_LEN (1e-4f)
7970
7973/* -------------------------------------------------------------------- */
7977enum {
7980};
7981
7984 "COORDINATES",
7985 0,
7986 "Coordinates",
7987 "Use static coordinates (defined by various means)"},
7988 {EDBM_CLNOR_POINTTO_MODE_MOUSE, "MOUSE", 0, "Mouse", "Follow mouse cursor"},
7989 {0, nullptr, 0, nullptr, nullptr},
7990};
7991
7992/* Initialize loop normal data */
7994{
7995 Object *obedit = CTX_data_edit_object(C);
7997 BMesh *bm = em->bm;
7998
8001
8002 op->customdata = lnors_ed_arr;
8003
8004 return (lnors_ed_arr->totloop != 0);
8005}
8006
8008{
8009 if (op->customdata != nullptr) {
8010 return true;
8011 }
8012 return point_normals_init(C, op);
8013}
8014
8016{
8017 if (op->customdata != nullptr) {
8018 BMLoopNorEditDataArray *lnors_ed_arr = static_cast<BMLoopNorEditDataArray *>(op->customdata);
8020 op->customdata = nullptr;
8021 }
8022}
8023
8025{
8027 ED_area_status_text(CTX_wm_area(C), nullptr);
8028}
8029
8031{
8032 auto get_modal_key_str = [&](int id) {
8033 return WM_modalkeymap_operator_items_to_string(op->type, id, true).value_or("");
8034 };
8035
8036 const std::string header = fmt::format(
8037 IFACE_("{}: confirm, {}: cancel, "
8038 "{}: point to mouse ({}), {}: point to Pivot, "
8039 "{}: point to object origin, {}: reset normals, "
8040 "{}: set & point to 3D cursor, {}: select & point to mesh item, "
8041 "{}: invert normals ({}), {}: spherize ({}), {}: align ({})"),
8042 get_modal_key_str(EDBM_CLNOR_MODAL_CONFIRM),
8043 get_modal_key_str(EDBM_CLNOR_MODAL_CANCEL),
8044 get_modal_key_str(EDBM_CLNOR_MODAL_POINTTO_USE_MOUSE),
8046 get_modal_key_str(EDBM_CLNOR_MODAL_POINTTO_USE_PIVOT),
8047 get_modal_key_str(EDBM_CLNOR_MODAL_POINTTO_USE_OBJECT),
8048 get_modal_key_str(EDBM_CLNOR_MODAL_POINTTO_RESET),
8051 get_modal_key_str(EDBM_CLNOR_MODAL_POINTTO_INVERT),
8052 WM_bool_as_string(RNA_boolean_get(op->ptr, "invert")),
8053 get_modal_key_str(EDBM_CLNOR_MODAL_POINTTO_SPHERIZE),
8054 WM_bool_as_string(RNA_boolean_get(op->ptr, "spherize")),
8055 get_modal_key_str(EDBM_CLNOR_MODAL_POINTTO_ALIGN),
8056 WM_bool_as_string(RNA_boolean_get(op->ptr, "align")));
8057
8058 ED_area_status_text(CTX_wm_area(C), header.c_str());
8059}
8060
8061/* TODO: move that to generic function in BMesh? */
8062static void bmesh_selected_verts_center_calc(BMesh *bm, float *r_center)
8063{
8064 BMVert *v;
8065 BMIter viter;
8066 int i = 0;
8067
8068 zero_v3(r_center);
8069 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
8071 add_v3_v3(r_center, v->co);
8072 i++;
8073 }
8074 }
8075 mul_v3_fl(r_center, 1.0f / float(i));
8076}
8077
8078static void point_normals_apply(bContext *C, wmOperator *op, float target[3], const bool do_reset)
8079{
8080 Object *obedit = CTX_data_edit_object(C);
8081 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
8082 BMLoopNorEditDataArray *lnors_ed_arr = static_cast<BMLoopNorEditDataArray *>(op->customdata);
8083
8084 const bool do_invert = RNA_boolean_get(op->ptr, "invert");
8085 const bool do_spherize = RNA_boolean_get(op->ptr, "spherize");
8086 const bool do_align = RNA_boolean_get(op->ptr, "align");
8087 float center[3];
8088
8089 if (do_align && !do_reset) {
8091 }
8092
8093 sub_v3_v3(target, obedit->loc); /* Move target to local coordinates. */
8094
8095 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
8096 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
8097 if (do_reset) {
8098 copy_v3_v3(lnor_ed->nloc, lnor_ed->niloc);
8099 }
8100 else if (do_spherize) {
8101 /* Note that this is *not* real spherical interpolation.
8102 * Probably good enough in this case though? */
8103 const float strength = RNA_float_get(op->ptr, "spherize_strength");
8104 float spherized_normal[3];
8105
8106 sub_v3_v3v3(spherized_normal, target, lnor_ed->loc);
8107
8108 /* otherwise, multiplication by strength is meaningless... */
8109 normalize_v3(spherized_normal);
8110
8111 mul_v3_fl(spherized_normal, strength);
8112 mul_v3_v3fl(lnor_ed->nloc, lnor_ed->niloc, 1.0f - strength);
8113 add_v3_v3(lnor_ed->nloc, spherized_normal);
8114 }
8115 else if (do_align) {
8116 sub_v3_v3v3(lnor_ed->nloc, target, center);
8117 }
8118 else {
8119 sub_v3_v3v3(lnor_ed->nloc, target, lnor_ed->loc);
8120 }
8121
8122 if (do_invert && !do_reset) {
8123 negate_v3(lnor_ed->nloc);
8124 }
8125 if (normalize_v3(lnor_ed->nloc) >= CLNORS_VALID_VEC_LEN) {
8127 bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->nloc, lnor_ed->clnors_data);
8128 }
8129 }
8130}
8131
8132static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent *event)
8133{
8134 /* As this operator passes events through, we can't be sure the user didn't exit edit-mode.
8135 * or performed some other operation. */
8136 if (!WM_operator_poll(C, op->type)) {
8137 point_normals_cancel(C, op);
8138 return OPERATOR_CANCELLED;
8139 }
8140
8141 View3D *v3d = CTX_wm_view3d(C);
8142 Scene *scene = CTX_data_scene(C);
8143 Object *obedit = CTX_data_edit_object(C);
8145 BMesh *bm = em->bm;
8146
8147 float target[3];
8148
8150 int mode = RNA_enum_get(op->ptr, "mode");
8151 int new_mode = mode;
8152 bool force_mousemove = false;
8153 bool do_reset = false;
8154
8155 PropertyRNA *prop_target = RNA_struct_find_property(op->ptr, "target_location");
8156
8157 if (event->type == EVT_MODAL_MAP) {
8158 switch (event->val) {
8160 RNA_property_float_get_array(op->ptr, prop_target, target);
8162 break;
8163
8165 do_reset = true;
8167 break;
8168
8170 do_reset = true;
8172 break;
8173
8175 PropertyRNA *prop_invert = RNA_struct_find_property(op->ptr, "invert");
8177 op->ptr, prop_invert, !RNA_property_boolean_get(op->ptr, prop_invert));
8178 RNA_property_float_get_array(op->ptr, prop_target, target);
8180 break;
8181 }
8182
8184 PropertyRNA *prop_spherize = RNA_struct_find_property(op->ptr, "spherize");
8186 op->ptr, prop_spherize, !RNA_property_boolean_get(op->ptr, prop_spherize));
8187 RNA_property_float_get_array(op->ptr, prop_target, target);
8189 break;
8190 }
8191
8193 PropertyRNA *prop_align = RNA_struct_find_property(op->ptr, "align");
8195 op->ptr, prop_align, !RNA_property_boolean_get(op->ptr, prop_align));
8196 RNA_property_float_get_array(op->ptr, prop_target, target);
8198 break;
8199 }
8200
8203 /* We want to immediately update to mouse cursor position... */
8204 force_mousemove = true;
8206 break;
8207
8210 copy_v3_v3(target, obedit->loc);
8212 break;
8213
8217 copy_v3_v3(target, scene->cursor.location);
8219 break;
8220
8225 params.sel_op = SEL_OP_SET;
8226 if (EDBM_select_pick(C, event->mval, &params)) {
8227 /* Point to newly selected active. */
8229
8230 add_v3_v3(target, obedit->loc);
8232 }
8233 break;
8234 }
8237 switch (scene->toolsettings->transform_pivot_point) {
8238 case V3D_AROUND_CENTER_BOUNDS: /* calculateCenterBound */
8239 {
8240 BMVert *v;
8241 BMIter viter;
8242 float min[3], max[3];
8243 int i = 0;
8244
8245 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
8247 if (i) {
8248 minmax_v3v3_v3(min, max, v->co);
8249 }
8250 else {
8251 copy_v3_v3(min, v->co);
8252 copy_v3_v3(max, v->co);
8253 }
8254 i++;
8255 }
8256 }
8257 mid_v3_v3v3(target, min, max);
8258 add_v3_v3(target, obedit->loc);
8259 break;
8260 }
8261
8264 add_v3_v3(target, obedit->loc);
8265 break;
8266 }
8267
8268 case V3D_AROUND_CURSOR:
8269 copy_v3_v3(target, scene->cursor.location);
8270 break;
8271
8272 case V3D_AROUND_ACTIVE:
8273 if (!blender::ed::object::calc_active_center_for_editmode(obedit, false, target)) {
8274 zero_v3(target);
8275 }
8276 add_v3_v3(target, obedit->loc);
8277 break;
8278
8279 default:
8280 BKE_report(op->reports, RPT_WARNING, "Does not support Individual Origins as pivot");
8281 copy_v3_v3(target, obedit->loc);
8282 }
8284 break;
8285 }
8286 default:
8287 break;
8288 }
8289 }
8290
8291 if (new_mode != mode) {
8292 mode = new_mode;
8293 RNA_enum_set(op->ptr, "mode", mode);
8294 }
8295
8296 /* Only handle mouse-move event in case we are in mouse mode. */
8297 if (event->type == MOUSEMOVE || force_mousemove) {
8298 if (mode == EDBM_CLNOR_POINTTO_MODE_MOUSE) {
8299 ARegion *region = CTX_wm_region(C);
8300 float center[3];
8301
8303
8304 ED_view3d_win_to_3d_int(v3d, region, center, event->mval, target);
8305
8307 }
8308 }
8309
8310 if (ret != OPERATOR_PASS_THROUGH) {
8312 RNA_property_float_set_array(op->ptr, prop_target, target);
8313 }
8314
8315 if (point_normals_ensure(C, op)) {
8316 point_normals_apply(C, op, target, do_reset);
8318 params.calc_looptris = true;
8319 params.calc_normals = false;
8320 params.is_destructive = false;
8321 /* Recheck booleans. */
8322 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
8323
8325 }
8326 else {
8328 }
8329 }
8330
8332 point_normals_cancel(C, op);
8333 }
8334
8335 /* If we allow other tools to run, we can't be sure if they will re-allocate
8336 * the data this operator uses, see: #68159.
8337 * Free the data here, then use #point_normals_ensure to add it back on demand. */
8338 if (ret == OPERATOR_PASS_THROUGH) {
8339 /* Don't free on mouse-move, causes creation/freeing of the loop data in an inefficient way. */
8340 if (!ISMOUSE_MOTION(event->type)) {
8342 }
8343 }
8344 return ret;
8345}
8346
8347static int edbm_point_normals_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
8348{
8349 if (!point_normals_init(C, op)) {
8350 point_normals_cancel(C, op);
8351 return OPERATOR_CANCELLED;
8352 }
8353
8355
8357
8360}
8361
8362/* TODO: make this work on multiple objects at once */
8364{
8365 Object *obedit = CTX_data_edit_object(C);
8366
8367 if (!point_normals_init(C, op)) {
8368 point_normals_cancel(C, op);
8369 return OPERATOR_CANCELLED;
8370 }
8371
8372 /* Note that 'mode' is ignored in exec case,
8373 * we directly use vector stored in target_location, whatever that is. */
8374
8375 float target[3];
8376 RNA_float_get_array(op->ptr, "target_location", target);
8377
8378 point_normals_apply(C, op, target, false);
8379
8381 params.calc_looptris = true;
8382 params.calc_normals = false;
8383 params.is_destructive = false;
8384 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
8385 point_normals_cancel(C, op);
8386
8387 return OPERATOR_FINISHED;
8388}
8389
8390static bool point_normals_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop, void * /*user_data*/)
8391{
8392 const char *prop_id = RNA_property_identifier(prop);
8393
8394 /* Only show strength option if spherize is enabled. */
8395 if (STREQ(prop_id, "spherize_strength")) {
8396 return RNA_boolean_get(ptr, "spherize");
8397 }
8398
8399 /* Else, show it! */
8400 return true;
8401}
8402
8404{
8405 uiLayout *layout = op->layout;
8407
8409
8410 uiLayoutSetPropSep(layout, true);
8411
8412 /* Main auto-draw call */
8413 uiDefAutoButsRNA(layout,
8414 &ptr,
8416 nullptr,
8417 nullptr,
8419 false);
8420}
8421
8423{
8424 /* identifiers */
8425 ot->name = "Point Normals to Target";
8426 ot->description = "Point selected custom normals to specified Target";
8427 ot->idname = "MESH_OT_point_normals";
8428
8429 /* api callbacks */
8436
8437 /* flags */
8439
8441 "mode",
8444 "Mode",
8445 "How to define coordinates to point custom normals to");
8447
8448 RNA_def_boolean(ot->srna, "invert", false, "Invert", "Invert affected normals");
8449
8450 RNA_def_boolean(ot->srna, "align", false, "Align", "Make all affected normals parallel");
8451
8453 "target_location",
8454 3,
8455 nullptr,
8456 -FLT_MAX,
8457 FLT_MAX,
8458 "Target",
8459 "Target location to which normals will point",
8460 -1000.0f,
8461 1000.0f);
8462
8464 ot->srna, "spherize", false, "Spherize", "Interpolate between original and new normals");
8465
8467 "spherize_strength",
8468 0.1,
8469 0.0f,
8470 1.0f,
8471 "Spherize Strength",
8472 "Ratio of spherized normal to original normal",
8473 0.0f,
8474 1.0f);
8475}
8476
8479/* -------------------------------------------------------------------- */
8483static void normals_merge(BMesh *bm, BMLoopNorEditDataArray *lnors_ed_arr)
8484{
8485 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
8486
8487 BLI_SMALLSTACK_DECLARE(clnors, short *);
8488
8490
8492
8493 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
8495
8496 if (BM_elem_flag_test(lnor_ed->loop, BM_ELEM_TAG)) {
8497 continue;
8498 }
8499
8500 MLoopNorSpace *lnor_space = bm->lnor_spacearr->lspacearr[lnor_ed->loop_index];
8501
8502 if ((lnor_space->flags & MLNOR_SPACE_IS_SINGLE) == 0) {
8503 LinkNode *loops = lnor_space->loops;
8504 float avg_normal[3] = {0.0f, 0.0f, 0.0f};
8505 short *clnors_data;
8506
8507 for (; loops; loops = loops->next) {
8508 BMLoop *l = static_cast<BMLoop *>(loops->link);
8509 const int loop_index = BM_elem_index_get(l);
8510
8511 BMLoopNorEditData *lnor_ed_tmp = lnors_ed_arr->lidx_to_lnor_editdata[loop_index];
8512 BLI_assert(lnor_ed_tmp->loop_index == loop_index && lnor_ed_tmp->loop == l);
8513 add_v3_v3(avg_normal, lnor_ed_tmp->nloc);
8514 BLI_SMALLSTACK_PUSH(clnors, lnor_ed_tmp->clnors_data);
8516 }
8517 if (normalize_v3(avg_normal) < CLNORS_VALID_VEC_LEN) {
8518 /* If avg normal is nearly 0, set clnor to default value. */
8519 zero_v3(avg_normal);
8520 }
8521 while ((clnors_data = static_cast<short *>(BLI_SMALLSTACK_POP(clnors)))) {
8522 BKE_lnor_space_custom_normal_to_data(lnor_space, avg_normal, clnors_data);
8523 }
8524 }
8525 }
8526}
8527
8529{
8530 BMFace *f;
8531 BMLoop *l, *l_curr, *l_first;
8532 BMIter fiter;
8533
8535
8537
8538 BLI_SMALLSTACK_DECLARE(loop_stack, BMLoop *);
8539
8540 const int cd_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
8541 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
8543
8544 l_curr = l_first = BM_FACE_FIRST_LOOP(f);
8545 do {
8546 if (BM_elem_flag_test(l_curr->v, BM_ELEM_SELECT) &&
8547 (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) ||
8549 {
8550 if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
8551 !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG))
8552 {
8553 const int loop_index = BM_elem_index_get(l_curr);
8554 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l_curr, cd_clnors_offset));
8556 bm->lnor_spacearr->lspacearr[loop_index], f->no, clnors);
8557 }
8558 else {
8559 BMVert *v_pivot = l_curr->v;
8560 UNUSED_VARS_NDEBUG(v_pivot);
8561 BMEdge *e_next;
8562 const BMEdge *e_org = l_curr->e;
8563 BMLoop *lfan_pivot, *lfan_pivot_next;
8564
8565 lfan_pivot = l_curr;
8566 e_next = lfan_pivot->e;
8567 float avg_normal[3] = {0.0f};
8568
8569 while (true) {
8570 lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next);
8571 if (lfan_pivot_next) {
8572 BLI_assert(lfan_pivot_next->v == v_pivot);
8573 }
8574 else {
8575 e_next = (lfan_pivot->e == e_next) ? lfan_pivot->prev->e : lfan_pivot->e;
8576 }
8577
8578 BLI_SMALLSTACK_PUSH(loop_stack, lfan_pivot);
8579 add_v3_v3(avg_normal, lfan_pivot->f->no);
8580
8581 if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) {
8582 break;
8583 }
8584 lfan_pivot = lfan_pivot_next;
8585 }
8586 if (normalize_v3(avg_normal) < CLNORS_VALID_VEC_LEN) {
8587 /* If avg normal is nearly 0, set clnor to default value. */
8588 zero_v3(avg_normal);
8589 }
8590 while ((l = static_cast<BMLoop *>(BLI_SMALLSTACK_POP(loop_stack)))) {
8591 const int l_index = BM_elem_index_get(l);
8592 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
8594 bm->lnor_spacearr->lspacearr[l_index], avg_normal, clnors);
8595 }
8596 }
8597 }
8598 } while ((l_curr = l_curr->next) != l_first);
8599 }
8600}
8601
8602static int normals_split_merge(bContext *C, const bool do_merge)
8603{
8604 const Scene *scene = CTX_data_scene(C);
8605 ViewLayer *view_layer = CTX_data_view_layer(C);
8607 scene, view_layer, CTX_wm_view3d(C));
8608
8609 for (Object *obedit : objects) {
8611 BMesh *bm = em->bm;
8612 BMEdge *e;
8613 BMIter eiter;
8614
8616
8617 /* Note that we need temp lnor editing data for all loops of all affected vertices, since by
8618 * setting some faces/edges as smooth we are going to change clnors spaces... See also #65809.
8619 */
8620 BMLoopNorEditDataArray *lnors_ed_arr = do_merge ?
8622 nullptr;
8623
8624 mesh_set_smooth_faces(em, do_merge);
8625
8626 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
8628 BM_elem_flag_set(e, BM_ELEM_SMOOTH, do_merge);
8629 }
8630 }
8631
8634
8635 if (do_merge) {
8636 normals_merge(bm, lnors_ed_arr);
8637 }
8638 else {
8640 }
8641
8642 if (lnors_ed_arr) {
8644 }
8645
8647 params.calc_looptris = true;
8648 params.calc_normals = false;
8649 params.is_destructive = false;
8650 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
8651 }
8652
8653 return OPERATOR_FINISHED;
8654}
8655
8657{
8658 return normals_split_merge(C, true);
8659}
8660
8662{
8663 /* identifiers */
8664 ot->name = "Merge Normals";
8665 ot->description = "Merge custom normals of selected vertices";
8666 ot->idname = "MESH_OT_merge_normals";
8667
8668 /* api callbacks */
8671
8672 /* flags */
8674}
8675
8677{
8678 return normals_split_merge(C, false);
8679}
8680
8682{
8683 /* identifiers */
8684 ot->name = "Split Normals";
8685 ot->description = "Split custom normals of selected vertices";
8686 ot->idname = "MESH_OT_split_normals";
8687
8688 /* api callbacks */
8691
8692 /* flags */
8694}
8695
8698/* -------------------------------------------------------------------- */
8702enum {
8706};
8707
8710 "CUSTOM_NORMAL",
8711 0,
8712 "Custom Normal",
8713 "Take average of vertex normals"},
8715 "FACE_AREA",
8716 0,
8717 "Face Area",
8718 "Set all vertex normals by face area"},
8720 "CORNER_ANGLE",
8721 0,
8722 "Corner Angle",
8723 "Set all vertex normals by corner angle"},
8724 {0, nullptr, 0, nullptr, nullptr},
8725};
8726
8728{
8729 const Scene *scene = CTX_data_scene(C);
8730 ViewLayer *view_layer = CTX_data_view_layer(C);
8732 scene, view_layer, CTX_wm_view3d(C));
8733 const int average_type = RNA_enum_get(op->ptr, "average_type");
8734 const float absweight = float(RNA_int_get(op->ptr, "weight"));
8735 const float threshold = RNA_float_get(op->ptr, "threshold");
8736
8737 HeapSimple *loop_weight = BLI_heapsimple_new();
8738 BLI_SMALLSTACK_DECLARE(loop_stack, BMLoop *);
8739
8740 for (uint ob_index = 0; ob_index < objects.size(); ob_index++) {
8742 BLI_assert(BLI_heapsimple_is_empty(loop_weight));
8743
8744 Object *obedit = objects[ob_index];
8746 BMesh *bm = em->bm;
8747 BMFace *f;
8748 BMLoop *l, *l_curr, *l_first;
8749 BMIter fiter;
8750
8753
8754 const int cd_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
8755
8756 float weight = absweight / 50.0f;
8757 if (absweight == 100.0f) {
8758 weight = float(SHRT_MAX);
8759 }
8760 else if (absweight == 1.0f) {
8761 weight = 1 / float(SHRT_MAX);
8762 }
8763 else if ((weight - 1) * 25 > 1) {
8764 weight = (weight - 1) * 25;
8765 }
8766
8768
8769 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
8770 l_curr = l_first = BM_FACE_FIRST_LOOP(f);
8771 do {
8772 if (BM_elem_flag_test(l_curr->v, BM_ELEM_SELECT) &&
8773 (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) ||
8775 {
8776 if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
8777 !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG))
8778 {
8779 const int loop_index = BM_elem_index_get(l_curr);
8780 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l_curr, cd_clnors_offset));
8782 bm->lnor_spacearr->lspacearr[loop_index], f->no, clnors);
8783 }
8784 else {
8785 BMVert *v_pivot = l_curr->v;
8786 UNUSED_VARS_NDEBUG(v_pivot);
8787 BMEdge *e_next;
8788 const BMEdge *e_org = l_curr->e;
8789 BMLoop *lfan_pivot, *lfan_pivot_next;
8790
8791 lfan_pivot = l_curr;
8792 e_next = lfan_pivot->e;
8793
8794 while (true) {
8795 lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next);
8796 if (lfan_pivot_next) {
8797 BLI_assert(lfan_pivot_next->v == v_pivot);
8798 }
8799 else {
8800 e_next = (lfan_pivot->e == e_next) ? lfan_pivot->prev->e : lfan_pivot->e;
8801 }
8802
8803 float val = 1.0f;
8804 if (average_type == EDBM_CLNOR_AVERAGE_FACE_AREA) {
8805 val = 1.0f / BM_face_calc_area(lfan_pivot->f);
8806 }
8807 else if (average_type == EDBM_CLNOR_AVERAGE_ANGLE) {
8808 val = 1.0f / BM_loop_calc_face_angle(lfan_pivot);
8809 }
8810
8811 BLI_heapsimple_insert(loop_weight, val, lfan_pivot);
8812
8813 if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) {
8814 break;
8815 }
8816 lfan_pivot = lfan_pivot_next;
8817 }
8818
8819 float wnor[3], avg_normal[3] = {0.0f}, count = 0;
8820 float val = BLI_heapsimple_top_value(loop_weight);
8821
8822 while (!BLI_heapsimple_is_empty(loop_weight)) {
8823 const float cur_val = BLI_heapsimple_top_value(loop_weight);
8824 if (!compare_ff(val, cur_val, threshold)) {
8825 count++;
8826 val = cur_val;
8827 }
8828 l = static_cast<BMLoop *>(BLI_heapsimple_pop_min(loop_weight));
8829 BLI_SMALLSTACK_PUSH(loop_stack, l);
8830
8831 const float n_weight = pow(weight, count);
8832
8833 if (average_type == EDBM_CLNOR_AVERAGE_LOOP) {
8834 const int l_index = BM_elem_index_get(l);
8835 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
8837 bm->lnor_spacearr->lspacearr[l_index], clnors, wnor);
8838 }
8839 else {
8840 copy_v3_v3(wnor, l->f->no);
8841 }
8842 mul_v3_fl(wnor, (1.0f / cur_val) * (1.0f / n_weight));
8843 add_v3_v3(avg_normal, wnor);
8844 }
8845
8846 if (normalize_v3(avg_normal) < CLNORS_VALID_VEC_LEN) {
8847 /* If avg normal is nearly 0, set clnor to default value. */
8848 zero_v3(avg_normal);
8849 }
8850 while ((l = static_cast<BMLoop *>(BLI_SMALLSTACK_POP(loop_stack)))) {
8851 const int l_index = BM_elem_index_get(l);
8852 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
8854 bm->lnor_spacearr->lspacearr[l_index], avg_normal, clnors);
8855 }
8856 }
8857 }
8858 } while ((l_curr = l_curr->next) != l_first);
8859 }
8860
8862 params.calc_looptris = true;
8863 params.calc_normals = false;
8864 params.is_destructive = false;
8865 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
8866 }
8867
8868 BLI_heapsimple_free(loop_weight, nullptr);
8869
8870 return OPERATOR_FINISHED;
8871}
8872
8874 PropertyRNA *prop,
8875 void * /*user_data*/)
8876{
8877 const char *prop_id = RNA_property_identifier(prop);
8878 const int average_type = RNA_enum_get(ptr, "average_type");
8879
8880 /* Only show weight/threshold options when not in loop average type. */
8881 const bool is_clor_average_loop = average_type == EDBM_CLNOR_AVERAGE_LOOP;
8882 if (STREQ(prop_id, "weight")) {
8883 return !is_clor_average_loop;
8884 }
8885 if (STREQ(prop_id, "threshold")) {
8886 return !is_clor_average_loop;
8887 }
8888
8889 /* Else, show it! */
8890 return true;
8891}
8892
8894{
8895 uiLayout *layout = op->layout;
8897
8899
8900 uiLayoutSetPropSep(layout, true);
8901
8902 /* Main auto-draw call */
8903 uiDefAutoButsRNA(layout,
8904 &ptr,
8906 nullptr,
8907 nullptr,
8909 false);
8910}
8911
8913{
8914 /* identifiers */
8915 ot->name = "Average Normals";
8916 ot->description = "Average custom normals of selected vertices";
8917 ot->idname = "MESH_OT_average_normals";
8918
8919 /* api callbacks */
8923
8924 /* flags */
8926
8928 "average_type",
8931 "Type",
8932 "Averaging method");
8933
8934 RNA_def_int(ot->srna, "weight", 50, 1, 100, "Weight", "Weight applied per face", 1, 100);
8935
8937 "threshold",
8938 0.01f,
8939 0,
8940 10,
8941 "Threshold",
8942 "Threshold value for different weights to be considered equal",
8943 0,
8944 5);
8945}
8946
8949/* -------------------------------------------------------------------- */
8953enum {
8959};
8960
8962 {EDBM_CLNOR_TOOLS_COPY, "COPY", 0, "Copy Normal", "Copy normal to the internal clipboard"},
8964 "PASTE",
8965 0,
8966 "Paste Normal",
8967 "Paste normal from the internal clipboard"},
8968 {EDBM_CLNOR_TOOLS_ADD, "ADD", 0, "Add Normal", "Add normal vector with selection"},
8970 "MULTIPLY",
8971 0,
8972 "Multiply Normal",
8973 "Multiply normal vector with selection"},
8975 "RESET",
8976 0,
8977 "Reset Normal",
8978 "Reset the internal clipboard and/or normal of selected element"},
8979 {0, nullptr, 0, nullptr, nullptr},
8980};
8981
8983{
8984 Scene *scene = CTX_data_scene(C);
8985 ViewLayer *view_layer = CTX_data_view_layer(C);
8987 scene, view_layer, CTX_wm_view3d(C));
8988 const int mode = RNA_enum_get(op->ptr, "mode");
8989 const bool absolute = RNA_boolean_get(op->ptr, "absolute");
8990 float *normal_vector = scene->toolsettings->normal_vector;
8991 bool done_copy = false;
8992
8993 for (Object *obedit : objects) {
8995 BMesh *bm = em->bm;
8996
8997 if (bm->totloop == 0) {
8998 continue;
8999 }
9000
9003 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
9004
9005 switch (mode) {
9007 if (bm->totfacesel == 0 && bm->totvertsel == 0) {
9009 continue;
9010 }
9011
9012 if (done_copy ||
9013 (bm->totfacesel != 1 && lnors_ed_arr->totloop != 1 && bm->totvertsel != 1))
9014 {
9015 BKE_report(op->reports,
9016 RPT_ERROR,
9017 "Can only copy one custom normal, vertex normal or face normal");
9019 continue;
9020 }
9021 if (lnors_ed_arr->totloop == 1) {
9022 copy_v3_v3(scene->toolsettings->normal_vector, lnors_ed_arr->lnor_editdata->nloc);
9023 }
9024 else if (bm->totfacesel == 1) {
9025 BMFace *f;
9026 BMIter fiter;
9027 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
9029 copy_v3_v3(scene->toolsettings->normal_vector, f->no);
9030 }
9031 }
9032 }
9033 else {
9034 /* 'Vertex' normal, i.e. common set of loop normals on the same vertex,
9035 * only if they are all the same. */
9036 bool are_same_lnors = true;
9037 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9038 if (!compare_v3v3(lnors_ed_arr->lnor_editdata->nloc, lnor_ed->nloc, 1e-4f)) {
9039 are_same_lnors = false;
9040 }
9041 }
9042 if (are_same_lnors) {
9043 copy_v3_v3(scene->toolsettings->normal_vector, lnors_ed_arr->lnor_editdata->nloc);
9044 }
9045 }
9046 done_copy = true;
9047 break;
9048
9050 if (!absolute) {
9051 if (normalize_v3(normal_vector) < CLNORS_VALID_VEC_LEN) {
9052 /* If normal is nearly 0, do nothing. */
9053 break;
9054 }
9055 }
9056 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9057 if (absolute) {
9058 float abs_normal[3];
9059 copy_v3_v3(abs_normal, lnor_ed->loc);
9060 negate_v3(abs_normal);
9061 add_v3_v3(abs_normal, normal_vector);
9062
9063 if (normalize_v3(abs_normal) < CLNORS_VALID_VEC_LEN) {
9064 /* If abs normal is nearly 0, set clnor to initial value. */
9065 copy_v3_v3(abs_normal, lnor_ed->niloc);
9066 }
9068 abs_normal,
9069 lnor_ed->clnors_data);
9070 }
9071 else {
9073 normal_vector,
9074 lnor_ed->clnors_data);
9075 }
9076 }
9077 break;
9078
9080 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9081 mul_v3_v3(lnor_ed->nloc, normal_vector);
9082
9083 if (normalize_v3(lnor_ed->nloc) < CLNORS_VALID_VEC_LEN) {
9084 /* If abs normal is nearly 0, set clnor to initial value. */
9085 copy_v3_v3(lnor_ed->nloc, lnor_ed->niloc);
9086 }
9088 lnor_ed->nloc,
9089 lnor_ed->clnors_data);
9090 }
9091 break;
9092
9094 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9095 add_v3_v3(lnor_ed->nloc, normal_vector);
9096
9097 if (normalize_v3(lnor_ed->nloc) < CLNORS_VALID_VEC_LEN) {
9098 /* If abs normal is nearly 0, set clnor to initial value. */
9099 copy_v3_v3(lnor_ed->nloc, lnor_ed->niloc);
9100 }
9102 lnor_ed->nloc,
9103 lnor_ed->clnors_data);
9104 }
9105 break;
9106
9108 zero_v3(normal_vector);
9109 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9111 normal_vector,
9112 lnor_ed->clnors_data);
9113 }
9114 break;
9115
9116 default:
9117 BLI_assert(0);
9118 break;
9119 }
9120
9122
9124 params.calc_looptris = true;
9125 params.calc_normals = false;
9126 params.is_destructive = false;
9127 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
9128 }
9129
9130 return OPERATOR_FINISHED;
9131}
9132
9133static bool normals_tools_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop, void * /*user_data*/)
9134{
9135 const char *prop_id = RNA_property_identifier(prop);
9136 const int mode = RNA_enum_get(ptr, "mode");
9137
9138 /* Only show absolute option in paste mode. */
9139 if (STREQ(prop_id, "absolute")) {
9140 return (mode == EDBM_CLNOR_TOOLS_PASTE);
9141 }
9142
9143 /* Else, show it! */
9144 return true;
9145}
9146
9148{
9149 uiLayout *layout = op->layout;
9151
9153
9154 /* Main auto-draw call */
9155 uiDefAutoButsRNA(layout,
9156 &ptr,
9158 nullptr,
9159 nullptr,
9161 false);
9162}
9163
9165{
9166 /* identifiers */
9167 ot->name = "Normals Vector Tools";
9168 ot->description = "Custom normals tools using Normal Vector of UI";
9169 ot->idname = "MESH_OT_normals_tools";
9170
9171 /* api callbacks */
9175
9176 /* flags */
9178
9180 "mode",
9183 "Mode",
9184 "Mode of tools taking input from interface");
9186
9188 "absolute",
9189 false,
9190 "Absolute Coordinates",
9191 "Copy Absolute coordinates of Normal vector");
9192}
9193
9196/* -------------------------------------------------------------------- */
9201{
9202 const Scene *scene = CTX_data_scene(C);
9203 ViewLayer *view_layer = CTX_data_view_layer(C);
9205 scene, view_layer, CTX_wm_view3d(C));
9206
9207 for (Object *obedit : objects) {
9209 BMesh *bm = em->bm;
9210 if (bm->totfacesel == 0) {
9211 continue;
9212 }
9213
9214 BMFace *f;
9215 BMVert *v;
9216 BMEdge *e;
9217 BMLoop *l;
9218 BMIter fiter, viter, eiter, liter;
9219
9220 const bool keep_sharp = RNA_boolean_get(op->ptr, "keep_sharp");
9221
9223
9224 float(*vert_normals)[3] = static_cast<float(*)[3]>(
9225 MEM_mallocN(sizeof(*vert_normals) * bm->totvert, __func__));
9226 {
9227 int v_index;
9228 BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, v_index) {
9229 BM_vert_calc_normal_ex(v, BM_ELEM_SELECT, vert_normals[v_index]);
9230 }
9231 }
9232
9233 BLI_bitmap *loop_set = BLI_BITMAP_NEW(bm->totloop, __func__);
9234 const int cd_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
9235
9236 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
9237 BM_ITER_ELEM (e, &eiter, f, BM_EDGES_OF_FACE) {
9238 if (!keep_sharp ||
9240 {
9241 BM_ITER_ELEM (v, &viter, e, BM_VERTS_OF_EDGE) {
9243 const int l_index = BM_elem_index_get(l);
9244 const int v_index = BM_elem_index_get(l->v);
9245
9246 if (!is_zero_v3(vert_normals[v_index])) {
9247 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
9249 bm->lnor_spacearr->lspacearr[l_index], vert_normals[v_index], clnors);
9250
9252 BLI_BITMAP_ENABLE(loop_set, l_index);
9253 }
9254 else {
9255 LinkNode *loops = bm->lnor_spacearr->lspacearr[l_index]->loops;
9256 for (; loops; loops = loops->next) {
9257 BLI_BITMAP_ENABLE(loop_set, BM_elem_index_get((BMLoop *)loops->link));
9258 }
9259 }
9260 }
9261 }
9262 }
9263 }
9264 }
9265
9266 int v_index;
9267 BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, v_index) {
9268 BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
9269 if (BLI_BITMAP_TEST(loop_set, BM_elem_index_get(l))) {
9270 const int loop_index = BM_elem_index_get(l);
9271 short *clnors = static_cast<short *>(BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset));
9273 bm->lnor_spacearr->lspacearr[loop_index], vert_normals[v_index], clnors);
9274 }
9275 }
9276 }
9277
9278 MEM_freeN(loop_set);
9279 MEM_freeN(vert_normals);
9281 params.calc_looptris = true;
9282 params.calc_normals = false;
9283 params.is_destructive = false;
9284 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
9285 }
9286
9287 return OPERATOR_FINISHED;
9288}
9289
9291{
9292 /* identifiers */
9293 ot->name = "Set Normals from Faces";
9294 ot->description = "Set the custom normals from the selected faces ones";
9295 ot->idname = "MESH_OT_set_normals_from_faces";
9296
9297 /* api callbacks */
9300
9301 /* flags */
9303
9305 ot->srna, "keep_sharp", false, "Keep Sharp Edges", "Do not set sharp edges to face");
9306}
9307
9310/* -------------------------------------------------------------------- */
9315{
9316 const Scene *scene = CTX_data_scene(C);
9317 ViewLayer *view_layer = CTX_data_view_layer(C);
9319 scene, view_layer, CTX_wm_view3d(C));
9320
9321 for (Object *obedit : objects) {
9323 BMesh *bm = em->bm;
9324 BMFace *f;
9325 BMLoop *l;
9326 BMIter fiter, liter;
9327
9330
9331 float(*smooth_normal)[3] = static_cast<float(*)[3]>(
9332 MEM_callocN(sizeof(*smooth_normal) * lnors_ed_arr->totloop, __func__));
9333
9334 /* NOTE(@mont29): This is weird choice of operation, taking all loops of faces of current
9335 * vertex. Could lead to some rather far away loops weighting as much as very close ones
9336 * (topologically speaking), with complex polygons.
9337 * Using topological distance here (rather than geometrical one)
9338 * makes sense IMHO, but would rather go with a more consistent and flexible code,
9339 * we could even add max topological distance to take into account, and a weighting curve.
9340 * Would do that later though, think for now we can live with that choice. */
9341 BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
9342 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9343 l = lnor_ed->loop;
9344 float loop_normal[3];
9345
9346 BM_ITER_ELEM (f, &fiter, l->v, BM_FACES_OF_VERT) {
9347 BMLoop *l_other;
9348 BM_ITER_ELEM (l_other, &liter, f, BM_LOOPS_OF_FACE) {
9349 const int l_index_other = BM_elem_index_get(l_other);
9350 short *clnors = static_cast<short *>(
9351 BM_ELEM_CD_GET_VOID_P(l_other, lnors_ed_arr->cd_custom_normal_offset));
9353 bm->lnor_spacearr->lspacearr[l_index_other], clnors, loop_normal);
9354 add_v3_v3(smooth_normal[i], loop_normal);
9355 }
9356 }
9357 }
9358
9359 const float factor = RNA_float_get(op->ptr, "factor");
9360
9361 lnor_ed = lnors_ed_arr->lnor_editdata;
9362 for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
9363 float current_normal[3];
9364
9365 if (normalize_v3(smooth_normal[i]) < CLNORS_VALID_VEC_LEN) {
9366 /* Skip in case the smooth normal is invalid. */
9367 continue;
9368 }
9369
9371 bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->clnors_data, current_normal);
9372
9373 /* NOTE: again, this is not true spherical interpolation that normals would need...
9374 * But it's probably good enough for now. */
9375 mul_v3_fl(current_normal, 1.0f - factor);
9376 mul_v3_fl(smooth_normal[i], factor);
9377 add_v3_v3(current_normal, smooth_normal[i]);
9378
9379 if (normalize_v3(current_normal) < CLNORS_VALID_VEC_LEN) {
9380 /* Skip in case the smoothed normal is invalid. */
9381 continue;
9382 }
9383
9385 bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], current_normal, lnor_ed->clnors_data);
9386 }
9387
9389 MEM_freeN(smooth_normal);
9390
9392 params.calc_looptris = true;
9393 params.calc_normals = false;
9394 params.is_destructive = false;
9395 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
9396 }
9397
9398 return OPERATOR_FINISHED;
9399}
9400
9402{
9403 /* identifiers */
9404 ot->name = "Smooth Normals Vectors";
9405 ot->description = "Smooth custom normals based on adjacent vertex normals";
9406 ot->idname = "MESH_OT_smooth_normals";
9407
9408 /* api callbacks */
9411
9412 /* flags */
9414
9416 "factor",
9417 0.5f,
9418 0.0f,
9419 1.0f,
9420 "Factor",
9421 "Specifies weight of smooth vs original normal",
9422 0.0f,
9423 1.0f);
9424}
9425
9428/* -------------------------------------------------------------------- */
9433{
9434 const Scene *scene = CTX_data_scene(C);
9435 ViewLayer *view_layer = CTX_data_view_layer(C);
9437 scene, view_layer, CTX_wm_view3d(C));
9438
9439 for (Object *obedit : objects) {
9441 BMesh *bm = em->bm;
9442 BMFace *f;
9443 BMIter fiter;
9444 const int face_strength = RNA_enum_get(op->ptr, "face_strength");
9445 const bool set = RNA_boolean_get(op->ptr, "set");
9446
9448
9449 const char *layer_id = MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID;
9450 int cd_prop_int_index = CustomData_get_named_layer_index(&bm->pdata, CD_PROP_INT32, layer_id);
9451 if (cd_prop_int_index == -1) {
9453 cd_prop_int_index = CustomData_get_named_layer_index(&bm->pdata, CD_PROP_INT32, layer_id);
9454 }
9455 cd_prop_int_index -= CustomData_get_layer_index(&bm->pdata, CD_PROP_INT32);
9456 const int cd_prop_int_offset = CustomData_get_n_offset(
9457 &bm->pdata, CD_PROP_INT32, cd_prop_int_index);
9458
9460
9461 if (set) {
9462 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
9464 int *strength = static_cast<int *>(BM_ELEM_CD_GET_VOID_P(f, cd_prop_int_offset));
9465 *strength = face_strength;
9466 }
9467 }
9468 }
9469 else {
9470 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
9471 int *strength = static_cast<int *>(BM_ELEM_CD_GET_VOID_P(f, cd_prop_int_offset));
9472 if (*strength == face_strength) {
9473 BM_face_select_set(bm, f, true);
9475 }
9476 else {
9477 BM_face_select_set(bm, f, false);
9478 }
9479 }
9480 }
9481
9483 params.calc_looptris = false;
9484 params.calc_normals = false;
9485 params.is_destructive = false;
9486 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
9487 }
9488
9489 return OPERATOR_FINISHED;
9490}
9491
9493 {FACE_STRENGTH_WEAK, "WEAK", 0, "Weak", ""},
9494 {FACE_STRENGTH_MEDIUM, "MEDIUM", 0, "Medium", ""},
9495 {FACE_STRENGTH_STRONG, "STRONG", 0, "Strong", ""},
9496 {0, nullptr, 0, nullptr, nullptr},
9497};
9498
9500{
9501 /* identifiers */
9502 ot->name = "Face Normals Strength";
9503 ot->description = "Set/Get strength of face (used in Weighted Normal modifier)";
9504 ot->idname = "MESH_OT_mod_weighted_strength";
9505
9506 /* api callbacks */
9509
9510 /* flags */
9512
9513 ot->prop = RNA_def_boolean(ot->srna, "set", false, "Set Value", "Set value of faces");
9514
9515 ot->prop = RNA_def_enum(
9516 ot->srna,
9517 "face_strength",
9520 "Face Strength",
9521 "Strength to use for assigning or selecting face influence for weighted normal modifier");
9522}
9523
9525{
9526 /* identifiers */
9527 ot->name = "Flip Quad Tessellation";
9528 ot->description = "Flips the tessellation of selected quads";
9529 ot->idname = "MESH_OT_flip_quad_tessellation";
9530
9533
9534 /* flags */
9536}
9537
int BKE_attribute_to_index(const AttributeOwner &owner, const CustomDataLayer *layer, AttrDomainMask domain_mask, eCustomDataMask layer_mask)
Definition attribute.cc:911
@ ATTR_DOMAIN_MASK_CORNER
const struct CustomDataLayer * BKE_attribute_search(const AttributeOwner &owner, const char *name, eCustomDataMask type, AttrDomainMask domain_mask)
Definition attribute.cc:636
#define CTX_DATA_BEGIN(C, Type, instance, member)
ScrArea * CTX_wm_area(const bContext *C)
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)
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:601
float BKE_defvert_find_weight(const MDeformVert *dvert, int defgroup)
Definition deform.cc:770
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
void BKE_editmesh_lnorspace_update(BMEditMesh *em)
Definition editmesh.cc:208
KeyBlock * BKE_keyblock_find_name(Key *key, const char name[])
Definition key.cc:1932
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:2456
void id_us_plus(ID *id)
Definition lib_id.cc:351
General operations, lookup, etc. for materials.
struct Material *** BKE_id_material_array_p(struct ID *id)
short * BKE_object_material_len_p(struct Object *ob)
void BKE_object_material_resize(struct Main *bmain, struct Object *ob, short totcol, bool do_id_user)
struct Material *** BKE_object_material_array_p(struct Object *ob)
void BKE_object_material_array_assign(struct Main *bmain, struct Object *ob, struct Material ***matar, int totcol, bool to_object_only)
void BKE_id_material_clear(struct Main *bmain, struct ID *id)
short * BKE_id_material_len_p(struct ID *id)
void BKE_id_material_resize(struct Main *bmain, struct ID *id, short totcol, bool do_id_user)
void BKE_lnor_space_custom_data_to_normal(const MLoopNorSpace *lnor_space, const short clnor_data[2], float r_custom_lnor[3])
@ MLNOR_SPACE_IS_SINGLE
Definition BKE_mesh.h:258
@ MLNOR_SPACEARR_BMLOOP_PTR
Definition BKE_mesh.h:277
void BKE_lnor_space_custom_normal_to_data(const MLoopNorSpace *lnor_space, const float custom_lnor[3], short r_clnor_data[2])
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:125
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_BITMAP_NEW(_num, _alloc_string)
Definition BLI_bitmap.h:41
#define BLI_BITMAP_TEST(_bitmap, _index)
Definition BLI_bitmap.h:65
#define BLI_BITMAP_ENABLE(_bitmap, _index)
Definition BLI_bitmap.h:82
unsigned int BLI_bitmap
Definition BLI_bitmap.h:17
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)
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
void BLI_listbase_rotate_first(struct ListBase *lb, void *vlink) ATTR_NONNULL(1
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_listbase_count_at_most(const struct ListBase *listbase, int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void * BLI_findptr(const struct ListBase *listbase, const void *ptr, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE int min_ii(int a, int b)
MINLINE int mod_i(int i, int n)
#define M_PI
MINLINE int compare_ff(float a, float b, float max_diff)
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])
#define DEG2RADF(_deg)
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)
Definition math_vector.c:36
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:58
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:93
int BLI_sortutil_cmp_float_reverse(const void *a_, const void *b_)
Definition sort_utils.c:39
unsigned int uint
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define STREQ(a, b)
#define BLT_I18NCONTEXT_ID_CURVE_LEGACY
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1068
@ ID_RECALC_GEOMETRY_ALL_MODES
Definition DNA_ID.h:1148
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
#define CD_MASK_COLOR_ALL
@ CD_CUSTOMLOOPNORMAL
@ CD_MDEFORMVERT
@ CD_PROP_INT32
@ CD_FREESTYLE_EDGE
@ CD_FREESTYLE_FACE
#define MAXMAT
@ ME_SYMMETRY_X
@ ME_EDIT_MIRROR_TOPO
@ FREESTYLE_FACE_MARK
@ FREESTYLE_EDGE_MARK
@ eModifierMode_Realtime
@ MOD_TRIANGULATE_QUAD_BEAUTY
@ eModifierType_Mirror
#define MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID
@ MOD_TRIANGULATE_NGON_BEAUTY
@ MOD_MIR_AXIS_Z
@ MOD_MIR_CLIPPING
@ MOD_MIR_AXIS_X
@ MOD_MIR_AXIS_Y
Object is a sort of wrapper for general info.
@ OB_MESH
@ PROP_SMOOTH
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
eDupli_ID_Flags
@ USER_DUP_MESH
@ USER_DUP_ACT
@ V3D_AROUND_ACTIVE
@ V3D_AROUND_CENTER_BOUNDS
@ V3D_AROUND_CURSOR
@ V3D_AROUND_CENTER_MEDIAN
@ OP_IS_MODAL_GRAB_CURSOR
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
void EDBM_flag_disable_all(BMEditMesh *em, char hflag)
void EDBM_automerge(Object *obedit, bool update, char hflag, float dist)
void void EDBM_redo_state_restore_and_free(BMBackup *backup, BMEditMesh *em, bool recalc_looptris) ATTR_NONNULL(1
void EDBM_update(Mesh *mesh, const EDBMUpdate_Params *params)
BMVert * EDBM_verts_mirror_get(BMEditMesh *em, BMVert *v)
void EDBM_verts_mirror_cache_begin(BMEditMesh *em, int axis, bool use_self, bool use_select, bool respecthide, bool use_topology)
void void void EDBM_redo_state_free(BMBackup *backup) ATTR_NONNULL(1)
void EDBM_mesh_normals_update(BMEditMesh *em)
bool EDBM_mesh_reveal(BMEditMesh *em, bool select)
bool EDBM_mesh_hide(BMEditMesh *em, bool swap)
void EDBM_mesh_stats_multi(blender::Span< Object * > objects, int totelem[3], int totelem_sel[3])
void ED_mesh_geometry_clear(Mesh *mesh)
void EDBM_verts_mirror_apply(BMEditMesh *em, int sel_from, int sel_to)
void EDBM_selectmode_flush(BMEditMesh *em)
BMBackup EDBM_redo_state_store(BMEditMesh *em)
void EDBM_selectmode_flush_ex(BMEditMesh *em, short selectmode)
void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em, int axis, bool use_self, bool use_select, bool respecthide, bool use_topology, float maxdist, int *r_index)
void EDBM_verts_mirror_cache_end(BMEditMesh *em)
bool EDBM_selectmode_disable_multi_ex(Scene *scene, blender::Span< Base * > bases, short selectmode_disable, short selectmode_fallback)
void EDBM_select_flush(BMEditMesh *em)
bool EDBM_select_pick(bContext *C, const int mval[2], const SelectPick_Params *params)
void ED_outliner_select_sync_from_object_tag(bContext *C)
void ED_area_status_text(ScrArea *area, const char *str)
Definition area.cc:803
bool ED_operator_scene_editable(bContext *C)
bool ED_operator_editmesh(bContext *C)
@ SEL_OP_SET
@ TFM_TRANSLATION
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 view3d_operator_needs_opengl(const bContext *C)
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:91
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:125
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
@ PROP_ENUM
Definition RNA_types.hh:69
PropertyFlag
Definition RNA_types.hh:201
@ PROP_NEVER_UNLINK
Definition RNA_types.hh:273
@ PROP_ENUM_NO_TRANSLATE
Definition RNA_types.hh:321
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
@ PROP_NONE
Definition RNA_types.hh:136
void uiLayoutSetActive(uiLayout *layout, bool active)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
@ 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)
uiLayout * uiLayoutRowWithHeading(uiLayout *layout, bool align, const char *heading)
#define UI_ITEM_NONE
void uiItemPointerR(uiLayout *layout, PointerRNA *ptr, const char *propname, PointerRNA *searchptr, const char *searchpropname, const char *name, int icon)
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_ITEM_R_EXPAND
@ OPTYPE_INTERNAL
Definition WM_types.hh:182
@ OPTYPE_BLOCKING
Definition WM_types.hh:164
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DRAW
Definition WM_types.hh:428
#define ND_DATA
Definition WM_types.hh:475
#define ND_SELECT
Definition WM_types.hh:474
#define NC_OBJECT
Definition WM_types.hh:346
#define BM_elem_cb_check_hflag_disabled_simple(type, hflag_n)
#define BM_DISK_EDGE_NEXT(e, v)
#define BM_ALL_NOLOOP
@ BM_SPACEARR_DIRTY_ALL
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SELECT
@ BM_ELEM_SMOOTH
@ BM_ELEM_TAG
#define BM_ALL
#define BM_FACE_FIRST_LOOP(p)
#define BM_ELEM_CD_GET_VOID_P(ele, offset)
void BM_mesh_copy_init_customdata(BMesh *bm_dst, BMesh *bm_src, const BMAllocTemplate *allocsize)
void BM_mesh_copy_init_customdata_all_layers(BMesh *bm_dst, BMesh *bm_src, const char htype, const BMAllocTemplate *allocsize)
void BM_vert_separate(BMesh *bm, BMVert *v, BMEdge **e_in, int e_in_len, const bool copy_select, BMVert ***r_vout, int *r_vout_len)
void BM_vert_kill(BMesh *bm, BMVert *v)
void BM_face_kill(BMesh *bm, BMFace *f)
BMEdge * BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *e_example, const eBMCreateFlag create_flag)
Main function for creating a new edge.
eBMCreateFlag
Definition bmesh_core.hh:23
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(BMesh *bm, CustomData *data, int type)
void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char *name)
void * BM_iter_at_index(BMesh *bm, const char itype, void *data, int index)
int BM_iter_elem_count_flag(const char itype, void *data, const char hflag, const bool value)
Elem Iter Flag Count.
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar)
@ BM_FACES_OF_EDGE
@ BM_FACES_OF_VERT
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_VERTS_OF_EDGE
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_VERT
@ BM_EDGES_OF_VERT
@ BM_EDGES_OF_FACE
@ BM_LOOPS_OF_FACE
ATTR_WARN_UNUSED_RESULT 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:29
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:81
void BM_mesh_free(BMesh *bm)
BMesh Free Mesh.
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
BMesh * BM_mesh_create(const BMAllocTemplate *allocsize, const BMeshCreateParams *params)
BMesh Make Mesh.
BLI_INLINE BMVert * BM_vert_at_index(BMesh *bm, const int index)
void BM_mesh_bm_from_me(BMesh *bm, const Mesh *mesh, const BMeshFromMeshParams *params)
void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *mesh, const BMeshToMeshParams *params)
void BM_mesh_copy_arrays(BMesh *bm_src, BMesh *bm_dst, BMVert **verts_src, uint verts_src_len, BMEdge **edges_src, uint edges_src_len, BMFace **faces_src, uint faces_src_len)
void BM_lnorspace_update(BMesh *bm)
void BM_normals_loops_edges_tag(BMesh *bm, const bool do_edges)
bool BM_custom_loop_normals_to_vector_layer(BMesh *bm)
void BM_mesh_normals_update(BMesh *bm)
BMLoopNorEditDataArray * BM_loop_normal_editdata_array_init(BMesh *bm, const bool do_all_loops_of_vert)
void BM_loop_normal_editdata_array_free(BMLoopNorEditDataArray *lnors_ed_arr)
bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr)
void BM_custom_loop_normals_from_vector_layer(BMesh *bm, bool add_sharp_edges)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
void * BMO_slot_buffer_alloc(BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, int len)
@ BMO_SYMMETRIZE_NEGATIVE_X
void BMO_slot_buffer_hflag_enable(BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, char hflag, bool do_flush)
BMO_FLAG_BUFFER.
void BMO_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.
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_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,...)
@ DEL_ONLYTAGGED
@ DEL_FACES_KEEP_BOUNDARY
@ DEL_EDGESFACES
@ DEL_ONLYFACES
#define BMO_FLAG_DEFAULTS
@ BMO_FLAG_RESPECT_HIDE
@ BMO_DELIM_NORMAL
@ SUBD_FALLOFF_LIN
@ SUBD_CORNER_FAN
@ SUBD_CORNER_STRAIGHT_CUT
@ SUBD_CORNER_PATH
@ SUBD_CORNER_INNERVERT
void BM_mesh_esubdivide(BMesh *bm, char edge_hflag, float smooth, short smooth_falloff, bool use_smooth_even, float fractal, float along_normal, int numcuts, int seltype, int cornertype, short use_single_edge, short use_grid_fill, short use_only_quads, int seed)
@ SUBD_RING_INTERP_SURF
@ SUBD_RING_INTERP_PATH
@ SUBD_RING_INTERP_LINEAR
@ SUBDIV_SELECT_ORIG
@ BMOP_POKE_MEDIAN_WEIGHTED
@ BMOP_POKE_BOUNDS
@ BMOP_POKE_MEDIAN
@ FACE_STRENGTH_STRONG
@ FACE_STRENGTH_WEAK
@ FACE_STRENGTH_MEDIUM
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
unsigned int U
Definition btGjkEpa3.h:78
btMatrix3x3 absolute() const
Return the matrix with all values non negative.
static unsigned long seed
Definition btSoftBody.h:39
static AttributeOwner from_id(ID *id)
Definition attribute.cc:43
const T * data() const
Definition BLI_array.hh:301
int64_t size() const
IndexRange index_range() const
pow(value.r - subtrahend, 2.0)") .do_static_compilation(true)
#define offsetof(t, d)
#define fabsf(x)
int len
void MESH_OT_poke(wmOperatorType *ot)
static int edbm_subdivide_edge_ring_exec(bContext *C, wmOperator *op)
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 int edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op)
static int edbm_do_smooth_vertex_exec(bContext *C, wmOperator *op)
static int edbm_fill_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)
void MESH_OT_uvs_reverse(wmOperatorType *ot)
static bool normals_tools_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop, void *)
void MESH_OT_mod_weighted_strength(wmOperatorType *ot)
static int edbm_rotate_colors_exec(bContext *C, wmOperator *op)
static bool edbm_sort_elements_poll_property(const bContext *, wmOperator *op, const PropertyRNA *prop)
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 void edbm_dissolve_prop__use_face_split(wmOperatorType *ot)
static int mesh_symmetry_snap_exec(bContext *C, wmOperator *op)
static int edbm_solidify_exec(bContext *C, wmOperator *op)
static EnumPropertyItem average_method_items[]
static int edbm_reverse_uvs_exec(bContext *C, wmOperator *op)
static int edbm_vert_connect_concave_exec(bContext *C, wmOperator *op)
static void edbm_blend_from_shape_ui(bContext *C, wmOperator *op)
static bool bm_vert_connect_pair(BMesh *bm, BMVert *v_a, BMVert *v_b)
static int edbm_set_normals_from_faces_exec(bContext *C, wmOperator *op)
static bool point_normals_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop, void *)
@ MESH_DELETE_EDGE_FACE
@ MESH_DELETE_EDGE
@ MESH_DELETE_FACE
@ MESH_DELETE_VERT
@ MESH_DELETE_ONLY_FACE
void MESH_OT_faces_shade_flat(wmOperatorType *ot)
static int edbm_vert_connect_nonplaner_exec(bContext *C, wmOperator *op)
static BMElem * edbm_add_edge_face_exec__tricky_extend_sel(BMesh *bm)
void MESH_OT_edge_rotate(wmOperatorType *ot)
static bool edbm_connect_vert_pair(BMEditMesh *em, Mesh *mesh, wmOperator *op)
@ EDBM_CLNOR_MODAL_POINTTO_RESET
@ EDBM_CLNOR_MODAL_CANCEL
@ EDBM_CLNOR_MODAL_POINTTO_INVERT
@ EDBM_CLNOR_MODAL_CONFIRM
@ EDBM_CLNOR_MODAL_POINTTO_ALIGN
@ EDBM_CLNOR_MODAL_POINTTO_USE_OBJECT
@ EDBM_CLNOR_MODAL_POINTTO_SET_USE_3DCURSOR
@ EDBM_CLNOR_MODAL_POINTTO_USE_MOUSE
@ EDBM_CLNOR_MODAL_POINTTO_SET_USE_SELECTED
@ EDBM_CLNOR_MODAL_POINTTO_USE_PIVOT
@ EDBM_CLNOR_MODAL_POINTTO_SPHERIZE
static int edbm_fill_grid_exec(bContext *C, 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 int edbm_sort_elements_exec(bContext *C, wmOperator *op)
static int edbm_mark_sharp_exec(bContext *C, wmOperator *op)
static const EnumPropertyItem prop_mesh_cornervert_types[]
static int edbm_vert_connect_path_exec(bContext *C, wmOperator *op)
void MESH_OT_edge_split(wmOperatorType *ot)
static int edbm_duplicate_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 int edbm_rotate_uvs_exec(bContext *C, wmOperator *op)
static int edbm_faces_shade_flat_exec(bContext *C, wmOperator *)
static int edbm_edge_split_exec(bContext *C, wmOperator *op)
static int edbm_bridge_tag_boundary_edges(BMesh *bm)
static int edbm_delete_loose_exec(bContext *C, wmOperator *op)
static void edbm_average_normals_ui(bContext *C, wmOperator *op)
void MESH_OT_beautify_fill(wmOperatorType *ot)
@ SRT_VIEW_XAXIS
@ SRT_SELECTED
@ SRT_RANDOMIZE
@ SRT_MATERIAL
@ SRT_CURSOR_DISTANCE
@ SRT_REVERSE
@ SRT_VIEW_ZAXIS
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 int edbm_flip_normals_exec(bContext *C, wmOperator *op)
void MESH_OT_colors_rotate(wmOperatorType *ot)
void MESH_OT_quads_convert_to_tris(wmOperatorType *ot)
static void edbm_flip_quad_tessellation(wmOperator *op, Object *obedit, BMEditMesh *em)
static bool bm_face_is_loose(BMFace *f)
static int edbm_smooth_normals_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 bool edbm_edge_split_selected_verts(wmOperator *op, Object *obedit, BMEditMesh *em)
static EnumPropertyItem clnors_pointto_mode_items[]
void MESH_OT_vertices_smooth(wmOperatorType *ot)
static int edbm_face_make_planar_exec(bContext *C, wmOperator *op)
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)
@ EDBM_CLNOR_TOOLS_RESET
@ EDBM_CLNOR_TOOLS_COPY
@ EDBM_CLNOR_TOOLS_ADD
@ EDBM_CLNOR_TOOLS_MULTIPLY
@ EDBM_CLNOR_TOOLS_PASTE
static int edbm_dissolve_faces_exec(bContext *C, 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)
void MESH_OT_delete_loose(wmOperatorType *ot)
void MESH_OT_split_normals(wmOperatorType *ot)
static void edbm_point_normals_ui(bContext *C, wmOperator *op)
void MESH_OT_fill(wmOperatorType *ot)
static void point_normals_cancel(bContext *C, wmOperator *op)
void MESH_OT_mark_seam(wmOperatorType *ot)
static bool bm_vert_connect_select_history_edge_to_vert_path(BMesh *bm, ListBase *r_selected)
static int edbm_average_normals_exec(bContext *C, wmOperator *op)
void MESH_OT_remove_doubles(wmOperatorType *ot)
void MESH_OT_offset_edge_loops(wmOperatorType *ot)
static BMLoopNorEditDataArray * flip_custom_normals_init_data(BMesh *bm)
static int edbm_edge_rotate_selected_exec(bContext *C, wmOperator *op)
static int mesh_symmetrize_exec(bContext *C, wmOperator *op)
static int edbm_offset_edgeloop_exec(bContext *C, wmOperator *op)
static int edbm_beautify_fill_exec(bContext *C, wmOperator *op)
void MESH_OT_subdivide_edgering(wmOperatorType *ot)
static bool mesh_separate_selected(Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old)
static int edbm_hide_exec(bContext *C, wmOperator *op)
void MESH_OT_separate(wmOperatorType *ot)
static int edbm_normals_make_consistent_exec(bContext *C, wmOperator *op)
static int edbm_merge_normals_exec(bContext *C, wmOperator *)
void MESH_OT_dissolve_limited(wmOperatorType *ot)
void MESH_OT_dissolve_degenerate(wmOperatorType *ot)
static int edbm_point_normals_invoke(bContext *C, wmOperator *op, const wmEvent *)
void MESH_OT_blend_from_shape(wmOperatorType *ot)
static int edbm_delete_edgeloop_exec(bContext *C, wmOperator *op)
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)
@ EDBM_CLNOR_AVERAGE_LOOP
@ EDBM_CLNOR_AVERAGE_FACE_AREA
@ EDBM_CLNOR_AVERAGE_ANGLE
void MESH_OT_decimate(wmOperatorType *ot)
void MESH_OT_point_normals(wmOperatorType *ot)
static int edbm_unsubdivide_exec(bContext *C, wmOperator *op)
static int edbm_faces_shade_smooth_exec(bContext *C, wmOperator *)
static int edbm_delete_exec(bContext *C, wmOperator *op)
static void edbm_flip_normals_custom_loop_normals(Object *obedit, BMEditMesh *em)
static int edbm_quads_convert_to_tris_exec(bContext *C, wmOperator *op)
static bool edbm_add_edge_face__smooth_get(BMesh *bm)
static bool average_normals_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop, void *)
static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent *event)
static int edbm_flip_quad_tessellation_exec(bContext *C, wmOperator *op)
static void edbm_add_edge_face_exec__tricky_finalize_sel(BMesh *bm, BMElem *ele_desel, BMFace *f)
static int edbm_bridge_edge_loops_exec(bContext *C, wmOperator *op)
static int edbm_reveal_exec(bContext *C, wmOperator *op)
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)
@ MESH_SEPARATE_LOOSE
@ MESH_SEPARATE_MATERIAL
@ MESH_SEPARATE_SELECTED
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)
static int edbm_dissolve_edges_exec(bContext *C, wmOperator *op)
void MESH_OT_unsubdivide(wmOperatorType *ot)
static int edbm_mark_seam_exec(bContext *C, wmOperator *op)
static int edbm_split_normals_exec(bContext *C, wmOperator *)
void MESH_OT_duplicate(wmOperatorType *ot)
static int edbm_collapse_edge_exec(bContext *C, wmOperator *op)
static bool shape_propagate(BMEditMesh *em, bool use_symmetry)
wmKeyMap * point_normals_modal_keymap(wmKeyConfig *keyconf)
void MESH_OT_normals_make_consistent(wmOperatorType *ot)
void MESH_OT_tris_convert_to_quads(wmOperatorType *ot)
static const EnumPropertyItem prop_mesh_face_strength_types[]
void MESH_OT_flip_normals(wmOperatorType *ot)
static void edbm_report_delete_info(ReportList *reports, const int totelem_old[3], const int totelem_new[3])
static bool edbm_fill_grid_prepare(BMesh *bm, int offset, int *span_p, const bool span_calc)
static int edbm_dissolve_mode_exec(bContext *C, wmOperator *op)
static void join_triangle_props(wmOperatorType *ot)
void MESH_OT_mark_sharp(wmOperatorType *ot)
void MESH_OT_smooth_normals(wmOperatorType *ot)
void MESH_OT_vert_connect_path(wmOperatorType *ot)
void MESH_OT_merge_normals(wmOperatorType *ot)
static int edbm_dissolve_verts_exec(bContext *C, wmOperator *op)
static bool point_normals_ensure(bContext *C, wmOperator *op)
void MESH_OT_reveal(wmOperatorType *ot)
static int edbm_merge_exec(bContext *C, wmOperator *op)
void MESH_OT_delete_edgeloop(wmOperatorType *ot)
static void sort_bmelem_flag(bContext *C, Scene *scene, Object *ob, RegionView3D *rv3d, const int types, const int flag, const int action, const int reverse, const uint seed)
static void edbm_dissolve_prop__use_boundary_tear(wmOperatorType *ot)
void MESH_OT_merge(wmOperatorType *ot)
void MESH_OT_sort_elements(wmOperatorType *ot)
void MESH_OT_fill_holes(wmOperatorType *ot)
#define CLNORS_VALID_VEC_LEN
static int edbm_subdivide_exec(bContext *C, wmOperator *op)
@ MESH_MERGE_LAST
@ MESH_MERGE_CENTER
@ MESH_MERGE_CURSOR
@ MESH_MERGE_FIRST
@ MESH_MERGE_COLLAPSE
@ MESH_BRIDGELOOP_PAIRS
@ MESH_BRIDGELOOP_SINGLE
@ MESH_BRIDGELOOP_CLOSED
void MESH_OT_solidify(wmOperatorType *ot)
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 point_normals_update_header(bContext *C, wmOperator *op)
static int edbm_add_edge_face_exec(bContext *C, wmOperator *op)
static void edbm_decimate_ui(bContext *, wmOperator *op)
static int edbm_poke_face_exec(bContext *C, wmOperator *op)
static bool point_normals_init(bContext *C, wmOperator *op)
static int edbm_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *)
static int edbm_dissolve_limited_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)
static int edbm_blend_from_shape_exec(bContext *C, wmOperator *op)
void MESH_OT_normals_tools(wmOperatorType *ot)
static int normals_split_merge(bContext *C, const bool do_merge)
void MESH_OT_average_normals(wmOperatorType *ot)
static void edbm_flip_normals_face_winding(wmOperator *op, Object *obedit, BMEditMesh *em)
@ EDBM_CLNOR_POINTTO_MODE_MOUSE
@ EDBM_CLNOR_POINTTO_MODE_COORDINATES
static int edbm_vert_connect_exec(bContext *C, wmOperator *op)
void MESH_OT_faces_shade_smooth(wmOperatorType *ot)
void MESH_OT_colors_reverse(wmOperatorType *ot)
static const EnumPropertyItem merge_type_items[]
static int edbm_mod_weighted_strength_exec(bContext *C, wmOperator *op)
void MESH_OT_dissolve_faces(wmOperatorType *ot)
static void normals_split(BMesh *bm)
static int edbm_point_normals_exec(bContext *C, wmOperator *op)
static int edbm_wireframe_exec(bContext *C, wmOperator *op)
static bool bm_edge_test_fill_grid_cb(BMEdge *e, void *)
static int edbm_decimate_exec(bContext *C, wmOperator *op)
static int edbm_dissolve_degenerate_exec(bContext *C, wmOperator *op)
static int edbm_fill_holes_exec(bContext *C, wmOperator *op)
void MESH_OT_edge_face_add(wmOperatorType *ot)
void MESH_OT_edge_collapse(wmOperatorType *ot)
void MESH_OT_vertices_smooth_laplacian(wmOperatorType *ot)
static int edbm_remove_doubles_exec(bContext *C, wmOperator *op)
static int edbm_separate_exec(bContext *C, wmOperator *op)
static bool edbm_decimate_check(bContext *, wmOperator *)
static int edbm_shape_propagate_to_all_exec(bContext *C, wmOperator *op)
void MESH_OT_dissolve_verts(wmOperatorType *ot)
static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op)
static int edbm_split_exec(bContext *C, wmOperator *op)
static int edbm_normals_tools_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 int edbm_reverse_colors_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)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
static float verts[][3]
uint col
BLI_INLINE float fb(float length, float L)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int count
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
ccl_device_inline float4 select(const int4 mask, const float4 a, const float4 b)
void MESH_OT_convex_hull(wmOperatorType *ot)
static void clear(Message &msg)
Definition msgfmt.cc:218
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(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_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:37
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:29
const EnumPropertyItem rna_enum_proportional_falloff_curve_only_items[]
Definition rna_scene.cc:114
const EnumPropertyItem rna_enum_symmetrize_direction_items[]
#define min(a, b)
Definition sort.c:32
#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]
struct BMEdge * e
BMHeader head
int totvert
int totfacesel
struct MLoopNorSpaceArray * lnor_spacearr
char elem_index_dirty
CustomData vdata
int totedge
ListBase selected
CustomData edata
int totvertsel
int totloop
int totedgesel
char spacearr_dirty
CustomData pdata
CustomData ldata
int totface
struct Object * object
CustomDataLayer * layers
const char * identifier
Definition RNA_types.hh:506
const char * name
Definition RNA_types.hh:510
Definition DNA_ID.h:413
char name[66]
Definition DNA_ID.h:425
char name[64]
short relative
ListBase block
void * data
struct LinkData * next
void * last
void * first
MLoopNorSpace ** lspacearr
Definition BKE_mesh.h:265
struct LinkNode * loops
Definition BKE_mesh.h:251
MeshRuntimeHandle * runtime
struct Key * key
ObjectRuntimeHandle * runtime
struct Material ** mat
float loc[3]
char * matbits
Definition rand.cc:33
float viewmat[4][4]
short val
Definition WM_types.hh:724
int mval[2]
Definition WM_types.hh:728
short type
Definition WM_types.hh:722
const void * modal_items
const char * name
Definition WM_types.hh:990
bool(* poll_property)(const bContext *C, wmOperator *op, const PropertyRNA *prop) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1048
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
bool(* check)(bContext *C, wmOperator *op)
Definition WM_types.hh:1014
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
void(* ui)(bContext *C, wmOperator *op)
Definition WM_types.hh:1053
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct ReportList * reports
IDProperty * properties
struct uiLayout * layout
struct wmOperatorType * type
struct PointerRNA * ptr
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:4126
wmOperatorType * ot
Definition wm_files.cc:4125
std::optional< std::string > WM_modalkeymap_operator_items_to_string(wmOperatorType *ot, const int propvalue, const bool compact)
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:933
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:960
const char * WM_bool_as_string(bool test)
void WM_operatortype_props_advanced_begin(wmOperatorType *ot)
void WM_operator_type_modal_from_exec_for_object_edit_coords(wmOperatorType *ot)
int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
uint8_t flag
Definition wm_window.cc:138