diff --git a/scripts/startup/bl_operators/geometry_nodes.py b/scripts/startup/bl_operators/geometry_nodes.py index 75f11611598..110f2ab3c16 100644 --- a/scripts/startup/bl_operators/geometry_nodes.py +++ b/scripts/startup/bl_operators/geometry_nodes.py @@ -230,6 +230,7 @@ class NewGeometryNodesModifier(Operator): return {'CANCELLED'} group = geometry_node_group_empty_new() + group.is_modifier = True modifier.node_group = group return {'FINISHED'} @@ -257,6 +258,7 @@ class NewGeometryNodeTreeAssign(Operator): if not modifier: return {'CANCELLED'} group = geometry_node_group_empty_new() + group.is_modifier = True modifier.node_group = group return {'FINISHED'} diff --git a/scripts/startup/bl_ui/properties_data_modifier.py b/scripts/startup/bl_ui/properties_data_modifier.py index 32cb44a0706..a66fc3a233e 100644 --- a/scripts/startup/bl_ui/properties_data_modifier.py +++ b/scripts/startup/bl_ui/properties_data_modifier.py @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -from bpy.types import Panel +from bpy.types import Panel, Menu class ModifierButtonsPanel: @@ -22,10 +22,151 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): def draw(self, _context): layout = self.layout - layout.operator_menu_enum("object.modifier_add", "type") + layout.operator("wm.call_menu", text="Add Modifier", icon='ADD').name="OBJECT_MT_modifier_add" layout.template_modifiers() +class OBJECT_MT_modifier_add(Menu): + bl_label = "Add Modifier" + + def draw(self, context): + layout = self.layout + ob_type = context.object.type + geometry_nodes_supported = ob_type in {'MESH', 'CURVE', 'CURVES', 'FONT', 'SURFACE', 'VOLUME', 'POINTCLOUD'} + if geometry_nodes_supported: + layout.operator("object.modifier_add", text="Empty Modifier").type='NODES' + if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}: + layout.menu("OBJECT_MT_modifier_add_edit") + if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'VOLUME'}: + layout.menu("OBJECT_MT_modifier_add_generate") + if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE', 'VOLUME'}: + layout.menu("OBJECT_MT_modifier_add_deform") + if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}: + layout.menu("OBJECT_MT_modifier_add_physics") + + if geometry_nodes_supported: + layout.menu_contents("OBJECT_MT_modifier_add_root_catalogs") + + +class OBJECT_MT_modifier_add_edit(Menu): + bl_label = "Edit" + + def draw(self, context): + layout = self.layout + ob_type = context.object.type + if ob_type == 'MESH': + layout.operator("object.modifier_add", text="Data Transfer", icon='MOD_DATA_TRANSFER').type='DATA_TRANSFER' + if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}: + layout.operator("object.modifier_add", text="Mesh Cache", icon='MOD_MESHDEFORM').type='MESH_CACHE' + if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}: + layout.operator("object.modifier_add", text="Mesh Sequence Cache", icon='MOD_MESHDEFORM').type='MESH_SEQUENCE_CACHE' + if ob_type == 'MESH': + layout.operator("object.modifier_add", text="Normal Edit", icon='MOD_NORMALEDIT').type='NORMAL_EDIT' + layout.operator("object.modifier_add", text="Weighted Normal", icon='MOD_NORMALEDIT').type='WEIGHTED_NORMAL' + layout.operator("object.modifier_add", text="UV Project", icon='MOD_UVPROJECT').type='UV_PROJECT' + layout.operator("object.modifier_add", text="UV Warp", icon='MOD_UVPROJECT').type='UV_WARP' + layout.operator("object.modifier_add", text="Vertex Weight Edit", icon='MOD_VERTEX_WEIGHT').type='VERTEX_WEIGHT_EDIT' + layout.operator("object.modifier_add", text="Vertex Weight Mix", icon='MOD_VERTEX_WEIGHT').type='VERTEX_WEIGHT_MIX' + layout.operator("object.modifier_add", text="Vertex Weight Proximity", icon='MOD_VERTEX_WEIGHT').type='VERTEX_WEIGHT_PROXIMITY' + layout.template_modifier_asset_menu_items(catalog_path=self.bl_label) + + +class OBJECT_MT_modifier_add_generate(Menu): + bl_label = "Generate" + + def draw(self, context): + layout = self.layout + ob_type = context.object.type + if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}: + layout.operator("object.modifier_add", text="Array", icon='MOD_ARRAY').type='ARRAY' + layout.operator("object.modifier_add", text="Bevel", icon='MOD_BEVEL').type='BEVEL' + if ob_type == 'MESH': + layout.operator("object.modifier_add", text="Boolean", icon='MOD_BOOLEAN').type='BOOLEAN' + if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}: + layout.operator("object.modifier_add", text="Build", icon='MOD_BUILD').type='BUILD' + layout.operator("object.modifier_add", text="Decimate", icon='MOD_DECIM').type='DECIMATE' + layout.operator("object.modifier_add", text="Edge Split", icon='MOD_EDGESPLIT').type='EDGE_SPLIT' + if ob_type == 'MESH': + layout.operator("object.modifier_add", text="Mask", icon='MOD_MASK').type='MASK' + if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}: + layout.operator("object.modifier_add", text="Mirror", icon='MOD_MIRROR').type='MIRROR' + if ob_type == 'VOLUME': + layout.operator("object.modifier_add", text="Mesh to Volume", icon='VOLUME_DATA').type='MESH_TO_VOLUME' + if ob_type == 'MESH': + layout.operator("object.modifier_add", text="Multiresolution", icon='MOD_MULTIRES').type='MULTIRES' + if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}: + layout.operator("object.modifier_add", text="Remesh", icon='MOD_REMESH').type='REMESH' + layout.operator("object.modifier_add", text="Screw", icon='MOD_SCREW').type='SCREW' + if ob_type == 'MESH': + layout.operator("object.modifier_add", text="Skin", icon='MOD_SKIN').type='SKIN' + if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}: + layout.operator("object.modifier_add", text="Solidify", icon='MOD_SOLIDIFY').type='SOLIDIFY' + layout.operator("object.modifier_add", text="Subdivision Surface", icon='MOD_SUBSURF').type='SUBSURF' + layout.operator("object.modifier_add", text="Triangulate", icon='MOD_TRIANGULATE').type='TRIANGULATE' + if ob_type == 'MESH': + layout.operator("object.modifier_add", text="Volume to Mesh", icon='VOLUME_DATA').type='VOLUME_TO_MESH' + if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}: + layout.operator("object.modifier_add", text="Weld", icon='AUTOMERGE_OFF').type='WELD' + if ob_type == 'MESH': + layout.operator("object.modifier_add", text="Wireframe", icon='MOD_WIREFRAME').type='WIREFRAME' + layout.template_modifier_asset_menu_items(catalog_path=self.bl_label) + + +class OBJECT_MT_modifier_add_deform(Menu): + bl_label = "Deform" + + def draw(self, context): + layout = self.layout + ob_type = context.object.type + if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}: + layout.operator("object.modifier_add", text="Armature", icon='MOD_ARMATURE').type='ARMATURE' + layout.operator("object.modifier_add", text="Cast", icon='MOD_CAST').type='CAST' + layout.operator("object.modifier_add", text="Curve", icon='MOD_CURVE').type='CURVE' + if ob_type == 'MESH': + layout.operator("object.modifier_add", text="Displace", icon='MOD_DISPLACE').type='DISPLACE' + if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}: + layout.operator("object.modifier_add", text="Hook", icon='HOOK').type='HOOK' + if ob_type == 'MESH': + layout.operator("object.modifier_add", text="Laplacian Deform", icon='MOD_MESHDEFORM').type='LAPLACIANDEFORM' + if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}: + layout.operator("object.modifier_add", text="Lattice", icon='MOD_LATTICE').type='LATTICE' + layout.operator("object.modifier_add", text="Mesh Deform", icon='MOD_MESHDEFORM').type='MESH_DEFORM' + layout.operator("object.modifier_add", text="Shrinkwrap", icon='MOD_SHRINKWRAP').type='SHRINKWRAP' + layout.operator("object.modifier_add", text="Simple Deform", icon='MOD_SIMPLEDEFORM').type='SIMPLE_DEFORM' + if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}: + layout.operator("object.modifier_add", text="Smooth", icon='MOD_SMOOTH').type='SMOOTH' + if ob_type == 'MESH': + layout.operator("object.modifier_add", text="Smooth Corrective", icon='MOD_SMOOTH').type='CORRECTIVE_SMOOTH' + layout.operator("object.modifier_add", text="Smooth Laplacian", icon='MOD_SMOOTH').type='LAPLACIANSMOOTH' + layout.operator("object.modifier_add", text="Surface Deform", icon='MOD_MESHDEFORM').type='SURFACE_DEFORM' + if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}: + layout.operator("object.modifier_add", text="Warp", icon='MOD_WARP').type='WARP' + layout.operator("object.modifier_add", text="Wave", icon='MOD_WAVE').type='WAVE' + if ob_type == 'VOLUME': + layout.operator("object.modifier_add", text="Volume Displace", icon='VOLUME_DATA').type='VOLUME_DISPLACE' + layout.template_modifier_asset_menu_items(catalog_path=self.bl_label) + + +class OBJECT_MT_modifier_add_physics(Menu): + bl_label = "Physics" + + def draw(self, context): + layout = self.layout + ob_type = context.object.type + if ob_type == 'MESH': + layout.operator("object.modifier_add", text="Cloth", icon='MOD_CLOTH').type='CLOTH' + layout.operator("object.modifier_add", text="Collision", icon='MOD_PHYSICS').type='COLLISION' + layout.operator("object.modifier_add", text="Dynamic Paint", icon='MOD_DYNAMICPAINT').type='DYNAMIC_PAINT' + layout.operator("object.modifier_add", text="Explode", icon='MOD_EXPLODE').type='EXPLODE' + layout.operator("object.modifier_add", text="Fluid", icon='MOD_FLUIDSIM').type='FLUID' + layout.operator("object.modifier_add", text="Ocean", icon='MOD_OCEAN').type='OCEAN' + layout.operator("object.modifier_add", text="Particle Instance", icon='MOD_PARTICLE_INSTANCE').type='PARTICLE_INSTANCE' + layout.operator("object.modifier_add", text="Particle System", icon='MOD_PARTICLES').type='PARTICLE_SYSTEM' + if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}: + layout.operator("object.modifier_add", text="Soft Body", icon='MOD_SOFT').type='SOFT_BODY' + layout.template_modifier_asset_menu_items(catalog_path=self.bl_label) + + class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): bl_label = "Modifiers" @@ -42,6 +183,11 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): classes = ( DATA_PT_modifiers, + OBJECT_MT_modifier_add, + OBJECT_MT_modifier_add_edit, + OBJECT_MT_modifier_add_generate, + OBJECT_MT_modifier_add_deform, + OBJECT_MT_modifier_add_physics, DATA_PT_gpencil_modifiers, ) diff --git a/scripts/startup/bl_ui/space_node.py b/scripts/startup/bl_ui/space_node.py index a36449d5d85..45c597db07e 100644 --- a/scripts/startup/bl_ui/space_node.py +++ b/scripts/startup/bl_ui/space_node.py @@ -160,6 +160,8 @@ class NODE_HT_header(Header): row.template_ID(active_modifier, "node_group", new="node.new_geometry_node_group_assign") else: row.template_ID(snode, "node_tree", new="node.new_geometry_nodes_modifier") + if snode.node_tree and snode.node_tree.asset_data: + layout.popover(panel="NODE_PT_geometry_node_asset_traits") else: layout.template_ID(snode, "node_tree", new="node.new_geometry_node_group_tool") if snode.node_tree and snode.node_tree.asset_data: @@ -448,18 +450,21 @@ class NODE_PT_geometry_node_asset_traits(Panel): snode = context.space_data group = snode.node_tree - col = layout.column(heading="Type") - col.prop(group, "is_tool") - col = layout.column(heading="Mode") - col.active = group.is_tool - col.prop(group, "is_mode_edit") - col.prop(group, "is_mode_sculpt") - col = layout.column(heading="Geometry") - col.active = group.is_tool - col.prop(group, "is_type_mesh") - col.prop(group, "is_type_curve") - if context.preferences.experimental.use_new_point_cloud_type: - col.prop(group, "is_type_point_cloud") + if snode.geometry_nodes_type == 'MODIFIER': + layout.prop(group, "is_modifier") + else: + col = layout.column(heading="Type") + col.prop(group, "is_tool") + col = layout.column(heading="Mode") + col.active = group.is_tool + col.prop(group, "is_mode_edit") + col.prop(group, "is_mode_sculpt") + col = layout.column(heading="Geometry") + col.active = group.is_tool + col.prop(group, "is_type_mesh") + col.prop(group, "is_type_curve") + if context.preferences.experimental.use_new_point_cloud_type: + col.prop(group, "is_type_point_cloud") class NODE_PT_node_color_presets(PresetPanel, Panel): diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt index f1ecc6ac1c5..0d66dfc1994 100644 --- a/source/blender/editors/asset/CMakeLists.txt +++ b/source/blender/editors/asset/CMakeLists.txt @@ -31,6 +31,7 @@ set(SRC intern/asset_library_reference_enum.cc intern/asset_list.cc intern/asset_mark_clear.cc + intern/asset_menu_utils.cc intern/asset_ops.cc intern/asset_shelf.cc intern/asset_shelf_asset_view.cc diff --git a/source/blender/editors/asset/ED_asset_catalog.hh b/source/blender/editors/asset/ED_asset_catalog.hh index 7a848dc31ee..4d7495e2b73 100644 --- a/source/blender/editors/asset/ED_asset_catalog.hh +++ b/source/blender/editors/asset/ED_asset_catalog.hh @@ -26,10 +26,6 @@ struct AssetLibrary; struct bScreen; -namespace blender::asset_system { -class AssetCatalogTreeItem; -} - /** * Returns if the catalogs of \a library are allowed to be editable, or if the UI should forbid * edits. @@ -59,16 +55,3 @@ void ED_asset_catalog_move( AssetLibrary *library, blender::asset_system::CatalogID src_catalog_id, std::optional dst_parent_catalog_id = std::nullopt); - -namespace blender::ed::asset { - -/** - * Some code needs to pass catalog paths to context and for this they need persistent pointers to - * the paths. Rather than keeping some local path storage, get a pointer into the asset system - * directly, which is persistent until the library is reloaded and can safely be held by context. - */ -PointerRNA persistent_catalog_path_rna_pointer(const bScreen &owner_screen, - const asset_system::AssetLibrary &library, - const asset_system::AssetCatalogTreeItem &item); - -} // namespace blender::ed::asset diff --git a/source/blender/editors/asset/ED_asset_handle.h b/source/blender/editors/asset/ED_asset_handle.h index 2dbc1b81dd2..2506dc15ab6 100644 --- a/source/blender/editors/asset/ED_asset_handle.h +++ b/source/blender/editors/asset/ED_asset_handle.h @@ -47,13 +47,3 @@ void ED_asset_handle_get_full_library_path( #ifdef __cplusplus } #endif - -#ifdef __cplusplus - -namespace blender::ed::asset { - -PointerRNA create_asset_rna_ptr(const asset_system::AssetRepresentation *asset); - -} - -#endif diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc index 8d17ef5478a..5a139ccb6a9 100644 --- a/source/blender/editors/asset/intern/asset_catalog.cc +++ b/source/blender/editors/asset/intern/asset_catalog.cc @@ -206,23 +206,3 @@ bool ED_asset_catalogs_get_save_catalogs_when_file_is_saved() { return asset_system::AssetLibrary::save_catalogs_when_file_is_saved; } - -namespace blender::ed::asset { - -PointerRNA persistent_catalog_path_rna_pointer(const bScreen &owner_screen, - const asset_system::AssetLibrary &library, - const asset_system::AssetCatalogTreeItem &item) -{ - const asset_system::AssetCatalog *catalog = library.catalog_service->find_catalog_by_path( - item.catalog_path()); - if (!catalog) { - return PointerRNA_NULL; - } - - const asset_system::AssetCatalogPath &path = catalog->path; - return {&const_cast(owner_screen.id), - &RNA_AssetCatalogPath, - const_cast(&path)}; -} - -} // namespace blender::ed::asset diff --git a/source/blender/editors/asset/intern/asset_handle.cc b/source/blender/editors/asset/intern/asset_handle.cc index dedffe94202..684ef1a0b52 100644 --- a/source/blender/editors/asset/intern/asset_handle.cc +++ b/source/blender/editors/asset/intern/asset_handle.cc @@ -57,16 +57,3 @@ void ED_asset_handle_get_full_library_path(const AssetHandle *asset_handle, BLI_strncpy(r_full_lib_path, library_path.c_str(), FILE_MAX); } - -namespace blender::ed::asset { - -PointerRNA create_asset_rna_ptr(const asset_system::AssetRepresentation *asset) -{ - PointerRNA ptr{}; - ptr.owner_id = nullptr; - ptr.type = &RNA_AssetRepresentation; - ptr.data = const_cast(asset); - return ptr; -} - -} // namespace blender::ed::asset diff --git a/source/blender/editors/asset/intern/asset_menu_utils.cc b/source/blender/editors/asset/intern/asset_menu_utils.cc new file mode 100644 index 00000000000..69a53d2bffd --- /dev/null +++ b/source/blender/editors/asset/intern/asset_menu_utils.cc @@ -0,0 +1,186 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edasset + */ + +#include "AS_asset_catalog.hh" +#include "AS_asset_catalog_tree.hh" +#include "AS_asset_library.hh" +#include "AS_asset_representation.hh" + +#include "DNA_screen_types.h" + +#include "BKE_asset.h" +#include "BKE_report.h" + +#include "BLT_translation.h" + +#include "WM_api.hh" + +#include "RNA_access.hh" +#include "RNA_define.hh" +#include "RNA_enum_types.hh" +#include "RNA_prototypes.h" + +#include "ED_asset_list.h" +#include "ED_asset_list.hh" +#include "ED_asset_menu_utils.hh" + +#include "UI_interface.hh" + +namespace blender::ed::asset { + +void operator_asset_reference_props_register(StructRNA &srna) +{ + PropertyRNA *prop; + prop = RNA_def_enum(&srna, + "asset_library_type", + rna_enum_aset_library_type_items, + ASSET_LIBRARY_LOCAL, + "Asset Library Type", + ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_string( + &srna, "asset_library_identifier", nullptr, 0, "Asset Library Identifier", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_string( + &srna, "relative_asset_identifier", nullptr, 0, "Relative Asset Identifier", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} + +void operator_asset_reference_props_set(const asset_system::AssetRepresentation &asset, + PointerRNA &ptr) +{ + AssetWeakReference *weak_ref = asset.make_weak_reference(); + RNA_enum_set(&ptr, "asset_library_type", weak_ref->asset_library_type); + RNA_string_set(&ptr, "asset_library_identifier", weak_ref->asset_library_identifier); + RNA_string_set(&ptr, "relative_asset_identifier", weak_ref->relative_asset_identifier); + BKE_asset_weak_reference_free(&weak_ref); +} + +/** + * #AssetLibrary::resolve_asset_weak_reference_to_full_path() currently does not support local + * assets. + */ +static const asset_system::AssetRepresentation *get_local_asset_from_relative_identifier( + const bContext &C, const StringRefNull relative_identifier, ReportList *reports) +{ + AssetLibraryReference library_ref{}; + library_ref.type = ASSET_LIBRARY_LOCAL; + ED_assetlist_storage_fetch(&library_ref, &C); + ED_assetlist_ensure_previews_job(&library_ref, &C); + + const asset_system::AssetRepresentation *matching_asset = nullptr; + ED_assetlist_iterate(library_ref, [&](asset_system::AssetRepresentation &asset) { + if (asset.get_identifier().library_relative_identifier() == relative_identifier) { + matching_asset = &asset; + return false; + } + return true; + }); + + if (reports && !matching_asset) { + if (ED_assetlist_is_loaded(&library_ref)) { + BKE_reportf( + reports, RPT_ERROR, "No asset found at path \"%s\"", relative_identifier.c_str()); + } + else { + BKE_report(reports, RPT_WARNING, "Asset loading is unfinished"); + } + } + return matching_asset; +} + +static const asset_system::AssetRepresentation *find_asset_from_weak_ref( + const bContext &C, const AssetWeakReference &weak_ref, ReportList *reports) +{ + if (weak_ref.asset_library_type == ASSET_LIBRARY_LOCAL) { + return get_local_asset_from_relative_identifier( + C, weak_ref.relative_asset_identifier, reports); + } + + const AssetLibraryReference library_ref = asset_system::all_library_reference(); + ED_assetlist_storage_fetch(&library_ref, &C); + ED_assetlist_ensure_previews_job(&library_ref, &C); + asset_system::AssetLibrary *all_library = ED_assetlist_library_get_once_available( + asset_system::all_library_reference()); + if (!all_library) { + BKE_report(reports, RPT_WARNING, "Asset loading is unfinished"); + } + + const std::string full_path = all_library->resolve_asset_weak_reference_to_full_path(weak_ref); + + const asset_system::AssetRepresentation *matching_asset = nullptr; + ED_assetlist_iterate(library_ref, [&](asset_system::AssetRepresentation &asset) { + if (asset.get_identifier().full_path() == full_path) { + matching_asset = &asset; + return false; + } + return true; + }); + + if (reports && !matching_asset) { + if (ED_assetlist_is_loaded(&library_ref)) { + BKE_reportf(reports, RPT_ERROR, "No asset found at path \"%s\"", full_path.c_str()); + } + } + return matching_asset; +} + +const asset_system::AssetRepresentation *operator_asset_reference_props_get_asset_from_all_library( + const bContext &C, PointerRNA &ptr, ReportList *reports) +{ + AssetWeakReference weak_ref{}; + weak_ref.asset_library_type = RNA_enum_get(&ptr, "asset_library_type"); + weak_ref.asset_library_identifier = RNA_string_get_alloc( + &ptr, "asset_library_identifier", nullptr, 0, nullptr); + weak_ref.relative_asset_identifier = RNA_string_get_alloc( + &ptr, "relative_asset_identifier", nullptr, 0, nullptr); + return find_asset_from_weak_ref(C, weak_ref, reports); +} + +PointerRNA persistent_catalog_path_rna_pointer(const bScreen &owner_screen, + const asset_system::AssetLibrary &library, + const asset_system::AssetCatalogTreeItem &item) +{ + const asset_system::AssetCatalog *catalog = library.catalog_service->find_catalog_by_path( + item.catalog_path()); + if (!catalog) { + return PointerRNA_NULL; + } + + const asset_system::AssetCatalogPath &path = catalog->path; + return {&const_cast(owner_screen.id), + &RNA_AssetCatalogPath, + const_cast(&path)}; +} + +PointerRNA create_asset_rna_ptr(const asset_system::AssetRepresentation *asset) +{ + PointerRNA ptr{}; + ptr.owner_id = nullptr; + ptr.type = &RNA_AssetRepresentation; + ptr.data = const_cast(asset); + return ptr; +} + +void draw_menu_for_catalog(const bScreen &owner_screen, + const asset_system::AssetLibrary &library, + const asset_system::AssetCatalogTreeItem &item, + const StringRefNull menu_name, + uiLayout &layout) +{ + PointerRNA path_ptr = asset::persistent_catalog_path_rna_pointer(owner_screen, library, item); + if (path_ptr.data == nullptr) { + return; + } + + uiLayout *col = uiLayoutColumn(&layout, false); + uiLayoutSetContextPointer(col, "asset_catalog_path", &path_ptr); + uiItemM(col, menu_name.c_str(), IFACE_(item.get_name().c_str()), ICON_NONE); +} + +} // namespace blender::ed::asset diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index fb3b2704676..85ad911f3af 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -10,6 +10,7 @@ #include "AS_asset_library.hh" #include "AS_asset_representation.hh" +#include "BKE_asset.h" #include "BKE_bpath.h" #include "BKE_context.h" #include "BKE_lib_id.h" @@ -33,6 +34,7 @@ #include "RNA_access.hh" #include "RNA_define.hh" +#include "RNA_enum_types.hh" #include "RNA_prototypes.h" #include "WM_api.hh" diff --git a/source/blender/editors/geometry/node_group_operator.cc b/source/blender/editors/geometry/node_group_operator.cc index 048e7873542..14ea9848014 100644 --- a/source/blender/editors/geometry/node_group_operator.cc +++ b/source/blender/editors/geometry/node_group_operator.cc @@ -42,12 +42,12 @@ #include "RNA_access.hh" #include "RNA_define.hh" -#include "RNA_enum_types.hh" #include "UI_interface.hh" #include "UI_resources.hh" #include "ED_asset.hh" +#include "ED_asset_menu_utils.hh" #include "ED_geometry.hh" #include "ED_mesh.hh" @@ -72,92 +72,10 @@ namespace blender::ed::geometry { /** \name Operator * \{ */ -/** - * #AssetLibrary::resolve_asset_weak_reference_to_full_path() currently does not support local - * assets. - */ -static const asset_system::AssetRepresentation *get_local_asset_from_relative_identifier( - const bContext &C, const StringRefNull relative_identifier, ReportList *reports) -{ - AssetLibraryReference library_ref{}; - library_ref.type = ASSET_LIBRARY_LOCAL; - ED_assetlist_storage_fetch(&library_ref, &C); - ED_assetlist_ensure_previews_job(&library_ref, &C); - - const asset_system::AssetRepresentation *matching_asset = nullptr; - ED_assetlist_iterate(library_ref, [&](asset_system::AssetRepresentation &asset) { - if (asset.get_identifier().library_relative_identifier() == relative_identifier) { - matching_asset = &asset; - return false; - } - return true; - }); - - if (reports && !matching_asset) { - if (ED_assetlist_is_loaded(&library_ref)) { - BKE_reportf( - reports, RPT_ERROR, "No asset found at path \"%s\"", relative_identifier.c_str()); - } - else { - BKE_report(reports, RPT_WARNING, "Asset loading is unfinished"); - } - } - return matching_asset; -} - -static const asset_system::AssetRepresentation *find_asset_from_weak_ref( - const bContext &C, const AssetWeakReference &weak_ref, ReportList *reports) -{ - if (weak_ref.asset_library_type == ASSET_LIBRARY_LOCAL) { - return get_local_asset_from_relative_identifier( - C, weak_ref.relative_asset_identifier, reports); - } - - const AssetLibraryReference library_ref = asset_system::all_library_reference(); - ED_assetlist_storage_fetch(&library_ref, &C); - ED_assetlist_ensure_previews_job(&library_ref, &C); - asset_system::AssetLibrary *all_library = ED_assetlist_library_get_once_available( - asset_system::all_library_reference()); - if (!all_library) { - BKE_report(reports, RPT_WARNING, "Asset loading is unfinished"); - } - - const std::string full_path = all_library->resolve_asset_weak_reference_to_full_path(weak_ref); - - const asset_system::AssetRepresentation *matching_asset = nullptr; - ED_assetlist_iterate(library_ref, [&](asset_system::AssetRepresentation &asset) { - if (asset.get_identifier().full_path() == full_path) { - matching_asset = &asset; - return false; - } - return true; - }); - - if (reports && !matching_asset) { - if (ED_assetlist_is_loaded(&library_ref)) { - BKE_reportf(reports, RPT_ERROR, "No asset found at path \"%s\"", full_path.c_str()); - } - } - return matching_asset; -} - -/** \note Does not check asset type or meta data. */ -static const asset_system::AssetRepresentation *get_asset(const bContext &C, - PointerRNA &ptr, - ReportList *reports) -{ - AssetWeakReference weak_ref{}; - weak_ref.asset_library_type = RNA_enum_get(&ptr, "asset_library_type"); - weak_ref.asset_library_identifier = RNA_string_get_alloc( - &ptr, "asset_library_identifier", nullptr, 0, nullptr); - weak_ref.relative_asset_identifier = RNA_string_get_alloc( - &ptr, "relative_asset_identifier", nullptr, 0, nullptr); - return find_asset_from_weak_ref(C, weak_ref, reports); -} - static const bNodeTree *get_node_group(const bContext &C, PointerRNA &ptr, ReportList *reports) { - const asset_system::AssetRepresentation *asset = get_asset(C, ptr, reports); + const asset_system::AssetRepresentation *asset = + asset::operator_asset_reference_props_get_asset_from_all_library(C, ptr, reports); if (!asset) { return nullptr; } @@ -376,7 +294,8 @@ static std::string run_node_group_get_description(bContext *C, wmOperatorType * /*ot*/, PointerRNA *ptr) { - const asset_system::AssetRepresentation *asset = get_asset(*C, *ptr, nullptr); + const asset_system::AssetRepresentation *asset = + asset::operator_asset_reference_props_get_asset_from_all_library(*C, *ptr, nullptr); if (!asset) { return ""; } @@ -534,20 +453,7 @@ void GEOMETRY_OT_execute_node_group(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - PropertyRNA *prop; - prop = RNA_def_enum(ot->srna, - "asset_library_type", - rna_enum_aset_library_type_items, - ASSET_LIBRARY_LOCAL, - "Asset Library Type", - ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - prop = RNA_def_string( - ot->srna, "asset_library_identifier", nullptr, 0, "Asset Library Identifier", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - prop = RNA_def_string( - ot->srna, "relative_asset_identifier", nullptr, 0, "Relative Asset Identifier", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + asset::operator_asset_reference_props_register(*ot->srna); } /** \} */ @@ -706,7 +612,6 @@ static void node_add_catalog_assets_draw(const bContext *C, Menu *menu) for (const asset_system::AssetRepresentation *asset : assets) { uiLayout *col = uiLayoutColumn(layout, false); wmOperatorType *ot = WM_operatortype_find("GEOMETRY_OT_execute_node_group", true); - AssetWeakReference *weak_ref = asset->make_weak_reference(); PointerRNA props_ptr; uiItemFullO_ptr(col, ot, @@ -716,11 +621,7 @@ static void node_add_catalog_assets_draw(const bContext *C, Menu *menu) WM_OP_INVOKE_DEFAULT, UI_ITEM_NONE, &props_ptr); - RNA_enum_set(&props_ptr, "asset_library_type", weak_ref->asset_library_type); - RNA_string_set(&props_ptr, "asset_library_identifier", weak_ref->asset_library_identifier); - RNA_string_set(&props_ptr, "relative_asset_identifier", weak_ref->relative_asset_identifier); - - BKE_asset_weak_reference_free(&weak_ref); + asset::operator_asset_reference_props_set(*asset, props_ptr); } asset_system::AssetLibrary *all_library = ED_assetlist_library_get_once_available( @@ -729,18 +630,9 @@ static void node_add_catalog_assets_draw(const bContext *C, Menu *menu) return; } - catalog_item->foreach_child([&](asset_system::AssetCatalogTreeItem &child_item) { - PointerRNA path_ptr = asset::persistent_catalog_path_rna_pointer( - screen, *all_library, child_item); - if (path_ptr.data == nullptr) { - return; - } - uiLayout *col = uiLayoutColumn(layout, false); - uiLayoutSetContextPointer(col, "asset_catalog_path", &path_ptr); - uiItemM(col, - "GEO_MT_node_operator_catalog_assets", - IFACE_(child_item.get_name().c_str()), - ICON_NONE); + catalog_item->foreach_child([&](asset_system::AssetCatalogTreeItem &item) { + asset::draw_menu_for_catalog( + screen, *all_library, item, "GEO_MT_node_operator_catalog_assets", *layout); }); } @@ -809,17 +701,10 @@ void ui_template_node_operator_asset_root_items(uiLayout &layout, bContext &C) eObjectMode(active_object->mode)); tree->catalogs.foreach_root_item([&](asset_system::AssetCatalogTreeItem &item) { - if (builtin_menus.contains(item.get_name())) { - return; + if (!builtin_menus.contains(item.get_name())) { + asset::draw_menu_for_catalog( + screen, *all_library, item, "GEO_MT_node_operator_catalog_assets", layout); } - PointerRNA path_ptr = asset::persistent_catalog_path_rna_pointer(screen, *all_library, item); - if (path_ptr.data == nullptr) { - return; - } - uiLayout *col = uiLayoutColumn(&layout, false); - uiLayoutSetContextPointer(col, "asset_catalog_path", &path_ptr); - const char *text = IFACE_(item.get_name().c_str()); - uiItemM(col, "GEO_MT_node_operator_catalog_assets", text, ICON_NONE); }); } diff --git a/source/blender/editors/include/ED_asset_menu_utils.hh b/source/blender/editors/include/ED_asset_menu_utils.hh new file mode 100644 index 00000000000..91860c72b0a --- /dev/null +++ b/source/blender/editors/include/ED_asset_menu_utils.hh @@ -0,0 +1,60 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edasset + * + * Code for dealing with dynamic asset menus and passing assets to operators with RNA properties. + */ + +#pragma once + +#include "BLI_string_ref.hh" + +#include "RNA_types.hh" + +struct AssetLibrary; +struct bScreen; +struct uiLayout; + +namespace blender::asset_system { +class AssetCatalogTreeItem; +class AssetLibrary; +class AssetRepresentation; +} // namespace blender::asset_system + +namespace blender::ed::asset { + +/** + * Some code needs to pass catalog paths to context and for this they need persistent pointers to + * the paths. Rather than keeping some local path storage, get a pointer into the asset system + * directly, which is persistent until the library is reloaded and can safely be held by context. + */ +PointerRNA persistent_catalog_path_rna_pointer(const bScreen &owner_screen, + const asset_system::AssetLibrary &library, + const asset_system::AssetCatalogTreeItem &item); + +void draw_menu_for_catalog(const bScreen &owner_screen, + const asset_system::AssetLibrary &library, + const asset_system::AssetCatalogTreeItem &item, + StringRefNull menu_name, + uiLayout &layout); + +PointerRNA create_asset_rna_ptr(const asset_system::AssetRepresentation *asset); + +void operator_asset_reference_props_set(const asset_system::AssetRepresentation &asset, + PointerRNA &ptr); +void operator_asset_reference_props_register(StructRNA &srna); + +/** + * Load all asset libraries to find an asset from the #operator_asset_reference_props_register + * properties. The loading happens in the background, so there may be no result immediately. In + * that case an "Asset loading is unfinished" report is added. + * + * \note Does not check asset type or meta data. + */ +const asset_system::AssetRepresentation *operator_asset_reference_props_get_asset_from_all_library( + const bContext &C, PointerRNA &ptr, ReportList *reports); + +} // namespace blender::ed::asset diff --git a/source/blender/editors/include/ED_object.hh b/source/blender/editors/include/ED_object.hh index 6e8a0a28fca..44cf76814a3 100644 --- a/source/blender/editors/include/ED_object.hh +++ b/source/blender/editors/include/ED_object.hh @@ -9,6 +9,8 @@ #pragma once #include "BLI_compiler_attrs.h" +#include "BLI_string_ref.hh" + #include "DNA_object_enums.h" #include "DNA_userdef_enums.h" #include "DNA_windowmanager_types.h" @@ -636,3 +638,9 @@ void ED_object_data_xform_by_mat4(XFormObjectData *xod, const float mat[4][4]); void ED_object_data_xform_restore(XFormObjectData *xod); void ED_object_data_xform_tag_update(XFormObjectData *xod); + +namespace blender::ed::object { + +void ui_template_modifier_asset_menu_items(uiLayout &layout, bContext &C, StringRef catalog_path); + +} diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index f1085ce21f1..d3d1fe4da90 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -4,6 +4,7 @@ set(INC ../include + ../../asset_system ../../blenfont ../../blenkernel ../../blentranslation @@ -30,6 +31,7 @@ set(INC_SYS ) set(SRC + add_modifier_assets.cc object_add.cc object_bake.cc object_bake_api.cc diff --git a/source/blender/editors/object/add_modifier_assets.cc b/source/blender/editors/object/add_modifier_assets.cc new file mode 100644 index 00000000000..4222410b980 --- /dev/null +++ b/source/blender/editors/object/add_modifier_assets.cc @@ -0,0 +1,314 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "AS_asset_catalog.hh" +#include "AS_asset_catalog_tree.hh" +#include "AS_asset_library.hh" +#include "AS_asset_representation.hh" + +#include "BLI_multi_value_map.hh" +#include "BLI_string.h" + +#include "DNA_modifier_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "BKE_asset.h" +#include "BKE_context.h" +#include "BKE_idprop.h" +#include "BKE_lib_id.h" +#include "BKE_report.h" +#include "BKE_screen.h" + +#include "BLT_translation.h" + +#include "RNA_access.hh" + +#include "ED_asset.hh" +#include "ED_asset_menu_utils.hh" +#include "ED_object.hh" +#include "ED_screen.hh" + +#include "MOD_nodes.hh" + +#include "UI_interface.hh" + +#include "WM_api.hh" + +#include "object_intern.h" + +namespace blender::ed::object { + +static bool all_loading_finished() +{ + AssetLibraryReference all_library_ref = asset_system::all_library_reference(); + return ED_assetlist_is_loaded(&all_library_ref); +} + +static asset::AssetItemTree build_catalog_tree(const bContext &C) +{ + AssetFilterSettings type_filter{}; + type_filter.id_types = FILTER_ID_NT; + auto meta_data_filter = [&](const AssetMetaData &meta_data) { + const IDProperty *tree_type = BKE_asset_metadata_idprop_find(&meta_data, "type"); + if (tree_type == nullptr || IDP_Int(tree_type) != NTREE_GEOMETRY) { + return false; + } + const IDProperty *traits_flag = BKE_asset_metadata_idprop_find( + &meta_data, "geometry_node_asset_traits_flag"); + if (traits_flag == nullptr || !(IDP_Int(traits_flag) & GEO_NODE_ASSET_MODIFIER)) { + return false; + } + return true; + }; + const AssetLibraryReference library = asset_system::all_library_reference(); + return asset::build_filtered_all_catalog_tree(library, C, type_filter, meta_data_filter); +} + +static asset::AssetItemTree *get_static_item_tree() +{ + static asset::AssetItemTree tree; + return &tree; +} + +static void catalog_assets_draw(const bContext *C, Menu *menu) +{ + bScreen &screen = *CTX_wm_screen(C); + asset::AssetItemTree &tree = *get_static_item_tree(); + + const PointerRNA menu_path_ptr = CTX_data_pointer_get(C, "asset_catalog_path"); + if (RNA_pointer_is_null(&menu_path_ptr)) { + return; + } + const asset_system::AssetCatalogPath &menu_path = + *static_cast(menu_path_ptr.data); + + const Span assets = tree.assets_per_path.lookup(menu_path); + asset_system::AssetCatalogTreeItem *catalog_item = tree.catalogs.find_item(menu_path); + BLI_assert(catalog_item != nullptr); + + if (assets.is_empty() && !catalog_item->has_children()) { + return; + } + + uiLayout *layout = menu->layout; + uiItemS(layout); + + for (const asset_system::AssetRepresentation *asset : assets) { + uiLayout *col = uiLayoutColumn(layout, false); + wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_modifier_add_asset", true); + PointerRNA props_ptr; + uiItemFullO_ptr(col, + ot, + IFACE_(asset->get_name().c_str()), + ICON_NONE, + nullptr, + WM_OP_INVOKE_DEFAULT, + UI_ITEM_NONE, + &props_ptr); + asset::operator_asset_reference_props_set(*asset, props_ptr); + } + + asset_system::AssetLibrary *all_library = ED_assetlist_library_get_once_available( + asset_system::all_library_reference()); + if (!all_library) { + return; + } + + catalog_item->foreach_child([&](asset_system::AssetCatalogTreeItem &item) { + asset::draw_menu_for_catalog( + screen, *all_library, item, "OBJECT_MT_add_modifier_catalog_assets", *layout); + }); +} + +static void root_catalogs_draw(const bContext *C, Menu *menu) +{ + const Object *object = ED_object_active_context(C); + if (!object) { + return; + } + bScreen &screen = *CTX_wm_screen(C); + uiLayout *layout = menu->layout; + + const bool loading_finished = all_loading_finished(); + + asset::AssetItemTree &tree = *get_static_item_tree(); + tree = build_catalog_tree(*C); + if (tree.catalogs.is_empty() && loading_finished) { + return; + } + + uiItemS(layout); + + if (!loading_finished) { + uiItemL(layout, IFACE_("Loading Asset Libraries"), ICON_INFO); + } + + static Set all_builtin_menus = [&]() { + Set menus; + if (ELEM(object->type, OB_MESH, OB_CURVES_LEGACY, OB_FONT, OB_SURF, OB_LATTICE)) { + menus.add_new("Edit"); + } + if (ELEM(object->type, OB_MESH, OB_CURVES_LEGACY, OB_FONT, OB_SURF, OB_VOLUME)) { + menus.add_new("Generate"); + } + if (ELEM(object->type, OB_MESH, OB_CURVES_LEGACY, OB_FONT, OB_SURF, OB_LATTICE, OB_VOLUME)) { + menus.add_new("Deform"); + } + if (ELEM(object->type, OB_MESH, OB_CURVES_LEGACY, OB_FONT, OB_SURF, OB_LATTICE)) { + menus.add_new("Physics"); + } + return menus; + }(); + + asset_system::AssetLibrary *all_library = ED_assetlist_library_get_once_available( + asset_system::all_library_reference()); + if (!all_library) { + return; + } + + tree.catalogs.foreach_root_item([&](asset_system::AssetCatalogTreeItem &item) { + if (!all_builtin_menus.contains(item.get_name())) { + asset::draw_menu_for_catalog( + screen, *all_library, item, "OBJECT_MT_add_modifier_catalog_assets", *layout); + } + }); +} + +static bNodeTree *get_node_group(const bContext &C, PointerRNA &ptr, ReportList *reports) +{ + const asset_system::AssetRepresentation *asset = + asset::operator_asset_reference_props_get_asset_from_all_library(C, ptr, reports); + if (!asset) { + return nullptr; + } + Main &bmain = *CTX_data_main(&C); + bNodeTree *node_group = reinterpret_cast( + asset::asset_local_id_ensure_imported(bmain, *asset)); + if (!node_group) { + return nullptr; + } + if (node_group->type != NTREE_GEOMETRY) { + if (reports) { + BKE_report(reports, RPT_ERROR, "Asset is not a geometry node group"); + } + return nullptr; + } + return node_group; +} + +static int modifier_add_asset_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Object *object = ED_object_active_context(C); + + NodesModifierData *nmd = reinterpret_cast( + ED_object_modifier_add(op->reports, bmain, scene, object, nullptr, eModifierType_Nodes)); + if (!nmd) { + return OPERATOR_CANCELLED; + } + + bNodeTree *node_group = get_node_group(*C, *op->ptr, op->reports); + if (!node_group) { + return OPERATOR_CANCELLED; + } + + nmd->node_group = node_group; + id_us_plus(&node_group->id); + MOD_nodes_update_interface(object, nmd); + + STRNCPY(nmd->modifier.name, DATA_(node_group->id.name + 2)); + + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object); + + return OPERATOR_FINISHED; +} + +static std::string modifier_add_asset_get_description(bContext *C, + wmOperatorType * /*ot*/, + PointerRNA *ptr) +{ + const asset_system::AssetRepresentation *asset = + asset::operator_asset_reference_props_get_asset_from_all_library(*C, *ptr, nullptr); + if (!asset) { + return ""; + } + if (!asset->get_metadata().description) { + return ""; + } + return TIP_(asset->get_metadata().description); +} + +static void OBJECT_OT_modifier_add_asset(wmOperatorType *ot) +{ + ot->name = "Add Modifier"; + ot->description = "Add a procedural operation/effect to the active object"; + ot->idname = "OBJECT_OT_modifier_add_asset"; + + ot->exec = modifier_add_asset_exec; + ot->poll = ED_operator_object_active_editable; + ot->get_description = modifier_add_asset_get_description; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + asset::operator_asset_reference_props_register(*ot->srna); +} + +static MenuType modifier_add_catalog_assets_menu_type() +{ + MenuType type{}; + STRNCPY(type.idname, "OBJECT_MT_add_modifier_catalog_assets"); + type.draw = catalog_assets_draw; + type.listener = asset::asset_reading_region_listen_fn; + type.context_dependent = true; + return type; +} + +static MenuType modifier_add_root_catalogs_menu_type() +{ + MenuType type{}; + STRNCPY(type.idname, "OBJECT_MT_modifier_add_root_catalogs"); + type.draw = root_catalogs_draw; + type.listener = asset::asset_reading_region_listen_fn; + type.context_dependent = true; + return type; +} + +void object_modifier_add_asset_register() +{ + WM_menutype_add(MEM_new(__func__, modifier_add_catalog_assets_menu_type())); + WM_menutype_add(MEM_new(__func__, modifier_add_root_catalogs_menu_type())); + WM_operatortype_append(OBJECT_OT_modifier_add_asset); +} + +void ui_template_modifier_asset_menu_items(uiLayout &layout, + bContext &C, + const StringRef catalog_path) +{ + using namespace blender; + using namespace blender::ed; + using namespace blender::ed::object; + bScreen &screen = *CTX_wm_screen(&C); + asset::AssetItemTree &tree = *get_static_item_tree(); + const asset_system::AssetCatalogTreeItem *item = tree.catalogs.find_root_item(catalog_path); + if (!item) { + return; + } + asset_system::AssetLibrary *all_library = ED_assetlist_library_get_once_available( + asset_system::all_library_reference()); + if (!all_library) { + return; + } + PointerRNA path_ptr = asset::persistent_catalog_path_rna_pointer(screen, *all_library, *item); + if (path_ptr.data == nullptr) { + return; + } + uiItemS(&layout); + uiLayout *col = uiLayoutColumn(&layout, false); + uiLayoutSetContextPointer(col, "asset_catalog_path", &path_ptr); + uiItemMContents(col, "OBJECT_MT_add_modifier_catalog_assets"); +} + +} // namespace blender::ed::object diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index dd24e8804d1..102b879b215 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -369,3 +369,13 @@ void OBJECT_OT_datalayout_transfer(struct wmOperatorType *ot); #ifdef __cplusplus } #endif + +#ifdef __cplusplus + +namespace blender::ed::object { + +void object_modifier_add_asset_register(); + +} + +#endif diff --git a/source/blender/editors/object/object_ops.cc b/source/blender/editors/object/object_ops.cc index 998e21ff1d9..bbfc3628261 100644 --- a/source/blender/editors/object/object_ops.cc +++ b/source/blender/editors/object/object_ops.cc @@ -28,6 +28,7 @@ void ED_operatortypes_object() { + using namespace blender::ed::object; WM_operatortype_append(OBJECT_OT_location_clear); WM_operatortype_append(OBJECT_OT_rotation_clear); WM_operatortype_append(OBJECT_OT_scale_clear); @@ -288,6 +289,8 @@ void ED_operatortypes_object() WM_operatortype_append(OBJECT_OT_light_linking_blockers_link); WM_operatortype_append(OBJECT_OT_light_linking_unlink_from_collection); + + object_modifier_add_asset_register(); } void ED_operatormacros_object() diff --git a/source/blender/editors/space_node/add_menu_assets.cc b/source/blender/editors/space_node/add_menu_assets.cc index 4cfe6d950d0..97a2528d54a 100644 --- a/source/blender/editors/space_node/add_menu_assets.cc +++ b/source/blender/editors/space_node/add_menu_assets.cc @@ -22,6 +22,7 @@ #include "RNA_access.hh" #include "ED_asset.hh" +#include "ED_asset_menu_utils.hh" #include "ED_screen.hh" #include "node_intern.hh" @@ -99,17 +100,9 @@ static void node_add_catalog_assets_draw(const bContext *C, Menu *menu) return; } - catalog_item->foreach_child([&](asset_system::AssetCatalogTreeItem &child_item) { - PointerRNA path_ptr = asset::persistent_catalog_path_rna_pointer( - screen, *all_library, child_item); - if (path_ptr.data == nullptr) { - return; - } - - uiLayout *col = uiLayoutColumn(layout, false); - uiLayoutSetContextPointer(col, "asset_catalog_path", &path_ptr); - uiItemM( - col, "NODE_MT_node_add_catalog_assets", IFACE_(child_item.get_name().c_str()), ICON_NONE); + catalog_item->foreach_child([&](asset_system::AssetCatalogTreeItem &item) { + asset::draw_menu_for_catalog( + screen, *all_library, item, "NODE_MT_node_add_catalog_assets", *layout); }); } @@ -181,16 +174,10 @@ static void add_root_catalogs_draw(const bContext *C, Menu *menu) } tree.catalogs.foreach_root_item([&](asset_system::AssetCatalogTreeItem &item) { - if (all_builtin_menus.contains(item.get_name())) { - return; + if (!all_builtin_menus.contains(item.get_name())) { + asset::draw_menu_for_catalog( + screen, *all_library, item, "NODE_MT_node_add_catalog_assets", *layout); } - PointerRNA path_ptr = asset::persistent_catalog_path_rna_pointer(screen, *all_library, item); - if (path_ptr.data == nullptr) { - return; - } - uiLayout *col = uiLayoutColumn(layout, false); - uiLayoutSetContextPointer(col, "asset_catalog_path", &path_ptr); - uiItemM(col, "NODE_MT_node_add_catalog_assets", IFACE_(item.get_name().c_str()), ICON_NONE); }); } diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index 6d801f08667..2f3d384f2b6 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -40,6 +40,7 @@ set(SRC ../include/ED_anim_api.hh ../include/ED_armature.hh ../include/ED_asset.hh + ../include/ED_asset_menu_utils.hh ../include/ED_buttons.hh ../include/ED_clip.hh ../include/ED_curve.hh diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 8b6a9e34928..ca227d2a3a9 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -897,6 +897,7 @@ typedef enum GeometryNodeAssetTraitFlag { GEO_NODE_ASSET_MESH = (1 << 3), GEO_NODE_ASSET_CURVE = (1 << 4), GEO_NODE_ASSET_POINT_CLOUD = (1 << 5), + GEO_NODE_ASSET_MODIFIER = (1 << 6), } GeometryNodeAssetTraitFlag; ENUM_OPERATORS(GeometryNodeAssetTraitFlag, GEO_NODE_ASSET_POINT_CLOUD); diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index fdde2e9dd1e..ecbe876b7d7 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -1785,6 +1785,15 @@ static void rna_GeometryNodeTree_is_tool_set(PointerRNA *ptr, bool value) geometry_node_asset_trait_flag_set(ptr, GEO_NODE_ASSET_TOOL, value); } +static bool rna_GeometryNodeTree_is_modifier_get(PointerRNA *ptr) +{ + return geometry_node_asset_trait_flag_get(ptr, GEO_NODE_ASSET_MODIFIER); +} +static void rna_GeometryNodeTree_is_modifier_set(PointerRNA *ptr, bool value) +{ + geometry_node_asset_trait_flag_set(ptr, GEO_NODE_ASSET_MODIFIER, value); +} + static bool rna_GeometryNodeTree_is_mode_edit_get(PointerRNA *ptr) { return geometry_node_asset_trait_flag_get(ptr, GEO_NODE_ASSET_EDIT); @@ -10308,6 +10317,14 @@ static void rna_def_geometry_nodetree(BlenderRNA *brna) prop, "rna_GeometryNodeTree_is_tool_get", "rna_GeometryNodeTree_is_tool_set"); RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_NodeTree_update"); + prop = RNA_def_property(srna, "is_modifier", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", GEO_NODE_ASSET_MODIFIER); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Modifier", "The node group is used as a geometry modifier"); + RNA_def_property_boolean_funcs( + prop, "rna_GeometryNodeTree_is_modifier_get", "rna_GeometryNodeTree_is_modifier_set"); + RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_NodeTree_update"); + prop = RNA_def_property(srna, "is_mode_edit", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", GEO_NODE_ASSET_EDIT); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); diff --git a/source/blender/makesrna/intern/rna_ui_api.cc b/source/blender/makesrna/intern/rna_ui_api.cc index d1c4d031179..434f3e68a8a 100644 --- a/source/blender/makesrna/intern/rna_ui_api.cc +++ b/source/blender/makesrna/intern/rna_ui_api.cc @@ -38,6 +38,7 @@ const EnumPropertyItem rna_enum_icon_items[] = { # include "DNA_asset_types.h" # include "ED_geometry.hh" +# include "ED_object.hh" const char *rna_translate_ui_text( const char *text, const char *text_ctxt, StructRNA *type, PropertyRNA *prop, bool translate) @@ -786,6 +787,16 @@ static void rna_uiLayout_template_node_operator_asset_menu_items(uiLayout *layou } } +static void rna_uiLayout_template_modifier_asset_menu_items(uiLayout *layout, + bContext *C, + const char *catalog_path) +{ + if (U.experimental.use_node_group_operators) { + blender::ed::object::ui_template_modifier_asset_menu_items( + *layout, *C, blender::StringRef(catalog_path)); + } +} + static void rna_uiLayout_template_node_operator_root_items(uiLayout *layout, bContext *C) { if (U.experimental.use_node_group_operators) { @@ -1893,6 +1904,12 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_function_flag(func, FUNC_USE_CONTEXT); parm = RNA_def_string(func, "catalog_path", nullptr, 0, "", ""); + func = RNA_def_function(srna, + "template_modifier_asset_menu_items", + "rna_uiLayout_template_modifier_asset_menu_items"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + parm = RNA_def_string(func, "catalog_path", nullptr, 0, "", ""); + func = RNA_def_function(srna, "template_node_operator_asset_menu_items", "rna_uiLayout_template_node_operator_asset_menu_items");