1
1

Compare commits

...

29 Commits

Author SHA1 Message Date
525a031935 Add "Is Loading" label for the first time the menu is opened 2022-10-17 15:28:43 -05:00
b280dce0a4 Merge branch 'master' into node-add-asset-menu 2022-10-17 13:22:35 -05:00
70ed39518e Trying to get redrawing to work 2022-10-12 12:11:11 -05:00
ddf206d100 Merge branch 'master' into node-add-asset-menu 2022-10-12 10:41:28 -05:00
eb665847c2 Fix: Use column layouts instead of rows 2022-10-07 11:50:29 -05:00
b0fc4e5106 Merge branch 'master' into node-add-asset-menu 2022-10-07 11:49:47 -05:00
701b6b8aab Merge branch 'master' into node-add-asset-menu 2022-10-06 15:07:50 -05:00
02e92af3ef Remove unrelated change 2022-10-06 14:57:54 -05:00
67f907a2c5 Avoid duplication of builtin menus with hack 2022-10-06 14:51:51 -05:00
13951cde59 Remove some unnecessary changes 2022-10-06 14:45:46 -05:00
bd04c4aef2 Also add items to existing menus 2022-10-06 14:38:15 -05:00
f100fd4609 Fix memory leaks
The storage is owned by the node editor until the asset system is more complete
2022-10-06 13:57:10 -05:00
d80dc33fe5 Merge branch 'master' into node-add-asset-menu 2022-10-06 12:20:14 -05:00
8b6946c911 Merge branch 'master' into node-add-asset-menu 2022-10-06 11:03:06 -05:00
f761a1bc7a Fix else after return 2022-10-05 20:23:08 -05:00
61466b761f Adding nodes works 2022-10-05 20:10:19 -05:00
ea335357c5 Proper catalog building 2022-10-05 17:39:16 -05:00
6025acafd4 Working asset catalogs 2022-10-05 15:47:50 -05:00
58abb84fe7 Revert some unnecessary C++ conversions of functions 2022-10-05 14:15:26 -05:00
3b07198a08 Merge branch 'master' into node-add-asset-menu 2022-10-05 13:57:24 -05:00
d4cd48a162 Fix cacalog path for child catalogs 2022-10-05 13:06:35 -05:00
d860d78523 Merge branch 'master' into node-add-asset-menu 2022-10-05 12:48:18 -05:00
789effa744 Merge branch 'master' into node-add-asset-menu 2022-10-05 11:43:10 -05:00
32153c56da Progress 2022-10-03 16:03:41 -05:00
6f368db104 Merge branch 'master' into node-add-asset-menu 2022-10-03 12:11:56 -05:00
11d4ed6725 Progress 2022-09-30 17:33:04 -05:00
e287c4d573 Merge branch 'master' into node-add-asset-menu 2022-09-30 10:35:03 -05:00
f1ab91e6fb Merge branch 'master' into node-add-asset-menu 2022-09-29 11:48:18 -05:00
eaa577bf13 Initial progress 2022-09-28 22:01:33 -05:00
21 changed files with 616 additions and 28 deletions

View File

@@ -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 = (
)

View File

@@ -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 = (

View File

@@ -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. */

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;
}
/* ---------------------------------------------------------------------- */
/* ---------------------------------------------------------------------- */

View File

@@ -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))
{
}

View File

@@ -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

View File

@@ -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)
{

View File

@@ -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.
*/

View File

@@ -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);

View File

@@ -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)
{

View File

@@ -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.

View File

@@ -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

View 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");
}

View File

@@ -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 &region = *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(&region.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;
}

View File

@@ -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

View File

@@ -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);

View 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);
}

View File

@@ -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);
}

View File

@@ -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);