diff options
Diffstat (limited to 'source/blender/editors/mesh/editmesh_tools.c')
-rw-r--r-- | source/blender/editors/mesh/editmesh_tools.c | 5024 |
1 files changed, 3563 insertions, 1461 deletions
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 7b687f7f045..35ab7fa9c55 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -41,20 +41,31 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BLI_bitmap.h" +#include "BLI_heap_simple.h" #include "BLI_listbase.h" +#include "BLI_linklist.h" +#include "BLI_linklist_stack.h" #include "BLI_noise.h" #include "BLI_math.h" #include "BLI_rand.h" #include "BLI_sort_utils.h" +#include "BLI_string.h" -#include "BKE_material.h" +#include "BKE_editmesh.h" #include "BKE_context.h" #include "BKE_deform.h" -#include "BKE_depsgraph.h" +#include "BKE_key.h" +#include "BKE_layer.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_mesh.h" #include "BKE_report.h" #include "BKE_texture.h" -#include "BKE_main.h" -#include "BKE_editmesh.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" #include "BLT_translation.h" @@ -90,29 +101,45 @@ static int edbm_subdivide_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); const int cuts = RNA_int_get(op->ptr, "number_cuts"); - float smooth = RNA_float_get(op->ptr, "smoothness"); + const float smooth = RNA_float_get(op->ptr, "smoothness"); const float fractal = RNA_float_get(op->ptr, "fractal") / 2.5f; const float along_normal = RNA_float_get(op->ptr, "fractal_along_normal"); - if (RNA_boolean_get(op->ptr, "quadtri") && + if (RNA_boolean_get(op->ptr, "ngon") && RNA_enum_get(op->ptr, "quadcorner") == SUBD_CORNER_STRAIGHT_CUT) { RNA_enum_set(op->ptr, "quadcorner", SUBD_CORNER_INNERVERT); } + const int quad_corner_type = RNA_enum_get(op->ptr, "quadcorner"); + const bool use_quad_tri = !RNA_boolean_get(op->ptr, "ngon"); + const int seed = RNA_int_get(op->ptr, "seed"); - BM_mesh_esubdivide( - em->bm, BM_ELEM_SELECT, - smooth, SUBD_FALLOFF_LIN, false, - fractal, along_normal, - cuts, - SUBDIV_SELECT_ORIG, RNA_enum_get(op->ptr, "quadcorner"), - RNA_boolean_get(op->ptr, "quadtri"), true, false, - RNA_int_get(op->ptr, "seed")); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); - EDBM_update_generic(em, true, true); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + if (!(em->bm->totedgesel || em->bm->totfacesel)) { + continue; + } + + BM_mesh_esubdivide( + em->bm, BM_ELEM_SELECT, + smooth, SUBD_FALLOFF_LIN, false, + fractal, along_normal, + cuts, + SUBDIV_SELECT_ORIG, quad_corner_type, + use_quad_tri, true, false, + seed); + + EDBM_update_generic(em, true, true); + } + + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -149,7 +176,9 @@ void MESH_OT_subdivide(wmOperatorType *ot) RNA_def_float(ot->srna, "smoothness", 0.0f, 0.0f, 1e3f, "Smoothness", "Smoothness factor", 0.0f, 1.0f); - RNA_def_boolean(ot->srna, "quadtri", 0, "Quad/Tri Mode", "Tries to prevent ngons"); + WM_operatortype_props_advanced_begin(ot); + + RNA_def_boolean(ot->srna, "ngon", true, "Create N-Gons", "When disabled, newly created faces are limited to 3-4 sided faces"); RNA_def_enum(ot->srna, "quadcorner", prop_mesh_cornervert_types, SUBD_CORNER_STRAIGHT_CUT, "Quad Corner Type", "How to subdivide quad corners (anything other than Straight Cut will prevent ngons)"); @@ -222,24 +251,36 @@ static void mesh_operator_edgering_props_get(wmOperator *op, struct EdgeRingOpSu static int edbm_subdivide_edge_ring_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); + + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); struct EdgeRingOpSubdProps op_props; mesh_operator_edgering_props_get(op, &op_props); - if (!EDBM_op_callf( - em, op, - "subdivide_edgering edges=%he interp_mode=%i cuts=%i smooth=%f " - "profile_shape=%i profile_shape_factor=%f", - BM_ELEM_SELECT, op_props.interp_mode, op_props.cuts, op_props.smooth, - op_props.profile_shape, op_props.profile_shape_factor)) - { - return OPERATOR_CANCELLED; - } + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - EDBM_update_generic(em, true, true); + if (em->bm->totedgesel == 0) { + continue; + } + if (!EDBM_op_callf( + em, op, + "subdivide_edgering edges=%he interp_mode=%i cuts=%i smooth=%f " + "profile_shape=%i profile_shape_factor=%f", + BM_ELEM_SELECT, op_props.interp_mode, op_props.cuts, op_props.smooth, + op_props.profile_shape, op_props.profile_shape_factor)) + { + continue; + } + + EDBM_update_generic(em, true, true); + } + + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -269,27 +310,39 @@ void MESH_OT_subdivide_edgering(wmOperatorType *ot) static int edbm_unsubdivide_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMOperator bmop; - const int iterations = RNA_int_get(op->ptr, "iterations"); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - EDBM_op_init(em, &bmop, op, - "unsubdivide verts=%hv iterations=%i", BM_ELEM_SELECT, iterations); + if ((em->bm->totvertsel == 0) && + (em->bm->totedgesel == 0) && + (em->bm->totfacesel == 0)) + { + continue; + } - BMO_op_exec(em->bm, &bmop); + BMOperator bmop; + EDBM_op_init(em, &bmop, op, + "unsubdivide verts=%hv iterations=%i", BM_ELEM_SELECT, iterations); - if (!EDBM_op_finish(em, &bmop, op, true)) { - return 0; - } + BMO_op_exec(em->bm, &bmop); - if ((em->selectmode & SCE_SELECT_VERTEX) == 0) { - EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX); /* need to flush vert->face first */ - } - EDBM_selectmode_flush(em); + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; + } - EDBM_update_generic(em, true, true); + if ((em->selectmode & SCE_SELECT_VERTEX) == 0) { + EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX); /* need to flush vert->face first */ + } + EDBM_selectmode_flush(em); + + EDBM_update_generic(em, true, true); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -314,6 +367,7 @@ void MESH_OT_unsubdivide(wmOperatorType *ot) void EDBM_project_snap_verts(bContext *C, ARegion *ar, BMEditMesh *em) { + Main *bmain = CTX_data_main(C); Object *obedit = em->ob; BMIter iter; BMVert *eve; @@ -321,21 +375,22 @@ void EDBM_project_snap_verts(bContext *C, ARegion *ar, BMEditMesh *em) ED_view3d_init_mats_rv3d(obedit, ar->regiondata); struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d( - CTX_data_main(C), CTX_data_scene(C), 0, + bmain, CTX_data_scene(C), CTX_data_depsgraph(C), 0, ar, CTX_wm_view3d(C)); BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { float mval[2], co_proj[3]; if (ED_view3d_project_float_object(ar, eve->co, mval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - if (ED_transform_snap_object_project_view3d_mixed( + if (ED_transform_snap_object_project_view3d( snap_context, - SCE_SELECT_FACE, + SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_NOT_ACTIVE, .use_object_edit_cage = false, + .use_occlusion_test = true, }, - mval, NULL, true, + mval, NULL, co_proj, NULL)) { mul_v3_m4v3(eve->co, obedit->imat, co_proj); @@ -362,52 +417,79 @@ enum { MESH_DELETE_ONLY_FACE = 4, }; -static void edbm_report_delete_info(ReportList *reports, BMesh *bm, const int totelem[3]) +static void edbm_report_delete_info(ReportList *reports, const int totelem_old[3], const int totelem_new[3]) { BKE_reportf(reports, RPT_INFO, "Removed: %d vertices, %d edges, %d faces", - totelem[0] - bm->totvert, totelem[1] - bm->totedge, totelem[2] - bm->totface); + totelem_old[0] - totelem_new[0], totelem_old[1] - totelem_new[1], totelem_old[2] - totelem_new[2]); } static int edbm_delete_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - const int type = RNA_enum_get(op->ptr, "type"); + ViewLayer *view_layer = CTX_data_view_layer(C); - switch (type) { - case MESH_DELETE_VERT: - if (!EDBM_op_callf(em, op, "delete geom=%hv context=%i", BM_ELEM_SELECT, DEL_VERTS)) /* Erase Vertices */ - return OPERATOR_CANCELLED; - break; - case MESH_DELETE_EDGE: - if (!EDBM_op_callf(em, op, "delete geom=%he context=%i", BM_ELEM_SELECT, DEL_EDGES)) /* Erase Edges */ - return OPERATOR_CANCELLED; - break; - case MESH_DELETE_FACE: - if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_FACES)) /* Erase Faces */ - return OPERATOR_CANCELLED; - break; - case MESH_DELETE_EDGE_FACE: - /* Edges and Faces */ - if (!EDBM_op_callf(em, op, "delete geom=%hef context=%i", BM_ELEM_SELECT, DEL_EDGESFACES)) - return OPERATOR_CANCELLED; - break; - case MESH_DELETE_ONLY_FACE: - /* Only faces. */ - if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_ONLYFACES)) - return OPERATOR_CANCELLED; - break; - default: - BLI_assert(0); - break; - } + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + bool changed_multi = false; - EDBM_flag_disable_all(em, BM_ELEM_SELECT); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + const int type = RNA_enum_get(op->ptr, "type"); - EDBM_update_generic(em, true, true); + switch (type) { + case MESH_DELETE_VERT: /* Erase Vertices */ + if (!(em->bm->totvertsel && + EDBM_op_callf(em, op, "delete geom=%hv context=%i", BM_ELEM_SELECT, DEL_VERTS))) + { + continue; + } + break; + case MESH_DELETE_EDGE: /* Erase Edges */ + if (!(em->bm->totedgesel && + EDBM_op_callf(em, op, "delete geom=%he context=%i", BM_ELEM_SELECT, DEL_EDGES))) + { + continue; + } + break; + case MESH_DELETE_FACE: /* Erase Faces */ + if (!(em->bm->totfacesel && + EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_FACES))) + { + continue; + } + break; + case MESH_DELETE_EDGE_FACE: + /* Edges and Faces */ + if (!((em->bm->totedgesel || em->bm->totfacesel) && + EDBM_op_callf(em, op, "delete geom=%hef context=%i", BM_ELEM_SELECT, DEL_EDGESFACES))) + { + continue; + } + break; + case MESH_DELETE_ONLY_FACE: + /* Only faces. */ + if (!(em->bm->totfacesel && + EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_ONLYFACES))) + { + continue; + } + break; + default: + BLI_assert(0); + break; + } - return OPERATOR_FINISHED; + changed_multi = true; + + EDBM_flag_disable_all(em, BM_ELEM_SELECT); + + EDBM_update_generic(em, true, true); + } + + MEM_freeN(objects); + + return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void MESH_OT_delete(wmOperatorType *ot) @@ -438,6 +520,7 @@ void MESH_OT_delete(wmOperatorType *ot) /* props */ ot->prop = RNA_def_enum(ot->srna, "type", prop_mesh_delete_types, MESH_DELETE_VERT, "Type", "Method used for deleting mesh data"); + RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /** \} */ @@ -462,61 +545,75 @@ static bool bm_face_is_loose(BMFace *f) static int edbm_delete_loose_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; - BMIter iter; + ViewLayer *view_layer = CTX_data_view_layer(C); + int totelem_old_sel[3]; + int totelem_old[3]; - const bool use_verts = (RNA_boolean_get(op->ptr, "use_verts") && bm->totvertsel); - const bool use_edges = (RNA_boolean_get(op->ptr, "use_edges") && bm->totedgesel); - const bool use_faces = (RNA_boolean_get(op->ptr, "use_faces") && bm->totfacesel); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); - const int totelem[3] = {bm->totvert, bm->totedge, bm->totface}; + EDBM_mesh_stats_multi(objects, objects_len, totelem_old, totelem_old_sel); + const bool use_verts = (RNA_boolean_get(op->ptr, "use_verts") && totelem_old_sel[0]); + const bool use_edges = (RNA_boolean_get(op->ptr, "use_edges") && totelem_old_sel[1]); + const bool use_faces = (RNA_boolean_get(op->ptr, "use_faces") && totelem_old_sel[2]); - BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; - if (use_faces) { - BMFace *f; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + BMIter iter; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - BM_elem_flag_set(f, BM_ELEM_TAG, bm_face_is_loose(f)); + BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); + + if (use_faces) { + BMFace *f; + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + BM_elem_flag_set(f, BM_ELEM_TAG, bm_face_is_loose(f)); + } } - } - BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_FACES); - } + BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_FACES); + } - if (use_edges) { - BMEdge *e; + if (use_edges) { + BMEdge *e; - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { - BM_elem_flag_set(e, BM_ELEM_TAG, BM_edge_is_wire(e)); + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { + BM_elem_flag_set(e, BM_ELEM_TAG, BM_edge_is_wire(e)); + } } - } - BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_EDGES); - } + BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_EDGES); + } - if (use_verts) { - BMVert *v; + if (use_verts) { + BMVert *v; - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { - BM_elem_flag_set(v, BM_ELEM_TAG, (v->e == NULL)); + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + BM_elem_flag_set(v, BM_ELEM_TAG, (v->e == NULL)); + } } + + BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_VERTS); } - BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_VERTS); + EDBM_flag_disable_all(em, BM_ELEM_SELECT); + + EDBM_update_generic(em, true, true); } - EDBM_flag_disable_all(em, BM_ELEM_SELECT); + int totelem_new[3]; + EDBM_mesh_stats_multi(objects, objects_len, totelem_new, NULL); - EDBM_update_generic(em, true, true); + edbm_report_delete_info(op->reports, totelem_old, totelem_new); - edbm_report_delete_info(op->reports, bm, totelem); + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -551,13 +648,24 @@ void MESH_OT_delete_loose(wmOperatorType *ot) static int edbm_collapse_edge_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - if (!EDBM_op_callf(em, op, "collapse edges=%he uvs=%b", BM_ELEM_SELECT, true)) - return OPERATOR_CANCELLED; + if (em->bm->totedgesel == 0) { + continue; + } - EDBM_update_generic(em, true, true); + if (!EDBM_op_callf(em, op, "collapse edges=%he uvs=%b", BM_ELEM_SELECT, true)) { + continue; + } + + EDBM_update_generic(em, true, true); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -760,70 +868,83 @@ static void edbm_add_edge_face_exec__tricky_finalize_sel(BMesh *bm, BMElem *ele_ static int edbm_add_edge_face_exec(bContext *C, wmOperator *op) { - BMOperator bmop; - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - const bool use_smooth = edbm_add_edge_face__smooth_get(em->bm); - const int totedge_orig = em->bm->totedge; - const int totface_orig = em->bm->totface; /* when this is used to dissolve we could avoid this, but checking isnt too slow */ -#ifdef USE_FACE_CREATE_SEL_EXTEND - BMElem *ele_desel; - BMFace *ele_desel_face; + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - /* be extra clever, figure out if a partial selection should be extended so we can create geometry - * with single vert or single edge selection */ - ele_desel = edbm_add_edge_face_exec__tricky_extend_sel(em->bm); -#endif + if ((em->bm->totvertsel == 0) && + (em->bm->totedgesel == 0) && + (em->bm->totvertsel == 0)) + { + continue; + } - if (!EDBM_op_init( - em, &bmop, op, - "contextual_create geom=%hfev mat_nr=%i use_smooth=%b", - BM_ELEM_SELECT, em->mat_nr, use_smooth)) - { - return OPERATOR_CANCELLED; - } + bool use_smooth = edbm_add_edge_face__smooth_get(em->bm); + int totedge_orig = em->bm->totedge; + int totface_orig = em->bm->totface; - BMO_op_exec(em->bm, &bmop); + BMOperator bmop; +#ifdef USE_FACE_CREATE_SEL_EXTEND + BMElem *ele_desel; + BMFace *ele_desel_face; - /* cancel if nothing was done */ - if ((totedge_orig == em->bm->totedge) && - (totface_orig == em->bm->totface)) - { - EDBM_op_finish(em, &bmop, op, true); - return OPERATOR_CANCELLED; - } + /* be extra clever, figure out if a partial selection should be extended so we can create geometry + * with single vert or single edge selection */ + ele_desel = edbm_add_edge_face_exec__tricky_extend_sel(em->bm); +#endif + if (!EDBM_op_init( + em, &bmop, op, + "contextual_create geom=%hfev mat_nr=%i use_smooth=%b", + BM_ELEM_SELECT, em->mat_nr, use_smooth)) + { + continue; + } + + BMO_op_exec(em->bm, &bmop); + /* cancel if nothing was done */ + if ((totedge_orig == em->bm->totedge) && + (totface_orig == em->bm->totface)) + { + EDBM_op_finish(em, &bmop, op, true); + continue; + } #ifdef USE_FACE_CREATE_SEL_EXTEND - /* normally we would want to leave the new geometry selected, - * but being able to press F many times to add geometry is too useful! */ - if (ele_desel && - (BMO_slot_buffer_count(bmop.slots_out, "faces.out") == 1) && - (ele_desel_face = BMO_slot_buffer_get_first(bmop.slots_out, "faces.out"))) - { - edbm_add_edge_face_exec__tricky_finalize_sel(em->bm, ele_desel, ele_desel_face); - } - else + /* normally we would want to leave the new geometry selected, + * but being able to press F many times to add geometry is too useful! */ + if (ele_desel && + (BMO_slot_buffer_count(bmop.slots_out, "faces.out") == 1) && + (ele_desel_face = BMO_slot_buffer_get_first(bmop.slots_out, "faces.out"))) + { + edbm_add_edge_face_exec__tricky_finalize_sel(em->bm, ele_desel, ele_desel_face); + } + else #endif - { - /* Newly created faces may include existing hidden edges, - * copying face data from surrounding, may have copied hidden face flag too. - * - * Important that faces use flushing since 'edges.out' wont include hidden edges that already existed. - */ - BMO_slot_buffer_hflag_disable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_HIDDEN, true); - BMO_slot_buffer_hflag_disable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_HIDDEN, false); + { + /* Newly created faces may include existing hidden edges, + * copying face data from surrounding, may have copied hidden face flag too. + * + * Important that faces use flushing since 'edges.out' wont include hidden edges that already existed. + */ + BMO_slot_buffer_hflag_disable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_HIDDEN, true); + BMO_slot_buffer_hflag_disable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_HIDDEN, false); - BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true); - BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true); - } + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true); + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true); + } - if (!EDBM_op_finish(em, &bmop, op, true)) { - return OPERATOR_CANCELLED; - } + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; + } - EDBM_update_generic(em, true, true); + EDBM_update_generic(em, true, true); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -852,37 +973,44 @@ void MESH_OT_edge_face_add(wmOperatorType *ot) static int edbm_mark_seam_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Object *obedit = CTX_data_edit_object(C); - Mesh *me = ((Mesh *)obedit->data); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; + ViewLayer *view_layer = CTX_data_view_layer(C); BMEdge *eed; BMIter iter; const bool clear = RNA_boolean_get(op->ptr, "clear"); - /* auto-enable seams drawing */ - if (clear == 0) { - me->drawflag |= ME_DRAWSEAMS; - } + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; - if (clear) { - BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { - if (!BM_elem_flag_test(eed, BM_ELEM_SELECT) || BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) - continue; + if (bm->totedgesel == 0) { + continue; + } - BM_elem_flag_disable(eed, BM_ELEM_SEAM); + if (clear) { + BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { + if (!BM_elem_flag_test(eed, BM_ELEM_SELECT) || BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { + continue; + } + + BM_elem_flag_disable(eed, BM_ELEM_SEAM); + } } - } - else { - BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { - if (!BM_elem_flag_test(eed, BM_ELEM_SELECT) || BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) - continue; - BM_elem_flag_enable(eed, BM_ELEM_SEAM); + else { + BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { + if (!BM_elem_flag_test(eed, BM_ELEM_SELECT) || BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { + continue; + } + BM_elem_flag_enable(eed, BM_ELEM_SEAM); + } } - } - ED_uvedit_live_unwrap(scene, obedit); - EDBM_update_generic(em, true, false); + ED_uvedit_live_unwrap(scene, obedit); + EDBM_update_generic(em, true, false); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -904,7 +1032,9 @@ void MESH_OT_mark_seam(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; prop = RNA_def_boolean(ot->srna, "clear", 0, "Clear", ""); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + WM_operatortype_props_advanced_begin(ot); } /** \} */ @@ -915,34 +1045,39 @@ void MESH_OT_mark_seam(wmOperatorType *ot) static int edbm_mark_sharp_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - Mesh *me = ((Mesh *)obedit->data); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; BMEdge *eed; BMIter iter; const bool clear = RNA_boolean_get(op->ptr, "clear"); const bool use_verts = RNA_boolean_get(op->ptr, "use_verts"); + ViewLayer *view_layer = CTX_data_view_layer(C); - /* auto-enable sharp edge drawing */ - if (clear == 0) { - me->drawflag |= ME_DRAWSHARP; - } + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; - BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { - if (use_verts) { - if (!(BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) || BM_elem_flag_test(eed->v2, BM_ELEM_SELECT))) { + if (bm->totedgesel == 0) { + continue; + } + + BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { + if (use_verts) { + if (!(BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) || BM_elem_flag_test(eed->v2, BM_ELEM_SELECT))) { + continue; + } + } + else if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) { continue; } - } - else if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) { - continue; + + BM_elem_flag_set(eed, BM_ELEM_SMOOTH, clear); } - BM_elem_flag_set(eed, BM_ELEM_SMOOTH, clear); + EDBM_update_generic(em, true, false); } - - EDBM_update_generic(em, true, false); + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -964,23 +1099,23 @@ void MESH_OT_mark_sharp(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; prop = RNA_def_boolean(ot->srna, "clear", false, "Clear", ""); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, "use_verts", false, "Vertices", "Consider vertices instead of edges to select which edges to (un)tag as sharp"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -static int edbm_vert_connect_exec(bContext *C, wmOperator *op) +static bool edbm_connect_vert_pair(BMEditMesh *em, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); BMesh *bm = em->bm; BMOperator bmop; - bool is_pair = (bm->totvertsel == 2); + const int verts_len = bm->totvertsel; + bool is_pair = (verts_len == 2); int len = 0; bool check_degenerate = true; - const int verts_len = bm->totvertsel; + BMVert **verts; + bool checks_succeded = true; verts = MEM_mallocN(sizeof(*verts) * verts_len, __func__); { @@ -988,20 +1123,18 @@ static int edbm_vert_connect_exec(bContext *C, wmOperator *op) BMVert *v; int i = 0; - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + BM_ITER_MESH(v, &iter, bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { verts[i++] = v; } } - if (is_pair) { - if (BM_vert_pair_share_face_check_cb( - verts[0], verts[1], - BM_elem_cb_check_hflag_disabled_simple(BMFace *, BM_ELEM_HIDDEN))) - { - check_degenerate = false; - is_pair = false; - } + if (BM_vert_pair_share_face_check_cb( + verts[0], verts[1], + BM_elem_cb_check_hflag_disabled_simple(BMFace *, BM_ELEM_HIDDEN))) + { + check_degenerate = false; + is_pair = false; } } @@ -1011,7 +1144,7 @@ static int edbm_vert_connect_exec(bContext *C, wmOperator *op) "connect_vert_pair verts=%eb verts_exclude=%hv faces_exclude=%hf", verts, verts_len, BM_ELEM_HIDDEN, BM_ELEM_HIDDEN)) { - goto finally; + checks_succeded = false; } } else { @@ -1020,33 +1153,49 @@ static int edbm_vert_connect_exec(bContext *C, wmOperator *op) "connect_verts verts=%eb faces_exclude=%hf check_degenerate=%b", verts, verts_len, BM_ELEM_HIDDEN, check_degenerate)) { - goto finally; + checks_succeded = false; } } + if (checks_succeded) { + BMO_op_exec(bm, &bmop); + len = BMO_slot_get(bmop.slots_out, "edges.out")->len; - BMO_op_exec(bm, &bmop); - len = BMO_slot_get(bmop.slots_out, "edges.out")->len; - - if (len) { - if (is_pair) { + if (len && is_pair) { /* new verts have been added, we have to select the edges, not just flush */ BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true); } - } - if (!EDBM_op_finish(em, &bmop, op, true)) { - len = 0; - } - else { - EDBM_selectmode_flush(em); /* so newly created edges get the selection state from the vertex */ + if (!EDBM_op_finish(em, &bmop, op, true)) { + len = 0; + } + else { + EDBM_selectmode_flush(em); /* so newly created edges get the selection state from the vertex */ - EDBM_update_generic(em, true, true); + EDBM_update_generic(em, true, true); + } } + MEM_freeN(verts); + return len; +} -finally: - MEM_freeN(verts); - return len ? OPERATOR_FINISHED : OPERATOR_CANCELLED; +static int edbm_vert_connect_exec(bContext *C, wmOperator *op) +{ + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + uint failed_objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + if (!edbm_connect_vert_pair(em, op)) { + failed_objects_len++; + } + } + MEM_freeN(objects); + return failed_objects_len == objects_len ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void MESH_OT_vert_connect(wmOperatorType *ot) @@ -1285,43 +1434,66 @@ static bool bm_vert_connect_select_history_edge_to_vert_path(BMesh *bm, ListBase static int edbm_vert_connect_path_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; - bool is_pair = (em->bm->totvertsel == 2); - ListBase selected_orig = {NULL, NULL}; - int retval; + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + uint failed_selection_order_len = 0; + uint failed_connect_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); - /* when there is only 2 vertices, we can ignore selection order */ - if (is_pair) { - return edbm_vert_connect_exec(C, op); - } + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + const bool is_pair = (em->bm->totvertsel == 2); + ListBase selected_orig = {NULL, NULL}; - if (bm->selected.first) { - BMEditSelection *ese = bm->selected.first; - if (ese->htype == BM_EDGE) { - if (bm_vert_connect_select_history_edge_to_vert_path(bm, &selected_orig)) { - SWAP(ListBase, bm->selected, selected_orig); + if (bm->totvertsel == 0) { + continue; + } + + /* when there is only 2 vertices, we can ignore selection order */ + if (is_pair) { + if (!edbm_connect_vert_pair(em, op)) { + failed_connect_len++; } + continue; } - } - if (bm_vert_connect_select_history(bm)) { - EDBM_selectmode_flush(em); - EDBM_update_generic(em, true, true); - retval = OPERATOR_FINISHED; + if (bm->selected.first) { + BMEditSelection *ese = bm->selected.first; + if (ese->htype == BM_EDGE) { + if (bm_vert_connect_select_history_edge_to_vert_path(bm, &selected_orig)) { + SWAP(ListBase, bm->selected, selected_orig); + } + } + } + + if (bm_vert_connect_select_history(bm)) { + EDBM_selectmode_flush(em); + EDBM_update_generic(em, true, true); + } + else { + failed_selection_order_len++; + } + + if (!BLI_listbase_is_empty(&selected_orig)) { + BM_select_history_clear(bm); + bm->selected = selected_orig; + } } - else { + + MEM_freeN(objects); + + if (failed_selection_order_len == objects_len) { BKE_report(op->reports, RPT_ERROR, "Invalid selection order"); - retval = OPERATOR_CANCELLED; + return OPERATOR_CANCELLED; } - - if (!BLI_listbase_is_empty(&selected_orig)) { - BM_select_history_clear(bm); - bm->selected = selected_orig; + else if (failed_connect_len == objects_len) { + BKE_report(op->reports, RPT_ERROR, "Could not connect vertices"); + return OPERATOR_CANCELLED; } - return retval; + return OPERATOR_FINISHED; } void MESH_OT_vert_connect_path(wmOperatorType *ot) @@ -1341,20 +1513,29 @@ void MESH_OT_vert_connect_path(wmOperatorType *ot) static int edbm_vert_connect_concave_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - if (!EDBM_op_call_and_selectf( - em, op, - "faces.out", true, - "connect_verts_concave faces=%hf", - BM_ELEM_SELECT)) - { - return OPERATOR_CANCELLED; - } + if (em->bm->totfacesel == 0) { + continue; + } + if (!EDBM_op_call_and_selectf( + em, op, + "faces.out", true, + "connect_verts_concave faces=%hf", + BM_ELEM_SELECT)) + { + continue; + } + EDBM_update_generic(em, true, true); + } - EDBM_update_generic(em, true, true); + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -1381,22 +1562,32 @@ void MESH_OT_vert_connect_concave(wmOperatorType *ot) static int edbm_vert_connect_nonplaner_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - + ViewLayer *view_layer = CTX_data_view_layer(C); const float angle_limit = RNA_float_get(op->ptr, "angle_limit"); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); - if (!EDBM_op_call_and_selectf( - em, op, - "faces.out", true, - "connect_verts_nonplanar faces=%hf angle_limit=%f", - BM_ELEM_SELECT, angle_limit)) - { - return OPERATOR_CANCELLED; - } + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + if (em->bm->totfacesel == 0) { + continue; + } + + if (!EDBM_op_call_and_selectf( + em, op, + "faces.out", true, + "connect_verts_nonplanar faces=%hf angle_limit=%f", + BM_ELEM_SELECT, angle_limit)) + { + continue; + } + + EDBM_update_generic(em, true, true); + } + MEM_freeN(objects); - EDBM_update_generic(em, true, true); return OPERATOR_FINISHED; } @@ -1430,19 +1621,31 @@ void MESH_OT_vert_connect_nonplanar(wmOperatorType *ot) static int edbm_face_make_planar_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + const int repeat = RNA_int_get(op->ptr, "repeat"); const float fac = RNA_float_get(op->ptr, "factor"); - if (!EDBM_op_callf( - em, op, "planar_faces faces=%hf iterations=%i factor=%f", - BM_ELEM_SELECT, repeat, fac)) - { - return OPERATOR_CANCELLED; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + if (em->bm->totfacesel == 0) { + continue; + } + + if (!EDBM_op_callf( + em, op, "planar_faces faces=%hf iterations=%i factor=%f", + BM_ELEM_SELECT, repeat, fac)) + { + continue; + } + + EDBM_update_generic(em, true, true); } + MEM_freeN(objects); - EDBM_update_generic(em, true, true); return OPERATOR_FINISHED; } @@ -1473,23 +1676,32 @@ void MESH_OT_face_make_planar(wmOperatorType *ot) static int edbm_edge_split_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + if (em->bm->totedgesel == 0) { + continue; + } - if (!EDBM_op_call_and_selectf( - em, op, - "edges.out", false, - "split_edges edges=%he", - BM_ELEM_SELECT)) - { - return OPERATOR_CANCELLED; - } + if (!EDBM_op_call_and_selectf( + em, op, + "edges.out", false, + "split_edges edges=%he", + BM_ELEM_SELECT)) + { + continue; + } - if (em->selectmode == SCE_SELECT_FACE) { - EDBM_select_flush(em); - } + if (em->selectmode == SCE_SELECT_FACE) { + EDBM_select_flush(em); + } - EDBM_update_generic(em, true, true); + EDBM_update_generic(em, true, true); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -1517,33 +1729,43 @@ void MESH_OT_edge_split(wmOperatorType *ot) static int edbm_duplicate_exec(bContext *C, wmOperator *op) { - Object *ob = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(ob); - BMesh *bm = em->bm; - BMOperator bmop; + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); - EDBM_op_init( - em, &bmop, op, - "duplicate geom=%hvef use_select_history=%b", - BM_ELEM_SELECT, true); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + if (em->bm->totvertsel == 0) { + continue; + } - BMO_op_exec(bm, &bmop); + BMOperator bmop; + BMesh *bm = em->bm; - /* de-select all would clear otherwise */ - BM_SELECT_HISTORY_BACKUP(bm); + EDBM_op_init( + em, &bmop, op, + "duplicate geom=%hvef use_select_history=%b", + BM_ELEM_SELECT, true); - EDBM_flag_disable_all(em, BM_ELEM_SELECT); + BMO_op_exec(bm, &bmop); - BMO_slot_buffer_hflag_enable(bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true); + /* de-select all would clear otherwise */ + BM_SELECT_HISTORY_BACKUP(bm); - /* rebuild editselection */ - BM_SELECT_HISTORY_RESTORE(bm); + EDBM_flag_disable_all(em, BM_ELEM_SELECT); - if (!EDBM_op_finish(em, &bmop, op, true)) { - return OPERATOR_CANCELLED; - } + BMO_slot_buffer_hflag_enable(bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true); - EDBM_update_generic(em, true, true); + /* rebuild editselection */ + BM_SELECT_HISTORY_RESTORE(bm); + + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; + } + EDBM_update_generic(em, true, true); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -1581,18 +1803,29 @@ void MESH_OT_duplicate(wmOperatorType *ot) * \{ */ static int edbm_flip_normals_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); - if (!EDBM_op_callf( - em, op, "reverse_faces faces=%hf flip_multires=%b", - BM_ELEM_SELECT, true)) - { - return OPERATOR_CANCELLED; - } + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - EDBM_update_generic(em, true, false); + if (em->bm->totfacesel == 0) { + continue; + } + if (!EDBM_op_callf( + em, op, "reverse_faces faces=%hf flip_multires=%b", + BM_ELEM_SELECT, true)) + { + continue; + } + + EDBM_update_generic(em, true, false); + } + + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -1622,71 +1855,99 @@ void MESH_OT_flip_normals(wmOperatorType *ot) */ static int edbm_edge_rotate_selected_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMOperator bmop; BMEdge *eed; BMIter iter; const bool use_ccw = RNA_boolean_get(op->ptr, "use_ccw"); - int tot = 0; - if (em->bm->totedgesel == 0) { - BKE_report(op->reports, RPT_ERROR, "Select edges or face pairs for edge loops to rotate about"); - return OPERATOR_CANCELLED; - } + int tot_rotate_all = 0, tot_failed_all = 0; + bool no_selected_edges = true, invalid_selected_edges = true; - /* first see if we have two adjacent faces */ - BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { - BM_elem_flag_disable(eed, BM_ELEM_TAG); - if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) { - BMFace *fa, *fb; - if (BM_edge_face_pair(eed, &fa, &fb)) { - /* if both faces are selected we rotate between them, - * otherwise - rotate between 2 unselected - but not mixed */ - if (BM_elem_flag_test(fa, BM_ELEM_SELECT) == BM_elem_flag_test(fb, BM_ELEM_SELECT)) { - BM_elem_flag_enable(eed, BM_ELEM_TAG); - tot++; + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + int tot = 0; + + if (em->bm->totedgesel == 0) { + continue; + } + no_selected_edges = false; + + /* first see if we have two adjacent faces */ + BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { + BM_elem_flag_disable(eed, BM_ELEM_TAG); + if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) { + BMFace *fa, *fb; + if (BM_edge_face_pair(eed, &fa, &fb)) { + /* if both faces are selected we rotate between them, + * otherwise - rotate between 2 unselected - but not mixed */ + if (BM_elem_flag_test(fa, BM_ELEM_SELECT) == BM_elem_flag_test(fb, BM_ELEM_SELECT)) { + BM_elem_flag_enable(eed, BM_ELEM_TAG); + tot++; + } } } } - } - /* ok, we don't have two adjacent faces, but we do have two selected ones. - * that's an error condition.*/ - if (tot == 0) { - BKE_report(op->reports, RPT_ERROR, "Could not find any selected edges that can be rotated"); - return OPERATOR_CANCELLED; - } + /* ok, we don't have two adjacent faces, but we do have two selected ones. + * that's an error condition.*/ + if (tot == 0) { + continue; + } + invalid_selected_edges = false; - EDBM_op_init(em, &bmop, op, "rotate_edges edges=%he use_ccw=%b", BM_ELEM_TAG, use_ccw); + BMOperator bmop; + EDBM_op_init(em, &bmop, op, "rotate_edges edges=%he use_ccw=%b", BM_ELEM_TAG, use_ccw); - /* avoids leaving old verts selected which can be a problem running multiple times, - * since this means the edges become selected around the face which then attempt to rotate */ - BMO_slot_buffer_hflag_disable(em->bm, bmop.slots_in, "edges", BM_EDGE, BM_ELEM_SELECT, true); + /* avoids leaving old verts selected which can be a problem running multiple times, + * since this means the edges become selected around the face which then attempt to rotate */ + BMO_slot_buffer_hflag_disable(em->bm, bmop.slots_in, "edges", BM_EDGE, BM_ELEM_SELECT, true); - BMO_op_exec(em->bm, &bmop); - /* edges may rotate into hidden vertices, if this does _not_ run we get an ilogical state */ - BMO_slot_buffer_hflag_disable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_HIDDEN, true); - BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true); + BMO_op_exec(em->bm, &bmop); + /* edges may rotate into hidden vertices, if this does _not_ run we get an illogical state */ + BMO_slot_buffer_hflag_disable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_HIDDEN, true); + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true); - const int tot_rotate = BMO_slot_buffer_count(bmop.slots_out, "edges.out"); - const int tot_failed = tot - tot_rotate; - if (tot_failed != 0) { - /* If some edges fail to rotate, we need to re-select them, - * otherwise we can end up with invalid selection - * (unselected edge between 2 selected faces). */ - BM_mesh_elem_hflag_enable_test(em->bm, BM_EDGE, BM_ELEM_SELECT, true, false, BM_ELEM_TAG); + const int tot_rotate = BMO_slot_buffer_count(bmop.slots_out, "edges.out"); + const int tot_failed = tot - tot_rotate; - BKE_reportf(op->reports, RPT_WARNING, "Unable to rotate %d edge(s)", tot_failed); + tot_rotate_all += tot_rotate; + tot_failed_all += tot_failed; + + if (tot_failed != 0) { + /* If some edges fail to rotate, we need to re-select them, + * otherwise we can end up with invalid selection + * (unselected edge between 2 selected faces). */ + BM_mesh_elem_hflag_enable_test(em->bm, BM_EDGE, BM_ELEM_SELECT, true, false, BM_ELEM_TAG); + } + + EDBM_selectmode_flush(em); + + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; + } + + EDBM_update_generic(em, true, true); } + MEM_freeN(objects); - EDBM_selectmode_flush(em); + if (no_selected_edges) { + BKE_report(op->reports, RPT_ERROR, "Select edges or face pairs for edge loops to rotate about"); + return OPERATOR_CANCELLED; + } - if (!EDBM_op_finish(em, &bmop, op, true)) { + /* Ok, we don't have two adjacent faces, but we do have two selected ones. + * that's an error condition. */ + if (invalid_selected_edges) { + BKE_report(op->reports, RPT_ERROR, "Could not find any selected edges that can be rotated"); return OPERATOR_CANCELLED; } - EDBM_update_generic(em, true, true); + if (tot_failed_all != 0) { + BKE_reportf(op->reports, RPT_WARNING, "Unable to rotate %d edge(s)", tot_failed_all); + } return OPERATOR_FINISHED; } @@ -1717,13 +1978,28 @@ void MESH_OT_edge_rotate(wmOperatorType *ot) static int edbm_hide_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); + const bool unselected = RNA_boolean_get(op->ptr, "unselected"); + ViewLayer *view_layer = CTX_data_view_layer(C); - EDBM_mesh_hide(em, RNA_boolean_get(op->ptr, "unselected")); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; - EDBM_update_generic(em, true, false); + if ((bm->totvertsel == 0) && + (bm->totedgesel == 0) && + (bm->totfacesel == 0)) + { + continue; + } + EDBM_mesh_hide(em, unselected); + EDBM_update_generic(em, true, false); + } + + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -1753,13 +2029,19 @@ void MESH_OT_hide(wmOperatorType *ot) static int edbm_reveal_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); const bool select = RNA_boolean_get(op->ptr, "select"); + ViewLayer *view_layer = CTX_data_view_layer(C); - EDBM_mesh_reveal(em, select); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - EDBM_update_generic(em, true, false); + EDBM_mesh_reveal(em, select); + EDBM_update_generic(em, true, false); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -1789,19 +2071,28 @@ void MESH_OT_reveal(wmOperatorType *ot) static int edbm_normals_make_consistent_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); + ViewLayer *view_layer = CTX_data_view_layer(C); - /* doflip has to do with bmesh_rationalize_normals, it's an internal - * thing */ - if (!EDBM_op_callf(em, op, "recalc_face_normals faces=%hf", BM_ELEM_SELECT)) - return OPERATOR_CANCELLED; + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - if (RNA_boolean_get(op->ptr, "inside")) { - EDBM_op_callf(em, op, "reverse_faces faces=%hf flip_multires=%b", BM_ELEM_SELECT, true); - } + if (em->bm->totfacesel == 0) { + continue; + } - EDBM_update_generic(em, true, false); + if (!EDBM_op_callf(em, op, "recalc_face_normals faces=%hf", BM_ELEM_SELECT)) { + continue; + } + if (RNA_boolean_get(op->ptr, "inside")) { + EDBM_op_callf(em, op, "reverse_faces faces=%hf flip_multires=%b", BM_ELEM_SELECT, true); + } + + EDBM_update_generic(em, true, false); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -1831,72 +2122,83 @@ void MESH_OT_normals_make_consistent(wmOperatorType *ot) static int edbm_do_smooth_vertex_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - Mesh *me = obedit->data; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - ModifierData *md; - bool mirrx = false, mirry = false, mirrz = false; - int i, repeat; - float clip_dist = 0.0f; const float fac = RNA_float_get(op->ptr, "factor"); - const bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; const bool xaxis = RNA_boolean_get(op->ptr, "xaxis"); const bool yaxis = RNA_boolean_get(op->ptr, "yaxis"); const bool zaxis = RNA_boolean_get(op->ptr, "zaxis"); + int repeat = RNA_int_get(op->ptr, "repeat"); - /* mirror before smooth */ - if (((Mesh *)obedit->data)->editflag & ME_EDIT_MIRROR_X) { - EDBM_verts_mirror_cache_begin(em, 0, false, true, use_topology); + if (!repeat) { + repeat = 1; } - /* if there is a mirror modifier with clipping, flag the verts that - * are within tolerance of the plane(s) of reflection - */ - for (md = obedit->modifiers.first; md; md = md->next) { - if (md->type == eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) { - MirrorModifierData *mmd = (MirrorModifierData *)md; + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + Mesh *me = obedit->data; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + ModifierData *md; + bool mirrx = false, mirry = false, mirrz = false; + int i; + float clip_dist = 0.0f; + const bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; + + if (em->bm->totvertsel == 0) { + continue; + } - if (mmd->flag & MOD_MIR_CLIPPING) { - if (mmd->flag & MOD_MIR_AXIS_X) - mirrx = true; - if (mmd->flag & MOD_MIR_AXIS_Y) - mirry = true; - if (mmd->flag & MOD_MIR_AXIS_Z) - mirrz = true; + /* mirror before smooth */ + if (((Mesh *)obedit->data)->editflag & ME_EDIT_MIRROR_X) { + EDBM_verts_mirror_cache_begin(em, 0, false, true, use_topology); + } - clip_dist = mmd->tolerance; + /* if there is a mirror modifier with clipping, flag the verts that + * are within tolerance of the plane(s) of reflection + */ + for (md = obedit->modifiers.first; md; md = md->next) { + if (md->type == eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) { + MirrorModifierData *mmd = (MirrorModifierData *)md; + + if (mmd->flag & MOD_MIR_CLIPPING) { + if (mmd->flag & MOD_MIR_AXIS_X) + mirrx = true; + if (mmd->flag & MOD_MIR_AXIS_Y) + mirry = true; + if (mmd->flag & MOD_MIR_AXIS_Z) + mirrz = true; + + clip_dist = mmd->tolerance; + } } } - } - repeat = RNA_int_get(op->ptr, "repeat"); - if (!repeat) - repeat = 1; + for (i = 0; i < repeat; i++) { + if (!EDBM_op_callf( + em, op, + "smooth_vert verts=%hv factor=%f mirror_clip_x=%b mirror_clip_y=%b mirror_clip_z=%b " + "clip_dist=%f use_axis_x=%b use_axis_y=%b use_axis_z=%b", + BM_ELEM_SELECT, fac, mirrx, mirry, mirrz, clip_dist, xaxis, yaxis, zaxis)) + { + continue; + } + } - for (i = 0; i < repeat; i++) { - if (!EDBM_op_callf( - em, op, - "smooth_vert verts=%hv factor=%f mirror_clip_x=%b mirror_clip_y=%b mirror_clip_z=%b " - "clip_dist=%f use_axis_x=%b use_axis_y=%b use_axis_z=%b", - BM_ELEM_SELECT, fac, mirrx, mirry, mirrz, clip_dist, xaxis, yaxis, zaxis)) - { - return OPERATOR_CANCELLED; + /* apply mirror */ + if (((Mesh *)obedit->data)->editflag & ME_EDIT_MIRROR_X) { + EDBM_verts_mirror_apply(em, BM_ELEM_SELECT, 0); + EDBM_verts_mirror_cache_end(em); } - } - /* apply mirror */ - if (((Mesh *)obedit->data)->editflag & ME_EDIT_MIRROR_X) { - EDBM_verts_mirror_apply(em, BM_ELEM_SELECT, 0); - EDBM_verts_mirror_cache_end(em); + EDBM_update_generic(em, true, false); } - - EDBM_update_generic(em, true, false); + MEM_freeN(objects); return OPERATOR_FINISHED; } - void MESH_OT_vertices_smooth(wmOperatorType *ot) { /* identifiers */ @@ -1911,8 +2213,11 @@ void MESH_OT_vertices_smooth(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_float(ot->srna, "factor", 0.5f, -10.0f, 10.0f, "Smoothing", "Smoothing factor", 0.0f, 1.0f); + ot->prop = RNA_def_float(ot->srna, "factor", 0.5f, -10.0f, 10.0f, "Smoothing", "Smoothing factor", 0.0f, 1.0f); RNA_def_int(ot->srna, "repeat", 1, 1, 1000, "Repeat", "Number of times to smooth the mesh", 1, 100); + + WM_operatortype_props_advanced_begin(ot); + RNA_def_boolean(ot->srna, "xaxis", true, "X-Axis", "Smooth along the X axis"); RNA_def_boolean(ot->srna, "yaxis", true, "Y-Axis", "Smooth along the Y axis"); RNA_def_boolean(ot->srna, "zaxis", true, "Z-Axis", "Smooth along the Z axis"); @@ -1926,59 +2231,91 @@ void MESH_OT_vertices_smooth(wmOperatorType *ot) static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - Mesh *me = obedit->data; - bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; - bool usex = true, usey = true, usez = true, preserve_volume = true; - int i, repeat; - float lambda_factor; - float lambda_border; BMIter fiter; BMFace *f; + int tot_invalid = 0; + int tot_unselected = 0; + ViewLayer *view_layer = CTX_data_view_layer(C); + + const float lambda_factor = RNA_float_get(op->ptr, "lambda_factor"); + const float lambda_border = RNA_float_get(op->ptr, "lambda_border"); + const bool usex = RNA_boolean_get(op->ptr, "use_x"); + const bool usey = RNA_boolean_get(op->ptr, "use_y"); + const bool usez = RNA_boolean_get(op->ptr, "use_z"); + const bool preserve_volume = RNA_boolean_get(op->ptr, "preserve_volume"); + int repeat = RNA_int_get(op->ptr, "repeat"); + + if (!repeat) { + repeat = 1; + } - /* Check if select faces are triangles */ - BM_ITER_MESH (f, &fiter, em->bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - if (f->len > 4) { - BKE_report(op->reports, RPT_WARNING, "Selected faces must be triangles or quads"); - return OPERATOR_CANCELLED; + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + Mesh *me = obedit->data; + bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; + + if (em->bm->totvertsel == 0) { + tot_unselected++; + tot_invalid++; + continue; + } + + bool is_invalid = false; + /* Check if select faces are triangles. */ + BM_ITER_MESH (f, &fiter, em->bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + if (f->len > 4) { + tot_invalid++; + is_invalid = true; + break; + } } } - } + if (is_invalid) { + continue; + } - /* mirror before smooth */ - if (((Mesh *)obedit->data)->editflag & ME_EDIT_MIRROR_X) { - EDBM_verts_mirror_cache_begin(em, 0, false, true, use_topology); - } + /* Mirror before smooth. */ + if (((Mesh *)obedit->data)->editflag & ME_EDIT_MIRROR_X) { + EDBM_verts_mirror_cache_begin(em, 0, false, true, use_topology); + } - repeat = RNA_int_get(op->ptr, "repeat"); - lambda_factor = RNA_float_get(op->ptr, "lambda_factor"); - lambda_border = RNA_float_get(op->ptr, "lambda_border"); - usex = RNA_boolean_get(op->ptr, "use_x"); - usey = RNA_boolean_get(op->ptr, "use_y"); - usez = RNA_boolean_get(op->ptr, "use_z"); - preserve_volume = RNA_boolean_get(op->ptr, "preserve_volume"); - if (!repeat) - repeat = 1; + bool failed_repeat_loop = false; + for (int i = 0; i < repeat; i++) { + if (!EDBM_op_callf( + em, op, + "smooth_laplacian_vert verts=%hv lambda_factor=%f lambda_border=%f use_x=%b use_y=%b use_z=%b preserve_volume=%b", + BM_ELEM_SELECT, lambda_factor, lambda_border, usex, usey, usez, preserve_volume)) + { + failed_repeat_loop = true; + break; + } + } + if (failed_repeat_loop) { + continue; + } - for (i = 0; i < repeat; i++) { - if (!EDBM_op_callf( - em, op, - "smooth_laplacian_vert verts=%hv lambda_factor=%f lambda_border=%f use_x=%b use_y=%b use_z=%b preserve_volume=%b", - BM_ELEM_SELECT, lambda_factor, lambda_border, usex, usey, usez, preserve_volume)) - { - return OPERATOR_CANCELLED; + /* Apply mirror. */ + if (((Mesh *)obedit->data)->editflag & ME_EDIT_MIRROR_X) { + EDBM_verts_mirror_apply(em, BM_ELEM_SELECT, 0); + EDBM_verts_mirror_cache_end(em); } - } - /* apply mirror */ - if (((Mesh *)obedit->data)->editflag & ME_EDIT_MIRROR_X) { - EDBM_verts_mirror_apply(em, BM_ELEM_SELECT, 0); - EDBM_verts_mirror_cache_end(em); + EDBM_update_generic(em, true, false); } + MEM_freeN(objects); - EDBM_update_generic(em, true, false); + if (tot_unselected == objects_len) { + BKE_report(op->reports, RPT_WARNING, "No selected vertex"); + return OPERATOR_CANCELLED; + } + else if (tot_invalid == objects_len) { + BKE_report(op->reports, RPT_WARNING, "Selected faces must be triangles or quads"); + return OPERATOR_CANCELLED; + } return OPERATOR_FINISHED; } @@ -1999,10 +2336,13 @@ void MESH_OT_vertices_smooth_laplacian(wmOperatorType *ot) RNA_def_int(ot->srna, "repeat", 1, 1, 1000, "Number of iterations to smooth the mesh", "", 1, 200); - RNA_def_float(ot->srna, "lambda_factor", 5e-5f, 1e-7f, 1000.0f, + RNA_def_float(ot->srna, "lambda_factor", 1.0f, 1e-7f, 1000.0f, "Lambda factor", "", 1e-7f, 1000.0f); RNA_def_float(ot->srna, "lambda_border", 5e-5f, 1e-7f, 1000.0f, "Lambda factor in border", "", 1e-7f, 1000.0f); + + WM_operatortype_props_advanced_begin(ot); + RNA_def_boolean(ot->srna, "use_x", true, "Smooth X Axis", "Smooth object along X axis"); RNA_def_boolean(ot->srna, "use_y", true, "Smooth Y Axis", "Smooth object along Y axis"); RNA_def_boolean(ot->srna, "use_z", true, "Smooth Z Axis", "Smooth object along Z axis"); @@ -2031,12 +2371,21 @@ static void mesh_set_smooth_faces(BMEditMesh *em, short smooth) static int edbm_faces_shade_smooth_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - mesh_set_smooth_faces(em, 1); + if (em->bm->totfacesel == 0) { + continue; + } - EDBM_update_generic(em, false, false); + mesh_set_smooth_faces(em, 1); + EDBM_update_generic(em, false, false); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -2064,12 +2413,21 @@ void MESH_OT_faces_shade_smooth(wmOperatorType *ot) static int edbm_faces_shade_flat_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - mesh_set_smooth_faces(em, 0); + if (em->bm->totfacesel == 0) { + continue; + } - EDBM_update_generic(em, false, false); + mesh_set_smooth_faces(em, 0); + EDBM_update_generic(em, false, false); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -2097,73 +2455,105 @@ void MESH_OT_faces_shade_flat(wmOperatorType *ot) static int edbm_rotate_uvs_exec(bContext *C, wmOperator *op) { - Object *ob = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(ob); - BMOperator bmop; - /* get the direction from RNA */ const bool use_ccw = RNA_boolean_get(op->ptr, "use_ccw"); - /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */ - EDBM_op_init(em, &bmop, op, "rotate_uvs faces=%hf use_ccw=%b", BM_ELEM_SELECT, use_ccw); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - /* execute the operator */ - BMO_op_exec(em->bm, &bmop); + if (em->bm->totfacesel == 0) { + continue; + } - /* finish the operator */ - if (!EDBM_op_finish(em, &bmop, op, true)) { - return OPERATOR_CANCELLED; - } + BMOperator bmop; - EDBM_update_generic(em, false, false); + /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */ + EDBM_op_init(em, &bmop, op, "rotate_uvs faces=%hf use_ccw=%b", BM_ELEM_SELECT, use_ccw); + /* execute the operator */ + BMO_op_exec(em->bm, &bmop); + + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; + } + + EDBM_update_generic(em, false, false); + } + + MEM_freeN(objects); return OPERATOR_FINISHED; } static int edbm_reverse_uvs_exec(bContext *C, wmOperator *op) { - Object *ob = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(ob); - BMOperator bmop; + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */ - EDBM_op_init(em, &bmop, op, "reverse_uvs faces=%hf", BM_ELEM_SELECT); + if (em->bm->totfacesel == 0) { + continue; + } - /* execute the operator */ - BMO_op_exec(em->bm, &bmop); + BMOperator bmop; - /* finish the operator */ - if (!EDBM_op_finish(em, &bmop, op, true)) { - return OPERATOR_CANCELLED; - } + /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */ + EDBM_op_init(em, &bmop, op, "reverse_uvs faces=%hf", BM_ELEM_SELECT); - EDBM_update_generic(em, false, false); + /* execute the operator */ + BMO_op_exec(em->bm, &bmop); + /* finish the operator */ + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; + } + EDBM_update_generic(em, false, false); + } + + MEM_freeN(objects); return OPERATOR_FINISHED; } static int edbm_rotate_colors_exec(bContext *C, wmOperator *op) { - Object *ob = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(ob); - BMOperator bmop; - - /* get the direction from RNA */ + /* get the direction from RNA */ const bool use_ccw = RNA_boolean_get(op->ptr, "use_ccw"); - /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */ - EDBM_op_init(em, &bmop, op, "rotate_colors faces=%hf use_ccw=%b", BM_ELEM_SELECT, use_ccw); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); - /* execute the operator */ - BMO_op_exec(em->bm, &bmop); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(ob); + if (em->bm->totfacesel == 0) { + continue; + } - /* finish the operator */ - if (!EDBM_op_finish(em, &bmop, op, true)) { - return OPERATOR_CANCELLED; + BMOperator bmop; + + /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */ + EDBM_op_init(em, &bmop, op, "rotate_colors faces=%hf use_ccw=%b", BM_ELEM_SELECT, use_ccw); + + /* execute the operator */ + BMO_op_exec(em->bm, &bmop); + + /* finish the operator */ + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; + } + + /* dependencies graph and notification stuff */ + EDBM_update_generic(em, false, false); } - /* dependencies graph and notification stuff */ - EDBM_update_generic(em, false, false); + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -2171,22 +2561,34 @@ static int edbm_rotate_colors_exec(bContext *C, wmOperator *op) static int edbm_reverse_colors_exec(bContext *C, wmOperator *op) { - Object *ob = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(ob); - BMOperator bmop; + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); - /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */ - EDBM_op_init(em, &bmop, op, "reverse_colors faces=%hf", BM_ELEM_SELECT); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(ob); - /* execute the operator */ - BMO_op_exec(em->bm, &bmop); + if (em->bm->totfacesel == 0) { + continue; + } - /* finish the operator */ - if (!EDBM_op_finish(em, &bmop, op, true)) { - return OPERATOR_CANCELLED; - } + BMOperator bmop; - EDBM_update_generic(em, false, false); + /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */ + EDBM_op_init(em, &bmop, op, "reverse_colors faces=%hf", BM_ELEM_SELECT); + + /* execute the operator */ + BMO_op_exec(em->bm, &bmop); + + /* finish the operator */ + if (!EDBM_op_finish(em, &bmop, op, true)) { + return OPERATOR_CANCELLED; + } + + EDBM_update_generic(em, false, false); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -2326,7 +2728,7 @@ static bool merge_target( const float *vco = NULL; if (use_cursor) { - vco = ED_view3d_cursor3d_get(scene, v3d); + vco = ED_view3d_cursor3d_get(scene, v3d)->location; copy_v3_v3(co, vco); invert_m4_m4(ob->imat, ob->obmat); mul_m4_v3(ob->imat, co); @@ -2368,44 +2770,60 @@ static int edbm_merge_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); const int type = RNA_enum_get(op->ptr, "type"); const bool uvs = RNA_boolean_get(op->ptr, "uvs"); - bool ok = false; - switch (type) { - case MESH_MERGE_CENTER: - ok = merge_target(em, scene, v3d, obedit, false, uvs, op); - break; - case MESH_MERGE_CURSOR: - ok = merge_target(em, scene, v3d, obedit, true, uvs, op); - break; - case MESH_MERGE_LAST: - ok = merge_firstlast(em, false, uvs, op); - break; - case MESH_MERGE_FIRST: - ok = merge_firstlast(em, true, uvs, op); - break; - case MESH_MERGE_COLLAPSE: - ok = EDBM_op_callf(em, op, "collapse edges=%he uvs=%b", BM_ELEM_SELECT, uvs); - break; - default: - BLI_assert(0); - break; - } + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - if (!ok) { - return OPERATOR_CANCELLED; - } + if (em->bm->totvertsel == 0) { + continue; + } - EDBM_update_generic(em, true, true); + bool ok = false; + switch (type) { + case MESH_MERGE_CENTER: + ok = merge_target(em, scene, v3d, obedit, false, uvs, op); + break; + case MESH_MERGE_CURSOR: + ok = merge_target(em, scene, v3d, obedit, true, uvs, op); + break; + case MESH_MERGE_LAST: + ok = merge_firstlast(em, false, uvs, op); + break; + case MESH_MERGE_FIRST: + ok = merge_firstlast(em, true, uvs, op); + break; + case MESH_MERGE_COLLAPSE: + ok = EDBM_op_callf(em, op, "collapse edges=%he uvs=%b", BM_ELEM_SELECT, uvs); + break; + default: + BLI_assert(0); + break; + } - /* once collapsed, we can't have edge/face selection */ - if ((em->selectmode & SCE_SELECT_VERTEX) == 0) { - EDBM_flag_disable_all(em, BM_ELEM_SELECT); + if (!ok) { + continue; + } + + EDBM_update_generic(em, true, true); + + /* once collapsed, we can't have edge/face selection */ + if ((em->selectmode & SCE_SELECT_VERTEX) == 0) { + EDBM_flag_disable_all(em, BM_ELEM_SELECT); + } + /* Only active object supported, see comment below. */ + if (ELEM(type, MESH_MERGE_FIRST, MESH_MERGE_LAST)) { + break; + } } + MEM_freeN(objects); + return OPERATOR_FINISHED; } @@ -2431,6 +2849,10 @@ static const EnumPropertyItem *merge_type_itemf(bContext *C, PointerRNA *UNUSED( if (obedit && obedit->type == OB_MESH) { BMEditMesh *em = BKE_editmesh_from_object(obedit); + /* Only active object supported: + * In practice it doesn't make sense to run this operation on non-active meshes + * since selecting will activate - we could have own code-path for these but it's a hassle + * for now just apply to the active (first) object. */ if (em->selectmode & SCE_SELECT_VERTEX) { if (em->bm->selected.first && em->bm->selected.last && ((BMEditSelection *)em->bm->selected.first)->htype == BM_VERT && @@ -2478,6 +2900,9 @@ void MESH_OT_merge(wmOperatorType *ot) /* properties */ ot->prop = RNA_def_enum(ot->srna, "type", merge_type_items, MESH_MERGE_CENTER, "Type", "Merge method to use"); RNA_def_enum_funcs(ot->prop, merge_type_itemf); + + WM_operatortype_props_advanced_begin(ot); + RNA_def_boolean(ot->srna, "uvs", false, "UVs", "Move UVs according to merge"); } @@ -2489,60 +2914,79 @@ void MESH_OT_merge(wmOperatorType *ot) static int edbm_remove_doubles_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMOperator bmop; const float threshold = RNA_float_get(op->ptr, "threshold"); const bool use_unselected = RNA_boolean_get(op->ptr, "use_unselected"); - const int totvert_orig = em->bm->totvert; - int count; - char htype_select; + int count_multi = 0; - /* avoid loosing selection state (select -> tags) */ - if (em->selectmode & SCE_SELECT_VERTEX) htype_select = BM_VERT; - else if (em->selectmode & SCE_SELECT_EDGE) htype_select = BM_EDGE; - else htype_select = BM_FACE; + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); - /* store selection as tags */ - BM_mesh_elem_hflag_enable_test(em->bm, htype_select, BM_ELEM_TAG, true, true, BM_ELEM_SELECT); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + /* Selection used as target with 'use_unselected'. */ + if (em->bm->totvertsel == 0) { + continue; + } - if (use_unselected) { - EDBM_op_init( - em, &bmop, op, - "automerge verts=%hv dist=%f", - BM_ELEM_SELECT, threshold); - BMO_op_exec(em->bm, &bmop); + BMOperator bmop; + const int totvert_orig = em->bm->totvert; - if (!EDBM_op_finish(em, &bmop, op, true)) { - return OPERATOR_CANCELLED; - } - } - else { - EDBM_op_init( - em, &bmop, op, - "find_doubles verts=%hv dist=%f", - BM_ELEM_SELECT, threshold); - BMO_op_exec(em->bm, &bmop); + /* avoid losing selection state (select -> tags) */ + char htype_select; + if (em->selectmode & SCE_SELECT_VERTEX) htype_select = BM_VERT; + else if (em->selectmode & SCE_SELECT_EDGE) htype_select = BM_EDGE; + else htype_select = BM_FACE; - if (!EDBM_op_callf(em, op, "weld_verts targetmap=%S", &bmop, "targetmap.out")) { - BMO_op_finish(em->bm, &bmop); - return OPERATOR_CANCELLED; + /* store selection as tags */ + BM_mesh_elem_hflag_enable_test(em->bm, htype_select, BM_ELEM_TAG, true, true, BM_ELEM_SELECT); + + + if (use_unselected) { + EDBM_op_init( + em, &bmop, op, + "automerge verts=%hv dist=%f", + BM_ELEM_SELECT, threshold); + BMO_op_exec(em->bm, &bmop); + + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; + } } + else { + EDBM_op_init( + em, &bmop, op, + "find_doubles verts=%hv dist=%f", + BM_ELEM_SELECT, threshold); - if (!EDBM_op_finish(em, &bmop, op, true)) { - return OPERATOR_CANCELLED; + BMO_op_exec(em->bm, &bmop); + + if (!EDBM_op_callf(em, op, "weld_verts targetmap=%S", &bmop, "targetmap.out")) { + BMO_op_finish(em->bm, &bmop); + continue; + } + + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; + } } - } - count = totvert_orig - em->bm->totvert; - BKE_reportf(op->reports, RPT_INFO, "Removed %d vertices", count); + const int count = (totvert_orig - em->bm->totvert); - /* restore selection from tags */ - BM_mesh_elem_hflag_enable_test(em->bm, htype_select, BM_ELEM_SELECT, true, true, BM_ELEM_TAG); - EDBM_selectmode_flush(em); + /* restore selection from tags */ + BM_mesh_elem_hflag_enable_test(em->bm, htype_select, BM_ELEM_SELECT, true, true, BM_ELEM_TAG); + EDBM_selectmode_flush(em); - EDBM_update_generic(em, true, true); + if (count) { + count_multi += count; + EDBM_update_generic(em, true, true); + } + } + MEM_freeN(objects); + + BKE_reportf(op->reports, RPT_INFO, "Removed %d vertices", count_multi); return OPERATOR_FINISHED; } @@ -2573,48 +3017,69 @@ void MESH_OT_remove_doubles(wmOperatorType *ot) * \{ */ /* BMESH_TODO this should be properly encapsulated in a bmop. but later.*/ -static void shape_propagate(BMEditMesh *em, wmOperator *op) +static bool shape_propagate(BMEditMesh *em) { BMIter iter; BMVert *eve = NULL; float *co; - int i, totshape = CustomData_number_of_layers(&em->bm->vdata, CD_SHAPEKEY); + int totshape = CustomData_number_of_layers(&em->bm->vdata, CD_SHAPEKEY); if (!CustomData_has_layer(&em->bm->vdata, CD_SHAPEKEY)) { - BKE_report(op->reports, RPT_ERROR, "Mesh does not have shape keys"); - return; + return false; } BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { - if (!BM_elem_flag_test(eve, BM_ELEM_SELECT) || BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) + if (!BM_elem_flag_test(eve, BM_ELEM_SELECT) || BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { continue; + } - for (i = 0; i < totshape; i++) { + for (int i = 0; i < totshape; i++) { co = CustomData_bmesh_get_n(&em->bm->vdata, eve->head.data, CD_SHAPEKEY, i); copy_v3_v3(co, eve->co); } } - -#if 0 - //TAG Mesh Objects that share this data - for (base = scene->base.first; base; base = base->next) { - if (base->object && base->object->data == me) { - DAG_id_tag_update(&base->object->id, OB_RECALC_DATA); - } - } -#endif + return true; } - static int edbm_shape_propagate_to_all_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - Mesh *me = obedit->data; - BMEditMesh *em = me->edit_btmesh; + ViewLayer *view_layer = CTX_data_view_layer(C); + int tot_shapekeys = 0; + int tot_selected_verts_objects = 0; - shape_propagate(em, op); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + Mesh *me = obedit->data; + BMEditMesh *em = me->edit_btmesh; - EDBM_update_generic(em, false, false); + if (em->bm->totvertsel == 0) { + continue; + } + tot_selected_verts_objects++; + + if (shape_propagate(em)) { + tot_shapekeys++; + } + + EDBM_update_generic(em, false, false); + } + MEM_freeN(objects); + + if (tot_selected_verts_objects == 0) { + BKE_report(op->reports, RPT_ERROR, "No selected vertex"); + return OPERATOR_CANCELLED; + } + else if (tot_shapekeys == 0) { + BKE_report( + op->reports, + RPT_ERROR, + objects_len > 1 ? + "Meshes do not have shape keys" : + "Mesh does not have shape keys"); + return OPERATOR_CANCELLED; + } return OPERATOR_FINISHED; } @@ -2644,55 +3109,95 @@ void MESH_OT_shape_propagate_to_all(wmOperatorType *ot) /* BMESH_TODO this should be properly encapsulated in a bmop. but later.*/ static int edbm_blend_from_shape_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - Mesh *me = obedit->data; - Key *key = me->key; - KeyBlock *kb = NULL; - BMEditMesh *em = me->edit_btmesh; + Object *obedit_ref = CTX_data_edit_object(C); + Mesh *me_ref = obedit_ref->data; + Key *key_ref = me_ref->key; + KeyBlock *kb_ref = NULL; + BMEditMesh *em_ref = me_ref->edit_btmesh; BMVert *eve; BMIter iter; + ViewLayer *view_layer = CTX_data_view_layer(C); float co[3], *sco; - int totshape; + int totshape_ref = 0; const float blend = RNA_float_get(op->ptr, "blend"); - const int shape = RNA_enum_get(op->ptr, "shape"); + int shape_ref = RNA_enum_get(op->ptr, "shape"); const bool use_add = RNA_boolean_get(op->ptr, "add"); - /* sanity check */ - totshape = CustomData_number_of_layers(&em->bm->vdata, CD_SHAPEKEY); - if (totshape == 0 || shape < 0 || shape >= totshape) - return OPERATOR_CANCELLED; + /* Sanity check. */ + totshape_ref = CustomData_number_of_layers(&em_ref->bm->vdata, CD_SHAPEKEY); - /* get shape key - needed for finding reference shape (for add mode only) */ - if (key) { - kb = BLI_findlink(&key->block, shape); + if (totshape_ref == 0 || shape_ref < 0) { + BKE_report(op->reports, RPT_ERROR, "Active mesh does not have shape keys"); + return OPERATOR_CANCELLED; + } + else if (shape_ref >= totshape_ref) { + /* This case occurs if operator was used before on object with more keys than current one. */ + shape_ref = 0; /* default to basis */ } - /* perform blending on selected vertices*/ - BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { - if (!BM_elem_flag_test(eve, BM_ELEM_SELECT) || BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) - continue; + /* Get shape key - needed for finding reference shape (for add mode only). */ + if (key_ref) { + kb_ref = BLI_findlink(&key_ref->block, shape_ref); + } - /* get coordinates of shapekey we're blending from */ - sco = CustomData_bmesh_get_n(&em->bm->vdata, eve->head.data, CD_SHAPEKEY, shape); - copy_v3_v3(co, sco); + int tot_selected_verts_objects = 0; + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + Mesh *me = obedit->data; + Key *key = me->key; + KeyBlock *kb = NULL; + BMEditMesh *em = me->edit_btmesh; + int shape; - if (use_add) { - /* in add mode, we add relative shape key offset */ - if (kb) { - const float *rco = CustomData_bmesh_get_n(&em->bm->vdata, eve->head.data, CD_SHAPEKEY, kb->relative); - sub_v3_v3v3(co, co, rco); - } + if (em->bm->totvertsel == 0) { + continue; + } + tot_selected_verts_objects++; - madd_v3_v3fl(eve->co, co, blend); + if (!key) { + continue; } else { - /* in blend mode, we interpolate to the shape key */ - interp_v3_v3v3(eve->co, eve->co, co, blend); + kb = BKE_keyblock_find_name(key, kb_ref->name); + shape = BLI_findindex(&key->block, kb); + } + + if (kb) { + /* Perform blending on selected vertices. */ + BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { + if (!BM_elem_flag_test(eve, BM_ELEM_SELECT) || BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) + continue; + + /* Get coordinates of shapekey we're blending from. */ + sco = CustomData_bmesh_get_n(&em->bm->vdata, eve->head.data, CD_SHAPEKEY, shape); + copy_v3_v3(co, sco); + + if (use_add) { + /* In add mode, we add relative shape key offset. */ + if (kb) { + const float *rco = CustomData_bmesh_get_n(&em->bm->vdata, eve->head.data, CD_SHAPEKEY, kb->relative); + sub_v3_v3v3(co, co, rco); + } + + madd_v3_v3fl(eve->co, co, blend); + } + else { + /* In blend mode, we interpolate to the shape key. */ + interp_v3_v3v3(eve->co, eve->co, co, blend); + } + } + EDBM_update_generic(em, true, false); } } + MEM_freeN(objects); - EDBM_update_generic(em, true, false); + if (tot_selected_verts_objects == 0) { + BKE_report(op->reports, RPT_ERROR, "No selected vertex"); + return OPERATOR_CANCELLED; + } return OPERATOR_FINISHED; } @@ -2779,39 +3284,48 @@ void MESH_OT_blend_from_shape(wmOperatorType *ot) static int edbm_solidify_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - Mesh *me = obedit->data; - BMEditMesh *em = me->edit_btmesh; - BMesh *bm = em->bm; - BMOperator bmop; - const float thickness = RNA_float_get(op->ptr, "thickness"); - if (!EDBM_op_init(em, &bmop, op, "solidify geom=%hf thickness=%f", BM_ELEM_SELECT, thickness)) { - return OPERATOR_CANCELLED; - } + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; - /* deselect only the faces in the region to be solidified (leave wire - * edges and loose verts selected, as there will be no corresponding - * geometry selected below) */ - BMO_slot_buffer_hflag_disable(bm, bmop.slots_in, "geom", BM_FACE, BM_ELEM_SELECT, true); + if (em->bm->totfacesel == 0) { + continue; + } - /* run the solidify operator */ - BMO_op_exec(bm, &bmop); + BMOperator bmop; - /* select the newly generated faces */ - BMO_slot_buffer_hflag_enable(bm, bmop.slots_out, "geom.out", BM_FACE, BM_ELEM_SELECT, true); + if (!EDBM_op_init(em, &bmop, op, "solidify geom=%hf thickness=%f", BM_ELEM_SELECT, thickness)) { + continue; + } - if (!EDBM_op_finish(em, &bmop, op, true)) { - return OPERATOR_CANCELLED; - } + /* deselect only the faces in the region to be solidified (leave wire + * edges and loose verts selected, as there will be no corresponding + * geometry selected below) */ + BMO_slot_buffer_hflag_disable(bm, bmop.slots_in, "geom", BM_FACE, BM_ELEM_SELECT, true); - EDBM_update_generic(em, true, true); + /* run the solidify operator */ + BMO_op_exec(bm, &bmop); + + /* select the newly generated faces */ + BMO_slot_buffer_hflag_enable(bm, bmop.slots_out, "geom.out", BM_FACE, BM_ELEM_SELECT, true); + + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; + } + EDBM_update_generic(em, true, true); + } + + MEM_freeN(objects); return OPERATOR_FINISHED; } - void MESH_OT_solidify(wmOperatorType *ot) { PropertyRNA *prop; @@ -3184,7 +3698,7 @@ enum { MESH_SEPARATE_LOOSE = 2, }; -static Base *mesh_separate_tagged(Main *bmain, Scene *scene, Base *base_old, BMesh *bm_old) +static Base *mesh_separate_tagged(Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old) { Base *base_new; Object *obedit = base_old->object; @@ -3205,11 +3719,11 @@ static Base *mesh_separate_tagged(Main *bmain, Scene *scene, Base *base_old, BMe CustomData_bmesh_init_pool(&bm_new->ldata, bm_mesh_allocsize_default.totloop, BM_LOOP); CustomData_bmesh_init_pool(&bm_new->pdata, bm_mesh_allocsize_default.totface, BM_FACE); - base_new = ED_object_add_duplicate(bmain, scene, base_old, USER_DUP_MESH); + base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_old, USER_DUP_MESH); /* DAG_relations_tag_update(bmain); */ /* normally would call directly after but in this case delay recalc */ assign_matarar(bmain, base_new->object, give_matarar(obedit), *give_totcolp(obedit)); /* new in 2.5 */ - ED_base_object_select(base_new, BA_SELECT); + ED_object_base_select(base_new, BA_SELECT); BMO_op_callf(bm_old, (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), "duplicate geom=%hvef dest=%p", BM_ELEM_TAG, bm_new); @@ -3231,7 +3745,7 @@ static Base *mesh_separate_tagged(Main *bmain, Scene *scene, Base *base_old, BMe return base_new; } -static bool mesh_separate_selected(Main *bmain, Scene *scene, Base *base_old, BMesh *bm_old) +static bool mesh_separate_selected(Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old) { /* we may have tags from previous operators */ BM_mesh_elem_hflag_disable_all(bm_old, BM_FACE | BM_EDGE | BM_VERT, BM_ELEM_TAG, false); @@ -3239,7 +3753,7 @@ static bool mesh_separate_selected(Main *bmain, Scene *scene, Base *base_old, BM /* sel -> tag */ BM_mesh_elem_hflag_enable_test(bm_old, BM_FACE | BM_EDGE | BM_VERT, BM_ELEM_TAG, true, false, BM_ELEM_SELECT); - return (mesh_separate_tagged(bmain, scene, base_old, bm_old) != NULL); + return (mesh_separate_tagged(bmain, scene, view_layer, base_old, bm_old) != NULL); } /* flush a hflag to from verts to edges/faces */ @@ -3338,7 +3852,7 @@ static void mesh_separate_material_assign_mat_nr(Main *bmain, Object *ob, const } } -static bool mesh_separate_material(Main *bmain, Scene *scene, Base *base_old, BMesh *bm_old) +static bool mesh_separate_material(Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old) { BMFace *f_cmp, *f; BMIter iter; @@ -3379,7 +3893,7 @@ static bool mesh_separate_material(Main *bmain, Scene *scene, Base *base_old, BM } /* Move selection into a separate object */ - base_new = mesh_separate_tagged(bmain, scene, base_old, bm_old); + base_new = mesh_separate_tagged(bmain, scene, view_layer, base_old, bm_old); if (base_new) { mesh_separate_material_assign_mat_nr(bmain, base_new->object, mat_nr); } @@ -3390,7 +3904,7 @@ static bool mesh_separate_material(Main *bmain, Scene *scene, Base *base_old, BM return result; } -static bool mesh_separate_loose(Main *bmain, Scene *scene, Base *base_old, BMesh *bm_old) +static bool mesh_separate_loose(Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_old, BMesh *bm_old) { int i; BMEdge *e; @@ -3443,7 +3957,7 @@ static bool mesh_separate_loose(Main *bmain, Scene *scene, Base *base_old, BMesh bm_mesh_hflag_flush_vert(bm_old, BM_ELEM_TAG); /* Move selection into a separate object */ - result |= (mesh_separate_tagged(bmain, scene, base_old, bm_old) != NULL); + result |= (mesh_separate_tagged(bmain, scene, view_layer, base_old, bm_old) != NULL); } return result; @@ -3453,42 +3967,52 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); const int type = RNA_enum_get(op->ptr, "type"); int retval = 0; if (ED_operator_editmesh(C)) { - Base *base = CTX_data_active_base(C); - BMEditMesh *em = BKE_editmesh_from_object(base->object); - - if (type == 0) { - if ((em->bm->totvertsel == 0) && - (em->bm->totedgesel == 0) && - (em->bm->totfacesel == 0)) - { - BKE_report(op->reports, RPT_ERROR, "Nothing selected"); - return OPERATOR_CANCELLED; + uint bases_len = 0; + uint empty_selection_len = 0; + Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(view_layer, &bases_len); + for (uint bs_index = 0; bs_index < bases_len; bs_index++) { + Base *base = bases[bs_index]; + BMEditMesh *em = BKE_editmesh_from_object(base->object); + + if (type == 0) { + if ((em->bm->totvertsel == 0) && + (em->bm->totedgesel == 0) && + (em->bm->totfacesel == 0)) + { + /* when all objects has no selection */ + if (++empty_selection_len == bases_len) { + BKE_report(op->reports, RPT_ERROR, "Nothing selected"); + } + continue; + } } - } - /* editmode separate */ - switch (type) { - case MESH_SEPARATE_SELECTED: - retval = mesh_separate_selected(bmain, scene, base, em->bm); - break; - case MESH_SEPARATE_MATERIAL: - retval = mesh_separate_material(bmain, scene, base, em->bm); - break; - case MESH_SEPARATE_LOOSE: - retval = mesh_separate_loose(bmain, scene, base, em->bm); - break; - default: - BLI_assert(0); - break; - } + /* editmode separate */ + switch (type) { + case MESH_SEPARATE_SELECTED: + retval = mesh_separate_selected(bmain, scene, view_layer, base, em->bm); + break; + case MESH_SEPARATE_MATERIAL: + retval = mesh_separate_material(bmain, scene, view_layer, base, em->bm); + break; + case MESH_SEPARATE_LOOSE: + retval = mesh_separate_loose(bmain, scene, view_layer, base, em->bm); + break; + default: + BLI_assert(0); + break; + } - if (retval) { - EDBM_update_generic(em, true, true); + if (retval) { + EDBM_update_generic(em, true, true); + } } + MEM_freeN(bases); } else { if (type == MESH_SEPARATE_SELECTED) { @@ -3514,10 +4038,10 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) switch (type) { case MESH_SEPARATE_MATERIAL: - retval_iter = mesh_separate_material(bmain, scene, base_iter, bm_old); + retval_iter = mesh_separate_material(bmain, scene, view_layer, base_iter, bm_old); break; case MESH_SEPARATE_LOOSE: - retval_iter = mesh_separate_loose(bmain, scene, base_iter, bm_old); + retval_iter = mesh_separate_loose(bmain, scene, view_layer, base_iter, bm_old); break; default: BLI_assert(0); @@ -3531,7 +4055,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) .calc_object_remap = true, })); - DAG_id_tag_update(&me->id, OB_RECALC_DATA); + DEG_id_tag_update(&me->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM | ND_DATA, me); } @@ -3546,7 +4070,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) if (retval) { /* delay depsgraph recalc until all objects are duplicated */ - DAG_relations_tag_update(bmain); + DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL); return OPERATOR_FINISHED; @@ -3588,45 +4112,64 @@ void MESH_OT_separate(wmOperatorType *ot) static int edbm_fill_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); const bool use_beauty = RNA_boolean_get(op->ptr, "use_beauty"); - BMOperator bmop; - const int totface_orig = em->bm->totface; - int ret; - if (em->bm->totedgesel == 0) { - BKE_report(op->reports, RPT_WARNING, "No edges selected"); - return OPERATOR_CANCELLED; - } + bool has_selected_edges = false, has_faces_filled = false; - if (!EDBM_op_init(em, &bmop, op, - "triangle_fill edges=%he use_beauty=%b", - BM_ELEM_SELECT, use_beauty)) - { - return OPERATOR_CANCELLED; - } + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMO_op_exec(em->bm, &bmop); + const int totface_orig = em->bm->totface; + + if (em->bm->totedgesel == 0) { + continue; + } + has_selected_edges = true; + + BMOperator bmop; + if (!EDBM_op_init( + em, &bmop, op, + "triangle_fill edges=%he use_beauty=%b", + BM_ELEM_SELECT, use_beauty)) + { + continue; + } + + BMO_op_exec(em->bm, &bmop); + + /* cancel if nothing was done */ + if (totface_orig == em->bm->totface) { + EDBM_op_finish(em, &bmop, op, true); + continue; + } + has_faces_filled = true; - if (totface_orig != em->bm->totface) { /* select new geometry */ BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "geom.out", BM_FACE | BM_EDGE, BM_ELEM_SELECT, true); - EDBM_update_generic(em, true, true); + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; + } - ret = OPERATOR_FINISHED; + EDBM_update_generic(em, true, true); } - else { - BKE_report(op->reports, RPT_WARNING, "No faces filled"); - ret = OPERATOR_CANCELLED; + MEM_freeN(objects); + + if (!has_selected_edges) { + BKE_report(op->reports, RPT_ERROR, "No edges selected"); + return OPERATOR_CANCELLED; } - if (!EDBM_op_finish(em, &bmop, op, true)) { - ret = OPERATOR_CANCELLED; + if (!has_faces_filled) { + BKE_report(op->reports, RPT_WARNING, "No faces filled"); + return OPERATOR_CANCELLED; } - return ret; + return OPERATOR_FINISHED; } void MESH_OT_fill(wmOperatorType *ot) @@ -3800,73 +4343,91 @@ static void edbm_fill_grid_prepare(BMesh *bm, int offset, int *r_span, bool span static int edbm_fill_grid_exec(bContext *C, wmOperator *op) { - BMOperator bmop; - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - const bool use_smooth = edbm_add_edge_face__smooth_get(em->bm); - const int totedge_orig = em->bm->totedge; - const int totface_orig = em->bm->totface; - const bool use_interp_simple = RNA_boolean_get(op->ptr, "use_interp_simple"); const bool use_prepare = true; + const bool use_interp_simple = RNA_boolean_get(op->ptr, "use_interp_simple"); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - if (use_prepare) { - /* use when we have a single loop selected */ - PropertyRNA *prop_span = RNA_struct_find_property(op->ptr, "span"); - PropertyRNA *prop_offset = RNA_struct_find_property(op->ptr, "offset"); - bool calc_span; + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - const int clamp = em->bm->totvertsel; - int span; - int offset; + const bool use_smooth = edbm_add_edge_face__smooth_get(em->bm); + const int totedge_orig = em->bm->totedge; + const int totface_orig = em->bm->totface; - if (RNA_property_is_set(op->ptr, prop_span)) { - span = RNA_property_int_get(op->ptr, prop_span); - span = min_ii(span, (clamp / 2) - 1); - calc_span = false; - } - else { - span = clamp / 4; - calc_span = true; + if (em->bm->totedgesel == 0) { + continue; } - offset = RNA_property_int_get(op->ptr, prop_offset); - offset = clamp ? mod_i(offset, clamp) : 0; + if (use_prepare) { + /* use when we have a single loop selected */ + PropertyRNA *prop_span = RNA_struct_find_property(op->ptr, "span"); + PropertyRNA *prop_offset = RNA_struct_find_property(op->ptr, "offset"); + bool calc_span; - /* in simple cases, move selection for tags, but also support more advanced cases */ - edbm_fill_grid_prepare(em->bm, offset, &span, calc_span); + const int clamp = em->bm->totvertsel; + int span; + int offset; - RNA_property_int_set(op->ptr, prop_span, span); - } - /* end tricky prepare code */ + if (RNA_property_is_set(op->ptr, prop_span)) { + span = RNA_property_int_get(op->ptr, prop_span); + span = min_ii(span, (clamp / 2) - 1); + calc_span = false; + } + else { + span = clamp / 4; + calc_span = true; + } + offset = RNA_property_int_get(op->ptr, prop_offset); + offset = clamp ? mod_i(offset, clamp) : 0; - if (!EDBM_op_init( - em, &bmop, op, - "grid_fill edges=%he mat_nr=%i use_smooth=%b use_interp_simple=%b", - use_prepare ? BM_ELEM_TAG : BM_ELEM_SELECT, - em->mat_nr, use_smooth, use_interp_simple)) - { - return OPERATOR_CANCELLED; - } + /* in simple cases, move selection for tags, but also support more advanced cases */ + edbm_fill_grid_prepare(em->bm, offset, &span, calc_span); - BMO_op_exec(em->bm, &bmop); + RNA_property_int_set(op->ptr, prop_span, span); + } + /* end tricky prepare code */ - /* cancel if nothing was done */ - if ((totedge_orig == em->bm->totedge) && - (totface_orig == em->bm->totface)) - { - EDBM_op_finish(em, &bmop, op, true); - return OPERATOR_CANCELLED; - } + BMOperator bmop; + if (!EDBM_op_init( + em, &bmop, op, + "grid_fill edges=%he mat_nr=%i use_smooth=%b use_interp_simple=%b", + use_prepare ? BM_ELEM_TAG : BM_ELEM_SELECT, + em->mat_nr, use_smooth, use_interp_simple)) + { + continue; + } - BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true); + BMO_op_exec(em->bm, &bmop); - if (!EDBM_op_finish(em, &bmop, op, true)) { - return OPERATOR_CANCELLED; + /* NOTE: EDBM_op_finish() will change bmesh pointer inside of edit mesh, + * so need to tell evaluated objects to sync new bmesh pointer to their + * edit mesh structures. + */ + DEG_id_tag_update(&obedit->id, 0); + + /* cancel if nothing was done */ + if ((totedge_orig == em->bm->totedge) && + (totface_orig == em->bm->totface)) + { + EDBM_op_finish(em, &bmop, op, true); + continue; + } + + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true); + + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; + } + + EDBM_update_generic(em, true, true); } - EDBM_update_generic(em, true, true); + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -3905,20 +4466,32 @@ void MESH_OT_fill_grid(wmOperatorType *ot) static int edbm_fill_holes_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); const int sides = RNA_int_get(op->ptr, "sides"); - if (!EDBM_op_call_and_selectf( - em, op, - "faces.out", true, - "holes_fill edges=%he sides=%i", - BM_ELEM_SELECT, sides)) - { - return OPERATOR_CANCELLED; - } + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); - EDBM_update_generic(em, true, true); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + if (em->bm->totedgesel == 0) { + continue; + } + + if (!EDBM_op_call_and_selectf( + em, op, + "faces.out", true, + "holes_fill edges=%he sides=%i", + BM_ELEM_SELECT, sides)) + { + continue; + } + + EDBM_update_generic(em, true, true); + } + MEM_freeN(objects); return OPERATOR_FINISHED; @@ -3950,40 +4523,51 @@ void MESH_OT_fill_holes(wmOperatorType *ot) static int edbm_beautify_fill_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); const float angle_max = M_PI; const float angle_limit = RNA_float_get(op->ptr, "angle_limit"); char hflag; - if (angle_limit >= angle_max) { - hflag = BM_ELEM_SELECT; - } - else { - BMIter iter; - BMEdge *e; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { - BM_elem_flag_set( - e, BM_ELEM_TAG, - (BM_elem_flag_test(e, BM_ELEM_SELECT) && - BM_edge_calc_face_angle_ex(e, angle_max) < angle_limit)); + if (em->bm->totfacesel == 0) { + continue; + } + if (angle_limit >= angle_max) { + hflag = BM_ELEM_SELECT; } + else { + BMIter iter; + BMEdge *e; - hflag = BM_ELEM_TAG; - } + BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { + BM_elem_flag_set( + e, BM_ELEM_TAG, + (BM_elem_flag_test(e, BM_ELEM_SELECT) && + BM_edge_calc_face_angle_ex(e, angle_max) < angle_limit)); - if (!EDBM_op_call_and_selectf( - em, op, "geom.out", true, - "beautify_fill faces=%hf edges=%he", - BM_ELEM_SELECT, hflag)) - { - return OPERATOR_CANCELLED; + } + hflag = BM_ELEM_TAG; + } + + if (!EDBM_op_call_and_selectf( + em, op, "geom.out", true, + "beautify_fill faces=%hf edges=%he", + BM_ELEM_SELECT, hflag)) + { + continue; + } + + EDBM_update_generic(em, true, true); } - EDBM_update_generic(em, true, true); + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -4018,30 +4602,40 @@ void MESH_OT_beautify_fill(wmOperatorType *ot) static int edbm_poke_face_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMOperator bmop; - const float offset = RNA_float_get(op->ptr, "offset"); const bool use_relative_offset = RNA_boolean_get(op->ptr, "use_relative_offset"); const int center_mode = RNA_enum_get(op->ptr, "center_mode"); - EDBM_op_init(em, &bmop, op, "poke faces=%hf offset=%f use_relative_offset=%b center_mode=%i", - BM_ELEM_SELECT, offset, use_relative_offset, center_mode); - BMO_op_exec(em->bm, &bmop); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + if (em->bm->totfacesel == 0) { + continue; + } - EDBM_flag_disable_all(em, BM_ELEM_SELECT); + BMOperator bmop; + EDBM_op_init(em, &bmop, op, "poke faces=%hf offset=%f use_relative_offset=%b center_mode=%i", + BM_ELEM_SELECT, offset, use_relative_offset, center_mode); + BMO_op_exec(em->bm, &bmop); - BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "verts.out", BM_VERT, BM_ELEM_SELECT, true); - BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true); + EDBM_flag_disable_all(em, BM_ELEM_SELECT); - if (!EDBM_op_finish(em, &bmop, op, true)) { - return OPERATOR_CANCELLED; - } + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "verts.out", BM_VERT, BM_ELEM_SELECT, true); + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true); - EDBM_mesh_normals_update(em); + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; + } - EDBM_update_generic(em, true, true); + EDBM_mesh_normals_update(em); + + EDBM_update_generic(em, true, true); + } + MEM_freeN(objects); return OPERATOR_FINISHED; @@ -4082,32 +4676,48 @@ void MESH_OT_poke(wmOperatorType *ot) static int edbm_quads_convert_to_tris_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMOperator bmop; const int quad_method = RNA_enum_get(op->ptr, "quad_method"); const int ngon_method = RNA_enum_get(op->ptr, "ngon_method"); - BMOIter oiter; - BMFace *f; + ViewLayer *view_layer = CTX_data_view_layer(C); - EDBM_op_init(em, &bmop, op, "triangulate faces=%hf quad_method=%i ngon_method=%i", BM_ELEM_SELECT, quad_method, ngon_method); - BMO_op_exec(em->bm, &bmop); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - /* select the output */ - BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true); + if (em->bm->totfacesel == 0) { + continue; + } - /* remove the doubles */ - BMO_ITER (f, &oiter, bmop.slots_out, "face_map_double.out", BM_FACE) { - BM_face_kill(em->bm, f); - } + BMOperator bmop; + BMOIter oiter; + BMFace *f; - EDBM_selectmode_flush(em); + EDBM_op_init( + em, &bmop, op, + "triangulate faces=%hf quad_method=%i ngon_method=%i", + BM_ELEM_SELECT, quad_method, ngon_method); + BMO_op_exec(em->bm, &bmop); - if (!EDBM_op_finish(em, &bmop, op, true)) { - return OPERATOR_CANCELLED; + /* select the output */ + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true); + + /* remove the doubles */ + BMO_ITER (f, &oiter, bmop.slots_out, "face_map_double.out", BM_FACE) { + BM_face_kill(em->bm, f); + } + + EDBM_selectmode_flush(em); + + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; + } + + EDBM_update_generic(em, true, true); } - EDBM_update_generic(em, true, true); + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -4141,52 +4751,69 @@ void MESH_OT_quads_convert_to_tris(wmOperatorType *ot) static int edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - bool do_seam, do_sharp, do_uvs, do_vcols, do_materials; - float angle_face_threshold, angle_shape_threshold; - PropertyRNA *prop; + ViewLayer *view_layer = CTX_data_view_layer(C); - /* When joining exactly 2 faces, no limit. - * this is useful for one off joins while editing. */ - prop = RNA_struct_find_property(op->ptr, "face_threshold"); - if ((em->bm->totfacesel == 2) && - (RNA_property_is_set(op->ptr, prop) == false)) - { - angle_face_threshold = DEG2RADF(180.0f); - } - else { - angle_face_threshold = RNA_property_float_get(op->ptr, prop); - } + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + bool is_face_pair; - prop = RNA_struct_find_property(op->ptr, "shape_threshold"); - if ((em->bm->totfacesel == 2) && - (RNA_property_is_set(op->ptr, prop) == false)) - { - angle_shape_threshold = DEG2RADF(180.0f); - } - else { - angle_shape_threshold = RNA_property_float_get(op->ptr, prop); - } - - do_seam = RNA_boolean_get(op->ptr, "seam"); - do_sharp = RNA_boolean_get(op->ptr, "sharp"); - do_uvs = RNA_boolean_get(op->ptr, "uvs"); - do_vcols = RNA_boolean_get(op->ptr, "vcols"); - do_materials = RNA_boolean_get(op->ptr, "materials"); - - if (!EDBM_op_call_and_selectf( - em, op, - "faces.out", true, - "join_triangles faces=%hf angle_face_threshold=%f angle_shape_threshold=%f " - "cmp_seam=%b cmp_sharp=%b cmp_uvs=%b cmp_vcols=%b cmp_materials=%b", - BM_ELEM_SELECT, angle_face_threshold, angle_shape_threshold, - do_seam, do_sharp, do_uvs, do_vcols, do_materials)) { - return OPERATOR_CANCELLED; + int totelem_sel[3]; + EDBM_mesh_stats_multi(objects, objects_len, NULL, totelem_sel); + is_face_pair = (totelem_sel[2] == 2); } - EDBM_update_generic(em, true, true); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + + BMEditMesh *em = BKE_editmesh_from_object(obedit); + bool do_seam, do_sharp, do_uvs, do_vcols, do_materials; + float angle_face_threshold, angle_shape_threshold; + PropertyRNA *prop; + + /* When joining exactly 2 faces, no limit. + * this is useful for one off joins while editing. */ + prop = RNA_struct_find_property(op->ptr, "face_threshold"); + if (is_face_pair && + (RNA_property_is_set(op->ptr, prop) == false)) + { + angle_face_threshold = DEG2RADF(180.0f); + } + else { + angle_face_threshold = RNA_property_float_get(op->ptr, prop); + } + + prop = RNA_struct_find_property(op->ptr, "shape_threshold"); + if (is_face_pair && + (RNA_property_is_set(op->ptr, prop) == false)) + { + angle_shape_threshold = DEG2RADF(180.0f); + } + else { + angle_shape_threshold = RNA_property_float_get(op->ptr, prop); + } + + do_seam = RNA_boolean_get(op->ptr, "seam"); + do_sharp = RNA_boolean_get(op->ptr, "sharp"); + do_uvs = RNA_boolean_get(op->ptr, "uvs"); + do_vcols = RNA_boolean_get(op->ptr, "vcols"); + do_materials = RNA_boolean_get(op->ptr, "materials"); + + if (!EDBM_op_call_and_selectf( + em, op, + "faces.out", true, + "join_triangles faces=%hf angle_face_threshold=%f angle_shape_threshold=%f " + "cmp_seam=%b cmp_sharp=%b cmp_uvs=%b cmp_vcols=%b cmp_materials=%b", + BM_ELEM_SELECT, angle_face_threshold, angle_shape_threshold, + do_seam, do_sharp, do_uvs, do_vcols, do_materials)) + { + continue; + } + + EDBM_update_generic(em, true, true); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -4242,10 +4869,6 @@ void MESH_OT_tris_convert_to_quads(wmOperatorType *ot) static int edbm_decimate_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; - const float ratio = RNA_float_get(op->ptr, "ratio"); bool use_vertex_group = RNA_boolean_get(op->ptr, "use_vertex_group"); const float vertex_group_factor = RNA_float_get(op->ptr, "vertex_group_factor"); @@ -4259,95 +4882,108 @@ static int edbm_decimate_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } - float *vweights = MEM_mallocN(sizeof(*vweights) * bm->totvert, __func__); - { - const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT); - const int defbase_act = obedit->actdef - 1; + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); - if (use_vertex_group && (cd_dvert_offset == -1)) { - BKE_report(op->reports, RPT_WARNING, "No active vertex group"); - use_vertex_group = false; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + if (bm->totedgesel == 0) { + continue; } - BMIter iter; - BMVert *v; - int i; - BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { - float weight = 0.0f; - if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { - if (use_vertex_group) { - const MDeformVert *dv = BM_ELEM_CD_GET_VOID_P(v, cd_dvert_offset); - weight = defvert_find_weight(dv, defbase_act); - if (invert_vertex_group) { - weight = 1.0f - weight; + float *vweights = MEM_mallocN(sizeof(*vweights) * bm->totvert, __func__); + { + const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT); + const int defbase_act = obedit->actdef - 1; + + if (use_vertex_group && (cd_dvert_offset == -1)) { + BKE_report(op->reports, RPT_WARNING, "No active vertex group"); + use_vertex_group = false; + } + + BMIter iter; + BMVert *v; + int i; + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + float weight = 0.0f; + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + if (use_vertex_group) { + const MDeformVert *dv = BM_ELEM_CD_GET_VOID_P(v, cd_dvert_offset); + weight = defvert_find_weight(dv, defbase_act); + if (invert_vertex_group) { + weight = 1.0f - weight; + } + } + else { + weight = 1.0f; } } - else { - weight = 1.0f; - } - } - vweights[i] = weight; - BM_elem_index_set(v, i); /* set_inline */ + vweights[i] = weight; + BM_elem_index_set(v, i); /* set_inline */ + } + bm->elem_index_dirty &= ~BM_VERT; } - bm->elem_index_dirty &= ~BM_VERT; - } - - float ratio_adjust; - - if ((bm->totface == bm->totfacesel) || (ratio == 0.0f)) { - ratio_adjust = ratio; - } - else { - /** - * Calculate a new ratio based on faces that could be remoevd during decimation. - * needed so 0..1 has a meaningful range when operating on the selection. - * - * This doesn't have to be totally accurate, - * but needs to be greater than the number of selected faces - */ - int totface_basis = 0; - int totface_adjacent = 0; - BMIter iter; - BMFace *f; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - /* count faces during decimation, ngons are triangulated */ - const int f_len = f->len > 4 ? (f->len - 2) : 1; - totface_basis += f_len; + float ratio_adjust; - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - if (vweights[BM_elem_index_get(l_iter->v)] != 0.0f) { - totface_adjacent += f_len; - break; - } - } while ((l_iter = l_iter->next) != l_first); + if ((bm->totface == bm->totfacesel) || (ratio == 0.0f)) { + ratio_adjust = ratio; } + else { + /** + * Calculate a new ratio based on faces that could be remoevd during decimation. + * needed so 0..1 has a meaningful range when operating on the selection. + * + * This doesn't have to be totally accurate, + * but needs to be greater than the number of selected faces + */ + + int totface_basis = 0; + int totface_adjacent = 0; + BMIter iter; + BMFace *f; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + /* count faces during decimation, ngons are triangulated */ + const int f_len = f->len > 4 ? (f->len - 2) : 1; + totface_basis += f_len; + + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (vweights[BM_elem_index_get(l_iter->v)] != 0.0f) { + totface_adjacent += f_len; + break; + } + } while ((l_iter = l_iter->next) != l_first); + } - ratio_adjust = ratio; - ratio_adjust = 1.0f - ratio_adjust; - ratio_adjust *= (float)totface_adjacent / (float)totface_basis; - ratio_adjust = 1.0f - ratio_adjust; - } + ratio_adjust = ratio; + ratio_adjust = 1.0f - ratio_adjust; + ratio_adjust *= (float)totface_adjacent / (float)totface_basis; + ratio_adjust = 1.0f - ratio_adjust; + } - BM_mesh_decimate_collapse( - em->bm, ratio_adjust, vweights, vertex_group_factor, false, - symmetry_axis, symmetry_eps); + BM_mesh_decimate_collapse( + em->bm, ratio_adjust, vweights, vertex_group_factor, false, + symmetry_axis, symmetry_eps); - MEM_freeN(vweights); + MEM_freeN(vweights); - { - short selectmode = em->selectmode; - if ((selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) == 0) { - /* ensure we flush edges -> faces */ - selectmode |= SCE_SELECT_EDGE; + { + short selectmode = em->selectmode; + if ((selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) == 0) { + /* ensure we flush edges -> faces */ + selectmode |= SCE_SELECT_EDGE; + } + EDBM_selectmode_flush_ex(em, selectmode); } - EDBM_selectmode_flush_ex(em, selectmode); + EDBM_update_generic(em, true, true); } - - EDBM_update_generic(em, true, true); + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -4446,22 +5082,32 @@ static void edbm_dissolve_prop__use_boundary_tear(wmOperatorType *ot) static int edbm_dissolve_verts_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split"); const bool use_boundary_tear = RNA_boolean_get(op->ptr, "use_boundary_tear"); - if (!EDBM_op_callf( - em, op, - "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b", - BM_ELEM_SELECT, use_face_split, use_boundary_tear)) - { - return OPERATOR_CANCELLED; - } + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); - EDBM_update_generic(em, true, true); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + if (em->bm->totvertsel == 0) { + continue; + } + + if (!EDBM_op_callf( + em, op, + "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b", + BM_ELEM_SELECT, use_face_split, use_boundary_tear)) + { + continue; + } + EDBM_update_generic(em, true, true); + } + + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -4491,21 +5137,32 @@ void MESH_OT_dissolve_verts(wmOperatorType *ot) static int edbm_dissolve_edges_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - const bool use_verts = RNA_boolean_get(op->ptr, "use_verts"); const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split"); - if (!EDBM_op_callf( - em, op, - "dissolve_edges edges=%he use_verts=%b use_face_split=%b", - BM_ELEM_SELECT, use_verts, use_face_split)) - { - return OPERATOR_CANCELLED; + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + if (em->bm->totedgesel == 0) { + continue; + } + + if (!EDBM_op_callf( + em, op, + "dissolve_edges edges=%he use_verts=%b use_face_split=%b", + BM_ELEM_SELECT, use_verts, use_face_split)) + { + continue; + } + + EDBM_update_generic(em, true, true); } - EDBM_update_generic(em, true, true); + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -4536,21 +5193,30 @@ void MESH_OT_dissolve_edges(wmOperatorType *ot) static int edbm_dissolve_faces_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - const bool use_verts = RNA_boolean_get(op->ptr, "use_verts"); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - if (!EDBM_op_call_and_selectf( - em, op, - "region.out", true, - "dissolve_faces faces=%hf use_verts=%b", - BM_ELEM_SELECT, use_verts)) - { - return OPERATOR_CANCELLED; - } + if (em->bm->totfacesel == 0) { + continue; + } - EDBM_update_generic(em, true, true); + if (!EDBM_op_call_and_selectf( + em, op, + "region.out", true, + "dissolve_faces faces=%hf use_verts=%b", + BM_ELEM_SELECT, use_verts)) + { + continue; + } + + EDBM_update_generic(em, true, true); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -4630,52 +5296,65 @@ void MESH_OT_dissolve_mode(wmOperatorType *ot) static int edbm_dissolve_limited_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; const float angle_limit = RNA_float_get(op->ptr, "angle_limit"); const bool use_dissolve_boundaries = RNA_boolean_get(op->ptr, "use_dissolve_boundaries"); const int delimit = RNA_enum_get(op->ptr, "delimit"); - char dissolve_flag; - if (em->selectmode == SCE_SELECT_FACE) { - /* flush selection to tags and untag edges/verts with partially selected faces */ - BMIter iter; - BMIter liter; - - BMElem *ele; - BMFace *f; - BMLoop *l; + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; - BM_ITER_MESH (ele, &iter, bm, BM_VERTS_OF_MESH) { - BM_elem_flag_set(ele, BM_ELEM_TAG, BM_elem_flag_test(ele, BM_ELEM_SELECT)); - } - BM_ITER_MESH (ele, &iter, bm, BM_EDGES_OF_MESH) { - BM_elem_flag_set(ele, BM_ELEM_TAG, BM_elem_flag_test(ele, BM_ELEM_SELECT)); + if ((bm->totvertsel == 0) && + (bm->totedgesel == 0) && + (bm->totfacesel == 0)) + { + continue; } - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - if (!BM_elem_flag_test(f, BM_ELEM_SELECT)) { - BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - BM_elem_flag_disable(l->v, BM_ELEM_TAG); - BM_elem_flag_disable(l->e, BM_ELEM_TAG); + if (em->selectmode == SCE_SELECT_FACE) { + /* flush selection to tags and untag edges/verts with partially selected faces */ + BMIter iter; + BMIter liter; + + BMElem *ele; + BMFace *f; + BMLoop *l; + + BM_ITER_MESH (ele, &iter, bm, BM_VERTS_OF_MESH) { + BM_elem_flag_set(ele, BM_ELEM_TAG, BM_elem_flag_test(ele, BM_ELEM_SELECT)); + } + BM_ITER_MESH (ele, &iter, bm, BM_EDGES_OF_MESH) { + BM_elem_flag_set(ele, BM_ELEM_TAG, BM_elem_flag_test(ele, BM_ELEM_SELECT)); + } + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (!BM_elem_flag_test(f, BM_ELEM_SELECT)) { + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + BM_elem_flag_disable(l->v, BM_ELEM_TAG); + BM_elem_flag_disable(l->e, BM_ELEM_TAG); + } } } - } - dissolve_flag = BM_ELEM_TAG; - } - else { - dissolve_flag = BM_ELEM_SELECT; - } + dissolve_flag = BM_ELEM_TAG; + } + else { + dissolve_flag = BM_ELEM_SELECT; + } - EDBM_op_call_and_selectf( - em, op, "region.out", true, - "dissolve_limit edges=%he verts=%hv angle_limit=%f use_dissolve_boundaries=%b delimit=%i", - dissolve_flag, dissolve_flag, angle_limit, use_dissolve_boundaries, delimit); + EDBM_op_call_and_selectf( + em, op, "region.out", true, + "dissolve_limit edges=%he verts=%hv angle_limit=%f use_dissolve_boundaries=%b delimit=%i", + dissolve_flag, dissolve_flag, angle_limit, use_dissolve_boundaries, delimit); - EDBM_update_generic(em, true, true); + EDBM_update_generic(em, true, true); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -4713,26 +5392,49 @@ void MESH_OT_dissolve_limited(wmOperatorType *ot) static int edbm_dissolve_degenerate_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); + ViewLayer *view_layer = CTX_data_view_layer(C); + int totelem_old[3] = {0, 0, 0}; + int totelem_new[3] = {0, 0, 0}; + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + totelem_old[0] += bm->totvert; + totelem_old[1] += bm->totedge; + totelem_old[2] += bm->totface; + } /* objects */ + const float thresh = RNA_float_get(op->ptr, "threshold"); - BMesh *bm = em->bm; - const int totelem[3] = {bm->totvert, bm->totedge, bm->totface}; - if (!EDBM_op_callf( - em, op, - "dissolve_degenerate edges=%he dist=%f", - BM_ELEM_SELECT, thresh)) - { - return OPERATOR_CANCELLED; - } + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; - /* tricky to maintain correct selection here, so just flush up from verts */ - EDBM_select_flush(em); + if (!EDBM_op_callf( + em, op, + "dissolve_degenerate edges=%he dist=%f", + BM_ELEM_SELECT, thresh)) + { + return OPERATOR_CANCELLED; + } - EDBM_update_generic(em, true, true); + /* tricky to maintain correct selection here, so just flush up from verts */ + EDBM_select_flush(em); + + EDBM_update_generic(em, true, true); + + totelem_new[0] += bm->totvert; + totelem_new[1] += bm->totedge; + totelem_new[2] += bm->totface; + } + MEM_freeN(objects); - edbm_report_delete_info(op->reports, bm, totelem); + edbm_report_delete_info(op->reports, totelem_old, totelem_new); return OPERATOR_FINISHED; } @@ -4764,42 +5466,52 @@ void MESH_OT_dissolve_degenerate(wmOperatorType *ot) /* internally uses dissolve */ static int edbm_delete_edgeloop_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split"); + ViewLayer *view_layer = CTX_data_view_layer(C); - /* deal with selection */ - { - BMEdge *e; - BMIter iter; + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false); + if (em->bm->totedgesel == 0) { + continue; + } - BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(e, BM_ELEM_SELECT) && e->l) { - BMLoop *l_iter = e->l; - do { - BM_elem_flag_enable(l_iter->f, BM_ELEM_TAG); - } while ((l_iter = l_iter->radial_next) != e->l); + /* deal with selection */ + { + BMEdge *e; + BMIter iter; + + BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false); + + BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_SELECT) && e->l) { + BMLoop *l_iter = e->l; + do { + BM_elem_flag_enable(l_iter->f, BM_ELEM_TAG); + } while ((l_iter = l_iter->radial_next) != e->l); + } } } - } - if (!EDBM_op_callf( - em, op, - "dissolve_edges edges=%he use_verts=%b use_face_split=%b", - BM_ELEM_SELECT, true, use_face_split)) - { - return OPERATOR_CANCELLED; - } + if (!EDBM_op_callf( + em, op, + "dissolve_edges edges=%he use_verts=%b use_face_split=%b", + BM_ELEM_SELECT, true, use_face_split)) + { + continue; + } - BM_mesh_elem_hflag_enable_test(em->bm, BM_FACE, BM_ELEM_SELECT, true, false, BM_ELEM_TAG); + BM_mesh_elem_hflag_enable_test(em->bm, BM_FACE, BM_ELEM_SELECT, true, false, BM_ELEM_TAG); - EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX); + EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX); - EDBM_update_generic(em, true, true); + EDBM_update_generic(em, true, true); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -4829,22 +5541,34 @@ void MESH_OT_delete_edgeloop(wmOperatorType *ot) static int edbm_split_exec(bContext *C, wmOperator *op) { - Object *ob = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(ob); - BMOperator bmop; + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + if ((em->bm->totvertsel == 0) && + (em->bm->totedgesel == 0) && + (em->bm->totfacesel == 0)) + { + continue; + } + BMOperator bmop; + EDBM_op_init(em, &bmop, op, "split geom=%hvef use_only_faces=%b", BM_ELEM_SELECT, false); + BMO_op_exec(em->bm, &bmop); + BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true); - EDBM_op_init(em, &bmop, op, "split geom=%hvef use_only_faces=%b", BM_ELEM_SELECT, false); - BMO_op_exec(em->bm, &bmop); - BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); - BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true); - if (!EDBM_op_finish(em, &bmop, op, true)) { - return OPERATOR_CANCELLED; - } + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; + } - /* Geometry has changed, need to recalc normals and looptris */ - EDBM_mesh_normals_update(em); + /* Geometry has changed, need to recalc normals and looptris */ + EDBM_mesh_normals_update(em); - EDBM_update_generic(em, true, true); + EDBM_update_generic(em, true, true); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -4898,6 +5622,7 @@ static int bmelemsort_comp(const void *v1, const void *v2) /* Reorders vertices/edges/faces using a given methods. Loops are not supported. */ static void sort_bmelem_flag( + bContext *C, Scene *scene, Object *ob, View3D *v3d, RegionView3D *rv3d, const int types, const int flag, const int action, @@ -5002,9 +5727,9 @@ static void sort_bmelem_flag( float fact = reverse ? -1.0 : 1.0; if (v3d && v3d->localvd) - copy_v3_v3(cur, v3d->cursor); + copy_v3_v3(cur, v3d->cursor.location); else - copy_v3_v3(cur, scene->cursor); + copy_v3_v3(cur, scene->cursor.location); invert_m4_m4(mat, ob->obmat); mul_m4_v3(mat, cur); @@ -5322,7 +6047,8 @@ static void sort_bmelem_flag( } BM_mesh_remap(em->bm, map[0], map[1], map[2]); -/* DAG_id_tag_update(ob->data, 0);*/ + DEG_id_tag_update(ob->data, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); for (j = 3; j--; ) { if (map[j]) @@ -5333,7 +6059,8 @@ static void sort_bmelem_flag( static int edbm_sort_elements_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_edit_object(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob_active = CTX_data_edit_object(C); /* may be NULL */ View3D *v3d = CTX_wm_view3d(C); @@ -5357,7 +6084,7 @@ static int edbm_sort_elements_exec(bContext *C, wmOperator *op) elem_types = RNA_property_enum_get(op->ptr, prop_elem_types); } else { - BMEditMesh *em = BKE_editmesh_from_object(ob); + BMEditMesh *em = BKE_editmesh_from_object(ob_active); if (em->selectmode & SCE_SELECT_VERTEX) elem_types |= BM_VERT; if (em->selectmode & SCE_SELECT_EDGE) @@ -5367,9 +6094,33 @@ static int edbm_sort_elements_exec(bContext *C, wmOperator *op) RNA_enum_set(op->ptr, "elements", elem_types); } - sort_bmelem_flag( - scene, ob, v3d, rv3d, - elem_types, BM_ELEM_SELECT, action, use_reverse, seed); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(ob); + BMesh *bm = em->bm; + + if (!((elem_types & BM_VERT && bm->totvertsel > 0) || + (elem_types & BM_EDGE && bm->totedgesel > 0) || + (elem_types & BM_FACE && bm->totfacesel > 0))) + { + continue; + } + + int seed_iter = seed; + + /* This gives a consistent result regardless of object order */ + if (ob_index) { + seed_iter += BLI_ghashutil_strhash_p(ob->id.name); + } + + sort_bmelem_flag( + C, scene, ob, v3d, rv3d, + elem_types, BM_ELEM_SELECT, action, use_reverse, seed_iter); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -5449,84 +6200,6 @@ void MESH_OT_sort_elements(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Noise (Deform Vertices) Operator - * \{ */ - -static int edbm_noise_exec(bContext *C, wmOperator *op) -{ - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - Material *ma; - Tex *tex; - BMVert *eve; - BMIter iter; - const float fac = RNA_float_get(op->ptr, "factor"); - - if (em == NULL) { - return OPERATOR_FINISHED; - } - - if ((ma = give_current_material(obedit, obedit->actcol)) == NULL || - (tex = give_current_material_texture(ma)) == NULL) - { - BKE_report(op->reports, RPT_WARNING, "Mesh has no material or texture assigned"); - return OPERATOR_FINISHED; - } - - if (tex->type == TEX_STUCCI) { - float b2, vec[3]; - float ofs = tex->turbul / 200.0f; - BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - b2 = BLI_hnoise(tex->noisesize, eve->co[0], eve->co[1], eve->co[2]); - if (tex->stype) ofs *= (b2 * b2); - vec[0] = fac * (b2 - BLI_hnoise(tex->noisesize, eve->co[0] + ofs, eve->co[1], eve->co[2])); - vec[1] = fac * (b2 - BLI_hnoise(tex->noisesize, eve->co[0], eve->co[1] + ofs, eve->co[2])); - vec[2] = fac * (b2 - BLI_hnoise(tex->noisesize, eve->co[0], eve->co[1], eve->co[2] + ofs)); - - add_v3_v3(eve->co, vec); - } - } - } - else { - BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - float tin = 0.0f, dum; - if (ma->mtex[ma->texact] != NULL) { - externtex(ma->mtex[ma->texact], eve->co, &tin, &dum, &dum, &dum, &dum, 0, NULL, false, false); - } - eve->co[2] += fac * tin; - } - } - } - - EDBM_mesh_normals_update(em); - - EDBM_update_generic(em, true, false); - - return OPERATOR_FINISHED; -} - -void MESH_OT_noise(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Noise"; - ot->description = "Use vertex coordinate as texture coordinate"; - ot->idname = "MESH_OT_noise"; - - /* api callbacks */ - ot->exec = edbm_noise_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_float(ot->srna, "factor", 0.1f, -1e4f, 1e4f, "Factor", "", 0.0f, 1.0f); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Bridge Operator * \{ */ @@ -5578,22 +6251,20 @@ static int edbm_bridge_tag_boundary_edges(BMesh *bm) return totface_del; } -static int edbm_bridge_edge_loops_exec(bContext *C, wmOperator *op) +static int edbm_bridge_edge_loops_for_single_editmesh( + wmOperator *op, + BMEditMesh *em, + const bool use_pairs, + const bool use_cyclic, + const bool use_merge, + const float merge_factor, + const int twist_offset) { BMOperator bmop; - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - const int type = RNA_enum_get(op->ptr, "type"); - const bool use_pairs = (type == MESH_BRIDGELOOP_PAIRS); - const bool use_cyclic = (type == MESH_BRIDGELOOP_CLOSED); - const bool use_merge = RNA_boolean_get(op->ptr, "use_merge"); - const float merge_factor = RNA_float_get(op->ptr, "merge_factor"); - const int twist_offset = RNA_int_get(op->ptr, "twist_offset"); - const bool use_faces = (em->bm->totfacesel != 0); char edge_hflag; - int totface_del = 0; BMFace **totface_del_arr = NULL; + const bool use_faces = (em->bm->totfacesel != 0); if (use_faces) { BMIter iter; @@ -5616,9 +6287,9 @@ static int edbm_bridge_edge_loops_exec(bContext *C, wmOperator *op) } EDBM_op_init( - em, &bmop, op, - "bridge_loops edges=%he use_pairs=%b use_cyclic=%b use_merge=%b merge_factor=%f twist_offset=%i", - edge_hflag, use_pairs, use_cyclic, use_merge, merge_factor, twist_offset); + em, &bmop, op, + "bridge_loops edges=%he use_pairs=%b use_cyclic=%b use_merge=%b merge_factor=%f twist_offset=%i", + edge_hflag, use_pairs, use_cyclic, use_merge, merge_factor, twist_offset); if (use_faces && totface_del) { int i; @@ -5627,9 +6298,9 @@ static int edbm_bridge_edge_loops_exec(bContext *C, wmOperator *op) BM_elem_flag_enable(totface_del_arr[i], BM_ELEM_TAG); } BMO_op_callf( - em->bm, BMO_FLAG_DEFAULTS, - "delete geom=%hf context=%i", - BM_ELEM_TAG, DEL_FACES_KEEP_BOUNDARY); + em->bm, BMO_FLAG_DEFAULTS, + "delete geom=%hf context=%i", + BM_ELEM_TAG, DEL_FACES_KEEP_BOUNDARY); } BMO_op_exec(em->bm, &bmop); @@ -5651,18 +6322,15 @@ static int edbm_bridge_edge_loops_exec(bContext *C, wmOperator *op) EDBM_mesh_normals_update(em); BMO_op_initf( - em->bm, &bmop_subd, 0, - "subdivide_edgering edges=%S interp_mode=%i cuts=%i smooth=%f " - "profile_shape=%i profile_shape_factor=%f", - &bmop, "edges.out", op_props.interp_mode, op_props.cuts, op_props.smooth, - op_props.profile_shape, op_props.profile_shape_factor - ); + em->bm, &bmop_subd, 0, + "subdivide_edgering edges=%S interp_mode=%i cuts=%i smooth=%f " + "profile_shape=%i profile_shape_factor=%f", + &bmop, "edges.out", op_props.interp_mode, op_props.cuts, op_props.smooth, + op_props.profile_shape, op_props.profile_shape_factor + ); BMO_op_exec(em->bm, &bmop_subd); - BMO_slot_buffer_hflag_enable(em->bm, bmop_subd.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true); - BMO_op_finish(em->bm, &bmop_subd); - } } } @@ -5671,15 +6339,44 @@ static int edbm_bridge_edge_loops_exec(bContext *C, wmOperator *op) MEM_freeN(totface_del_arr); } - if (!EDBM_op_finish(em, &bmop, op, true)) { - /* grr, need to return finished so the user can select different options */ - //return OPERATOR_CANCELLED; - return OPERATOR_FINISHED; - } - else { + if (EDBM_op_finish(em, &bmop, op, true)) { EDBM_update_generic(em, true, true); - return OPERATOR_FINISHED; } + + /* Always return finished so the user can select different options. */ + return OPERATOR_FINISHED; +} + +static int edbm_bridge_edge_loops_exec(bContext *C, wmOperator *op) +{ + const int type = RNA_enum_get(op->ptr, "type"); + const bool use_pairs = (type == MESH_BRIDGELOOP_PAIRS); + const bool use_cyclic = (type == MESH_BRIDGELOOP_CLOSED); + const bool use_merge = RNA_boolean_get(op->ptr, "use_merge"); + const float merge_factor = RNA_float_get(op->ptr, "merge_factor"); + const int twist_offset = RNA_int_get(op->ptr, "twist_offset"); + ViewLayer *view_layer = CTX_data_view_layer(C); + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + if (em->bm->totvertsel == 0) { + continue; + } + + edbm_bridge_edge_loops_for_single_editmesh(op, + em, + use_pairs, + use_cyclic, + use_merge, + merge_factor, + twist_offset); + } + MEM_freeN(objects); + return OPERATOR_FINISHED; } void MESH_OT_bridge_edge_loops(wmOperatorType *ot) @@ -5721,9 +6418,6 @@ void MESH_OT_bridge_edge_loops(wmOperatorType *ot) static int edbm_wireframe_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMOperator bmop; const bool use_boundary = RNA_boolean_get(op->ptr, "use_boundary"); const bool use_even_offset = RNA_boolean_get(op->ptr, "use_even_offset"); const bool use_replace = RNA_boolean_get(op->ptr, "use_replace"); @@ -5733,25 +6427,41 @@ static int edbm_wireframe_exec(bContext *C, wmOperator *op) const float thickness = RNA_float_get(op->ptr, "thickness"); const float offset = RNA_float_get(op->ptr, "offset"); - EDBM_op_init( - em, &bmop, op, - "wireframe faces=%hf use_replace=%b use_boundary=%b use_even_offset=%b use_relative_offset=%b " - "use_crease=%b crease_weight=%f thickness=%f offset=%f", - BM_ELEM_SELECT, use_replace, use_boundary, use_even_offset, use_relative_offset, - use_crease, crease_weight, thickness, offset); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMO_op_exec(em->bm, &bmop); + if (em->bm->totfacesel == 0) { + continue; + } - BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); - BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true); + BMOperator bmop; + + EDBM_op_init( + em, &bmop, op, + "wireframe faces=%hf use_replace=%b use_boundary=%b use_even_offset=%b use_relative_offset=%b " + "use_crease=%b crease_weight=%f thickness=%f offset=%f", + BM_ELEM_SELECT, use_replace, use_boundary, use_even_offset, use_relative_offset, + use_crease, crease_weight, thickness, offset); + + BMO_op_exec(em->bm, &bmop); + + BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true); + + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; + } - if (!EDBM_op_finish(em, &bmop, op, true)) { - return OPERATOR_CANCELLED; - } - else { EDBM_update_generic(em, true, true); - return OPERATOR_FINISHED; } + + MEM_freeN(objects); + + return OPERATOR_FINISHED; } void MESH_OT_wireframe(wmOperatorType *ot) @@ -5792,37 +6502,62 @@ void MESH_OT_wireframe(wmOperatorType *ot) static int edbm_offset_edgeloop_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMOperator bmop; + bool mode_change = false; const bool use_cap_endpoint = RNA_boolean_get(op->ptr, "use_cap_endpoint"); + int ret = OPERATOR_CANCELLED; - EDBM_op_init( - em, &bmop, op, - "offset_edgeloops edges=%he use_cap_endpoint=%b", - BM_ELEM_SELECT, use_cap_endpoint); + { + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + if (em->selectmode == SCE_SELECT_FACE) { + EDBM_selectmode_to_scene(C); + mode_change = true; + } + } - BMO_op_exec(em->bm, &bmop); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); + /** If in face-only select mode, switch to edge select mode so that + * an edge-only selection is not inconsistent state. + * + * We need to run this for all objects, even when nothing is selected. + * This way we keep them in sync. */ + if (mode_change) { + em->selectmode = SCE_SELECT_EDGE; + EDBM_selectmode_set(em); + } - /* If in face-only select mode, switch to edge select mode so that - * an edge-only selection is not inconsistent state */ - if (em->selectmode == SCE_SELECT_FACE) { - em->selectmode = SCE_SELECT_EDGE; - EDBM_selectmode_set(em); - EDBM_selectmode_to_scene(C); - } + if (em->bm->totedgesel == 0) { + continue; + } - BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true); + BMOperator bmop; + EDBM_op_init( + em, &bmop, op, + "offset_edgeloops edges=%he use_cap_endpoint=%b", + BM_ELEM_SELECT, use_cap_endpoint); - if (!EDBM_op_finish(em, &bmop, op, true)) { - return OPERATOR_CANCELLED; - } - else { - EDBM_update_generic(em, true, true); - return OPERATOR_FINISHED; + BMO_op_exec(em->bm, &bmop); + + BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); + + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true); + + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; + } + else { + EDBM_update_generic(em, true, true); + ret = OPERATOR_FINISHED; + } } + MEM_freeN(objects); + return ret; } void MESH_OT_offset_edge_loops(wmOperatorType *ot) @@ -5853,73 +6588,89 @@ void MESH_OT_offset_edge_loops(wmOperatorType *ot) #ifdef WITH_BULLET static int edbm_convex_hull_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMOperator bmop; + const bool use_existing_faces = RNA_boolean_get(op->ptr, "use_existing_faces"); + const bool delete_unused = RNA_boolean_get(op->ptr, "delete_unused"); + const bool make_holes = RNA_boolean_get(op->ptr, "make_holes"); + const bool join_triangles = RNA_boolean_get(op->ptr, "join_triangles"); - EDBM_op_init( - em, &bmop, op, "convex_hull input=%hvef " - "use_existing_faces=%b", - BM_ELEM_SELECT, - RNA_boolean_get(op->ptr, "use_existing_faces")); - BMO_op_exec(em->bm, &bmop); + float angle_face_threshold = RNA_float_get(op->ptr, "face_threshold"); + float angle_shape_threshold = RNA_float_get(op->ptr, "shape_threshold"); - /* Hull fails if input is coplanar */ - if (BMO_error_occurred(em->bm)) { - EDBM_op_finish(em, &bmop, op, true); - return OPERATOR_CANCELLED; - } + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "geom.out", BM_FACE, BM_ELEM_SELECT, true); + if (em->bm->totvertsel == 0) { + continue; + } - /* Delete unused vertices, edges, and faces */ - if (RNA_boolean_get(op->ptr, "delete_unused")) { - if (!EDBM_op_callf( - em, op, "delete geom=%S context=%i", - &bmop, "geom_unused.out", DEL_ONLYTAGGED)) - { + BMOperator bmop; + + EDBM_op_init( + em, &bmop, op, "convex_hull input=%hvef " + "use_existing_faces=%b", + BM_ELEM_SELECT, + use_existing_faces); + BMO_op_exec(em->bm, &bmop); + + /* Hull fails if input is coplanar */ + if (BMO_error_occurred(em->bm)) { EDBM_op_finish(em, &bmop, op, true); - return OPERATOR_CANCELLED; + continue; } - } - /* Delete hole edges/faces */ - if (RNA_boolean_get(op->ptr, "make_holes")) { - if (!EDBM_op_callf( - em, op, "delete geom=%S context=%i", - &bmop, "geom_holes.out", DEL_ONLYTAGGED)) - { - EDBM_op_finish(em, &bmop, op, true); - return OPERATOR_CANCELLED; + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "geom.out", BM_FACE, BM_ELEM_SELECT, true); + + /* Delete unused vertices, edges, and faces */ + if (delete_unused) { + if (!EDBM_op_callf( + em, op, "delete geom=%S context=%i", + &bmop, "geom_unused.out", DEL_ONLYTAGGED)) + { + EDBM_op_finish(em, &bmop, op, true); + continue; + } } - } - /* Merge adjacent triangles */ - if (RNA_boolean_get(op->ptr, "join_triangles")) { - float angle_face_threshold = RNA_float_get(op->ptr, "face_threshold"); - float angle_shape_threshold = RNA_float_get(op->ptr, "shape_threshold"); + /* Delete hole edges/faces */ + if (make_holes) { + if (!EDBM_op_callf( + em, op, "delete geom=%S context=%i", + &bmop, "geom_holes.out", DEL_ONLYTAGGED)) + { + EDBM_op_finish(em, &bmop, op, true); + continue; + } + } - if (!EDBM_op_call_and_selectf( - em, op, - "faces.out", true, - "join_triangles faces=%S " - "angle_face_threshold=%f angle_shape_threshold=%f", - &bmop, "geom.out", - angle_face_threshold, angle_shape_threshold)) - { - EDBM_op_finish(em, &bmop, op, true); - return OPERATOR_CANCELLED; + /* Merge adjacent triangles */ + if (join_triangles) { + if (!EDBM_op_call_and_selectf( + em, op, + "faces.out", true, + "join_triangles faces=%S " + "angle_face_threshold=%f angle_shape_threshold=%f", + &bmop, "geom.out", + angle_face_threshold, angle_shape_threshold)) + { + EDBM_op_finish(em, &bmop, op, true); + continue; + } + } + + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; } - } - if (!EDBM_op_finish(em, &bmop, op, true)) { - return OPERATOR_CANCELLED; - } - else { EDBM_update_generic(em, true, true); EDBM_selectmode_flush(em); - return OPERATOR_FINISHED; } + + MEM_freeN(objects); + return OPERATOR_FINISHED; } void MESH_OT_convex_hull(wmOperatorType *ot) @@ -5965,30 +6716,41 @@ void MESH_OT_convex_hull(wmOperatorType *ot) static int mesh_symmetrize_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMOperator bmop; - const float thresh = RNA_float_get(op->ptr, "threshold"); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); - EDBM_op_init( - em, &bmop, op, - "symmetrize input=%hvef direction=%i dist=%f", - BM_ELEM_SELECT, RNA_enum_get(op->ptr, "direction"), thresh); - BMO_op_exec(em->bm, &bmop); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - EDBM_flag_disable_all(em, BM_ELEM_SELECT); + if (em->bm->totvertsel == 0 ) { + continue; + } - BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true); + BMOperator bmop; + EDBM_op_init( + em, &bmop, op, + "symmetrize input=%hvef direction=%i dist=%f", + BM_ELEM_SELECT, RNA_enum_get(op->ptr, "direction"), thresh); + BMO_op_exec(em->bm, &bmop); - if (!EDBM_op_finish(em, &bmop, op, true)) { - return OPERATOR_CANCELLED; - } - else { - EDBM_update_generic(em, true, true); - EDBM_selectmode_flush(em); - return OPERATOR_FINISHED; + EDBM_flag_disable_all(em, BM_ELEM_SELECT); + + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true); + + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; + } + else { + EDBM_update_generic(em, true, true); + EDBM_selectmode_flush(em); + } } + MEM_freeN(objects); + + return OPERATOR_FINISHED; } void MESH_OT_symmetrize(struct wmOperatorType *ot) @@ -6023,103 +6785,114 @@ static int mesh_symmetry_snap_exec(bContext *C, wmOperator *op) { const float eps = 0.00001f; const float eps_sq = eps * eps; - - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; - int *index = MEM_mallocN(bm->totvert * sizeof(*index), __func__); const bool use_topology = false; const float thresh = RNA_float_get(op->ptr, "threshold"); const float fac = RNA_float_get(op->ptr, "factor"); const bool use_center = RNA_boolean_get(op->ptr, "use_center"); + const int axis_dir = RNA_enum_get(op->ptr, "direction"); - /* stats */ - int totmirr = 0, totfail = 0, totfound = 0; + /* Vertices stats (total over all selected objects). */ + int totvertfound = 0, totvertmirr = 0, totvertfail = 0; - /* axix */ - const int axis_dir = RNA_enum_get(op->ptr, "direction"); + /* Axis. */ int axis = axis_dir % 3; bool axis_sign = axis != axis_dir; - /* vertex iter */ - BMIter iter; - BMVert *v; - int i; + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; - EDBM_verts_mirror_cache_begin_ex(em, axis, true, true, use_topology, thresh, index); + if (em->bm->totvertsel == 0) { + continue; + } - BM_mesh_elem_table_ensure(bm, BM_VERT); + /* Only allocate memory after checking whether to skip object. */ + int *index = MEM_mallocN(bm->totvert * sizeof(*index), __func__); - BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false); + /* Vertex iter. */ + BMIter iter; + BMVert *v; + int i; + EDBM_verts_mirror_cache_begin_ex(em, axis, true, true, use_topology, thresh, index); - BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { - if ((BM_elem_flag_test(v, BM_ELEM_SELECT) != false) && - (BM_elem_flag_test(v, BM_ELEM_TAG) == false)) - { - int i_mirr = index[i]; - if (i_mirr != -1) { + BM_mesh_elem_table_ensure(bm, BM_VERT); - BMVert *v_mirr = BM_vert_at_index(bm, index[i]); + BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false); - if (v != v_mirr) { - float co[3], co_mirr[3]; + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + if ((BM_elem_flag_test(v, BM_ELEM_SELECT) != false) && + (BM_elem_flag_test(v, BM_ELEM_TAG) == false)) + { + int i_mirr = index[i]; + if (i_mirr != -1) { - if ((v->co[axis] > v_mirr->co[axis]) == axis_sign) { - SWAP(BMVert *, v, v_mirr); - } + BMVert *v_mirr = BM_vert_at_index(bm, index[i]); - copy_v3_v3(co_mirr, v_mirr->co); - co_mirr[axis] *= -1.0f; + if (v != v_mirr) { + float co[3], co_mirr[3]; - if (len_squared_v3v3(v->co, co_mirr) > eps_sq) { - totmirr++; - } + if ((v->co[axis] > v_mirr->co[axis]) == axis_sign) { + SWAP(BMVert *, v, v_mirr); + } - interp_v3_v3v3(co, v->co, co_mirr, fac); + copy_v3_v3(co_mirr, v_mirr->co); + co_mirr[axis] *= -1.0f; - copy_v3_v3(v->co, co); + if (len_squared_v3v3(v->co, co_mirr) > eps_sq) { + totvertmirr++; + } - co[axis] *= -1.0f; - copy_v3_v3(v_mirr->co, co); + interp_v3_v3v3(co, v->co, co_mirr, fac); - BM_elem_flag_enable(v, BM_ELEM_TAG); - BM_elem_flag_enable(v_mirr, BM_ELEM_TAG); - totfound++; - } - else { - if (use_center) { + copy_v3_v3(v->co, co); - if (fabsf(v->co[axis]) > eps) { - totmirr++; - } + co[axis] *= -1.0f; + copy_v3_v3(v_mirr->co, co); - v->co[axis] = 0.0f; + BM_elem_flag_enable(v, BM_ELEM_TAG); + BM_elem_flag_enable(v_mirr, BM_ELEM_TAG); + totvertfound++; + } + else { + if (use_center) { + + if (fabsf(v->co[axis]) > eps) { + totvertmirr++; + } + + v->co[axis] = 0.0f; + } + BM_elem_flag_enable(v, BM_ELEM_TAG); + totvertfound++; } - BM_elem_flag_enable(v, BM_ELEM_TAG); - totfound++; } - } - else { - totfail++; + else { + totvertfail++; + } } } - } + /* No need to end cache, just free the array. */ + MEM_freeN(index); + } + MEM_freeN(objects); - if (totfail) { + if (totvertfail) { BKE_reportf(op->reports, RPT_WARNING, "%d already symmetrical, %d pairs mirrored, %d failed", - totfound - totmirr, totmirr, totfail); + totvertfound - totvertmirr, totvertmirr, totvertfail); } else { BKE_reportf(op->reports, RPT_INFO, "%d already symmetrical, %d pairs mirrored", - totfound - totmirr, totmirr); + totvertfound - totvertmirr, totvertmirr); } - /* no need to end cache, just free the array */ - MEM_freeN(index); - return OPERATOR_FINISHED; } @@ -6158,45 +6931,53 @@ void MESH_OT_symmetry_snap(struct wmOperatorType *ot) static int edbm_mark_freestyle_edge_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - Mesh *me = (Mesh *)obedit->data; - BMEditMesh *em = BKE_editmesh_from_object(obedit); BMEdge *eed; BMIter iter; FreestyleEdge *fed; const bool clear = RNA_boolean_get(op->ptr, "clear"); + ViewLayer *view_layer = CTX_data_view_layer(C); - if (em == NULL) - return OPERATOR_FINISHED; + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - /* auto-enable Freestyle edge mark drawing */ - if (clear == 0) { - me->drawflag |= ME_DRAW_FREESTYLE_EDGE; - } + if (em == NULL) { + continue; + } - if (!CustomData_has_layer(&em->bm->edata, CD_FREESTYLE_EDGE)) { - BM_data_layer_add(em->bm, &em->bm->edata, CD_FREESTYLE_EDGE); - } + BMesh *bm = em->bm; - if (clear) { - BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(eed, BM_ELEM_SELECT) && !BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - fed = CustomData_bmesh_get(&em->bm->edata, eed->head.data, CD_FREESTYLE_EDGE); - fed->flag &= ~FREESTYLE_EDGE_MARK; + if (bm->totedgesel == 0) { + continue; + } + + if (!CustomData_has_layer(&em->bm->edata, CD_FREESTYLE_EDGE)) { + BM_data_layer_add(em->bm, &em->bm->edata, CD_FREESTYLE_EDGE); + } + + if (clear) { + BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(eed, BM_ELEM_SELECT) && !BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { + fed = CustomData_bmesh_get(&em->bm->edata, eed->head.data, CD_FREESTYLE_EDGE); + fed->flag &= ~FREESTYLE_EDGE_MARK; + } } } - } - else { - BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(eed, BM_ELEM_SELECT) && !BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - fed = CustomData_bmesh_get(&em->bm->edata, eed->head.data, CD_FREESTYLE_EDGE); - fed->flag |= FREESTYLE_EDGE_MARK; + else { + BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(eed, BM_ELEM_SELECT) && !BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { + fed = CustomData_bmesh_get(&em->bm->edata, eed->head.data, CD_FREESTYLE_EDGE); + fed->flag |= FREESTYLE_EDGE_MARK; + } } } - } - DAG_id_tag_update(obedit->data, OB_RECALC_DATA); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + DEG_id_tag_update(obedit->data, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -6218,7 +6999,7 @@ void MESH_OT_mark_freestyle_edge(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; prop = RNA_def_boolean(ot->srna, "clear", false, "Clear", ""); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /** \} */ @@ -6229,44 +7010,51 @@ void MESH_OT_mark_freestyle_edge(wmOperatorType *ot) static int edbm_mark_freestyle_face_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - Mesh *me = (Mesh *)obedit->data; - BMEditMesh *em = BKE_editmesh_from_object(obedit); BMFace *efa; BMIter iter; FreestyleFace *ffa; const bool clear = RNA_boolean_get(op->ptr, "clear"); + ViewLayer *view_layer = CTX_data_view_layer(C); - if (em == NULL) return OPERATOR_FINISHED; + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); - /* auto-enable Freestyle face mark drawing */ - if (!clear) { - me->drawflag |= ME_DRAW_FREESTYLE_FACE; - } + if (em == NULL) { + continue; + } - if (!CustomData_has_layer(&em->bm->pdata, CD_FREESTYLE_FACE)) { - BM_data_layer_add(em->bm, &em->bm->pdata, CD_FREESTYLE_FACE); - } + if (em->bm->totfacesel == 0) { + continue; + } + + if (!CustomData_has_layer(&em->bm->pdata, CD_FREESTYLE_FACE)) { + BM_data_layer_add(em->bm, &em->bm->pdata, CD_FREESTYLE_FACE); + } - if (clear) { - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(efa, BM_ELEM_SELECT) && !BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - ffa = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_FREESTYLE_FACE); - ffa->flag &= ~FREESTYLE_FACE_MARK; + if (clear) { + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(efa, BM_ELEM_SELECT) && !BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + ffa = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_FREESTYLE_FACE); + ffa->flag &= ~FREESTYLE_FACE_MARK; + } } } - } - else { - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(efa, BM_ELEM_SELECT) && !BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - ffa = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_FREESTYLE_FACE); - ffa->flag |= FREESTYLE_FACE_MARK; + else { + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(efa, BM_ELEM_SELECT) && !BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + ffa = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_FREESTYLE_FACE); + ffa->flag |= FREESTYLE_FACE_MARK; + } } } - } - DAG_id_tag_update(obedit->data, OB_RECALC_DATA); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + DEG_id_tag_update(obedit->data, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + } + MEM_freeN(objects); return OPERATOR_FINISHED; } @@ -6288,9 +7076,1323 @@ void MESH_OT_mark_freestyle_face(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; prop = RNA_def_boolean(ot->srna, "clear", false, "Clear", ""); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /** \} */ #endif /* WITH_FREESTYLE */ + +/********************** Loop normals editing tools modal map. **********************/ + +/* NOTE: these defines are saved in keymap files, do not change values but just add new ones */ +/* NOTE: We could add more here, like e.g. a switch between local or global coordinates of target, + * use numinput to type in explicit vector values... */ +enum { + /* Generic commands. */ + EDBM_CLNOR_MODAL_CANCEL = 1, + EDBM_CLNOR_MODAL_CONFIRM = 2, + + /* Point To operator. */ + EDBM_CLNOR_MODAL_POINTTO_RESET = 101, + EDBM_CLNOR_MODAL_POINTTO_INVERT = 102, + EDBM_CLNOR_MODAL_POINTTO_SPHERIZE = 103, + EDBM_CLNOR_MODAL_POINTTO_ALIGN = 104, + + EDBM_CLNOR_MODAL_POINTTO_USE_MOUSE = 110, + EDBM_CLNOR_MODAL_POINTTO_USE_PIVOT = 111, + EDBM_CLNOR_MODAL_POINTTO_USE_OBJECT = 112, + EDBM_CLNOR_MODAL_POINTTO_SET_USE_3DCURSOR = 113, + EDBM_CLNOR_MODAL_POINTTO_SET_USE_SELECTED = 114, +}; + +/* called in transform_ops.c, on each regeneration of keymaps */ +wmKeyMap *point_normals_modal_keymap(wmKeyConfig *keyconf) +{ + static const EnumPropertyItem modal_items[] = { + {EDBM_CLNOR_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""}, + {EDBM_CLNOR_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""}, + + /* Point To operator. */ + {EDBM_CLNOR_MODAL_POINTTO_RESET, "RESET", 0, "Reset", "Reset normals to initial ones"}, + {EDBM_CLNOR_MODAL_POINTTO_INVERT, "INVERT", 0, "Invert", "Toggle inversion of affected normals"}, + {EDBM_CLNOR_MODAL_POINTTO_SPHERIZE, "SPHERIZE", 0, "Spherize", "Interpolate between new and original normals"}, + {EDBM_CLNOR_MODAL_POINTTO_ALIGN, "ALIGN", 0, "Align", "Make all affected normals parallel"}, + + {EDBM_CLNOR_MODAL_POINTTO_USE_MOUSE, "USE_MOUSE", 0, "Use Mouse", "Follow mouse cursor position"}, + {EDBM_CLNOR_MODAL_POINTTO_USE_PIVOT, "USE_PIVOT", 0, "Use Pivot", + "Use current rotation/scaling pivot point coordinates"}, + {EDBM_CLNOR_MODAL_POINTTO_USE_OBJECT, "USE_OBJECT", 0, "Use Object", "Use current edited object's location"}, + {EDBM_CLNOR_MODAL_POINTTO_SET_USE_3DCURSOR, "SET_USE_3DCURSOR", 0, "Set and Use 3D Cursor", + "Set new 3D cursor position and use it"}, + {EDBM_CLNOR_MODAL_POINTTO_SET_USE_SELECTED, "SET_USE_SELECTED", 0, "Select and Use Mesh Item", + "Select new active mesh element and use its location"}, + {0, NULL, 0, NULL, NULL} + }; + static const char *keymap_name = "Custom Normals Modal Map"; + + wmKeyMap *keymap = WM_modalkeymap_get(keyconf, keymap_name); + + /* We only need to add map once */ + if (keymap && keymap->modal_items) + return NULL; + + keymap = WM_modalkeymap_add(keyconf, keymap_name, modal_items); + + WM_modalkeymap_assign(keymap, "MESH_OT_point_normals"); + + return keymap; +} + +#define CLNORS_VALID_VEC_LEN (1e-4f) + +/********************** 'Point to' Loop Normals **********************/ + +enum { + EDBM_CLNOR_POINTTO_MODE_COORDINATES = 1, + EDBM_CLNOR_POINTTO_MODE_MOUSE = 2, +}; + +static EnumPropertyItem clnors_pointto_mode_items[] = { + {EDBM_CLNOR_POINTTO_MODE_COORDINATES, "COORDINATES", 0, "Coordinates", + "Use static coordinates (defined by various means)"}, + {EDBM_CLNOR_POINTTO_MODE_MOUSE, "MOUSE", 0, "Mouse", "Follow mouse cursor"}, + {0, NULL, 0, NULL, NULL} +}; + +/* Initialize loop normal data */ +static int point_normals_init(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + + BKE_editmesh_lnorspace_update(em); + BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm); + + op->customdata = lnors_ed_arr; + + return lnors_ed_arr->totloop; +} + +static void point_normals_free(bContext *C, wmOperator *op) +{ + BMLoopNorEditDataArray *lnors_ed_arr = op->customdata; + BM_loop_normal_editdata_array_free(lnors_ed_arr); + op->customdata = NULL; + ED_area_status_text(CTX_wm_area(C), NULL); +} + +static void point_normals_update_header(bContext *C, wmOperator *op) +{ + char header[UI_MAX_DRAW_STR]; + char buf[UI_MAX_DRAW_STR]; + + char *p = buf; + int available_len = sizeof(buf); + +#define WM_MODALKEY(_id) \ + WM_modalkeymap_operator_items_to_string_buf(op->type, (_id), true, UI_MAX_SHORTCUT_STR, &available_len, &p) + + BLI_snprintf(header, sizeof(header), IFACE_("%s: confirm, %s: cancel, " + "%s: point to mouse (%s), %s: point to Pivot, " + "%s: point to object origin, %s: reset normals, " + "%s: set & point to 3D cursor, %s: select & point to mesh item, " + "%s: invert normals (%s), %s: spherize (%s), %s: align (%s)"), + WM_MODALKEY(EDBM_CLNOR_MODAL_CONFIRM), WM_MODALKEY(EDBM_CLNOR_MODAL_CANCEL), + WM_MODALKEY(EDBM_CLNOR_MODAL_POINTTO_USE_MOUSE), + WM_bool_as_string(RNA_enum_get(op->ptr, "mode") == EDBM_CLNOR_POINTTO_MODE_MOUSE), + WM_MODALKEY(EDBM_CLNOR_MODAL_POINTTO_USE_PIVOT), WM_MODALKEY(EDBM_CLNOR_MODAL_POINTTO_USE_OBJECT), + WM_MODALKEY(EDBM_CLNOR_MODAL_POINTTO_RESET), WM_MODALKEY(EDBM_CLNOR_MODAL_POINTTO_SET_USE_3DCURSOR), + WM_MODALKEY(EDBM_CLNOR_MODAL_POINTTO_SET_USE_SELECTED), + WM_MODALKEY(EDBM_CLNOR_MODAL_POINTTO_INVERT), WM_bool_as_string(RNA_boolean_get(op->ptr, "invert")), + WM_MODALKEY(EDBM_CLNOR_MODAL_POINTTO_SPHERIZE), + WM_bool_as_string(RNA_boolean_get(op->ptr, "spherize")), + WM_MODALKEY(EDBM_CLNOR_MODAL_POINTTO_ALIGN), WM_bool_as_string(RNA_boolean_get(op->ptr, "align"))); + +#undef WM_MODALKEY + + ED_area_status_text(CTX_wm_area(C), header); +} + +/* TODO move that to generic function in BMesh? */ +static void bmesh_selected_verts_center_calc(BMesh *bm, float *r_center) +{ + BMVert *v; + BMIter viter; + int i = 0; + + zero_v3(r_center); + BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + add_v3_v3(r_center, v->co); + i++; + } + } + mul_v3_fl(r_center, 1.0f / (float)i); +} + +static void point_normals_apply(bContext *C, wmOperator *op, float target[3], const bool do_reset) +{ + Object *obedit = CTX_data_edit_object(C); + BMesh *bm = BKE_editmesh_from_object(obedit)->bm; + BMLoopNorEditDataArray *lnors_ed_arr = op->customdata; + + const bool do_invert = RNA_boolean_get(op->ptr, "invert"); + const bool do_spherize = RNA_boolean_get(op->ptr, "spherize"); + const bool do_align = RNA_boolean_get(op->ptr, "align"); + float center[3]; + + if (do_align && !do_reset) { + bmesh_selected_verts_center_calc(bm, center); + } + + sub_v3_v3(target, obedit->loc); /* Move target to local coordinates. */ + + BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata; + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + if (do_reset) { + copy_v3_v3(lnor_ed->nloc, lnor_ed->niloc); + } + else if (do_spherize) { + /* Note that this is *not* real spherical interpolation. Probably good enough in this case though? */ + const float strength = RNA_float_get(op->ptr, "spherize_strength"); + float spherized_normal[3]; + + sub_v3_v3v3(spherized_normal, target, lnor_ed->loc); + normalize_v3(spherized_normal); /* otherwise, multiplication by strength is meaningless... */ + mul_v3_fl(spherized_normal, strength); + mul_v3_v3fl(lnor_ed->nloc, lnor_ed->niloc, 1.0f - strength); + add_v3_v3(lnor_ed->nloc, spherized_normal); + } + else if (do_align) { + sub_v3_v3v3(lnor_ed->nloc, target, center); + } + else { + sub_v3_v3v3(lnor_ed->nloc, target, lnor_ed->loc); + } + + if (do_invert && !do_reset) { + negate_v3(lnor_ed->nloc); + } + if (normalize_v3(lnor_ed->nloc) >= CLNORS_VALID_VEC_LEN) { + BKE_lnor_space_custom_normal_to_data( + bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->nloc, lnor_ed->clnors_data); + } + } +} + +static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + View3D *v3d = CTX_wm_view3d(C); + Scene *scene = CTX_data_scene(C); + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + + float target[3]; + + int ret = OPERATOR_PASS_THROUGH; + int mode = RNA_enum_get(op->ptr, "mode"); + int new_mode = mode; + bool force_mousemove = false; + bool do_reset = false; + + PropertyRNA *prop_target = RNA_struct_find_property(op->ptr, "target_location"); + + if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case EDBM_CLNOR_MODAL_CONFIRM: + RNA_property_float_get_array(op->ptr, prop_target, target); + ret = OPERATOR_FINISHED; + break; + + case EDBM_CLNOR_MODAL_CANCEL: + do_reset = true; + ret = OPERATOR_CANCELLED; + break; + + case EDBM_CLNOR_MODAL_POINTTO_RESET: + do_reset = true; + ret = OPERATOR_RUNNING_MODAL; + break; + + case EDBM_CLNOR_MODAL_POINTTO_INVERT: + { + PropertyRNA *prop_invert = RNA_struct_find_property(op->ptr, "invert"); + RNA_property_boolean_set(op->ptr, prop_invert, !RNA_property_boolean_get(op->ptr, prop_invert)); + RNA_property_float_get_array(op->ptr, prop_target, target); + ret = OPERATOR_RUNNING_MODAL; + break; + } + + case EDBM_CLNOR_MODAL_POINTTO_SPHERIZE: + { + PropertyRNA *prop_spherize = RNA_struct_find_property(op->ptr, "spherize"); + RNA_property_boolean_set(op->ptr, prop_spherize, !RNA_property_boolean_get(op->ptr, prop_spherize)); + RNA_property_float_get_array(op->ptr, prop_target, target); + ret = OPERATOR_RUNNING_MODAL; + break; + } + + case EDBM_CLNOR_MODAL_POINTTO_ALIGN: + { + PropertyRNA *prop_align = RNA_struct_find_property(op->ptr, "align"); + RNA_property_boolean_set(op->ptr, prop_align, !RNA_property_boolean_get(op->ptr, prop_align)); + RNA_property_float_get_array(op->ptr, prop_target, target); + ret = OPERATOR_RUNNING_MODAL; + break; + } + + case EDBM_CLNOR_MODAL_POINTTO_USE_MOUSE: + new_mode = EDBM_CLNOR_POINTTO_MODE_MOUSE; + force_mousemove = true; /* We want to immediately update to mouse cursor position... */ + ret = OPERATOR_RUNNING_MODAL; + break; + + case EDBM_CLNOR_MODAL_POINTTO_USE_OBJECT: + new_mode = EDBM_CLNOR_POINTTO_MODE_COORDINATES; + copy_v3_v3(target, obedit->loc); + ret = OPERATOR_RUNNING_MODAL; + break; + + case EDBM_CLNOR_MODAL_POINTTO_SET_USE_3DCURSOR: + new_mode = EDBM_CLNOR_POINTTO_MODE_COORDINATES; + ED_view3d_cursor3d_update(C, event->mval, false, V3D_CURSOR_ORIENT_NONE); + copy_v3_v3(target, ED_view3d_cursor3d_get(scene, v3d)->location); + ret = OPERATOR_RUNNING_MODAL; + break; + + case EDBM_CLNOR_MODAL_POINTTO_SET_USE_SELECTED: + new_mode = EDBM_CLNOR_POINTTO_MODE_COORDINATES; + view3d_operator_needs_opengl(C); + if (EDBM_select_pick(C, event->mval, false, false, false)) { + ED_object_editmode_calc_active_center(obedit, false, target); /* Point to newly selected active. */ + add_v3_v3(target, obedit->loc); + ret = OPERATOR_RUNNING_MODAL; + } + break; + + case EDBM_CLNOR_MODAL_POINTTO_USE_PIVOT: + new_mode = EDBM_CLNOR_POINTTO_MODE_COORDINATES; + switch (scene->toolsettings->transform_pivot_point) { + case V3D_AROUND_CENTER_BOUNDS: /* calculateCenterBound */ + { + BMVert *v; + BMIter viter; + float min[3], max[3]; + int i = 0; + + BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + if (i) { + minmax_v3v3_v3(min, max, v->co); + } + else { + copy_v3_v3(min, v->co); + copy_v3_v3(max, v->co); + } + i++; + } + } + mid_v3_v3v3(target, min, max); + add_v3_v3(target, obedit->loc); + break; + } + + case V3D_AROUND_CENTER_MEAN: + { + bmesh_selected_verts_center_calc(bm, target); + add_v3_v3(target, obedit->loc); + break; + } + + case V3D_AROUND_CURSOR: + copy_v3_v3(target, ED_view3d_cursor3d_get(scene, v3d)->location); + break; + + case V3D_AROUND_ACTIVE: + if (!ED_object_editmode_calc_active_center(obedit, false, target)) { + zero_v3(target); + } + add_v3_v3(target, obedit->loc); + break; + + default: + BKE_report(op->reports, RPT_WARNING, "Does not support Individual Origin as pivot"); + copy_v3_v3(target, obedit->loc); + } + ret = OPERATOR_RUNNING_MODAL; + break; + default: + break; + } + } + + if (new_mode != mode) { + mode = new_mode; + RNA_enum_set(op->ptr, "mode", mode); + } + + /* Only handle mousemove event in case we are in mouse mode. */ + if (event->type == MOUSEMOVE || force_mousemove) { + if (mode == EDBM_CLNOR_POINTTO_MODE_MOUSE) { + ARegion *ar = CTX_wm_region(C); + float center[3]; + + bmesh_selected_verts_center_calc(bm, center); + + ED_view3d_win_to_3d_int(v3d, ar, center, event->mval, target); + + ret = OPERATOR_RUNNING_MODAL; + } + } + + if (ret != OPERATOR_PASS_THROUGH) { + if (!ELEM(ret, OPERATOR_CANCELLED, OPERATOR_FINISHED)) { + RNA_property_float_set_array(op->ptr, prop_target, target); + } + point_normals_apply(C, op, target, do_reset); + EDBM_update_generic(em, true, false); /* Recheck bools. */ + + point_normals_update_header(C, op); + } + + if (ELEM(ret, OPERATOR_CANCELLED, OPERATOR_FINISHED)) { + point_normals_free(C, op); + } + + return ret; +} + +static int edbm_point_normals_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!point_normals_init(C, op, event)) { + point_normals_free(C, op); + return OPERATOR_CANCELLED; + } + + WM_event_add_modal_handler(C, op); + + point_normals_update_header(C, op); + + op->flag |= OP_IS_MODAL_GRAB_CURSOR; + return OPERATOR_RUNNING_MODAL; +} + +static int edbm_point_normals_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + if (!point_normals_init(C, op, NULL)) { + point_normals_free(C, op); + return OPERATOR_CANCELLED; + } + + /* Note that 'mode' is ignored in exec case, we directly use vector stored in target_location, whatever that is. */ + + float target[3]; + RNA_float_get_array(op->ptr, "target_location", target); + + point_normals_apply(C, op, target, false); + + EDBM_update_generic(em, true, false); + point_normals_free(C, op); + + return OPERATOR_FINISHED; +} + +static bool point_normals_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop, void *UNUSED(user_data)) +{ + const char *prop_id = RNA_property_identifier(prop); + + /* Only show strength option if spherize is enabled. */ + if (STREQ(prop_id, "spherize_strength")) { + return (bool)RNA_boolean_get(ptr, "spherize"); + } + + /* Else, show it! */ + return true; +} + +static void edbm_point_normals_ui(bContext *C, wmOperator *op) +{ + uiLayout *layout = op->layout; + wmWindowManager *wm = CTX_wm_manager(C); + PointerRNA ptr; + + RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); + + /* Main auto-draw call */ + uiDefAutoButsRNA(layout, &ptr, point_normals_draw_check_prop, NULL, '\0', false); +} + +void MESH_OT_point_normals(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Point Normals to Target"; + ot->description = "Point selected custom normals to specified Target"; + ot->idname = "MESH_OT_point_normals"; + + /* api callbacks */ + ot->exec = edbm_point_normals_exec; + ot->invoke = edbm_point_normals_invoke; + ot->modal = edbm_point_normals_modal; + ot->poll = ED_operator_editmesh_auto_smooth; + ot->ui = edbm_point_normals_ui; + ot->cancel = point_normals_free; + + /* flags */ + ot->flag = OPTYPE_BLOCKING | OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "mode", clnors_pointto_mode_items, EDBM_CLNOR_POINTTO_MODE_COORDINATES, + "Mode", "How to define coordinates to point custom normals to"); + RNA_def_property_flag(ot->prop, PROP_HIDDEN); + + RNA_def_boolean(ot->srna, "invert", false, "Invert", "Invert affected normals"); + + RNA_def_boolean(ot->srna, "align", false, "Align", "Make all affected normals parallel"); + + RNA_def_float_vector(ot->srna, "target_location", 3, (float[3]){0.0f, 0.0f, 0.0f}, -FLT_MAX, FLT_MAX, + "Target", "Target location to which normals will point", -1000.0f, 1000.0f); + + RNA_def_boolean(ot->srna, "spherize", false, + "Spherize", "Interpolate between original and new normals"); + + RNA_def_float(ot->srna, "spherize_strength", 0.1, 0.0f, 1.0f, + "Spherize Strength", "Ratio of spherized normal to original normal", 0.0f, 1.0f); +} + +/********************** Split/Merge Loop Normals **********************/ + +static void normals_merge(BMesh *bm, BMLoopNorEditDataArray *lnors_ed_arr) +{ + BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata; + + BLI_SMALLSTACK_DECLARE(clnors, short *); + + BLI_assert(bm->lnor_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR); + + BM_normals_loops_edges_tag(bm, false); + + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + if (BM_elem_flag_test(lnor_ed->loop, BM_ELEM_TAG)) { + continue; + } + + MLoopNorSpace *lnor_space = bm->lnor_spacearr->lspacearr[lnor_ed->loop_index]; + + if ((lnor_space->flags & MLNOR_SPACE_IS_SINGLE) == 0) { + LinkNode *loops = lnor_space->loops; + float avg_normal[3] = {0.0f, 0.0f, 0.0f}; + short *clnors_data; + + for (; loops; loops = loops->next) { + BMLoop *l = loops->link; + const int loop_index = BM_elem_index_get(l); + + BMLoopNorEditData *lnor_ed_tmp = lnors_ed_arr->lidx_to_lnor_editdata[loop_index]; + BLI_assert(lnor_ed_tmp->loop_index == loop_index && lnor_ed_tmp->loop == l); + add_v3_v3(avg_normal, lnor_ed_tmp->nloc); + BLI_SMALLSTACK_PUSH(clnors, lnor_ed_tmp->clnors_data); + BM_elem_flag_enable(l, BM_ELEM_TAG); + } + if (normalize_v3(avg_normal) < CLNORS_VALID_VEC_LEN) { + /* If avg normal is nearly 0, set clnor to default value. */ + zero_v3(avg_normal); + } + while ((clnors_data = BLI_SMALLSTACK_POP(clnors))) { + BKE_lnor_space_custom_normal_to_data(lnor_space, avg_normal, clnors_data); + } + } + } +} + +static void normals_split(BMesh *bm) +{ + BMFace *f; + BMLoop *l, *l_curr, *l_first; + BMIter fiter; + + BLI_assert(bm->lnor_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR); + + BM_normals_loops_edges_tag(bm, true); + + const int cd_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL); + BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) { + l_curr = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (BM_elem_flag_test(l_curr->v, BM_ELEM_SELECT) && (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) || + (!BM_elem_flag_test(l_curr, BM_ELEM_TAG) && BM_loop_check_cyclic_smooth_fan(l_curr)))) + { + if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) && !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG)) { + const int loop_index = BM_elem_index_get(l_curr); + short *clnors = BM_ELEM_CD_GET_VOID_P(l_curr, cd_clnors_offset); + BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[loop_index], f->no, clnors); + } + else { + BMVert *v_pivot = l_curr->v; + UNUSED_VARS_NDEBUG(v_pivot); + BMEdge *e_next; + const BMEdge *e_org = l_curr->e; + BMLoop *lfan_pivot, *lfan_pivot_next; + + lfan_pivot = l_curr; + e_next = lfan_pivot->e; + BLI_SMALLSTACK_DECLARE(loops, BMLoop *); + float avg_normal[3] = { 0.0f }; + + while (true) { + lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next); + if (lfan_pivot_next) { + BLI_assert(lfan_pivot_next->v == v_pivot); + } + else { + e_next = (lfan_pivot->e == e_next) ? lfan_pivot->prev->e : lfan_pivot->e; + } + + BLI_SMALLSTACK_PUSH(loops, lfan_pivot); + add_v3_v3(avg_normal, lfan_pivot->f->no); + + if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) { + break; + } + lfan_pivot = lfan_pivot_next; + } + if (normalize_v3(avg_normal) < CLNORS_VALID_VEC_LEN) { + /* If avg normal is nearly 0, set clnor to default value. */ + zero_v3(avg_normal); + } + while ((l = BLI_SMALLSTACK_POP(loops))) { + const int l_index = BM_elem_index_get(l); + short *clnors = BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset); + BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[l_index], avg_normal, clnors); + } + } + } + } while ((l_curr = l_curr->next) != l_first); + } +} + +static int normals_split_merge(bContext *C, const bool do_merge) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + BMEdge *e; + BMIter eiter; + + BKE_editmesh_lnorspace_update(em); + + BMLoopNorEditDataArray *lnors_ed_arr = do_merge ? BM_loop_normal_editdata_array_init(bm) : NULL; + + mesh_set_smooth_faces(em, do_merge); + + BM_ITER_MESH(e, &eiter, bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { + BM_elem_flag_set(e, BM_ELEM_SMOOTH, do_merge); + } + } + + bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; + BKE_editmesh_lnorspace_update(em); + + if (do_merge) { + normals_merge(bm, lnors_ed_arr); + } + else { + normals_split(bm); + } + + if (lnors_ed_arr) { + BM_loop_normal_editdata_array_free(lnors_ed_arr); + } + + EDBM_update_generic(em, true, false); + + return OPERATOR_FINISHED; +} + +static int edbm_merge_normals_exec(bContext *C, wmOperator *UNUSED(op)) +{ + return normals_split_merge(C, true); +} + +void MESH_OT_merge_normals(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Merge Normals"; + ot->description = "Merge custom normals of selected vertices"; + ot->idname = "MESH_OT_merge_normals"; + + /* api callbacks */ + ot->exec = edbm_merge_normals_exec; + ot->poll = ED_operator_editmesh_auto_smooth; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int edbm_split_normals_exec(bContext *C, wmOperator *UNUSED(op)) +{ + return normals_split_merge(C, false); +} + +void MESH_OT_split_normals(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Split Normals"; + ot->description = "Split custom normals of selected vertices"; + ot->idname = "MESH_OT_split_normals"; + + /* api callbacks */ + ot->exec = edbm_split_normals_exec; + ot->poll = ED_operator_editmesh_auto_smooth; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/********************** Average Loop Normals **********************/ + +enum { + EDBM_CLNOR_AVERAGE_LOOP = 1, + EDBM_CLNOR_AVERAGE_FACE_AREA = 2, + EDBM_CLNOR_AVERAGE_ANGLE = 3, +}; + +static EnumPropertyItem average_method_items[] = { + {EDBM_CLNOR_AVERAGE_LOOP, "CUSTOM_NORMAL", 0, "Custom Normal", "Take Average of vert Normals"}, + {EDBM_CLNOR_AVERAGE_FACE_AREA, "FACE_AREA", 0, "Face Area", "Set all vert normals by Face Area"}, + {EDBM_CLNOR_AVERAGE_ANGLE, "CORNER_ANGLE", 0, "Corner Angle", "Set all vert normals by Corner Angle"}, + {0, NULL, 0, NULL, NULL} +}; + +static int edbm_average_normals_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + BMFace *f; + BMLoop *l, *l_curr, *l_first; + BMIter fiter; + + bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; + BKE_editmesh_lnorspace_update(em); + + const int average_type = RNA_enum_get(op->ptr, "average_type"); + const int cd_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL); + const float absweight = (float) RNA_int_get(op->ptr, "weight"); + const float threshold = RNA_float_get(op->ptr, "threshold"); + + float weight = absweight / 50.0f; + if (absweight == 100.0f) { + weight = (float)SHRT_MAX; + } + else if (absweight == 1.0f) { + weight = 1 / (float)SHRT_MAX; + } + else if ((weight - 1) * 25 > 1) { + weight = (weight - 1) * 25; + } + + BM_normals_loops_edges_tag(bm, true); + + HeapSimple *loop_weight = BLI_heapsimple_new(); + + BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) { + l_curr = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (BM_elem_flag_test(l_curr->v, BM_ELEM_SELECT) && (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) || + (!BM_elem_flag_test(l_curr, BM_ELEM_TAG) && BM_loop_check_cyclic_smooth_fan(l_curr)))) + { + if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) && !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG)) { + const int loop_index = BM_elem_index_get(l_curr); + short *clnors = BM_ELEM_CD_GET_VOID_P(l_curr, cd_clnors_offset); + BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[loop_index], f->no, clnors); + } + else { + BMVert *v_pivot = l_curr->v; + UNUSED_VARS_NDEBUG(v_pivot); + BMEdge *e_next; + const BMEdge *e_org = l_curr->e; + BMLoop *lfan_pivot, *lfan_pivot_next; + + lfan_pivot = l_curr; + e_next = lfan_pivot->e; + + while (true) { + lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next); + if (lfan_pivot_next) { + BLI_assert(lfan_pivot_next->v == v_pivot); + } + else { + e_next = (lfan_pivot->e == e_next) ? lfan_pivot->prev->e : lfan_pivot->e; + } + + float val = 1.0f; + if (average_type == EDBM_CLNOR_AVERAGE_FACE_AREA) { + val = 1.0f / BM_face_calc_area(lfan_pivot->f); + } + else if (average_type == EDBM_CLNOR_AVERAGE_ANGLE) { + val = 1.0f / BM_loop_calc_face_angle(lfan_pivot); + } + + BLI_heapsimple_insert(loop_weight, val, lfan_pivot); + + if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) { + break; + } + lfan_pivot = lfan_pivot_next; + } + + BLI_SMALLSTACK_DECLARE(loops, BMLoop *); + float wnor[3], avg_normal[3] = { 0.0f }, count = 0; + float val = BLI_heapsimple_top_value(loop_weight); + + while (!BLI_heapsimple_is_empty(loop_weight)) { + const float cur_val = BLI_heapsimple_top_value(loop_weight); + if (!compare_ff(val, cur_val, threshold)) { + count++; + val = cur_val; + } + l = BLI_heapsimple_pop_min(loop_weight); + BLI_SMALLSTACK_PUSH(loops, l); + + const float n_weight = pow(weight, count); + + if (average_type == EDBM_CLNOR_AVERAGE_LOOP) { + const int l_index = BM_elem_index_get(l); + short *clnors = BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset); + BKE_lnor_space_custom_data_to_normal(bm->lnor_spacearr->lspacearr[l_index], clnors, wnor); + } + else { + copy_v3_v3(wnor, l->f->no); + } + mul_v3_fl(wnor, (1.0f / cur_val) * (1.0f / n_weight)); + add_v3_v3(avg_normal, wnor); + } + + if (normalize_v3(avg_normal) < CLNORS_VALID_VEC_LEN) { + /* If avg normal is nearly 0, set clnor to default value. */ + zero_v3(avg_normal); + } + while ((l = BLI_SMALLSTACK_POP(loops))) { + const int l_index = BM_elem_index_get(l); + short *clnors = BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset); + BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[l_index], avg_normal, clnors); + } + } + } + } while ((l_curr = l_curr->next) != l_first); + } + + BLI_heapsimple_free(loop_weight, NULL); + EDBM_update_generic(em, true, false); + + return OPERATOR_FINISHED; +} + +static bool average_normals_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop, void *UNUSED(user_data)) +{ + const char *prop_id = RNA_property_identifier(prop); + const int average_type = RNA_enum_get(ptr, "average_type"); + + /* Only show weight/threshold options in loop average type. */ + if (STREQ(prop_id, "weight")) { + return (average_type == EDBM_CLNOR_AVERAGE_LOOP); + } + else if (STREQ(prop_id, "threshold")) { + return (average_type == EDBM_CLNOR_AVERAGE_LOOP); + } + + /* Else, show it! */ + return true; +} + +static void edbm_average_normals_ui(bContext *C, wmOperator *op) +{ + uiLayout *layout = op->layout; + wmWindowManager *wm = CTX_wm_manager(C); + PointerRNA ptr; + + RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); + + /* Main auto-draw call */ + uiDefAutoButsRNA(layout, &ptr, average_normals_draw_check_prop, NULL, '\0', false); +} + +void MESH_OT_average_normals(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Average Normals"; + ot->description = "Average custom normals of selected vertices"; + ot->idname = "MESH_OT_average_normals"; + + /* api callbacks */ + ot->exec = edbm_average_normals_exec; + ot->poll = ED_operator_editmesh_auto_smooth; + ot->ui = edbm_average_normals_ui; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "average_type", average_method_items, EDBM_CLNOR_AVERAGE_LOOP, + "Type", "Averaging method"); + RNA_def_property_flag(ot->prop, PROP_HIDDEN); + + RNA_def_int(ot->srna, "weight", 50, 1, 100, "Weight", "Weight applied per face", 1, 100); + + RNA_def_float(ot->srna, "threshold", 0.01f, 0, 10, "Threshold", + "Threshold value for different weights to be considered equal", 0, 5); +} + +/********************** Custom Normal Interface Tools **********************/ + +enum { + EDBM_CLNOR_TOOLS_COPY = 1, + EDBM_CLNOR_TOOLS_PASTE = 2, + EDBM_CLNOR_TOOLS_MULTIPLY = 3, + EDBM_CLNOR_TOOLS_ADD = 4, + EDBM_CLNOR_TOOLS_RESET = 5, +}; + +static EnumPropertyItem normal_vector_tool_items[] = { + {EDBM_CLNOR_TOOLS_COPY, "COPY", 0, "Copy Normal", "Copy normal to buffer"}, + {EDBM_CLNOR_TOOLS_PASTE, "PASTE", 0, "Paste Normal", "Paste normal from buffer"}, + {EDBM_CLNOR_TOOLS_ADD, "ADD", 0, "Add Normal", "Add normal vector with selection"}, + {EDBM_CLNOR_TOOLS_MULTIPLY, "MULTIPLY", 0, "Multiply Normal", "Multiply normal vector with selection"}, + {EDBM_CLNOR_TOOLS_RESET, "RESET", 0, "Reset Normal", "Reset buffer and/or normal of selected element"}, + {0, NULL, 0, NULL, NULL} +}; + +static int edbm_normals_tools_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + Scene *scene = CTX_data_scene(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + + const int mode = RNA_enum_get(op->ptr, "mode"); + const bool absolute = RNA_boolean_get(op->ptr, "absolute"); + + BKE_editmesh_lnorspace_update(em); + BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm); + BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata; + + float *normal_vector = scene->toolsettings->normal_vector; + + switch (mode) { + case EDBM_CLNOR_TOOLS_COPY: + if (bm->totfacesel != 1 && lnors_ed_arr->totloop != 1 && bm->totvertsel != 1) { + BKE_report(op->reports, RPT_ERROR, "Can only copy custom normal, vertex normal or face normal"); + BM_loop_normal_editdata_array_free(lnors_ed_arr); + return OPERATOR_CANCELLED; + } + bool join = true; + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + if (!compare_v3v3(lnors_ed_arr->lnor_editdata->nloc, lnor_ed->nloc, 1e-4f)) { + join = false; + } + } + if (lnors_ed_arr->totloop == 1) { + copy_v3_v3(scene->toolsettings->normal_vector, lnors_ed_arr->lnor_editdata->nloc); + } + else if (bm->totfacesel == 1) { + BMFace *f; + BMIter fiter; + BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + copy_v3_v3(scene->toolsettings->normal_vector, f->no); + } + } + } + else if (join) { + copy_v3_v3(scene->toolsettings->normal_vector, lnors_ed_arr->lnor_editdata->nloc); + } + break; + + case EDBM_CLNOR_TOOLS_PASTE: + if (!absolute) { + if (normalize_v3(normal_vector) < CLNORS_VALID_VEC_LEN) { + /* If normal is nearly 0, do nothing. */ + break; + } + } + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + if (absolute) { + float abs_normal[3]; + copy_v3_v3(abs_normal, lnor_ed->loc); + negate_v3(abs_normal); + add_v3_v3(abs_normal, normal_vector); + + if (normalize_v3(abs_normal) < CLNORS_VALID_VEC_LEN) { + /* If abs normal is nearly 0, set clnor to initial value. */ + copy_v3_v3(abs_normal, lnor_ed->niloc); + } + BKE_lnor_space_custom_normal_to_data( + bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], abs_normal, lnor_ed->clnors_data); + } + else { + BKE_lnor_space_custom_normal_to_data( + bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], normal_vector, lnor_ed->clnors_data); + } + } + break; + + case EDBM_CLNOR_TOOLS_MULTIPLY: + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + mul_v3_v3(lnor_ed->nloc, normal_vector); + + if (normalize_v3(lnor_ed->nloc) < CLNORS_VALID_VEC_LEN) { + /* If abs normal is nearly 0, set clnor to initial value. */ + copy_v3_v3(lnor_ed->nloc, lnor_ed->niloc); + } + BKE_lnor_space_custom_normal_to_data( + bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->nloc, lnor_ed->clnors_data); + } + break; + + case EDBM_CLNOR_TOOLS_ADD: + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + add_v3_v3(lnor_ed->nloc, normal_vector); + + if (normalize_v3(lnor_ed->nloc) < CLNORS_VALID_VEC_LEN) { + /* If abs normal is nearly 0, set clnor to initial value. */ + copy_v3_v3(lnor_ed->nloc, lnor_ed->niloc); + } + BKE_lnor_space_custom_normal_to_data( + bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->nloc, lnor_ed->clnors_data); + } + break; + + case EDBM_CLNOR_TOOLS_RESET: + zero_v3(normal_vector); + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + BKE_lnor_space_custom_normal_to_data( + bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], normal_vector, lnor_ed->clnors_data); + } + break; + + default: + BLI_assert(0); + break; + } + + BM_loop_normal_editdata_array_free(lnors_ed_arr); + + EDBM_update_generic(em, true, false); + return OPERATOR_FINISHED; +} + +static bool normals_tools_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop, void *UNUSED(user_data)) +{ + const char *prop_id = RNA_property_identifier(prop); + const int mode = RNA_enum_get(ptr, "mode"); + + /* Only show absolute option in paste mode. */ + if (STREQ(prop_id, "absolute")) { + return (mode == EDBM_CLNOR_TOOLS_PASTE); + } + + /* Else, show it! */ + return true; +} + +static void edbm_normals_tools_ui(bContext *C, wmOperator *op) +{ + uiLayout *layout = op->layout; + wmWindowManager *wm = CTX_wm_manager(C); + PointerRNA ptr; + + RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); + + /* Main auto-draw call */ + uiDefAutoButsRNA(layout, &ptr, normals_tools_draw_check_prop, NULL, '\0', false); +} + +void MESH_OT_normals_tools(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Normals Vector Tools"; + ot->description = "Custom normals tools using Normal Vector of UI"; + ot->idname = "MESH_OT_normals_tools"; + + /* api callbacks */ + ot->exec = edbm_normals_tools_exec; + ot->poll = ED_operator_editmesh_auto_smooth; + ot->ui = edbm_normals_tools_ui; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "mode", normal_vector_tool_items, EDBM_CLNOR_TOOLS_COPY, + "Mode", "Mode of tools taking input from Interface"); + RNA_def_property_flag(ot->prop, PROP_HIDDEN); + + RNA_def_boolean(ot->srna, "absolute", false, "Absolute Coordinates", "Copy Absolute coordinates or Normal vector"); +} + +static int edbm_set_normals_from_faces_exec(bContext *C, wmOperator *op) +{ + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + BMFace *f; + BMVert *v; + BMEdge *e; + BMLoop *l; + BMIter fiter, viter, eiter, liter; + + const bool keep_sharp = RNA_boolean_get(op->ptr, "keep_sharp"); + + BKE_editmesh_lnorspace_update(em); + + float(*vnors)[3] = MEM_callocN(sizeof(*vnors) * bm->totvert, __func__); + BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + BM_ITER_ELEM(v, &viter, f, BM_VERTS_OF_FACE) { + const int v_index = BM_elem_index_get(v); + add_v3_v3(vnors[v_index], f->no); + } + } + } + for (int i = 0; i < bm->totvert; i++) { + if (!is_zero_v3(vnors[i]) && normalize_v3(vnors[i]) < CLNORS_VALID_VEC_LEN) { + zero_v3(vnors[i]); + } + } + + BLI_bitmap *loop_set = BLI_BITMAP_NEW(bm->totloop, __func__); + const int cd_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL); + + BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) { + BM_ITER_ELEM(e, &eiter, f, BM_EDGES_OF_FACE) { + if (!keep_sharp || (BM_elem_flag_test(e, BM_ELEM_SMOOTH) && BM_elem_flag_test(e, BM_ELEM_SELECT))) { + BM_ITER_ELEM(v, &viter, e, BM_VERTS_OF_EDGE) { + l = BM_face_vert_share_loop(f, v); + const int l_index = BM_elem_index_get(l); + const int v_index = BM_elem_index_get(l->v); + + if (!is_zero_v3(vnors[v_index])) { + short *clnors = BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset); + BKE_lnor_space_custom_normal_to_data( + bm->lnor_spacearr->lspacearr[l_index], vnors[v_index], clnors); + + if (bm->lnor_spacearr->lspacearr[l_index]->flags & MLNOR_SPACE_IS_SINGLE) { + BLI_BITMAP_ENABLE(loop_set, l_index); + } + else { + LinkNode *loops = bm->lnor_spacearr->lspacearr[l_index]->loops; + for (; loops; loops = loops->next) { + BLI_BITMAP_ENABLE(loop_set, BM_elem_index_get((BMLoop *)loops->link)); + } + } + } + } + } + } + } + + int v_index; + BM_ITER_MESH_INDEX(v, &viter, bm, BM_VERTS_OF_MESH, v_index) { + BM_ITER_ELEM(l, &liter, v, BM_LOOPS_OF_VERT) { + if (BLI_BITMAP_TEST(loop_set, BM_elem_index_get(l))) { + const int loop_index = BM_elem_index_get(l); + short *clnors = BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset); + BKE_lnor_space_custom_normal_to_data( + bm->lnor_spacearr->lspacearr[loop_index], vnors[v_index], clnors); + } + } + } + + MEM_freeN(loop_set); + MEM_freeN(vnors); + EDBM_update_generic(em, true, false); + } + + return OPERATOR_FINISHED; +} + +void MESH_OT_set_normals_from_faces(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set Normals From Faces"; + ot->description = "Set the custom normals from the selected faces ones"; + ot->idname = "MESH_OT_set_normals_from_faces"; + + /* api callbacks */ + ot->exec = edbm_set_normals_from_faces_exec; + ot->poll = ED_operator_editmesh_auto_smooth; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, "keep_sharp", 0, "Keep Sharp Edges", "Do not set sharp edges to face"); +} + +static int edbm_smoothen_normals_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + BMFace *f; + BMLoop *l; + BMIter fiter, liter; + + BKE_editmesh_lnorspace_update(em); + BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm); + + float(*smooth_normal)[3] = MEM_callocN(sizeof(*smooth_normal) * lnors_ed_arr->totloop, __func__); + + /* This is weird choice of operation, taking all loops of faces of current vertex... Could lead to some rather + * far away loops weighting as much as very close ones (topologically speaking), with complex polygons. + * Using topological distance here (rather than geometrical one) makes sense imho, but would rather go with + * a more consistent and flexible code, we could even add max topological distance to take into account, + * and a weighting curve... + * Would do that later though, think for now we can live with that choice. --mont29 */ + BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata; + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + l = lnor_ed->loop; + float loop_normal[3]; + + BM_ITER_ELEM(f, &fiter, l->v, BM_FACES_OF_VERT) { + BMLoop *l_other; + BM_ITER_ELEM(l_other, &liter, f, BM_LOOPS_OF_FACE) { + const int l_index_other = BM_elem_index_get(l_other); + short *clnors = BM_ELEM_CD_GET_VOID_P(l_other, lnors_ed_arr->cd_custom_normal_offset); + BKE_lnor_space_custom_data_to_normal(bm->lnor_spacearr->lspacearr[l_index_other], clnors, loop_normal); + add_v3_v3(smooth_normal[i], loop_normal); + } + } + } + + const float factor = RNA_float_get(op->ptr, "factor"); + + lnor_ed = lnors_ed_arr->lnor_editdata; + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + float current_normal[3]; + + if (normalize_v3(smooth_normal[i]) < CLNORS_VALID_VEC_LEN) { + /* Skip in case smoothen normal is invalid... */ + continue; + } + + BKE_lnor_space_custom_data_to_normal( + bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->clnors_data, current_normal); + + /* Note: again, this is not true spherical interpolation that normals would need... + * But it's probably good enough for now. */ + mul_v3_fl(current_normal, 1.0f - factor); + mul_v3_fl(smooth_normal[i], factor); + add_v3_v3(current_normal, smooth_normal[i]); + + if (normalize_v3(current_normal) < CLNORS_VALID_VEC_LEN) { + /* Skip in case smoothen normal is invalid... */ + continue; + } + + BKE_lnor_space_custom_normal_to_data( + bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], current_normal, lnor_ed->clnors_data); + } + + BM_loop_normal_editdata_array_free(lnors_ed_arr); + MEM_freeN(smooth_normal); + + EDBM_update_generic(em, true, false); + + return OPERATOR_FINISHED; +} + +void MESH_OT_smoothen_normals(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Smoothen Normals"; + ot->description = "Smoothen custom normals based on adjacent vertex normals"; + ot->idname = "MESH_OT_smoothen_normals"; + + /* api callbacks */ + ot->exec = edbm_smoothen_normals_exec; + ot->poll = ED_operator_editmesh_auto_smooth; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 1.0f, "Factor", + "Specifies weight of smooth vs original normal", 0.0f, 1.0f); +} + +/********************** Weighted Normal Modifier Face Strength **********************/ + +static int edbm_mod_weighted_strength_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + BMFace *f; + BMIter fiter; + + BM_select_history_clear(bm); + + const char *layer_id = MOD_WEIGHTEDNORMALS_FACEWEIGHT_CDLAYER_ID; + int cd_prop_int_index = CustomData_get_named_layer_index(&bm->pdata, CD_PROP_INT, layer_id); + if (cd_prop_int_index == -1) { + BM_data_layer_add_named(bm, &bm->pdata, CD_PROP_INT, layer_id); + cd_prop_int_index = CustomData_get_named_layer_index(&bm->pdata, CD_PROP_INT, layer_id); + } + cd_prop_int_index -= CustomData_get_layer_index(&bm->pdata, CD_PROP_INT); + const int cd_prop_int_offset = CustomData_get_n_offset(&bm->pdata, CD_PROP_INT, cd_prop_int_index); + + const int face_strength = scene->toolsettings->face_strength; + const bool set = RNA_boolean_get(op->ptr, "set"); + BM_mesh_elem_index_ensure(bm, BM_FACE); + + if (set) { + BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + int *strength = BM_ELEM_CD_GET_VOID_P(f, cd_prop_int_offset); + *strength = face_strength; + } + } + } + else { + BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) { + int *strength = BM_ELEM_CD_GET_VOID_P(f, cd_prop_int_offset); + if (*strength == face_strength) { + BM_face_select_set(bm, f, true); + BM_select_history_store(bm, f); + } + else { + BM_face_select_set(bm, f, false); + } + } + } + + EDBM_update_generic(em, false, false); + return OPERATOR_FINISHED; +} + +void MESH_OT_mod_weighted_strength(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Face Strength"; + ot->description = "Set/Get strength of face (used in Weighted Normal modifier)"; + ot->idname = "MESH_OT_mod_weighted_strength"; + + /* api callbacks */ + ot->exec = edbm_mod_weighted_strength_exec; + ot->poll = ED_operator_editmesh_auto_smooth; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_boolean(ot->srna, "set", 0, "Set value", "Set Value of faces"); + RNA_def_property_flag(ot->prop, PROP_HIDDEN); +} |