From ebdf2e4aa5179a94e8b052c289b08a5625ce98cd Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 16 Oct 2023 14:16:20 +0200 Subject: [PATCH 1/2] Mesh: Add operator to select elements based on boolean attribute Adds a "Select by Attribute" operator as mentioned in the discussion in #105317. This is done in 4.0 to limit the breaking aspect of the removal of face maps. The selection storage functionality is replaced by boolean attributes. We already have a way to control the data in the boolean attribute with the "Set Attribute" operator, but we didn't have a way to convert the attribute into a selection. This operator works on the active attribute if is a boolean attribute and isn't on the face corner domain. It adds to the existing selection similar to other existing operators. While this behavior can be recreated as a node tool, we add it as a builtin operator here to avoid limitations of the new node-based tool system and to make the late-in-the-release-cycle change safer. --- scripts/startup/bl_ui/space_view3d.py | 4 + .../blender/editors/mesh/editmesh_select.cc | 99 +++++++++++++++++++ source/blender/editors/mesh/mesh_intern.h | 1 + source/blender/editors/mesh/mesh_ops.cc | 1 + 4 files changed, 105 insertions(+) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 34f547c4eb8..b39a242fd0d 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -1846,6 +1846,10 @@ class VIEW3D_MT_select_edit_mesh(Menu): layout.operator("mesh.select_axis", text="Side of Active") layout.operator("mesh.select_mirror") + layout.separator() + + layout.operator("mesh.select_by_attribute", text="By Attribute") + layout.template_node_operator_asset_menu_items(catalog_path=self.bl_label) diff --git a/source/blender/editors/mesh/editmesh_select.cc b/source/blender/editors/mesh/editmesh_select.cc index 5d62ecf2bcb..fbbf558e67e 100644 --- a/source/blender/editors/mesh/editmesh_select.cc +++ b/source/blender/editors/mesh/editmesh_select.cc @@ -6,6 +6,8 @@ * \ingroup edmesh */ +#include + #include "MEM_guardedalloc.h" #include "BLI_bitmap.h" @@ -5358,4 +5360,101 @@ void MESH_OT_loop_to_region(wmOperatorType *ot) "Select bigger regions instead of smaller ones"); } +static bool edbm_select_by_attribute_poll(bContext *C) +{ + if (!ED_operator_editmesh(C)) { + return false; + } + Object *obedit = CTX_data_edit_object(C); + const Mesh *mesh = static_cast(obedit->data); + const CustomDataLayer *layer = BKE_id_attributes_active_get(&const_cast(mesh->id)); + if (!layer) { + CTX_wm_operator_poll_msg_set(C, "There must be an active attribute"); + return false; + } + if (layer->type != CD_PROP_BOOL) { + CTX_wm_operator_poll_msg_set(C, "The active attribute must have a boolean type"); + return false; + } + if (BKE_id_attribute_domain(&mesh->id, layer) == ATTR_DOMAIN_CORNER) { + CTX_wm_operator_poll_msg_set( + C, "The active attribute must be on the vertex, edge, or face domain"); + return false; + } + return true; +} + +static std::optional domain_to_iter_type(const eAttrDomain domain) +{ + switch (domain) { + case ATTR_DOMAIN_POINT: + return BM_VERTS_OF_MESH; + case ATTR_DOMAIN_EDGE: + return BM_EDGES_OF_MESH; + case ATTR_DOMAIN_FACE: + return BM_FACES_OF_MESH; + default: + return std::nullopt; + } +} + +static int edbm_select_by_attribute_exec(bContext *C, wmOperator * /*op*/) +{ + const Scene *scene = CTX_data_scene(C); + 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( + scene, view_layer, CTX_wm_view3d(C), &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + Mesh *mesh = static_cast(obedit->data); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + + const CustomDataLayer *layer = BKE_id_attributes_active_get(&mesh->id); + if (!layer) { + continue; + } + if (layer->type != CD_PROP_BOOL) { + continue; + } + if (BKE_id_attribute_domain(&mesh->id, layer) == ATTR_DOMAIN_CORNER) { + continue; + } + const std::optional iter_type = domain_to_iter_type( + BKE_id_attribute_domain(&mesh->id, layer)); + if (!iter_type) { + continue; + } + + BMElem *elem; + BMIter iter; + BM_ITER_MESH (elem, &iter, bm, *iter_type) { + if (BM_ELEM_CD_GET_BOOL(elem, layer->offset)) { + BM_elem_select_set(bm, elem, true); + } + } + + EDBM_selectmode_flush(em); + + DEG_id_tag_update(static_cast(obedit->data), ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + } + MEM_freeN(objects); + + return OPERATOR_FINISHED; +} + +void MESH_OT_select_by_attribute(wmOperatorType *ot) +{ + ot->name = "Select by Attribute"; + ot->idname = "MESH_OT_select_by_attribute"; + ot->description = "Select elements based on the active boolean attribute"; + + ot->exec = edbm_select_by_attribute_exec; + ot->poll = edbm_select_by_attribute_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /** \} */ diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 8c45e66c71d..1e2911ea8d0 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -228,6 +228,7 @@ void MESH_OT_select_ungrouped(struct wmOperatorType *ot); void MESH_OT_select_axis(struct wmOperatorType *ot); void MESH_OT_region_to_loop(struct wmOperatorType *ot); void MESH_OT_loop_to_region(struct wmOperatorType *ot); +void MESH_OT_select_by_attribute(struct wmOperatorType *ot); void MESH_OT_shortest_path_select(struct wmOperatorType *ot); extern struct EnumPropertyItem *corner_type_items; diff --git a/source/blender/editors/mesh/mesh_ops.cc b/source/blender/editors/mesh/mesh_ops.cc index 7088dae2e06..252d31a4158 100644 --- a/source/blender/editors/mesh/mesh_ops.cc +++ b/source/blender/editors/mesh/mesh_ops.cc @@ -73,6 +73,7 @@ void ED_operatortypes_mesh() WM_operatortype_append(MESH_OT_edge_rotate); WM_operatortype_append(MESH_OT_shortest_path_select); WM_operatortype_append(MESH_OT_loop_to_region); + WM_operatortype_append(MESH_OT_select_by_attribute); WM_operatortype_append(MESH_OT_region_to_loop); WM_operatortype_append(MESH_OT_select_axis); -- 2.30.2 From 3240bd549a39e51b411d31f8d812d7963406de61 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 17 Oct 2023 09:23:53 +0200 Subject: [PATCH 2/2] Add "changed" check and don't affect hidden faces --- source/blender/editors/mesh/editmesh_select.cc | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_select.cc b/source/blender/editors/mesh/editmesh_select.cc index fbbf558e67e..511e7b287b3 100644 --- a/source/blender/editors/mesh/editmesh_select.cc +++ b/source/blender/editors/mesh/editmesh_select.cc @@ -5427,18 +5427,25 @@ static int edbm_select_by_attribute_exec(bContext *C, wmOperator * /*op*/) continue; } + bool changed = false; BMElem *elem; BMIter iter; BM_ITER_MESH (elem, &iter, bm, *iter_type) { + if (BM_elem_flag_test(elem, BM_ELEM_HIDDEN | BM_ELEM_SELECT)) { + continue; + } if (BM_ELEM_CD_GET_BOOL(elem, layer->offset)) { BM_elem_select_set(bm, elem, true); + changed = true; } } - EDBM_selectmode_flush(em); + if (changed) { + EDBM_selectmode_flush(em); - DEG_id_tag_update(static_cast(obedit->data), ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + DEG_id_tag_update(static_cast(obedit->data), ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + } } MEM_freeN(objects); -- 2.30.2