diff --git a/scripts/startup/bl_operators/geometry_nodes.py b/scripts/startup/bl_operators/geometry_nodes.py index 08fabc181d9..f1802af7c4d 100644 --- a/scripts/startup/bl_operators/geometry_nodes.py +++ b/scripts/startup/bl_operators/geometry_nodes.py @@ -237,12 +237,17 @@ class NewGeometryNodeTreeAssign(Operator): return geometry_modifier_poll(context) def execute(self, context): - modifier = get_context_modifier(context) - if not modifier: - return {'CANCELLED'} - - group = geometry_node_group_empty_new() - modifier.node_group = group + snode = context.space_data + if snode and snode.geometry_nodes_type == 'OPERATOR': + group = geometry_node_group_empty_new() + snode.node_tree = group + return {'FINISHED'} + else: + modifier = get_context_modifier(context) + if not modifier: + return {'CANCELLED'} + group = geometry_node_group_empty_new() + modifier.node_group = group return {'FINISHED'} diff --git a/scripts/startup/bl_ui/space_node.py b/scripts/startup/bl_ui/space_node.py index 78448e668b6..da2df2f286f 100644 --- a/scripts/startup/bl_ui/space_node.py +++ b/scripts/startup/bl_ui/space_node.py @@ -139,25 +139,28 @@ class NODE_HT_header(Header): layout.prop(snode_id, "use_nodes") elif snode.tree_type == 'GeometryNodeTree': + layout.prop(snode, "geometry_nodes_type", text="") NODE_MT_editor_menus.draw_collapsible(context, layout) layout.separator_spacer() - ob = context.object + if snode.geometry_nodes_type == 'MODIFIER': + ob = context.object - row = layout.row() - if snode.pin: - row.enabled = False - row.template_ID(snode, "node_tree", new="node.new_geometry_node_group_assign") - elif ob: - active_modifier = ob.modifiers.active - if active_modifier and active_modifier.type == 'NODES': - if active_modifier.node_group: - row.template_ID(active_modifier, "node_group", new="object.geometry_node_tree_copy_assign") + row = layout.row() + if snode.pin: + row.enabled = False + row.template_ID(snode, "node_tree", new="node.new_geometry_node_group_assign") + elif ob: + active_modifier = ob.modifiers.active + if active_modifier and active_modifier.type == 'NODES': + if active_modifier.node_group: + row.template_ID(active_modifier, "node_group", new="object.geometry_node_tree_copy_assign") + else: + row.template_ID(active_modifier, "node_group", new="node.new_geometry_node_group_assign") else: - 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") - + 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_assign") else: # Custom node tree is edited as independent ID block NODE_MT_editor_menus.draw_collapsible(context, layout) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index d289bdb3455..77f5826a8cf 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -1018,6 +1018,7 @@ class VIEW3D_MT_editor_menus(Menu): if mode_string == 'SCULPT_CURVES': layout.menu("VIEW3D_MT_select_sculpt_curves") layout.menu("VIEW3D_MT_sculpt_curves") + layout.template_node_operator_asset_root_items() else: layout.menu("VIEW3D_MT_object") @@ -2153,6 +2154,8 @@ class VIEW3D_MT_select_sculpt_curves(Menu): layout.operator("curves.select_ends", text="Endpoints") layout.operator("sculpt_curves.select_grow", text="Grow") + layout.template_node_operator_asset_menu_items(catalog_path="Select") + class VIEW3D_MT_mesh_add(Menu): bl_idname = "VIEW3D_MT_mesh_add" @@ -3408,6 +3411,8 @@ class VIEW3D_MT_sculpt_curves(Menu): layout.separator() layout.operator("curves.convert_to_particle_system", text="Convert to Particle System") + layout.template_node_operator_asset_menu_items(catalog_path="Curves") + class VIEW3D_MT_mask(Menu): bl_label = "Mask" diff --git a/source/blender/asset_system/AS_asset_library.hh b/source/blender/asset_system/AS_asset_library.hh index 96e723ce19b..aaf33d1d6b6 100644 --- a/source/blender/asset_system/AS_asset_library.hh +++ b/source/blender/asset_system/AS_asset_library.hh @@ -173,6 +173,8 @@ class AssetLibrary { Vector all_valid_asset_library_refs(); +AssetLibraryReference all_library_reference(); + } // namespace blender::asset_system /** diff --git a/source/blender/asset_system/intern/asset_library.cc b/source/blender/asset_system/intern/asset_library.cc index 4500e0faaf9..3c8e16b5c60 100644 --- a/source/blender/asset_system/intern/asset_library.cc +++ b/source/blender/asset_system/intern/asset_library.cc @@ -359,4 +359,12 @@ Vector all_valid_asset_library_refs() return result; } +AssetLibraryReference all_library_reference() +{ + AssetLibraryReference all_library_ref{}; + all_library_ref.custom_library_index = -1; + all_library_ref.type = ASSET_LIBRARY_ALL; + return all_library_ref; +} + } // namespace blender::asset_system diff --git a/source/blender/editors/asset/ED_asset_catalog.hh b/source/blender/editors/asset/ED_asset_catalog.hh index 55c4b3db6b4..397499e7dda 100644 --- a/source/blender/editors/asset/ED_asset_catalog.hh +++ b/source/blender/editors/asset/ED_asset_catalog.hh @@ -24,6 +24,11 @@ #include "BLI_string_ref.hh" 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 @@ -54,3 +59,16 @@ 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_filter.hh b/source/blender/editors/asset/ED_asset_filter.hh index d10857ee65a..809fa6d86a1 100644 --- a/source/blender/editors/asset/ED_asset_filter.hh +++ b/source/blender/editors/asset/ED_asset_filter.hh @@ -10,7 +10,15 @@ #pragma once +#include "BLI_function_ref.hh" +#include "BLI_multi_value_map.hh" + +#include "AS_asset_catalog_path.hh" +#include "AS_asset_catalog_tree.hh" + struct AssetFilterSettings; +struct AssetLibraryReference; +struct bContext; namespace blender::asset_system { class AssetRepresentation; @@ -30,3 +38,19 @@ class AssetRepresentation; */ bool ED_asset_filter_matches_asset(const AssetFilterSettings *filter, const blender::asset_system::AssetRepresentation &asset); + +namespace blender::ed::asset { + +struct AssetItemTree { + asset_system::AssetCatalogTree catalogs; + MultiValueMap + assets_per_path; +}; + +AssetItemTree build_filtered_all_catalog_tree( + const AssetLibraryReference &library_ref, + const bContext &C, + const AssetFilterSettings &filter_settings, + FunctionRef meta_data_filter = {}); + +} // 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 8a39551b81a..8fa4553e167 100644 --- a/source/blender/editors/asset/ED_asset_handle.h +++ b/source/blender/editors/asset/ED_asset_handle.h @@ -17,6 +17,14 @@ #include "DNA_ID_enums.h" #include "DNA_asset_types.h" +#include "RNA_types.h" + +#ifdef __cplusplus +namespace blender::asset_system { +class AssetRepresentation; +} +#endif + #ifdef __cplusplus extern "C" { #endif @@ -38,3 +46,13 @@ 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/ED_asset_list.h b/source/blender/editors/asset/ED_asset_list.h index d7f97598807..2e53b60a6c5 100644 --- a/source/blender/editors/asset/ED_asset_list.h +++ b/source/blender/editors/asset/ED_asset_list.h @@ -24,6 +24,7 @@ struct AssetLibraryReference; struct ID; struct bContext; struct wmNotifier; +struct wmRegionListenerParams; /** * Invoke asset list reading, potentially in a parallel job. Won't wait until the job is done, @@ -80,3 +81,13 @@ int ED_assetlist_size(const struct AssetLibraryReference *library_reference); #ifdef __cplusplus } #endif + +#ifdef __cplusplus + +namespace blender::ed::asset { + +void asset_reading_region_listen_fn(const wmRegionListenerParams *params); + +} // namespace blender::ed::asset + +#endif diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc index ba570cd8e00..d0dbf13c2e1 100644 --- a/source/blender/editors/asset/intern/asset_catalog.cc +++ b/source/blender/editors/asset/intern/asset_catalog.cc @@ -9,10 +9,15 @@ #include "AS_asset_library.hh" #include "AS_asset_catalog.hh" +#include "AS_asset_catalog_tree.hh" + #include "BKE_main.h" #include "BLI_string_utils.h" +#include "RNA_access.h" +#include "RNA_prototypes.h" + #include "ED_asset_catalog.h" #include "ED_asset_catalog.hh" @@ -201,3 +206,23 @@ 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_filter.cc b/source/blender/editors/asset/intern/asset_filter.cc index 4ffdf2b4c71..42883177f23 100644 --- a/source/blender/editors/asset/intern/asset_filter.cc +++ b/source/blender/editors/asset/intern/asset_filter.cc @@ -8,13 +8,20 @@ #include "AS_asset_representation.hh" +#include "BKE_asset.h" #include "BKE_idtype.h" #include "BLI_listbase.h" #include "DNA_asset_types.h" +#include "AS_asset_catalog_tree.hh" +#include "AS_asset_library.hh" + #include "ED_asset_filter.hh" +#include "ED_asset_library.h" +#include "ED_asset_list.h" +#include "ED_asset_list.hh" using namespace blender; @@ -41,3 +48,62 @@ bool ED_asset_filter_matches_asset(const AssetFilterSettings *filter, /* Successfully passed through all filters. */ return true; } + +namespace blender::ed::asset { + +AssetItemTree build_filtered_all_catalog_tree( + const AssetLibraryReference &library_ref, + const bContext &C, + const AssetFilterSettings &filter_settings, + const FunctionRef meta_data_filter) +{ + MultiValueMap + assets_per_path; + + ED_assetlist_storage_fetch(&library_ref, &C); + ED_assetlist_ensure_previews_job(&library_ref, &C); + asset_system::AssetLibrary *library = ED_assetlist_library_get_once_available(library_ref); + if (!library) { + return {}; + } + + ED_assetlist_iterate(library_ref, [&](asset_system::AssetRepresentation &asset) { + if (!ED_asset_filter_matches_asset(&filter_settings, asset)) { + return true; + } + const AssetMetaData &meta_data = asset.get_metadata(); + if (BLI_uuid_is_nil(meta_data.catalog_id)) { + return true; + } + + if (meta_data_filter && !meta_data_filter(meta_data)) { + return true; + } + + const asset_system::AssetCatalog *catalog = library->catalog_service->find_catalog( + meta_data.catalog_id); + if (catalog == nullptr) { + return true; + } + assets_per_path.add(catalog->path, &asset); + return true; + }); + + asset_system::AssetCatalogTree catalogs_with_node_assets; + asset_system::AssetCatalogTree &catalog_tree = *library->catalog_service->get_catalog_tree(); + catalog_tree.foreach_item([&](asset_system::AssetCatalogTreeItem &item) { + if (assets_per_path.lookup(item.catalog_path()).is_empty()) { + return; + } + asset_system::AssetCatalog *catalog = library->catalog_service->find_catalog( + item.get_catalog_id()); + if (catalog == nullptr) { + return; + } + catalogs_with_node_assets.insert_item(*catalog); + }); + + return {std::move(catalogs_with_node_assets), std::move(assets_per_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 da4c11457fe..b33a18682ed 100644 --- a/source/blender/editors/asset/intern/asset_handle.cc +++ b/source/blender/editors/asset/intern/asset_handle.cc @@ -19,6 +19,8 @@ #include "DNA_space_types.h" +#include "RNA_prototypes.h" + #include "ED_asset_handle.h" AssetRepresentation *ED_asset_handle_get_representation(const AssetHandle *asset) @@ -64,3 +66,16 @@ 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_list.cc b/source/blender/editors/asset/intern/asset_list.cc index a61bfe7695c..99a19db8faa 100644 --- a/source/blender/editors/asset/intern/asset_list.cc +++ b/source/blender/editors/asset/intern/asset_list.cc @@ -17,6 +17,7 @@ #include "AS_asset_library.hh" #include "BKE_context.h" +#include "BKE_screen.h" #include "BLI_map.hh" #include "BLI_utility_mixins.hh" @@ -34,6 +35,7 @@ #include "ED_asset_indexer.h" #include "ED_asset_list.h" #include "ED_asset_list.hh" +#include "ED_screen.h" #include "asset_library_reference.hh" namespace blender::ed::asset { @@ -426,6 +428,20 @@ AssetListStorage::AssetListMap &AssetListStorage::global_storage() /** \} */ +void asset_reading_region_listen_fn(const wmRegionListenerParams *params) +{ + const wmNotifier *wmn = params->notifier; + ARegion *region = params->region; + + switch (wmn->category) { + case NC_ASSET: + if (wmn->data == ND_ASSET_LIST_READING) { + ED_region_tag_refresh_ui(region); + } + break; + } +} + } // namespace blender::ed::asset /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/geometry/CMakeLists.txt b/source/blender/editors/geometry/CMakeLists.txt index 08368e536a1..79137034590 100644 --- a/source/blender/editors/geometry/CMakeLists.txt +++ b/source/blender/editors/geometry/CMakeLists.txt @@ -4,15 +4,21 @@ set(INC ../include + ../../asset_system ../../blenkernel ../../blenlib + ../../blentranslation ../../depsgraph ../../functions ../../makesdna ../../makesrna + ../../nodes ../../windowmanager ../../../../intern/guardedalloc ../../bmesh + + # RNA_prototypes.h + ${CMAKE_BINARY_DIR}/source/blender/makesrna ) set(INC_SYS @@ -21,6 +27,7 @@ set(INC_SYS set(SRC geometry_attributes.cc geometry_ops.cc + node_group_operator.cc geometry_intern.hh ) @@ -33,3 +40,6 @@ set(LIB ) blender_add_lib(bf_editor_geometry "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +# RNA_prototypes.h dna_type_offsets.h +add_dependencies(bf_editor_geometry bf_rna) diff --git a/source/blender/editors/geometry/geometry_intern.hh b/source/blender/editors/geometry/geometry_intern.hh index 4db19086ab0..b877aa5e82f 100644 --- a/source/blender/editors/geometry/geometry_intern.hh +++ b/source/blender/editors/geometry/geometry_intern.hh @@ -22,4 +22,6 @@ void GEOMETRY_OT_color_attribute_duplicate(struct wmOperatorType *ot); void GEOMETRY_OT_attribute_convert(struct wmOperatorType *ot); void GEOMETRY_OT_color_attribute_convert(struct wmOperatorType *ot); +void GEOMETRY_OT_execute_node_group(struct wmOperatorType *ot); + } // namespace blender::ed::geometry diff --git a/source/blender/editors/geometry/geometry_ops.cc b/source/blender/editors/geometry/geometry_ops.cc index 18db78ad383..43b5b73d6bb 100644 --- a/source/blender/editors/geometry/geometry_ops.cc +++ b/source/blender/editors/geometry/geometry_ops.cc @@ -26,4 +26,5 @@ void ED_operatortypes_geometry(void) WM_operatortype_append(GEOMETRY_OT_color_attribute_duplicate); WM_operatortype_append(GEOMETRY_OT_attribute_convert); WM_operatortype_append(GEOMETRY_OT_color_attribute_convert); + WM_operatortype_append(GEOMETRY_OT_execute_node_group); } diff --git a/source/blender/editors/geometry/node_group_operator.cc b/source/blender/editors/geometry/node_group_operator.cc new file mode 100644 index 00000000000..87cf92ed14d --- /dev/null +++ b/source/blender/editors/geometry/node_group_operator.cc @@ -0,0 +1,546 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edcurves + */ + +#include "ED_curves.h" +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_select_utils.h" +#include "ED_view3d.h" + +#include "WM_api.h" + +#include "BKE_asset.h" +#include "BKE_attribute_math.hh" +#include "BKE_compute_contexts.hh" +#include "BKE_context.h" +#include "BKE_curves.hh" +#include "BKE_editmesh.h" +#include "BKE_geometry_set.hh" +#include "BKE_layer.h" +#include "BKE_lib_id.h" +#include "BKE_material.h" +#include "BKE_mesh.hh" +#include "BKE_mesh_wrapper.h" +#include "BKE_node_runtime.hh" +#include "BKE_object.h" +#include "BKE_pointcloud.h" +#include "BKE_report.h" +#include "BKE_screen.h" + +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "RNA_access.h" +#include "RNA_enum_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "ED_asset.h" +#include "ED_geometry.h" +#include "ED_mesh.h" + +#include "BLT_translation.h" + +#include "FN_lazy_function_execute.hh" + +#include "NOD_geometry_nodes_execute.hh" +#include "NOD_geometry_nodes_lazy_function.hh" + +#include "AS_asset_catalog.hh" +#include "AS_asset_catalog_path.hh" +#include "AS_asset_catalog_tree.hh" +#include "AS_asset_library.hh" +#include "AS_asset_representation.hh" + +#include "geometry_intern.hh" + +namespace blender::ed::geometry { + +/* -------------------------------------------------------------------- */ +/** \name Operator + * \{ */ + +static const asset_system::AssetRepresentation *get_context_asset(const bContext &C) +{ + return reinterpret_cast(CTX_wm_asset(&C)); +} + +static const bNodeTree *get_node_group(const bContext &C) +{ + const asset_system::AssetRepresentation *asset = get_context_asset(C); + if (!asset) { + return nullptr; + } + bNodeTree *node_group = reinterpret_cast( + ED_asset_get_local_id_from_asset_or_append_and_reuse(CTX_data_main(&C), *asset, ID_NT)); + if (!node_group) { + return nullptr; + } + if (node_group->type != NTREE_GEOMETRY) { + return nullptr; + } + return node_group; +} + +class OperatorComputeContext : public ComputeContext { + private: + static constexpr const char *s_static_type = "OPERATOR"; + + std::string operator_name_; + + public: + OperatorComputeContext(std::string operator_name) + : ComputeContext(s_static_type, nullptr), operator_name_(std::move(operator_name)) + { + hash_.mix_in(s_static_type, strlen(s_static_type)); + hash_.mix_in(operator_name_.data(), operator_name_.size()); + } + + private: + void print_current_in_line(std::ostream &stream) const override + { + stream << "Operator: " << operator_name_; + } +}; + +/** + * Geometry nodes currently requires working on "evaluated" data-blocks (rather than "original" + * data-blocks that are part of a #Main data-base). This could change in the future, but for now, + * we need to create evaluated copies of geometry before passing it to geometry nodes. Implicit + * sharing lets us avoid copying attribute data though. + */ +static bke::GeometrySet get_original_geometry_eval_copy(Object &object) +{ + switch (object.type) { + case OB_CURVES: { + Curves *curves = BKE_curves_copy_for_eval(static_cast(object.data)); + return bke::GeometrySet::create_with_curves(curves); + } + case OB_POINTCLOUD: { + PointCloud *points = BKE_pointcloud_copy_for_eval( + static_cast(object.data)); + return bke::GeometrySet::create_with_pointcloud(points); + } + case OB_MESH: { + const Mesh *mesh = static_cast(object.data); + if (mesh->edit_mesh) { + Mesh *mesh_copy = BKE_mesh_wrapper_from_editmesh(mesh->edit_mesh, nullptr, mesh); + BKE_mesh_wrapper_ensure_mdata(mesh_copy); + Mesh *final_copy = BKE_mesh_copy_for_eval(mesh_copy); + BKE_id_free(nullptr, mesh_copy); + return bke::GeometrySet::create_with_mesh(final_copy); + } + return bke::GeometrySet::create_with_mesh(BKE_mesh_copy_for_eval(mesh)); + } + default: + return {}; + } +} + +static void store_result_geometry(Main &bmain, Object &object, bke::GeometrySet geometry) +{ + switch (object.type) { + case OB_CURVES: { + Curves &curves = *static_cast(object.data); + Curves *new_curves = geometry.get_curves_for_write(); + if (!new_curves) { + curves.geometry.wrap() = {}; + break; + } + + /* Anonymous attributes shouldn't be available on the applied geometry. */ + new_curves->geometry.wrap().attributes_for_write().remove_anonymous(); + + curves.geometry.wrap() = std::move(new_curves->geometry.wrap()); + BKE_object_material_from_eval_data(&bmain, &object, &new_curves->id); + break; + } + case OB_POINTCLOUD: { + PointCloud &points = *static_cast(object.data); + PointCloud *new_points = + geometry.get_component_for_write().release(); + if (!new_points) { + CustomData_free(&points.pdata, points.totpoint); + points.totpoint = 0; + break; + } + + /* Anonymous attributes shouldn't be available on the applied geometry. */ + new_points->attributes_for_write().remove_anonymous(); + + BKE_object_material_from_eval_data(&bmain, &object, &new_points->id); + BKE_pointcloud_nomain_to_pointcloud(new_points, &points); + break; + } + case OB_MESH: { + Mesh &mesh = *static_cast(object.data); + Mesh *new_mesh = geometry.get_component_for_write().release(); + if (!new_mesh) { + BKE_mesh_clear_geometry(&mesh); + if (object.mode == OB_MODE_EDIT) { + EDBM_mesh_make(&object, SCE_SELECT_VERTEX, true); + } + break; + } + + /* Anonymous attributes shouldn't be available on the applied geometry. */ + new_mesh->attributes_for_write().remove_anonymous(); + + BKE_object_material_from_eval_data(&bmain, &object, &new_mesh->id); + BKE_mesh_nomain_to_mesh(new_mesh, &mesh, &object); + if (object.mode == OB_MODE_EDIT) { + EDBM_mesh_make(&object, SCE_SELECT_VERTEX, true); + BKE_editmesh_looptri_and_normals_calc(mesh.edit_mesh); + } + break; + } + } +} + +static int run_node_group_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *active_object = CTX_data_active_object(C); + if (!active_object) { + return OPERATOR_CANCELLED; + } + if (active_object->mode == OB_MODE_OBJECT) { + return OPERATOR_CANCELLED; + } + const eObjectMode mode = eObjectMode(active_object->mode); + + const bNodeTree *node_tree = get_node_group(*C); + if (!node_tree) { + return OPERATOR_CANCELLED; + } + + const nodes::GeometryNodesLazyFunctionGraphInfo *lf_graph_info = + nodes::ensure_geometry_nodes_lazy_function_graph(*node_tree); + if (lf_graph_info == nullptr) { + BKE_report(op->reports, RPT_ERROR, "Cannot evaluate node group"); + return OPERATOR_CANCELLED; + } + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data( + scene, view_layer, CTX_wm_view3d(C), &objects_len, mode); + + OperatorComputeContext compute_context(op->type->idname); + + for (Object *object : Span(objects, objects_len)) { + if (!ELEM(object->type, OB_CURVES, OB_POINTCLOUD, OB_MESH)) { + continue; + } + nodes::GeoNodesOperatorData operator_eval_data{}; + operator_eval_data.depsgraph = depsgraph; + operator_eval_data.self_object = object; + + bke::GeometrySet geometry_orig = get_original_geometry_eval_copy(*object); + + bke::GeometrySet new_geometry = nodes::execute_geometry_nodes_on_geometry( + *node_tree, + op->properties, + compute_context, + std::move(geometry_orig), + [&](nodes::GeoNodesLFUserData &user_data) { + user_data.operator_data = &operator_eval_data; + user_data.log_socket_values = false; + }); + + store_result_geometry(*bmain, *object, std::move(new_geometry)); + + DEG_id_tag_update(static_cast(object->data), ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, object->data); + } + + MEM_SAFE_FREE(objects); + + return OPERATOR_FINISHED; +} + +static int run_node_group_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) +{ + const bNodeTree *node_tree = get_node_group(*C); + if (!node_tree) { + return OPERATOR_CANCELLED; + } + + nodes::update_input_properties_from_node_tree(*node_tree, op->properties, *op->properties); + nodes::update_output_properties_from_node_tree(*node_tree, op->properties, *op->properties); + + return run_node_group_exec(C, op); +} + +static char *run_node_group_get_description(bContext *C, + wmOperatorType * /*ot*/, + PointerRNA * /*ptr*/) +{ + const asset_system::AssetRepresentation *asset = get_context_asset(*C); + if (!asset) { + return nullptr; + } + const char *description = asset->get_metadata().description; + if (!description) { + return nullptr; + } + return BLI_strdup(description); +} + +static bool run_node_group_poll(bContext *C) +{ + const asset_system::AssetRepresentation *asset = get_context_asset(*C); + if (!asset) { + return false; + } + const Object *object = CTX_data_active_object(C); + if (object->type != OB_CURVES) { + return false; + } + if (object->mode != OB_MODE_SCULPT_CURVES) { + return false; + } + return true; +} + +void GEOMETRY_OT_execute_node_group(wmOperatorType *ot) +{ + ot->name = "Run Node Group"; + ot->idname = __func__; + ot->description = "Execute a node group on geometry"; + + ot->poll = run_node_group_poll; + ot->invoke = run_node_group_invoke; + ot->exec = run_node_group_exec; + ot->get_description = run_node_group_get_description; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Menu + * \{ */ + +static bool asset_menu_poll(const bContext *C, MenuType * /*mt*/) +{ + return CTX_wm_view3d(C); +} + +static asset::AssetItemTree &get_static_item_tree() +{ + static asset::AssetItemTree tree; + return tree; +} + +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; + AssetTag operator_tag; + STRNCPY(operator_tag.name, "Operator"); + BLI_addtail(&type_filter.tags, &operator_tag); + 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; + } + return true; + }; + const AssetLibraryReference library = asset_system::all_library_reference(); + return asset::build_filtered_all_catalog_tree(library, C, type_filter, meta_data_filter); +} + +/** + * Avoid adding a separate root catalog when the assets have already been added to one of the + * builtin menus. The need to define the builtin menu labels here is non-ideal. We don't have + * any UI introspection that can do this though. + */ +static Set get_builtin_menus(const ObjectType object_type, const eObjectMode mode) +{ + Set menus; + switch (object_type) { + case OB_CURVES: + menus.add_new("View"); + menus.add_new("Select"); + menus.add_new("Curves"); + break; + case OB_MESH: + switch (mode) { + case OB_MODE_EDIT: + menus.add_new("View"); + menus.add_new("Select"); + menus.add_new("Add"); + menus.add_new("Mesh"); + menus.add_new("Vertex"); + menus.add_new("Edge"); + menus.add_new("Face"); + menus.add_new("UV"); + break; + case OB_MODE_SCULPT: + menus.add_new("View"); + menus.add_new("Sculpt"); + menus.add_new("Mask"); + menus.add_new("Face Sets"); + break; + case OB_MODE_VERTEX_PAINT: + menus.add_new("View"); + menus.add_new("Paint"); + break; + case OB_MODE_WEIGHT_PAINT: + menus.add_new("View"); + menus.add_new("Weights"); + break; + default: + break; + } + default: + break; + } + return menus; +} + +static void node_add_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 auto &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); + PointerRNA asset_ptr = asset::create_asset_rna_ptr(asset); + uiLayoutSetContextPointer(col, "asset", &asset_ptr); + uiItemO(col, IFACE_(asset->get_name().c_str()), ICON_NONE, "GEOMETRY_OT_execute_node_group"); + } + + 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 &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); + }); +} + +MenuType node_group_operator_assets_menu() +{ + MenuType type{}; + STRNCPY(type.idname, "GEO_MT_node_operator_catalog_assets"); + type.poll = asset_menu_poll; + type.draw = node_add_catalog_assets_draw; + type.listener = asset::asset_reading_region_listen_fn; + return type; +} + +void ui_template_node_operator_asset_menu_items(uiLayout &layout, + bContext &C, + const StringRef catalog_path) +{ + 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, "GEO_MT_node_operator_catalog_assets"); +} + +void ui_template_node_operator_asset_root_items(uiLayout &layout, bContext &C) +{ + bScreen &screen = *CTX_wm_screen(&C); + const Object *active_object = CTX_data_active_object(&C); + if (!active_object) { + return; + } + asset::AssetItemTree &tree = get_static_item_tree(); + tree = build_catalog_tree(C); + + const bool loading_finished = all_loading_finished(); + if (tree.catalogs.is_empty() && loading_finished) { + return; + } + if (!loading_finished) { + uiItemL(&layout, IFACE_("Loading Asset Libraries"), ICON_INFO); + } + + asset_system::AssetLibrary *all_library = ED_assetlist_library_get_once_available( + asset_system::all_library_reference()); + if (!all_library) { + return; + } + + const Set builtin_menus = get_builtin_menus(ObjectType(active_object->type), + eObjectMode(active_object->mode)); + + tree.catalogs.foreach_root_item([&](asset_system::AssetCatalogTreeItem &item) { + if (builtin_menus.contains(item.get_name())) { + return; + } + 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); + }); +} + +/** \} */ + +} // namespace blender::ed::geometry diff --git a/source/blender/editors/include/ED_geometry.h b/source/blender/editors/include/ED_geometry.h index 477da62a3d8..a1ba3491512 100644 --- a/source/blender/editors/include/ED_geometry.h +++ b/source/blender/editors/include/ED_geometry.h @@ -8,9 +8,15 @@ #pragma once -#include "BKE_attribute.h" #include "DNA_customdata_types.h" +#include "BKE_attribute.h" +#include "BKE_screen.h" + +#ifdef __cplusplus +# include "BLI_string_ref.hh" +#endif + #ifdef __cplusplus extern "C" { #endif @@ -34,3 +40,18 @@ bool ED_geometry_attribute_convert(struct Mesh *mesh, #ifdef __cplusplus } #endif + +#ifdef __cplusplus + +namespace blender::ed::geometry { + +MenuType node_group_operator_assets_menu(); + +void ui_template_node_operator_asset_menu_items(uiLayout &layout, + bContext &C, + StringRef catalog_path); +void ui_template_node_operator_asset_root_items(uiLayout &layout, bContext &C); + +} // namespace blender::ed::geometry + +#endif diff --git a/source/blender/editors/space_node/add_menu_assets.cc b/source/blender/editors/space_node/add_menu_assets.cc index ef779776a54..07aefc51d1f 100644 --- a/source/blender/editors/space_node/add_menu_assets.cc +++ b/source/blender/editors/space_node/add_menu_assets.cc @@ -19,7 +19,6 @@ #include "BLT_translation.h" #include "RNA_access.h" -#include "RNA_prototypes.h" #include "ED_asset.h" #include "ED_screen.h" @@ -33,129 +32,25 @@ static bool node_add_menu_poll(const bContext *C, MenuType * /*mt*/) return CTX_wm_space_node(C); } -static void node_add_menu_assets_listen_fn(const wmRegionListenerParams *params) -{ - const wmNotifier *wmn = params->notifier; - ARegion *region = params->region; - - switch (wmn->category) { - case NC_ASSET: - if (wmn->data == ND_ASSET_LIST_READING) { - ED_region_tag_refresh_ui(region); - } - break; - } -} - -struct AssetItemTree { - asset_system::AssetCatalogTree catalogs; - MultiValueMap - assets_per_path; -}; - -static AssetLibraryReference all_library_reference() -{ - AssetLibraryReference all_library_ref{}; - all_library_ref.custom_library_index = -1; - all_library_ref.type = ASSET_LIBRARY_ALL; - return all_library_ref; -} - static bool all_loading_finished() { - AssetLibraryReference all_library_ref = all_library_reference(); + AssetLibraryReference all_library_ref = asset_system::all_library_reference(); return ED_assetlist_is_loaded(&all_library_ref); } -static asset_system::AssetLibrary *get_all_library_once_available() +static asset::AssetItemTree build_catalog_tree(const bContext &C, const bNodeTree &node_tree) { - const AssetLibraryReference all_library_ref = all_library_reference(); - return ED_assetlist_library_get_once_available(all_library_ref); -} - -/** - * The menus want 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. - */ -static PointerRNA persistent_catalog_path_rna_pointer( - 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 {&owner_screen.id, - &RNA_AssetCatalogPath, - const_cast(&path)}; -} - -static AssetItemTree build_catalog_tree(const bContext &C, const bNodeTree *node_tree) -{ - if (!node_tree) { - return {}; - } - - /* Find all the matching node group assets for every catalog path. */ - MultiValueMap - assets_per_path; - AssetFilterSettings type_filter{}; type_filter.id_types = FILTER_ID_NT; - - const AssetLibraryReference all_library_ref = all_library_reference(); - - ED_assetlist_storage_fetch(&all_library_ref, &C); - ED_assetlist_ensure_previews_job(&all_library_ref, &C); - - asset_system::AssetLibrary *all_library = get_all_library_once_available(); - if (!all_library) { - return {}; - } - - ED_assetlist_iterate(all_library_ref, [&](asset_system::AssetRepresentation &asset) { - if (!ED_asset_filter_matches_asset(&type_filter, asset)) { - return true; - } - const AssetMetaData &meta_data = asset.get_metadata(); + 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) != node_tree->type) { - return true; + if (tree_type == nullptr || IDP_Int(tree_type) != node_tree.type) { + return false; } - if (BLI_uuid_is_nil(meta_data.catalog_id)) { - return true; - } - - const asset_system::AssetCatalog *catalog = all_library->catalog_service->find_catalog( - meta_data.catalog_id); - if (catalog == nullptr) { - return true; - } - assets_per_path.add(catalog->path, &asset); return true; - }); - - /* Build an own tree without any of the catalogs that don't have proper node group assets. */ - asset_system::AssetCatalogTree catalogs_with_node_assets; - asset_system::AssetCatalogTree &catalog_tree = *all_library->catalog_service->get_catalog_tree(); - catalog_tree.foreach_item([&](asset_system::AssetCatalogTreeItem &item) { - if (assets_per_path.lookup(item.catalog_path()).is_empty()) { - return; - } - asset_system::AssetCatalog *catalog = all_library->catalog_service->find_catalog( - item.get_catalog_id()); - if (catalog == nullptr) { - return; - } - catalogs_with_node_assets.insert_item(*catalog); - }); - - return {std::move(catalogs_with_node_assets), std::move(assets_per_path)}; + }; + const AssetLibraryReference library = asset_system::all_library_reference(); + return asset::build_filtered_all_catalog_tree(library, C, type_filter, meta_data_filter); } static void node_add_catalog_assets_draw(const bContext *C, Menu *menu) @@ -166,7 +61,7 @@ static void node_add_catalog_assets_draw(const bContext *C, Menu *menu) BLI_assert_unreachable(); return; } - AssetItemTree &tree = *snode.runtime->assets_for_menu; + asset::AssetItemTree &tree = *snode.runtime->assets_for_menu; const bNodeTree *edit_tree = snode.edittree; if (!edit_tree) { return; @@ -192,21 +87,20 @@ static void node_add_catalog_assets_draw(const bContext *C, Menu *menu) for (const asset_system::AssetRepresentation *asset : assets) { uiLayout *col = uiLayoutColumn(layout, false); - - PointerRNA asset_ptr{ - nullptr, &RNA_AssetRepresentation, const_cast(asset)}; + PointerRNA asset_ptr = asset::create_asset_rna_ptr(asset); uiLayoutSetContextPointer(col, "asset", &asset_ptr); - uiItemO(col, IFACE_(asset->get_name().c_str()), ICON_NONE, "NODE_OT_add_group_asset"); } - asset_system::AssetLibrary *all_library = get_all_library_once_available(); + 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 &child_item) { - PointerRNA path_ptr = persistent_catalog_path_rna_pointer(screen, *all_library, child_item); + PointerRNA path_ptr = asset::persistent_catalog_path_rna_pointer( + screen, *all_library, child_item); if (path_ptr.data == nullptr) { return; } @@ -222,15 +116,18 @@ static void add_root_catalogs_draw(const bContext *C, Menu *menu) { bScreen &screen = *CTX_wm_screen(C); SpaceNode &snode = *CTX_wm_space_node(C); - const bNodeTree *edit_tree = snode.edittree; uiLayout *layout = menu->layout; + const bNodeTree *edit_tree = snode.edittree; + if (!edit_tree) { + return; + } - snode.runtime->assets_for_menu = std::make_shared( - build_catalog_tree(*C, edit_tree)); + snode.runtime->assets_for_menu = std::make_shared( + build_catalog_tree(*C, *edit_tree)); const bool loading_finished = all_loading_finished(); - AssetItemTree &tree = *snode.runtime->assets_for_menu; + asset::AssetItemTree &tree = *snode.runtime->assets_for_menu; if (tree.catalogs.is_empty() && loading_finished) { return; } @@ -275,7 +172,8 @@ static void add_root_catalogs_draw(const bContext *C, Menu *menu) return menus; }(); - asset_system::AssetLibrary *all_library = get_all_library_once_available(); + asset_system::AssetLibrary *all_library = ED_assetlist_library_get_once_available( + asset_system::all_library_reference()); if (!all_library) { return; } @@ -284,7 +182,7 @@ static void add_root_catalogs_draw(const bContext *C, Menu *menu) if (all_builtin_menus.contains(item.get_name())) { return; } - PointerRNA path_ptr = persistent_catalog_path_rna_pointer(screen, *all_library, item); + PointerRNA path_ptr = asset::persistent_catalog_path_rna_pointer(screen, *all_library, item); if (path_ptr.data == nullptr) { return; } @@ -300,7 +198,7 @@ MenuType add_catalog_assets_menu_type() STRNCPY(type.idname, "NODE_MT_node_add_catalog_assets"); type.poll = node_add_menu_poll; type.draw = node_add_catalog_assets_draw; - type.listener = node_add_menu_assets_listen_fn; + type.listener = asset::asset_reading_region_listen_fn; return type; } @@ -310,40 +208,36 @@ MenuType add_root_catalogs_menu_type() STRNCPY(type.idname, "NODE_MT_node_add_root_catalogs"); type.poll = node_add_menu_poll; type.draw = add_root_catalogs_draw; - type.listener = node_add_menu_assets_listen_fn; + type.listener = asset::asset_reading_region_listen_fn; return type; } } // namespace blender::ed::space_node -/* Note: This is only necessary because Python can't set an asset catalog path context item. */ void uiTemplateNodeAssetMenuItems(uiLayout *layout, bContext *C, const char *catalog_path) { using namespace blender; + using namespace blender::ed; using namespace blender::ed::space_node; bScreen &screen = *CTX_wm_screen(C); SpaceNode &snode = *CTX_wm_space_node(C); - if (snode.runtime->assets_for_menu == nullptr) { return; } - - AssetItemTree &tree = *snode.runtime->assets_for_menu; + asset::AssetItemTree &tree = *snode.runtime->assets_for_menu; const asset_system::AssetCatalogTreeItem *item = tree.catalogs.find_root_item(catalog_path); if (!item) { return; } - - asset_system::AssetLibrary *all_library = get_all_library_once_available(); + asset_system::AssetLibrary *all_library = ED_assetlist_library_get_once_available( + asset_system::all_library_reference()); if (!all_library) { return; } - - PointerRNA path_ptr = persistent_catalog_path_rna_pointer(screen, *all_library, *item); + 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); diff --git a/source/blender/editors/space_node/add_node_search.cc b/source/blender/editors/space_node/add_node_search.cc index cabf50f50fe..70556702cf4 100644 --- a/source/blender/editors/space_node/add_node_search.cc +++ b/source/blender/editors/space_node/add_node_search.cc @@ -93,9 +93,7 @@ static void gather_search_items_for_all_assets(const bContext &C, const bNodeType &group_node_type = *nodeTypeFind(node_tree.typeinfo->group_idname); nodes::GatherAddNodeSearchParams params(C, group_node_type, node_tree, search_items); - AssetLibraryReference library_ref{}; - library_ref.custom_library_index = -1; - library_ref.type = ASSET_LIBRARY_ALL; + const AssetLibraryReference library_ref = asset_system::all_library_reference(); AssetFilterSettings filter_settings{}; filter_settings.id_types = FILTER_ID_NT; diff --git a/source/blender/editors/space_node/node_context_path.cc b/source/blender/editors/space_node/node_context_path.cc index 0bf54fee8fa..7c2321cd853 100644 --- a/source/blender/editors/space_node/node_context_path.cc +++ b/source/blender/editors/space_node/node_context_path.cc @@ -128,7 +128,7 @@ static void get_context_path_node_geometry(const bContext &C, SpaceNode &snode, Vector &path) { - if (snode.flag & SNODE_PIN) { + if (snode.flag & SNODE_PIN || snode.geometry_nodes_type == SNODE_GEOMETRY_OPERATOR) { context_path_add_node_tree_and_node_groups(snode, path); } else { diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index f2a8093c49b..9396e4e7c41 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -35,9 +35,11 @@ extern "C" { extern const char *node_context_dir[]; }; -namespace blender::ed::space_node { - +namespace blender::ed::asset { struct AssetItemTree; +} + +namespace blender::ed::space_node { /** Temporary data used in node link drag modal operator. */ struct bNodeLinkDrag { @@ -112,7 +114,7 @@ struct SpaceNode_Runtime { * * Stored with a shared pointer so that it can be forward declared. */ - std::shared_ptr assets_for_menu; + std::shared_ptr assets_for_menu; }; enum NodeResizeDirection { diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc index 1e70d638f71..c187531724a 100644 --- a/source/blender/editors/space_view3d/space_view3d.cc +++ b/source/blender/editors/space_view3d/space_view3d.cc @@ -51,6 +51,7 @@ #include "BKE_viewer_path.h" #include "BKE_workspace.h" +#include "ED_geometry.h" #include "ED_object.h" #include "ED_outliner.h" #include "ED_render.h" @@ -2222,5 +2223,8 @@ void ED_spacetype_view3d() art->regionid = RGN_TYPE_XR; BLI_addhead(&st->regiontypes, art); + WM_menutype_add( + MEM_new(__func__, blender::ed::geometry::node_group_operator_assets_menu())); + BKE_spacetype_register(st); } diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 7fc2ccaa71b..9eb2c5d1993 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -1605,7 +1605,12 @@ typedef struct SpaceNode { /** Texture-from object, world or brush (#eSpaceNode_TexFrom). */ short texfrom; /** Shader from object or world (#eSpaceNode_ShaderFrom). */ - short shaderfrom; + char shaderfrom; + /** + * Whether to edit any geometry node group, or follow the active modifier context. + * #SpaceNodeGeometryNodesType. + */ + char geometry_nodes_type; /** Grease-pencil data. */ struct bGPdata *gpd; @@ -1649,6 +1654,12 @@ typedef enum eSpaceNode_ShaderFrom { SNODE_SHADER_LINESTYLE = 2, } eSpaceNode_ShaderFrom; +/** #SpaceNode.geometry_nodes_type */ +typedef enum SpaceNodeGeometryNodesType { + SNODE_GEOMETRY_MODIFIER = 0, + SNODE_GEOMETRY_OPERATOR = 1, +} SpaceNodeGeometryNodesType; + /** #SpaceNode.insert_ofs_dir */ enum { SNODE_INSERTOFS_DIR_RIGHT = 0, diff --git a/source/blender/makesrna/intern/rna_space.cc b/source/blender/makesrna/intern/rna_space.cc index 28cb580a9ae..a1046665780 100644 --- a/source/blender/makesrna/intern/rna_space.cc +++ b/source/blender/makesrna/intern/rna_space.cc @@ -7435,6 +7435,20 @@ static void rna_def_space_node(BlenderRNA *brna) {0, nullptr, 0, nullptr, nullptr}, }; + static const EnumPropertyItem geometry_nodes_type_items[] = { + {SNODE_GEOMETRY_MODIFIER, + "MODIFIER", + 0, + "Modifier", + "Edit node group from active object's active modifier"}, + {SNODE_GEOMETRY_OPERATOR, + "OPERATOR", + 0, + "Operator", + "Edit any geometry node group for use as an operator"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + static const EnumPropertyItem backdrop_channels_items[] = { {SNODE_USE_ALPHA, "COLOR_ALPHA", @@ -7489,6 +7503,13 @@ static void rna_def_space_node(BlenderRNA *brna) RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, nullptr); + prop = RNA_def_property(srna, "geometry_nodes_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "geometry_nodes_type"); + RNA_def_property_enum_items(prop, geometry_nodes_type_items); + RNA_def_property_ui_text(prop, "Geometry Nodes Type", ""); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, NULL); + prop = RNA_def_property(srna, "id", PROP_POINTER, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "ID", "Data-block whose nodes are being edited"); diff --git a/source/blender/makesrna/intern/rna_ui_api.cc b/source/blender/makesrna/intern/rna_ui_api.cc index 824d1c673e2..fc560e934b4 100644 --- a/source/blender/makesrna/intern/rna_ui_api.cc +++ b/source/blender/makesrna/intern/rna_ui_api.cc @@ -37,6 +37,8 @@ const EnumPropertyItem rna_enum_icon_items[] = { # include "DNA_asset_types.h" +# include "ED_geometry.h" + const char *rna_translate_ui_text( const char *text, const char *text_ctxt, StructRNA *type, PropertyRNA *prop, bool translate) { @@ -732,6 +734,19 @@ static uiLayout *rna_uiLayoutColumnWithHeading( return uiLayoutColumnWithHeading(layout, align, heading); } +static void rna_uiLayout_template_node_operator_asset_menu_items(uiLayout *layout, + bContext *C, + const char *catalog_path) +{ + blender::ed::geometry::ui_template_node_operator_asset_menu_items( + *layout, *C, blender::StringRef(catalog_path)); +} + +static void rna_uiLayout_template_node_operator_root_items(uiLayout *layout, bContext *C) +{ + blender::ed::geometry::ui_template_node_operator_asset_root_items(*layout, *C); +} + static int rna_ui_get_rnaptr_icon(bContext *C, PointerRNA *ptr_icon) { return UI_icon_from_rnaptr(C, ptr_icon, RNA_struct_ui_icon(ptr_icon->type), false); @@ -1805,6 +1820,17 @@ 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_node_operator_asset_menu_items", + "rna_uiLayout_template_node_operator_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_root_items", + "rna_uiLayout_template_node_operator_root_items"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + func = RNA_def_function(srna, "template_texture_user", "uiTemplateTextureUser"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); diff --git a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh index 61f1e1394f3..78763d4e6f6 100644 --- a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh +++ b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh @@ -74,6 +74,13 @@ struct GeoNodesModifierData { const Set *socket_log_contexts = nullptr; }; +struct GeoNodesOperatorData { + /** The object currently effected by the operator. */ + const Object *self_object = nullptr; + /** Current evaluated depsgraph. */ + Depsgraph *depsgraph = nullptr; +}; + /** * Custom user data that is passed to every geometry nodes related lazy-function evaluation. */ @@ -82,6 +89,10 @@ struct GeoNodesLFUserData : public lf::UserData { * Data from the modifier that is being evaluated. */ GeoNodesModifierData *modifier_data = nullptr; + /** + * Data from execution as operator in 3D viewport. + */ + GeoNodesOperatorData *operator_data = nullptr; /** * Current compute context. This is different depending in the (nested) node group that is being * evaluated. diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index fa0faa4af87..3ab1a21f182 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -33,6 +33,12 @@ bNodeTreeType *ntreeType_Geometry; static void geometry_node_tree_get_from_context( const bContext *C, bNodeTreeType * /*treetype*/, bNodeTree **r_ntree, ID **r_id, ID **r_from) { + const SpaceNode *snode = CTX_wm_space_node(C); + if (snode->geometry_nodes_type == SNODE_GEOMETRY_OPERATOR) { + *r_ntree = snode->nodetree; + return; + } + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); BKE_view_layer_synced_ensure(scene, view_layer); diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 4a4dd9c740e..f7c8c2e1bbb 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -3463,6 +3463,9 @@ Vector GeometryNodesLazyFunctionSideEffectProvider:: { GeoNodesLFUserData *user_data = dynamic_cast(context.user_data); BLI_assert(user_data != nullptr); + if (!user_data->modifier_data) { + return {}; + } const ComputeContextHash &context_hash = user_data->compute_context->hash(); const GeoNodesModifierData &modifier_data = *user_data->modifier_data; return modifier_data.side_effect_nodes->lookup(context_hash); @@ -3520,6 +3523,10 @@ destruct_ptr GeoNodesLFUserData::get_local(LinearAllocator<> GeoNodesLFLocalUserData::GeoNodesLFLocalUserData(GeoNodesLFUserData &user_data) { + if (user_data.modifier_data == nullptr) { + this->tree_logger = nullptr; + return; + } if (user_data.modifier_data->eval_log != nullptr) { this->tree_logger = &user_data.modifier_data->eval_log->get_local_tree_logger( *user_data.compute_context);