Geometry Nodes: Node group operators initial phase
This PR adds a new operator to run a node group on object geometry. Only curves sculpt mode is supported for now, to simplify the design. A new geometry node editor context to edit operator groups is also added. This allows changing any node group, rather than only node groups that are part of the active modifier context. 3D viewport menus are added with any geometry node group asset in a catalog that contains the `Operator` tag. Currently Blender must be restarted to refresh the list of available operators. This is only the first phase of the node group operator feature. Many more features will be added in next steps. See #101778 Pull Request: https://projects.blender.org/blender/blender/pulls/108947
This commit is contained in:
@@ -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'}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -173,6 +173,8 @@ class AssetLibrary {
|
||||
|
||||
Vector<AssetLibraryReference> all_valid_asset_library_refs();
|
||||
|
||||
AssetLibraryReference all_library_reference();
|
||||
|
||||
} // namespace blender::asset_system
|
||||
|
||||
/**
|
||||
|
||||
@@ -359,4 +359,12 @@ Vector<AssetLibraryReference> 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
|
||||
|
||||
@@ -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<blender::asset_system::CatalogID> 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
|
||||
|
||||
@@ -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<asset_system::AssetCatalogPath, asset_system::AssetRepresentation *>
|
||||
assets_per_path;
|
||||
};
|
||||
|
||||
AssetItemTree build_filtered_all_catalog_tree(
|
||||
const AssetLibraryReference &library_ref,
|
||||
const bContext &C,
|
||||
const AssetFilterSettings &filter_settings,
|
||||
FunctionRef<bool(const AssetMetaData &)> meta_data_filter = {});
|
||||
|
||||
} // namespace blender::ed::asset
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<ID &>(owner_screen.id),
|
||||
&RNA_AssetCatalogPath,
|
||||
const_cast<asset_system::AssetCatalogPath *>(&path)};
|
||||
}
|
||||
|
||||
} // namespace blender::ed::asset
|
||||
|
||||
@@ -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<bool(const AssetMetaData &)> meta_data_filter)
|
||||
{
|
||||
MultiValueMap<asset_system::AssetCatalogPath, asset_system::AssetRepresentation *>
|
||||
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
|
||||
|
||||
@@ -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_system::AssetRepresentation *>(asset);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::asset
|
||||
|
||||
@@ -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
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
546
source/blender/editors/geometry/node_group_operator.cc
Normal file
546
source/blender/editors/geometry/node_group_operator.cc
Normal file
@@ -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<const asset_system::AssetRepresentation *>(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<bNodeTree *>(
|
||||
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<const Curves *>(object.data));
|
||||
return bke::GeometrySet::create_with_curves(curves);
|
||||
}
|
||||
case OB_POINTCLOUD: {
|
||||
PointCloud *points = BKE_pointcloud_copy_for_eval(
|
||||
static_cast<const PointCloud *>(object.data));
|
||||
return bke::GeometrySet::create_with_pointcloud(points);
|
||||
}
|
||||
case OB_MESH: {
|
||||
const Mesh *mesh = static_cast<const Mesh *>(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<Curves *>(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<PointCloud *>(object.data);
|
||||
PointCloud *new_points =
|
||||
geometry.get_component_for_write<bke::PointCloudComponent>().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<Mesh *>(object.data);
|
||||
Mesh *new_mesh = geometry.get_component_for_write<bke::MeshComponent>().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<ID *>(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<std::string> get_builtin_menus(const ObjectType object_type, const eObjectMode mode)
|
||||
{
|
||||
Set<std::string> 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<const asset_system::AssetCatalogPath *>(menu_path_ptr.data);
|
||||
const Span<asset_system::AssetRepresentation *> 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<std::string> 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
|
||||
@@ -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
|
||||
|
||||
@@ -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<asset_system::AssetCatalogPath, asset_system::AssetRepresentation *>
|
||||
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<asset_system::AssetCatalogPath *>(&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<asset_system::AssetCatalogPath, asset_system::AssetRepresentation *>
|
||||
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_system::AssetRepresentation *>(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<AssetItemTree>(
|
||||
build_catalog_tree(*C, edit_tree));
|
||||
snode.runtime->assets_for_menu = std::make_shared<asset::AssetItemTree>(
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -128,7 +128,7 @@ static void get_context_path_node_geometry(const bContext &C,
|
||||
SpaceNode &snode,
|
||||
Vector<ui::ContextPathItem> &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 {
|
||||
|
||||
@@ -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<AssetItemTree> assets_for_menu;
|
||||
std::shared_ptr<asset::AssetItemTree> assets_for_menu;
|
||||
};
|
||||
|
||||
enum NodeResizeDirection {
|
||||
|
||||
@@ -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<MenuType>(__func__, blender::ed::geometry::node_group_operator_assets_menu()));
|
||||
|
||||
BKE_spacetype_register(st);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -74,6 +74,13 @@ struct GeoNodesModifierData {
|
||||
const Set<ComputeContextHash> *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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -3463,6 +3463,9 @@ Vector<const lf::FunctionNode *> GeometryNodesLazyFunctionSideEffectProvider::
|
||||
{
|
||||
GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(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<lf::LocalUserData> 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);
|
||||
|
||||
Reference in New Issue
Block a user