diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index be0e65f8e9b..7cdb190a66a 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -1902,6 +1902,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/blenkernel/BKE_mesh_legacy_convert.hh b/source/blender/blenkernel/BKE_mesh_legacy_convert.hh index e23221eea31..d5d9be43abe 100644 --- a/source/blender/blenkernel/BKE_mesh_legacy_convert.hh +++ b/source/blender/blenkernel/BKE_mesh_legacy_convert.hh @@ -72,7 +72,7 @@ void BKE_mesh_legacy_convert_polys_to_offsets(Mesh *mesh); void BKE_mesh_legacy_convert_loops_to_corners(Mesh *mesh); -void BKE_mesh_legacy_face_map_to_generic(Mesh *mesh); +void BKE_mesh_legacy_face_map_to_generic(Main *bmain); /** * Recreate #MFace Tessellation. diff --git a/source/blender/blenkernel/intern/mesh_legacy_convert.cc b/source/blender/blenkernel/intern/mesh_legacy_convert.cc index 9594a44f2c6..1af63be2b68 100644 --- a/source/blender/blenkernel/intern/mesh_legacy_convert.cc +++ b/source/blender/blenkernel/intern/mesh_legacy_convert.cc @@ -17,11 +17,13 @@ #include "DNA_object_types.h" #include "BLI_array_utils.hh" +#include "BLI_listbase.h" #include "BLI_map.hh" #include "BLI_math_geom.h" #include "BLI_math_matrix.h" #include "BLI_math_vector_types.hh" #include "BLI_memarena.h" +#include "BLI_multi_value_map.hh" #include "BLI_polyfill_2d.h" #include "BLI_resource_scope.hh" #include "BLI_string.h" @@ -31,6 +33,7 @@ #include "BKE_attribute.hh" #include "BKE_customdata.h" #include "BKE_global.h" +#include "BKE_main.h" #include "BKE_mesh.hh" #include "BKE_mesh_legacy_convert.hh" #include "BKE_multires.hh" @@ -1346,18 +1349,18 @@ void BKE_mesh_legacy_face_set_to_generic(Mesh *mesh) /** \name Face Map Conversion * \{ */ -void BKE_mesh_legacy_face_map_to_generic(Mesh *mesh) +static void move_face_map_data_to_attributes(Mesh *mesh) { using namespace blender; if (mesh->attributes().contains("face_maps")) { return; } - void *data = nullptr; + int *data = nullptr; const ImplicitSharingInfo *sharing_info = nullptr; for (const int i : IndexRange(mesh->face_data.totlayer)) { CustomDataLayer &layer = mesh->face_data.layers[i]; if (layer.type == CD_FACEMAP) { - data = layer.data; + data = static_cast(layer.data); sharing_info = layer.sharing_info; layer.data = nullptr; layer.sharing_info = nullptr; @@ -1365,13 +1368,53 @@ void BKE_mesh_legacy_face_map_to_generic(Mesh *mesh) break; } } - if (data != nullptr) { - CustomData_add_layer_named_with_data( - &mesh->face_data, CD_PROP_INT32, data, mesh->faces_num, "face_maps", sharing_info); + if (!data) { + return; } + + CustomData_add_layer_named_with_data( + &mesh->face_data, CD_PROP_INT32, data, mesh->faces_num, "face_maps", sharing_info); if (sharing_info != nullptr) { sharing_info->remove_user_and_delete_if_last(); } + + MultiValueMap groups; + for (const int i : IndexRange(mesh->faces_num)) { + if (data[i] == -1) { + /* -1 values "didn't have" a face map. */ + continue; + } + groups.add(data[i], i); + } + + bke::MutableAttributeAccessor attributes = mesh->attributes_for_write(); + for (const auto item : groups.items()) { + bke::SpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span( + ".temp_face_map_" + std::to_string(item.key), ATTR_DOMAIN_FACE); + if (attribute) { + attribute.span.fill_indices(item.value.as_span(), true); + attribute.finish(); + } + } +} + +void BKE_mesh_legacy_face_map_to_generic(Main *bmain) +{ + LISTBASE_FOREACH (Mesh *, mesh, &bmain->meshes) { + move_face_map_data_to_attributes(mesh); + } + + LISTBASE_FOREACH (Object *, object, &bmain->objects) { + if (object->type != OB_MESH) { + continue; + } + Mesh *mesh = static_cast(object->data); + int i; + LISTBASE_FOREACH_INDEX (bFaceMap *, face_map, &object->fmaps, i) { + mesh->attributes_for_write().rename(".temp_face_map_" + std::to_string(i), face_map->name); + } + BLI_freelistN(&object->fmaps); + } } /** \} */ diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 07d7c66c9a3..a162a0b1233 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -707,6 +707,7 @@ static void object_blend_read_data(BlendDataReader *reader, ID *id) /* Only for versioning, vertex group names are now stored on object data. */ BLO_read_list(reader, &ob->defbase); + BLO_read_list(reader, &ob->fmaps); /* XXX deprecated - old animation system <<< */ direct_link_nlastrips(reader, &ob->nlastrips); diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 57abf901ad8..7662f2ab4bf 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -311,6 +311,10 @@ void do_versions_after_linking_400(FileData *fd, Main *bmain) } } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 34)) { + BKE_mesh_legacy_face_map_to_generic(bmain); + } + /** * Versioning code until next subversion bump goes here. * @@ -1097,9 +1101,6 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) } if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 6)) { - LISTBASE_FOREACH (Mesh *, mesh, &bmain->meshes) { - BKE_mesh_legacy_face_map_to_generic(mesh); - } FOREACH_NODETREE_BEGIN (bmain, ntree, id) { versioning_replace_legacy_glossy_node(ntree); versioning_remove_microfacet_sharp_distribution(ntree); diff --git a/source/blender/editors/mesh/editmesh_select.cc b/source/blender/editors/mesh/editmesh_select.cc index 51fcef74cf9..32993d6b309 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,108 @@ 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; + } + + 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; + } + } + + 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); + } + } + 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); diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 4f8945d9964..27e71ac4ab4 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -60,6 +60,16 @@ typedef struct bDeformGroup { char flag, _pad0[7]; } bDeformGroup; +#ifdef DNA_DEPRECATED_ALLOW +typedef struct bFaceMap { + struct bFaceMap *next, *prev; + /** MAX_VGROUP_NAME. */ + char name[64]; + char flag; + char _pad0[7]; +} bFaceMap; +#endif + #define MAX_VGROUP_NAME 64 /** #bDeformGroup::flag */ @@ -349,6 +359,7 @@ typedef struct Object { ListBase constraintChannels DNA_DEPRECATED; /* XXX deprecated... old animation system */ ListBase effect DNA_DEPRECATED; /* XXX deprecated... keep for readfile */ ListBase defbase DNA_DEPRECATED; /* Only for versioning, moved to object data. */ + ListBase fmaps DNA_DEPRECATED; /* For versioning, moved to generic attributes. */ /** List of ModifierData structures. */ ListBase modifiers; /** List of GpencilModifierData structures. */