Compare commits
29 Commits
cmbasnett/
...
node-add-a
Author | SHA1 | Date | |
---|---|---|---|
525a031935 | |||
b280dce0a4 | |||
70ed39518e | |||
ddf206d100 | |||
eb665847c2 | |||
b0fc4e5106 | |||
701b6b8aab | |||
02e92af3ef | |||
67f907a2c5 | |||
13951cde59 | |||
bd04c4aef2 | |||
f100fd4609 | |||
d80dc33fe5 | |||
8b6946c911 | |||
f761a1bc7a | |||
61466b761f | |||
ea335357c5 | |||
6025acafd4 | |||
58abb84fe7 | |||
3b07198a08 | |||
d4cd48a162 | |||
d860d78523 | |||
789effa744 | |||
32153c56da | |||
6f368db104 | |||
11d4ed6725 | |||
e287c4d573 | |||
f1ab91e6fb | |||
eaa577bf13 |
@@ -58,6 +58,12 @@ def draw_node_group_add_menu(context, layout):
|
||||
ops.name = "node_tree"
|
||||
ops.value = "bpy.data.node_groups[%r]" % group.name
|
||||
|
||||
def draw_assets_for_catalog(layout, catalog_path):
|
||||
layout.template_node_asset_menu_items(catalog_path=catalog_path)
|
||||
|
||||
def draw_root_assets(layout):
|
||||
layout.menu_contents("NODE_MT_node_add_root_catalogs")
|
||||
|
||||
|
||||
classes = (
|
||||
)
|
||||
|
@@ -16,6 +16,7 @@ class NODE_MT_geometry_node_GEO_ATTRIBUTE(Menu):
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeAttributeDomainSize")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeRemoveAttribute")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeStoreNamedAttribute")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_geometry_node_GEO_COLOR(Menu):
|
||||
@@ -32,6 +33,7 @@ class NODE_MT_geometry_node_GEO_COLOR(Menu):
|
||||
ops.value = "'RGBA'"
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeRGBCurve")
|
||||
node_add_menu.add_node_type(layout, "FunctionNodeSeparateColor")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_geometry_node_GEO_CURVE(Menu):
|
||||
@@ -70,6 +72,7 @@ class NODE_MT_geometry_node_GEO_CURVE(Menu):
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeSetSplineCyclic")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeSetSplineResolution")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeCurveSplineType")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_geometry_node_GEO_PRIMITIVES_CURVE(Menu):
|
||||
@@ -86,6 +89,7 @@ class NODE_MT_geometry_node_GEO_PRIMITIVES_CURVE(Menu):
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeCurveQuadraticBezier")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeCurvePrimitiveQuadrilateral")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeCurveStar")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_geometry_node_curve_topology(Menu):
|
||||
@@ -97,6 +101,7 @@ class NODE_MT_geometry_node_curve_topology(Menu):
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeOffsetPointInCurve")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeCurveOfPoint")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodePointsOfCurve")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_geometry_node_GEO_GEOMETRY(Menu):
|
||||
@@ -122,6 +127,7 @@ class NODE_MT_geometry_node_GEO_GEOMETRY(Menu):
|
||||
layout.separator()
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeSetID")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeSetPosition")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_geometry_node_GEO_INPUT(Menu):
|
||||
@@ -149,6 +155,7 @@ class NODE_MT_geometry_node_GEO_INPUT(Menu):
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeInputPosition")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeInputRadius")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeInputSceneTime")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_geometry_node_GEO_INSTANCE(Menu):
|
||||
@@ -166,6 +173,7 @@ class NODE_MT_geometry_node_GEO_INSTANCE(Menu):
|
||||
layout.separator()
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeInputInstanceRotation")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeInputInstanceScale")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_geometry_node_GEO_MATERIAL(Menu):
|
||||
@@ -181,6 +189,7 @@ class NODE_MT_geometry_node_GEO_MATERIAL(Menu):
|
||||
layout.separator()
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeSetMaterial")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeSetMaterialIndex")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_geometry_node_GEO_MESH(Menu):
|
||||
@@ -219,6 +228,7 @@ class NODE_MT_geometry_node_GEO_MESH(Menu):
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeInputMeshVertexNeighbors")
|
||||
layout.separator()
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeSetShadeSmooth")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_category_PRIMITIVES_MESH(Menu):
|
||||
@@ -235,6 +245,7 @@ class NODE_MT_category_PRIMITIVES_MESH(Menu):
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeMeshCircle")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeMeshLine")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeMeshUVSphere")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_geometry_node_mesh_topology(Menu):
|
||||
@@ -250,6 +261,7 @@ class NODE_MT_geometry_node_mesh_topology(Menu):
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeFaceOfCorner"),
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeOffsetCornerInFace"),
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeVertexOfCorner"),
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_category_GEO_OUTPUT(Menu):
|
||||
@@ -259,6 +271,7 @@ class NODE_MT_category_GEO_OUTPUT(Menu):
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeViewer")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_category_GEO_POINT(Menu):
|
||||
@@ -274,6 +287,7 @@ class NODE_MT_category_GEO_POINT(Menu):
|
||||
node_add_menu.add_node_type(layout, "GeometryNodePointsToVolume")
|
||||
layout.separator()
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeSetPointRadius")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_category_GEO_TEXT(Menu):
|
||||
@@ -290,6 +304,7 @@ class NODE_MT_category_GEO_TEXT(Menu):
|
||||
node_add_menu.add_node_type(layout, "FunctionNodeValueToString")
|
||||
layout.separator()
|
||||
node_add_menu.add_node_type(layout, "FunctionNodeInputSpecialCharacters")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_category_GEO_TEXTURE(Menu):
|
||||
@@ -308,6 +323,7 @@ class NODE_MT_category_GEO_TEXTURE(Menu):
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeTexVoronoi")
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeTexWave")
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeTexWhiteNoise")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_category_GEO_UTILITIES(Menu):
|
||||
@@ -331,6 +347,7 @@ class NODE_MT_category_GEO_UTILITIES(Menu):
|
||||
node_add_menu.add_node_type(layout, "FunctionNodeRandomValue")
|
||||
node_add_menu.add_node_type(layout, "FunctionNodeRotateEuler")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeSwitch")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_category_GEO_UV(Menu):
|
||||
@@ -341,6 +358,7 @@ class NODE_MT_category_GEO_UV(Menu):
|
||||
layout = self.layout
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeUVPackIslands")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeUVUnwrap")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_category_GEO_VECTOR(Menu):
|
||||
@@ -354,6 +372,7 @@ class NODE_MT_category_GEO_VECTOR(Menu):
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeVectorCurve")
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeVectorMath")
|
||||
node_add_menu.add_node_type(layout, "ShaderNodeVectorRotate")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_category_GEO_VOLUME(Menu):
|
||||
@@ -364,6 +383,7 @@ class NODE_MT_category_GEO_VOLUME(Menu):
|
||||
layout = self.layout
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeVolumeCube")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeVolumeToMesh")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_category_GEO_GROUP(Menu):
|
||||
@@ -373,6 +393,7 @@ class NODE_MT_category_GEO_GROUP(Menu):
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
node_add_menu.draw_node_group_add_menu(context, layout)
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_category_GEO_LAYOUT(Menu):
|
||||
@@ -383,6 +404,7 @@ class NODE_MT_category_GEO_LAYOUT(Menu):
|
||||
layout = self.layout
|
||||
node_add_menu.add_node_type(layout, "NodeFrame")
|
||||
node_add_menu.add_node_type(layout, "NodeReroute")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
class NODE_MT_geometry_node_add_all(Menu):
|
||||
@@ -413,6 +435,7 @@ class NODE_MT_geometry_node_add_all(Menu):
|
||||
layout.menu("NODE_MT_category_GEO_VOLUME")
|
||||
layout.menu("NODE_MT_category_GEO_GROUP")
|
||||
layout.menu("NODE_MT_category_GEO_LAYOUT")
|
||||
node_add_menu.draw_root_assets(layout)
|
||||
|
||||
|
||||
classes = (
|
||||
|
@@ -307,7 +307,7 @@ class AssetCatalogTreeItem {
|
||||
|
||||
/** Iterate over children calling \a callback for each of them, but do not recurse into their
|
||||
* children. */
|
||||
void foreach_child(const ItemIterFn callback);
|
||||
void foreach_child(ItemIterFn callback);
|
||||
|
||||
protected:
|
||||
/** Child tree items, ordered by their names. */
|
||||
@@ -345,10 +345,15 @@ class AssetCatalogTree {
|
||||
/** Ensure an item representing \a path is in the tree, adding it if necessary. */
|
||||
void insert_item(const AssetCatalog &catalog);
|
||||
|
||||
void foreach_item(const AssetCatalogTreeItem::ItemIterFn callback);
|
||||
void foreach_item(ItemIterFn callback);
|
||||
/** Iterate over root items calling \a callback for each of them, but do not recurse into their
|
||||
* children. */
|
||||
void foreach_root_item(const ItemIterFn callback);
|
||||
void foreach_root_item(ItemIterFn callback);
|
||||
|
||||
bool is_empty() const;
|
||||
|
||||
AssetCatalogTreeItem *find_item(const AssetCatalogPath &path);
|
||||
AssetCatalogTreeItem *find_root_item(const AssetCatalogPath &path);
|
||||
|
||||
protected:
|
||||
/** Child tree items, ordered by their names. */
|
||||
|
@@ -49,7 +49,7 @@ class AssetCatalogPath {
|
||||
|
||||
AssetCatalogPath() = default;
|
||||
AssetCatalogPath(StringRef path);
|
||||
AssetCatalogPath(const std::string &path);
|
||||
AssetCatalogPath(std::string path);
|
||||
AssetCatalogPath(const char *path);
|
||||
AssetCatalogPath(const AssetCatalogPath &other_path) = default;
|
||||
AssetCatalogPath(AssetCatalogPath &&other_path) noexcept;
|
||||
|
@@ -10,6 +10,8 @@
|
||||
# error This is a C++-only header file. Use BKE_asset_library.h instead.
|
||||
#endif
|
||||
|
||||
#include "DNA_asset_types.h"
|
||||
|
||||
#include "BKE_asset_library.h"
|
||||
|
||||
#include "BKE_asset_catalog.hh"
|
||||
@@ -44,19 +46,24 @@ struct AssetLibrary {
|
||||
* No-op if the catalog cannot be found. This could be the kind of "the
|
||||
* catalog definition file is corrupt/lost" scenario that the simple name is
|
||||
* meant to help recover from. */
|
||||
void refresh_catalog_simplename(struct AssetMetaData *asset_data);
|
||||
void refresh_catalog_simplename(AssetMetaData *asset_data);
|
||||
|
||||
void on_blend_save_handler_register();
|
||||
void on_blend_save_handler_unregister();
|
||||
|
||||
void on_blend_save_post(struct Main *, struct PointerRNA **pointers, int num_pointers);
|
||||
void on_blend_save_post(Main *bmain, PointerRNA **pointers, int num_pointers);
|
||||
|
||||
private:
|
||||
bCallbackFuncStore on_save_callback_store_{};
|
||||
};
|
||||
|
||||
Vector<AssetLibraryReference> all_asset_library_refs();
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
blender::bke::AssetLibrary *BKE_asset_library_load(const Main *bmain,
|
||||
const AssetLibraryReference &library_reference);
|
||||
|
||||
blender::bke::AssetCatalogService *BKE_asset_library_get_catalog_service(
|
||||
const ::AssetLibrary *library);
|
||||
blender::bke::AssetCatalogTree *BKE_asset_library_get_catalog_tree(const ::AssetLibrary *library);
|
||||
|
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "BKE_asset_catalog.hh"
|
||||
#include "BKE_asset_library.h"
|
||||
#include "BKE_asset_library.hh"
|
||||
|
||||
#include "BLI_fileops.hh"
|
||||
#include "BLI_path_util.h"
|
||||
@@ -787,6 +788,41 @@ void AssetCatalogTree::foreach_root_item(const ItemIterFn callback)
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetCatalogTree::is_empty() const
|
||||
{
|
||||
return root_items_.empty();
|
||||
}
|
||||
|
||||
AssetCatalogTreeItem *AssetCatalogTree::find_item(const AssetCatalogPath &path)
|
||||
{
|
||||
AssetCatalogTreeItem *result = nullptr;
|
||||
this->foreach_item([&](AssetCatalogTreeItem &item) {
|
||||
if (result) {
|
||||
/* There is no way to stop iteration. */
|
||||
return;
|
||||
}
|
||||
if (item.catalog_path() == path) {
|
||||
result = &item;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
AssetCatalogTreeItem *AssetCatalogTree::find_root_item(const AssetCatalogPath &path)
|
||||
{
|
||||
AssetCatalogTreeItem *result = nullptr;
|
||||
this->foreach_root_item([&](AssetCatalogTreeItem &item) {
|
||||
if (result) {
|
||||
/* There is no way to stop iteration. */
|
||||
return;
|
||||
}
|
||||
if (item.catalog_path() == path) {
|
||||
result = &item;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
@@ -12,7 +12,7 @@ namespace blender::bke {
|
||||
|
||||
const char AssetCatalogPath::SEPARATOR = '/';
|
||||
|
||||
AssetCatalogPath::AssetCatalogPath(const std::string &path) : path_(path)
|
||||
AssetCatalogPath::AssetCatalogPath(std::string path) : path_(std::move(path))
|
||||
{
|
||||
}
|
||||
|
||||
|
@@ -19,6 +19,13 @@
|
||||
|
||||
bool blender::bke::AssetLibrary::save_catalogs_when_file_is_saved = true;
|
||||
|
||||
blender::bke::AssetLibrary *BKE_asset_library_load(const Main *bmain,
|
||||
const AssetLibraryReference &library_reference)
|
||||
{
|
||||
blender::bke::AssetLibraryService *service = blender::bke::AssetLibraryService::get();
|
||||
return service->get_asset_library(bmain, library_reference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loading an asset library at this point only means loading the catalogs. Later on this should
|
||||
* invoke reading of asset representations too.
|
||||
@@ -172,4 +179,23 @@ void AssetLibrary::refresh_catalog_simplename(struct AssetMetaData *asset_data)
|
||||
}
|
||||
STRNCPY(asset_data->catalog_simple_name, catalog->simple_name.c_str());
|
||||
}
|
||||
|
||||
Vector<AssetLibraryReference> all_asset_library_refs()
|
||||
{
|
||||
Vector<AssetLibraryReference> result;
|
||||
int i;
|
||||
LISTBASE_FOREACH_INDEX (const bUserAssetLibrary *, asset_library, &U.asset_libraries, i) {
|
||||
AssetLibraryReference library_ref{};
|
||||
library_ref.custom_library_index = i;
|
||||
library_ref.type = ASSET_LIBRARY_CUSTOM;
|
||||
result.append(library_ref);
|
||||
}
|
||||
|
||||
AssetLibraryReference library_ref{};
|
||||
library_ref.custom_library_index = -1;
|
||||
library_ref.type = ASSET_LIBRARY_LOCAL;
|
||||
result.append(library_ref);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
@@ -7,11 +7,15 @@
|
||||
#include "asset_library_service.hh"
|
||||
|
||||
#include "BKE_blender.h"
|
||||
#include "BKE_preferences.h"
|
||||
|
||||
#include "BLI_fileops.h" /* For PATH_MAX (at least on Windows). */
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "DNA_asset_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
static CLG_LogRef LOG = {"bke.asset_service"};
|
||||
@@ -38,6 +42,38 @@ void AssetLibraryService::destroy()
|
||||
instance_.reset();
|
||||
}
|
||||
|
||||
AssetLibrary *AssetLibraryService::get_asset_library(
|
||||
const Main *bmain, const AssetLibraryReference &library_reference)
|
||||
{
|
||||
if (library_reference.type == ASSET_LIBRARY_LOCAL) {
|
||||
/* For the "Current File" library we get the asset library root path based on main. */
|
||||
char root_path[FILE_MAX];
|
||||
if (bmain) {
|
||||
BKE_asset_library_find_suitable_root_path_from_main(bmain, root_path);
|
||||
}
|
||||
else {
|
||||
root_path[0] = '\0';
|
||||
}
|
||||
|
||||
if (root_path[0] == '\0') {
|
||||
/* File wasn't saved yet. */
|
||||
return get_asset_library_current_file();
|
||||
}
|
||||
|
||||
return get_asset_library_on_disk(root_path);
|
||||
}
|
||||
if (library_reference.type == ASSET_LIBRARY_CUSTOM) {
|
||||
bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index(
|
||||
&U, library_reference.custom_library_index);
|
||||
|
||||
if (user_library) {
|
||||
return get_asset_library_on_disk(user_library->path);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::string normalize_directory_path(StringRefNull directory)
|
||||
{
|
||||
|
@@ -44,6 +44,9 @@ class AssetLibraryService {
|
||||
/** Destroy the AssetLibraryService singleton. It will be reallocated by #get() if necessary. */
|
||||
static void destroy();
|
||||
|
||||
AssetLibrary *get_asset_library(const Main *bmain,
|
||||
const AssetLibraryReference &library_reference);
|
||||
|
||||
/**
|
||||
* Get the given asset library. Opens it (i.e. creates a new AssetLibrary instance) if necessary.
|
||||
*/
|
||||
|
@@ -23,6 +23,7 @@ struct wmNotifier;
|
||||
*/
|
||||
void ED_assetlist_storage_fetch(const struct AssetLibraryReference *library_reference,
|
||||
const struct bContext *C);
|
||||
bool ED_assetlist_is_loaded(const struct AssetLibraryReference *library_reference);
|
||||
void ED_assetlist_ensure_previews_job(const struct AssetLibraryReference *library_reference,
|
||||
const struct bContext *C);
|
||||
void ED_assetlist_clear(const struct AssetLibraryReference *library_reference, struct bContext *C);
|
||||
|
@@ -422,6 +422,18 @@ void ED_assetlist_storage_fetch(const AssetLibraryReference *library_reference,
|
||||
AssetListStorage::fetch_library(*library_reference, *C);
|
||||
}
|
||||
|
||||
bool ED_assetlist_is_loaded(const AssetLibraryReference *library_reference)
|
||||
{
|
||||
AssetList *list = AssetListStorage::lookup_list(*library_reference);
|
||||
if (!list) {
|
||||
return false;
|
||||
}
|
||||
if (list->needsRefetch()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ED_assetlist_ensure_previews_job(const AssetLibraryReference *library_reference,
|
||||
const bContext *C)
|
||||
{
|
||||
|
@@ -2524,6 +2524,7 @@ void uiTemplateNodeView(uiLayout *layout,
|
||||
struct bNodeTree *ntree,
|
||||
struct bNode *node,
|
||||
struct bNodeSocket *input);
|
||||
void uiTemplateNodeAssetMenuItems(uiLayout *layout, struct bContext *C, const char *catalog_path);
|
||||
void uiTemplateTextureUser(uiLayout *layout, struct bContext *C);
|
||||
/**
|
||||
* Button to quickly show texture in Properties Editor texture tab.
|
||||
|
@@ -29,6 +29,7 @@ set(INC
|
||||
|
||||
set(SRC
|
||||
add_node_search.cc
|
||||
add_menu_assets.cc
|
||||
drawnode.cc
|
||||
link_drag_search.cc
|
||||
node_add.cc
|
||||
|
298
source/blender/editors/space_node/add_menu_assets.cc
Normal file
298
source/blender/editors/space_node/add_menu_assets.cc
Normal file
@@ -0,0 +1,298 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_multi_value_map.hh"
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "BKE_asset.h"
|
||||
#include "BKE_asset_catalog.hh"
|
||||
#include "BKE_asset_library.hh"
|
||||
#include "BKE_idprop.h"
|
||||
#include "BKE_screen.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "ED_asset.h"
|
||||
|
||||
#include "node_intern.hh"
|
||||
|
||||
namespace blender::ed::space_node {
|
||||
|
||||
static bool node_add_menu_poll(const bContext *C, MenuType * /*mt*/)
|
||||
{
|
||||
return CTX_wm_space_node(C);
|
||||
}
|
||||
|
||||
struct LibraryAsset {
|
||||
AssetLibraryReference library_ref;
|
||||
AssetHandle handle;
|
||||
};
|
||||
|
||||
struct LibraryCatalog {
|
||||
bke::AssetLibrary *library;
|
||||
const bke::AssetCatalog *catalog;
|
||||
};
|
||||
|
||||
struct AssetItemTree {
|
||||
bke::AssetCatalogTree catalogs;
|
||||
MultiValueMap<bke::AssetCatalogPath, LibraryAsset> assets_per_path;
|
||||
Map<const bke::AssetCatalogTreeItem *, bke::AssetCatalogPath> full_catalog_per_tree_item;
|
||||
};
|
||||
|
||||
static bool all_loading_finished()
|
||||
{
|
||||
for (const AssetLibraryReference &library : bke::all_asset_library_refs()) {
|
||||
if (!ED_assetlist_is_loaded(&library)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static AssetItemTree build_catalog_tree(const bContext &C, const bNodeTree *node_tree)
|
||||
{
|
||||
if (!node_tree) {
|
||||
return {};
|
||||
}
|
||||
const Main &bmain = *CTX_data_main(&C);
|
||||
const Vector<AssetLibraryReference> all_libraries = bke::all_asset_library_refs();
|
||||
|
||||
/* Merge catalogs from all libraries to deduplicate menu items. Also store the catalog and
|
||||
* library for each asset ID in order to use them later when retrieving assets and removing
|
||||
* empty catalogs. */
|
||||
Map<bke::CatalogID, LibraryCatalog> id_to_catalog_map;
|
||||
bke::AssetCatalogTree catalogs_from_all_libraries;
|
||||
for (const AssetLibraryReference &ref : all_libraries) {
|
||||
if (bke::AssetLibrary *library = BKE_asset_library_load(&bmain, ref)) {
|
||||
if (bke::AssetCatalogTree *tree = library->catalog_service->get_catalog_tree()) {
|
||||
tree->foreach_item([&](bke::AssetCatalogTreeItem &item) {
|
||||
const bke::CatalogID &id = item.get_catalog_id();
|
||||
bke::AssetCatalog *catalog = library->catalog_service->find_catalog(id);
|
||||
catalogs_from_all_libraries.insert_item(*catalog);
|
||||
id_to_catalog_map.add(item.get_catalog_id(), LibraryCatalog{library, catalog});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Find all the matching node group assets for every catalog path. */
|
||||
MultiValueMap<bke::AssetCatalogPath, LibraryAsset> assets_per_path;
|
||||
for (const AssetLibraryReference &library_ref : all_libraries) {
|
||||
AssetFilterSettings type_filter{};
|
||||
type_filter.id_types = FILTER_ID_NT;
|
||||
|
||||
ED_assetlist_storage_fetch(&library_ref, &C);
|
||||
ED_assetlist_ensure_previews_job(&library_ref, &C);
|
||||
ED_assetlist_iterate(library_ref, [&](AssetHandle asset) {
|
||||
if (!ED_asset_filter_matches_asset(&type_filter, &asset)) {
|
||||
return true;
|
||||
}
|
||||
const AssetMetaData &meta_data = *ED_asset_handle_get_metadata(&asset);
|
||||
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 (BLI_uuid_is_nil(meta_data.catalog_id)) {
|
||||
return true;
|
||||
}
|
||||
const LibraryCatalog &library_catalog = id_to_catalog_map.lookup(meta_data.catalog_id);
|
||||
assets_per_path.add(library_catalog.catalog->path, LibraryAsset{library_ref, asset});
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/* Build the final tree without any of the catalogs that don't have proper node group assets. */
|
||||
bke::AssetCatalogTree catalogs_with_node_assets;
|
||||
catalogs_from_all_libraries.foreach_item([&](bke::AssetCatalogTreeItem &item) {
|
||||
if (!assets_per_path.lookup(item.catalog_path()).is_empty()) {
|
||||
const bke::CatalogID &id = item.get_catalog_id();
|
||||
const LibraryCatalog &library_catalog = id_to_catalog_map.lookup(id);
|
||||
bke::AssetCatalog *catalog = library_catalog.library->catalog_service->find_catalog(id);
|
||||
catalogs_with_node_assets.insert_item(*catalog);
|
||||
}
|
||||
});
|
||||
|
||||
/* Build another map storing full asset paths for each tree item, in order to have stable
|
||||
* pointers to asset catalog paths to use for context pointers. This is necessary because
|
||||
* #bke::AssetCatalogTreeItem doesn't store its full path directly. */
|
||||
Map<const bke::AssetCatalogTreeItem *, bke::AssetCatalogPath> full_catalog_per_tree_item;
|
||||
catalogs_with_node_assets.foreach_item([&](bke::AssetCatalogTreeItem &item) {
|
||||
full_catalog_per_tree_item.add_new(&item, item.catalog_path());
|
||||
});
|
||||
|
||||
return {std::move(catalogs_with_node_assets),
|
||||
std::move(assets_per_path),
|
||||
std::move(full_catalog_per_tree_item)};
|
||||
}
|
||||
|
||||
static void node_add_catalog_assets_draw(const bContext *C, Menu *menu)
|
||||
{
|
||||
bScreen &screen = *CTX_wm_screen(C);
|
||||
const SpaceNode &snode = *CTX_wm_space_node(C);
|
||||
if (!snode.runtime->assets_for_menu) {
|
||||
BLI_assert_unreachable();
|
||||
return;
|
||||
}
|
||||
AssetItemTree &tree = *snode.runtime->assets_for_menu;
|
||||
const bNodeTree *edit_tree = snode.edittree;
|
||||
if (!edit_tree) {
|
||||
return;
|
||||
}
|
||||
|
||||
const PointerRNA menu_path_ptr = CTX_data_pointer_get(C, "asset_catalog_path");
|
||||
if (RNA_pointer_is_null(&menu_path_ptr)) {
|
||||
return;
|
||||
}
|
||||
const bke::AssetCatalogPath &menu_path = *static_cast<const bke::AssetCatalogPath *>(
|
||||
menu_path_ptr.data);
|
||||
|
||||
const Span<LibraryAsset> asset_items = tree.assets_per_path.lookup(menu_path);
|
||||
bke::AssetCatalogTreeItem *catalog_item = tree.catalogs.find_item(menu_path);
|
||||
BLI_assert(catalog_item != nullptr);
|
||||
|
||||
if (asset_items.is_empty() && !catalog_item->has_children()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uiLayout *layout = menu->layout;
|
||||
uiItemS(layout);
|
||||
|
||||
for (const LibraryAsset &item : asset_items) {
|
||||
uiLayout *col = uiLayoutColumn(layout, false);
|
||||
PointerRNA file{
|
||||
&screen.id, &RNA_FileSelectEntry, const_cast<FileDirEntry *>(item.handle.file_data)};
|
||||
uiLayoutSetContextPointer(col, "active_file", &file);
|
||||
|
||||
PointerRNA library_ptr{&screen.id,
|
||||
&RNA_AssetLibraryReference,
|
||||
const_cast<AssetLibraryReference *>(&item.library_ref)};
|
||||
uiLayoutSetContextPointer(col, "asset_library_ref", &library_ptr);
|
||||
|
||||
uiItemO(col, ED_asset_handle_get_name(&item.handle), ICON_NONE, "NODE_OT_add_group_asset");
|
||||
}
|
||||
|
||||
catalog_item->foreach_child([&](bke::AssetCatalogTreeItem &child_item) {
|
||||
const bke::AssetCatalogPath &path = tree.full_catalog_per_tree_item.lookup(&child_item);
|
||||
PointerRNA path_ptr{
|
||||
&screen.id, &RNA_AssetCatalogPath, const_cast<bke::AssetCatalogPath *>(&path)};
|
||||
uiLayout *col = uiLayoutColumn(layout, false);
|
||||
uiLayoutSetContextPointer(col, "asset_catalog_path", &path_ptr);
|
||||
uiItemM(col, "NODE_MT_node_add_catalog_assets", path.name().c_str(), ICON_NONE);
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
snode.runtime->assets_for_menu.reset();
|
||||
snode.runtime->assets_for_menu = std::make_shared<AssetItemTree>(
|
||||
build_catalog_tree(*C, edit_tree));
|
||||
|
||||
const bool loading_finished = all_loading_finished();
|
||||
|
||||
AssetItemTree &tree = *snode.runtime->assets_for_menu;
|
||||
if (tree.catalogs.is_empty() && loading_finished) {
|
||||
return;
|
||||
}
|
||||
|
||||
uiItemS(layout);
|
||||
|
||||
if (!loading_finished) {
|
||||
uiItemL(layout, IFACE_("Loading Asset Libraries"), ICON_INFO);
|
||||
}
|
||||
|
||||
/* Avoid adding a separate root catalog when the assets have already been added to one of the
|
||||
* builtin menus.
|
||||
* TODO: The need to define the builtin menu labels here is completely non-ideal. We don't have
|
||||
* any UI introspection that can do this though. This can be solved in the near future by
|
||||
* removing the need to define the add menu completely, instead using a per-node-type path which
|
||||
* can be merged with catalog tree.
|
||||
*/
|
||||
static Set<std::string> all_builtin_menus = []() {
|
||||
Set<std::string> menus;
|
||||
menus.add_new("Attribute");
|
||||
menus.add_new("Color");
|
||||
menus.add_new("Curve");
|
||||
menus.add_new("Curve Primitives");
|
||||
menus.add_new("Curve Topology");
|
||||
menus.add_new("Geometry");
|
||||
menus.add_new("Input");
|
||||
menus.add_new("Instances");
|
||||
menus.add_new("Material");
|
||||
menus.add_new("Mesh");
|
||||
menus.add_new("Mesh Primitives");
|
||||
menus.add_new("Mesh Topology");
|
||||
menus.add_new("Output");
|
||||
menus.add_new("Point");
|
||||
menus.add_new("Text");
|
||||
menus.add_new("Texture");
|
||||
menus.add_new("Utilities");
|
||||
menus.add_new("UV");
|
||||
menus.add_new("Vector");
|
||||
menus.add_new("Volume");
|
||||
menus.add_new("Group");
|
||||
menus.add_new("Layout");
|
||||
return menus;
|
||||
}();
|
||||
|
||||
tree.catalogs.foreach_root_item([&](bke::AssetCatalogTreeItem &item) {
|
||||
if (all_builtin_menus.contains(item.get_name())) {
|
||||
return;
|
||||
}
|
||||
const bke::AssetCatalogPath &path = tree.full_catalog_per_tree_item.lookup(&item);
|
||||
PointerRNA path_ptr{
|
||||
&screen.id, &RNA_AssetCatalogPath, const_cast<bke::AssetCatalogPath *>(&path)};
|
||||
uiLayout *col = uiLayoutColumn(layout, false);
|
||||
uiLayoutSetContextPointer(col, "asset_catalog_path", &path_ptr);
|
||||
uiItemM(col, "NODE_MT_node_add_catalog_assets", path.name().c_str(), ICON_NONE);
|
||||
});
|
||||
}
|
||||
|
||||
MenuType add_catalog_assets_menu_type()
|
||||
{
|
||||
MenuType type{};
|
||||
BLI_strncpy(type.idname, "NODE_MT_node_add_catalog_assets", sizeof(type.idname));
|
||||
type.poll = node_add_menu_poll;
|
||||
type.draw = node_add_catalog_assets_draw;
|
||||
return type;
|
||||
}
|
||||
|
||||
MenuType add_root_catalogs_menu_type()
|
||||
{
|
||||
MenuType type{};
|
||||
BLI_strncpy(type.idname, "NODE_MT_node_add_root_catalogs", sizeof(type.idname));
|
||||
type.poll = node_add_menu_poll;
|
||||
type.draw = add_root_catalogs_draw;
|
||||
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::space_node;
|
||||
bScreen &screen = *CTX_wm_screen(C);
|
||||
SpaceNode &snode = *CTX_wm_space_node(C);
|
||||
AssetItemTree &tree = *snode.runtime->assets_for_menu;
|
||||
const bke::AssetCatalogTreeItem *item = tree.catalogs.find_root_item(catalog_path);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
const bke::AssetCatalogPath &path = tree.full_catalog_per_tree_item.lookup(item);
|
||||
PointerRNA path_ptr{
|
||||
&screen.id, &RNA_AssetCatalogPath, const_cast<bke::AssetCatalogPath *>(&path)};
|
||||
uiItemS(layout);
|
||||
uiLayout *col = uiLayoutColumn(layout, false);
|
||||
uiLayoutSetContextPointer(col, "asset_catalog_path", &path_ptr);
|
||||
uiItemMContents(col, "NODE_MT_node_add_catalog_assets");
|
||||
}
|
@@ -30,6 +30,7 @@
|
||||
|
||||
#include "DEG_depsgraph_build.h"
|
||||
|
||||
#include "ED_asset.h"
|
||||
#include "ED_node.h" /* own include */
|
||||
#include "ED_render.h"
|
||||
#include "ED_screen.h"
|
||||
@@ -244,38 +245,36 @@ void NODE_OT_add_reroute(wmOperatorType *ot)
|
||||
/** \name Add Node Group Operator
|
||||
* \{ */
|
||||
|
||||
static bNodeTree *node_add_group_get_and_poll_group_node_tree(Main *bmain,
|
||||
wmOperator *op,
|
||||
bNodeTree *ntree)
|
||||
static bool node_group_add_poll(const bNodeTree &node_tree,
|
||||
const bNodeTree &node_group,
|
||||
ReportList &reports)
|
||||
{
|
||||
bNodeTree *node_group = reinterpret_cast<bNodeTree *>(
|
||||
WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_NT));
|
||||
if (!node_group) {
|
||||
return nullptr;
|
||||
if (node_group.type != node_tree.type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *disabled_hint = nullptr;
|
||||
if ((node_group->type != ntree->type) || !nodeGroupPoll(ntree, node_group, &disabled_hint)) {
|
||||
if (!nodeGroupPoll(&node_tree, &node_group, &disabled_hint)) {
|
||||
if (disabled_hint) {
|
||||
BKE_reportf(op->reports,
|
||||
BKE_reportf(&reports,
|
||||
RPT_ERROR,
|
||||
"Can not add node group '%s' to '%s':\n %s",
|
||||
node_group->id.name + 2,
|
||||
ntree->id.name + 2,
|
||||
node_group.id.name + 2,
|
||||
node_tree.id.name + 2,
|
||||
disabled_hint);
|
||||
}
|
||||
else {
|
||||
BKE_reportf(op->reports,
|
||||
BKE_reportf(&reports,
|
||||
RPT_ERROR,
|
||||
"Can not add node group '%s' to '%s'",
|
||||
node_group->id.name + 2,
|
||||
ntree->id.name + 2);
|
||||
node_group.id.name + 2,
|
||||
node_tree.id.name + 2);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return node_group;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int node_add_group_exec(bContext *C, wmOperator *op)
|
||||
@@ -284,10 +283,14 @@ static int node_add_group_exec(bContext *C, wmOperator *op)
|
||||
SpaceNode *snode = CTX_wm_space_node(C);
|
||||
bNodeTree *ntree = snode->edittree;
|
||||
|
||||
bNodeTree *node_group = node_add_group_get_and_poll_group_node_tree(bmain, op, ntree);
|
||||
bNodeTree *node_group = reinterpret_cast<bNodeTree *>(
|
||||
WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_NT));
|
||||
if (!node_group) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
if (!node_group_add_poll(*ntree, *node_group, *op->reports)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
|
||||
|
||||
@@ -320,9 +323,8 @@ static bool node_add_group_poll(bContext *C)
|
||||
}
|
||||
const SpaceNode *snode = CTX_wm_space_node(C);
|
||||
if (snode->edittree->type == NTREE_CUSTOM) {
|
||||
CTX_wm_operator_poll_msg_set(C,
|
||||
"This node editor displays a custom (Python defined) node tree. "
|
||||
"Dropping node groups isn't supported for this");
|
||||
CTX_wm_operator_poll_msg_set(
|
||||
C, "Adding node groups isn't supported for custom (Python defined) node trees");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -366,6 +368,102 @@ void NODE_OT_add_group(wmOperatorType *ot)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Add Node Group Asset Operator
|
||||
* \{ */
|
||||
|
||||
static bool add_node_group_asset(const bContext &C,
|
||||
const AssetLibraryReference &library_ref,
|
||||
const AssetHandle asset,
|
||||
ReportList &reports)
|
||||
{
|
||||
Main &bmain = *CTX_data_main(&C);
|
||||
SpaceNode &snode = *CTX_wm_space_node(&C);
|
||||
bNodeTree &edit_tree = *snode.edittree;
|
||||
|
||||
bNodeTree *node_group = reinterpret_cast<bNodeTree *>(
|
||||
asset::get_local_id_from_asset_or_append_and_reuse(bmain, library_ref, asset));
|
||||
if (!node_group) {
|
||||
return false;
|
||||
}
|
||||
if (!node_group_add_poll(edit_tree, *node_group, reports)) {
|
||||
/* Remove the node group if it was newly appended but can't be added to the tree. */
|
||||
id_us_plus(&node_group->id);
|
||||
BKE_id_free_us(&bmain, node_group);
|
||||
return false;
|
||||
}
|
||||
|
||||
ED_preview_kill_jobs(CTX_wm_manager(&C), CTX_data_main(&C));
|
||||
|
||||
bNode *group_node = add_node(
|
||||
C, ntreeTypeFind(node_group->idname)->group_idname, snode.runtime->cursor);
|
||||
if (!group_node) {
|
||||
BKE_report(&reports, RPT_WARNING, "Could not add node group");
|
||||
return false;
|
||||
}
|
||||
group_node->flag &= ~NODE_OPTIONS;
|
||||
|
||||
group_node->id = &node_group->id;
|
||||
id_us_plus(group_node->id);
|
||||
BKE_ntree_update_tag_node_property(&edit_tree, group_node);
|
||||
|
||||
nodeSetActive(&edit_tree, group_node);
|
||||
ED_node_tree_propagate_change(&C, &bmain, nullptr);
|
||||
DEG_relations_tag_update(&bmain);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int node_add_group_asset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
ARegion ®ion = *CTX_wm_region(C);
|
||||
SpaceNode &snode = *CTX_wm_space_node(C);
|
||||
|
||||
const AssetLibraryReference *library_ref = CTX_wm_asset_library_ref(C);
|
||||
if (!library_ref) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
bool is_valid;
|
||||
const AssetHandle handle = CTX_wm_asset_handle(C, &is_valid);
|
||||
if (!is_valid) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Convert mouse coordinates to v2d space. */
|
||||
UI_view2d_region_to_view(®ion.v2d,
|
||||
event->mval[0],
|
||||
event->mval[1],
|
||||
&snode.runtime->cursor[0],
|
||||
&snode.runtime->cursor[1]);
|
||||
|
||||
snode.runtime->cursor /= UI_DPI_FAC;
|
||||
|
||||
const bool success = add_node_group_asset(*C, *library_ref, handle, *op->reports);
|
||||
|
||||
wmOperatorType *ot = WM_operatortype_find("NODE_OT_translate_attach_remove_on_cancel", true);
|
||||
BLI_assert(ot);
|
||||
PointerRNA ptr;
|
||||
WM_operator_properties_create_ptr(&ptr, ot);
|
||||
WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, nullptr);
|
||||
WM_operator_properties_free(&ptr);
|
||||
|
||||
return success ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
void NODE_OT_add_group_asset(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Add Node Group Asset";
|
||||
ot->description = "Add a node group asset to the node editor";
|
||||
ot->idname = "NODE_OT_add_group_asset";
|
||||
|
||||
ot->invoke = node_add_group_asset_invoke;
|
||||
ot->poll = node_add_group_poll;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Add Node Object Operator
|
||||
* \{ */
|
||||
@@ -393,7 +491,7 @@ static int node_add_object_exec(bContext *C, wmOperator *op)
|
||||
|
||||
bNodeSocket *sock = nodeFindSocket(object_node, SOCK_IN, "Object");
|
||||
if (!sock) {
|
||||
BKE_report(op->reports, RPT_WARNING, "Could not find node object socket");
|
||||
BLI_assert_unreachable();
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
|
@@ -38,6 +38,8 @@ extern const char *node_context_dir[];
|
||||
|
||||
namespace blender::ed::space_node {
|
||||
|
||||
struct AssetItemTree;
|
||||
|
||||
/** Temporary data used in node link drag modal operator. */
|
||||
struct bNodeLinkDrag {
|
||||
/** Links dragged by the operator. */
|
||||
@@ -96,6 +98,15 @@ struct SpaceNode_Runtime {
|
||||
/* XXX hack for translate_attach op-macros to pass data from transform op to insert_offset op */
|
||||
/** Temporary data for node insert offset (in UI called Auto-offset). */
|
||||
struct NodeInsertOfsData *iofsd;
|
||||
|
||||
/**
|
||||
* Temporary data for node add menu in order to provide longer-term storage for context pointers.
|
||||
* Recreated every time the root menu is opened. In the future this will be replaced with an "all
|
||||
* libraries" cache in the asset system itself.
|
||||
*
|
||||
* Stored with a shared pointer so that it can be forward declared.
|
||||
*/
|
||||
std::shared_ptr<AssetItemTree> assets_for_menu;
|
||||
};
|
||||
|
||||
enum NodeResizeDirection {
|
||||
@@ -253,6 +264,7 @@ bNode *add_static_node(const bContext &C, int type, const float2 &location);
|
||||
void NODE_OT_add_reroute(wmOperatorType *ot);
|
||||
void NODE_OT_add_search(wmOperatorType *ot);
|
||||
void NODE_OT_add_group(wmOperatorType *ot);
|
||||
void NODE_OT_add_group_asset(wmOperatorType *ot);
|
||||
void NODE_OT_add_object(wmOperatorType *ot);
|
||||
void NODE_OT_add_collection(wmOperatorType *ot);
|
||||
void NODE_OT_add_file(wmOperatorType *ot);
|
||||
@@ -383,4 +395,9 @@ void invoke_node_link_drag_add_menu(bContext &C,
|
||||
|
||||
void invoke_add_node_search_menu(bContext &C, const float2 &cursor, bool use_transform);
|
||||
|
||||
/* add_menu_assets.cc */
|
||||
|
||||
MenuType add_catalog_assets_menu_type();
|
||||
MenuType add_root_catalogs_menu_type();
|
||||
|
||||
} // namespace blender::ed::space_node
|
||||
|
@@ -78,6 +78,7 @@ void node_operatortypes()
|
||||
|
||||
WM_operatortype_append(NODE_OT_add_search);
|
||||
WM_operatortype_append(NODE_OT_add_group);
|
||||
WM_operatortype_append(NODE_OT_add_group_asset);
|
||||
WM_operatortype_append(NODE_OT_add_object);
|
||||
WM_operatortype_append(NODE_OT_add_collection);
|
||||
WM_operatortype_append(NODE_OT_add_file);
|
||||
|
@@ -1170,5 +1170,8 @@ void ED_spacetype_node()
|
||||
art->draw = node_toolbar_region_draw;
|
||||
BLI_addhead(&st->regiontypes, art);
|
||||
|
||||
WM_menutype_add(MEM_new<MenuType>(__func__, add_catalog_assets_menu_type()));
|
||||
WM_menutype_add(MEM_new<MenuType>(__func__, add_root_catalogs_menu_type()));
|
||||
|
||||
BKE_spacetype_register(st);
|
||||
}
|
||||
|
@@ -472,6 +472,15 @@ static void rna_def_asset_handle(BlenderRNA *brna)
|
||||
rna_def_asset_handle_api(srna);
|
||||
}
|
||||
|
||||
static void rna_def_asset_catalog_path(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "AssetCatalogPath", NULL);
|
||||
RNA_def_struct_ui_text(srna, "Catalog Path", "");
|
||||
}
|
||||
|
||||
static void rna_def_asset_library_reference(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna = RNA_def_struct(brna, "AssetLibraryReference", NULL);
|
||||
@@ -498,6 +507,7 @@ void RNA_def_asset(BlenderRNA *brna)
|
||||
rna_def_asset_data(brna);
|
||||
rna_def_asset_library_reference(brna);
|
||||
rna_def_asset_handle(brna);
|
||||
rna_def_asset_catalog_path(brna);
|
||||
|
||||
RNA_define_animate_sdna(true);
|
||||
}
|
||||
|
@@ -1785,6 +1785,10 @@ void RNA_api_ui_layout(StructRNA *srna)
|
||||
parm = RNA_def_pointer(func, "socket", "NodeSocket", "", "");
|
||||
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||
|
||||
func = RNA_def_function(srna, "template_node_asset_menu_items", "uiTemplateNodeAssetMenuItems");
|
||||
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
|
||||
parm = RNA_def_string(func, "catalog_path", NULL, 0, "", "");
|
||||
|
||||
func = RNA_def_function(srna, "template_texture_user", "uiTemplateTextureUser");
|
||||
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
|
||||
|
||||
|
Reference in New Issue
Block a user