From 355ed0838cd73b0a66bcbdb8750b6d7ec9104076 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 26 Sep 2023 16:12:33 -0400 Subject: [PATCH] Geometry Nodes: Expose non-asset tools Node tools are no longer required to be assets, and they aren't assets by default anymore. Non-assets don't have catalogs for header menu organization, so they are only exposed in the "Unassigned" menu, which is now just an icon so it takes less spac, makes the connection to the asset browser, and signals more that it's not the "final" place for a tool. Tool node groups have fake user set by default, since they don't have users. The "Is Tool" status of a node group is configurable in the editor N-panel, just like the modifier status. This is similar to af3461c387702b56f752, and has the same check for whether a node group is "local." --- .../startup/bl_operators/geometry_nodes.py | 3 +- scripts/startup/bl_ui/space_node.py | 2 +- .../editors/geometry/node_group_operator.cc | 109 ++++++++++++++---- 3 files changed, 92 insertions(+), 22 deletions(-) diff --git a/scripts/startup/bl_operators/geometry_nodes.py b/scripts/startup/bl_operators/geometry_nodes.py index 3f4ddf13477..37dbb20b180 100644 --- a/scripts/startup/bl_operators/geometry_nodes.py +++ b/scripts/startup/bl_operators/geometry_nodes.py @@ -43,7 +43,8 @@ def geometry_node_group_empty_modifier_new(name): def geometry_node_group_empty_tool_new(context): group = geometry_node_group_empty_new(data_("Tool")) - group.asset_mark() + # Node tools have fake users by default, otherwise Blender will delete them since they have no users. + group.use_fake_user = True group.is_tool = True ob_type = context.object.type if context.object else 'MESH' diff --git a/scripts/startup/bl_ui/space_node.py b/scripts/startup/bl_ui/space_node.py index eaf2db0bc7c..0c9ef8432f8 100644 --- a/scripts/startup/bl_ui/space_node.py +++ b/scripts/startup/bl_ui/space_node.py @@ -163,7 +163,7 @@ class NODE_HT_header(Header): row.template_ID(snode, "node_tree", new="node.new_geometry_nodes_modifier") else: layout.template_ID(snode, "node_tree", new="node.new_geometry_node_group_tool") - if snode.node_tree and snode.node_tree.asset_data: + if snode.node_tree: layout.popover(panel="NODE_PT_geometry_node_tool_object_types", text="Types") layout.popover(panel="NODE_PT_geometry_node_tool_mode", text="Modes") display_pin = False diff --git a/source/blender/editors/geometry/node_group_operator.cc b/source/blender/editors/geometry/node_group_operator.cc index ee73507e675..0f5dd78aad0 100644 --- a/source/blender/editors/geometry/node_group_operator.cc +++ b/source/blender/editors/geometry/node_group_operator.cc @@ -25,6 +25,7 @@ #include "BKE_geometry_set.hh" #include "BKE_layer.h" #include "BKE_lib_id.h" +#include "BKE_main.h" #include "BKE_material.h" #include "BKE_mesh.hh" #include "BKE_mesh_wrapper.hh" @@ -72,26 +73,38 @@ namespace blender::ed::geometry { /** \name Operator * \{ */ -static const bNodeTree *get_node_group(const bContext &C, PointerRNA &ptr, ReportList *reports) +static const bNodeTree *get_asset_or_local_node_group(const bContext &C, + PointerRNA &ptr, + ReportList *reports) { + Main &bmain = *CTX_data_main(&C); + if (bNodeTree *group = reinterpret_cast( + WM_operator_properties_id_lookup_from_name_or_session_uuid(&bmain, &ptr, ID_NT))) + { + return group; + } + 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 reinterpret_cast(asset::asset_local_id_ensure_imported(bmain, *asset)); +} + +static const bNodeTree *get_node_group(const bContext &C, PointerRNA &ptr, ReportList *reports) +{ + const bNodeTree *group = get_asset_or_local_node_group(C, ptr, reports); + if (!group) { return nullptr; } - if (node_group->type != NTREE_GEOMETRY) { + if (group->type != NTREE_GEOMETRY) { if (reports) { BKE_report(reports, RPT_ERROR, "Asset is not a geometry node group"); } return nullptr; } - return node_group; + return group; } class OperatorComputeContext : public ComputeContext { @@ -455,11 +468,16 @@ static bool run_node_ui_poll(wmOperatorType * /*ot*/, PointerRNA *ptr) static std::string run_node_group_get_name(wmOperatorType * /*ot*/, PointerRNA *ptr) { int len; - char *name_c = RNA_string_get_alloc(ptr, "relative_asset_identifier", nullptr, 0, &len); - StringRef ref(name_c, len); - std::string name = ref.drop_prefix(ref.find_last_of('/') + 1); - MEM_freeN(name_c); - return name; + char *local_name = RNA_string_get_alloc(ptr, "name", nullptr, 0, &len); + BLI_SCOPED_DEFER([&]() { MEM_SAFE_FREE(local_name); }) + if (len > 0) { + return std::string(local_name, len); + } + char *library_asset_identifier = RNA_string_get_alloc( + ptr, "relative_asset_identifier", nullptr, 0, &len); + BLI_SCOPED_DEFER([&]() { MEM_SAFE_FREE(library_asset_identifier); }) + StringRef ref(library_asset_identifier, len); + return ref.drop_prefix(ref.find_last_of('/') + 1); } void GEOMETRY_OT_execute_node_group(wmOperatorType *ot) @@ -479,6 +497,7 @@ void GEOMETRY_OT_execute_node_group(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; asset::operator_asset_reference_props_register(*ot->srna); + WM_operator_properties_id_lookup(ot, true); } /** \} */ @@ -672,8 +691,8 @@ static void catalog_assets_draw(const bContext *C, Menu *menu) } catalog_item->foreach_child([&](asset_system::AssetCatalogTreeItem &item) { - asset::draw_menu_for_catalog( - screen, *all_library, item, "GEO_MT_node_operator_catalog_assets", *layout); + asset::draw_menu_for_catalog( + screen, *all_library, item, "GEO_MT_node_operator_catalog_assets", *layout); }); } @@ -688,6 +707,26 @@ MenuType node_group_operator_assets_menu() return type; } +static bool unassigned_local_poll(const bContext &C) +{ + Main &bmain = *CTX_data_main(&C); + const GeometryNodeAssetTraitFlag flag = asset_flag_for_context( + eContextObjectMode(CTX_data_mode_enum(&C))); + + LISTBASE_FOREACH (const bNodeTree *, group, &bmain.nodetrees) { + /* Assets are displayed in other menus, and non-local data-blocks aren't added to this menu. */ + if (group->id.library_weak_reference || group->id.asset_data) { + continue; + } + if (!group->geometry_node_asset_traits || + (group->geometry_node_asset_traits->flag & flag) != flag) { + continue; + } + return true; + } + return false; +} + static void catalog_assets_draw_unassigned(const bContext *C, Menu *menu) { asset::AssetItemTree *tree = get_static_item_tree(*C); @@ -708,12 +747,45 @@ static void catalog_assets_draw_unassigned(const bContext *C, Menu *menu) &props_ptr); asset::operator_asset_reference_props_set(*asset, props_ptr); } + + const GeometryNodeAssetTraitFlag flag = asset_flag_for_context( + eContextObjectMode(CTX_data_mode_enum(C))); + + bool add_separator = !tree->unassigned_assets.is_empty(); + Main &bmain = *CTX_data_main(C); + LISTBASE_FOREACH (const bNodeTree *, group, &bmain.nodetrees) { + /* Assets are displayed in other menus, and non-local data-blocks aren't added to this menu. */ + if (group->id.library_weak_reference || group->id.asset_data) { + continue; + } + if (!group->geometry_node_asset_traits || + (group->geometry_node_asset_traits->flag & flag) != flag) { + continue; + } + + if (add_separator) { + uiItemS(layout); + uiItemL(layout, IFACE_("Non-Assets"), ICON_NONE); + add_separator = false; + } + + PointerRNA props_ptr; + uiItemFullO_ptr(layout, + ot, + group->id.name + 2, + ICON_NONE, + nullptr, + WM_OP_INVOKE_REGION_WIN, + UI_ITEM_NONE, + &props_ptr); + WM_operator_properties_id_lookup_set_from_id(&props_ptr, &group->id); + } } MenuType node_group_operator_assets_menu_unassigned() { MenuType type{}; - STRNCPY(type.idname, "GEO_MT_node_operator_catalog_assets_unassigned"); + STRNCPY(type.idname, "GEO_MT_node_operator_unassigned"); type.poll = asset_menu_poll; type.draw = catalog_assets_draw_unassigned; type.listener = asset::asset_reading_region_listen_fn; @@ -783,11 +855,8 @@ void ui_template_node_operator_asset_root_items(uiLayout &layout, const bContext } }); - if (!tree->unassigned_assets.is_empty()) { - uiItemM(&layout, - "GEO_MT_node_operator_catalog_assets_unassigned", - IFACE_("Unassigned"), - ICON_NONE); + if (!tree->unassigned_assets.is_empty() || unassigned_local_poll(C)) { + uiItemM(&layout, "GEO_MT_node_operator_unassigned", "", ICON_FILE_HIDDEN); } }