Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/space_node')
-rw-r--r--source/blender/editors/space_node/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_node/add_menu_assets.cc317
-rw-r--r--source/blender/editors/space_node/drawnode.cc17
-rw-r--r--source/blender/editors/space_node/node_add.cc143
-rw-r--r--source/blender/editors/space_node/node_draw.cc2
-rw-r--r--source/blender/editors/space_node/node_edit.cc32
-rw-r--r--source/blender/editors/space_node/node_group.cc93
-rw-r--r--source/blender/editors/space_node/node_intern.hh17
-rw-r--r--source/blender/editors/space_node/node_ops.cc1
-rw-r--r--source/blender/editors/space_node/space_node.cc5
10 files changed, 556 insertions, 72 deletions
diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt
index d7eccbf0b68..ff9e5352d0a 100644
--- a/source/blender/editors/space_node/CMakeLists.txt
+++ b/source/blender/editors/space_node/CMakeLists.txt
@@ -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
diff --git a/source/blender/editors/space_node/add_menu_assets.cc b/source/blender/editors/space_node/add_menu_assets.cc
new file mode 100644
index 00000000000..5458a25d74a
--- /dev/null
+++ b/source/blender/editors/space_node/add_menu_assets.cc
@@ -0,0 +1,317 @@
+/* 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 "ED_screen.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);
+}
+
+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 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_valid_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_valid_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 &library_ref : all_libraries) {
+ if (bke::AssetLibrary *library = BKE_asset_library_load(&bmain, library_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_ptr(meta_data.catalog_id);
+ if (library_catalog == nullptr) {
+ return true;
+ }
+ 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 = 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;
+ type.listener = node_add_menu_assets_listen_fn;
+ 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;
+ type.listener = node_add_menu_assets_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::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");
+}
diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc
index df31a0342cb..c66b8ad4ff0 100644
--- a/source/blender/editors/space_node/drawnode.cc
+++ b/source/blender/editors/space_node/drawnode.cc
@@ -1581,10 +1581,12 @@ void draw_nodespace_back_pix(const bContext &C,
GPU_matrix_pop();
}
-static float2 socket_link_connection_location(const bNodeSocket &socket, const bNodeLink &link)
+static float2 socket_link_connection_location(const bNode &node,
+ const bNodeSocket &socket,
+ const bNodeLink &link)
{
const float2 socket_location(socket.locx, socket.locy);
- if (socket.is_multi_input() && socket.is_input() && !(socket.owner_node().flag & NODE_HIDDEN)) {
+ if (socket.is_multi_input() && socket.is_input() && !(node.flag & NODE_HIDDEN)) {
return node_link_calculate_multi_input_position(
socket_location, link.multi_input_socket_index, socket.total_inputs);
}
@@ -1620,8 +1622,8 @@ static void calculate_inner_link_bezier_points(std::array<float2, 4> &points)
static std::array<float2, 4> node_link_bezier_points(const bNodeLink &link)
{
std::array<float2, 4> points;
- points[0] = socket_link_connection_location(*link.fromsock, link);
- points[3] = socket_link_connection_location(*link.tosock, link);
+ points[0] = socket_link_connection_location(*link.fromnode, *link.fromsock, link);
+ points[3] = socket_link_connection_location(*link.tonode, *link.tosock, link);
calculate_inner_link_bezier_points(points);
return points;
}
@@ -2212,8 +2214,11 @@ static std::array<float2, 4> node_link_bezier_points_dragged(const SpaceNode &sn
{
const float2 cursor = snode.runtime->cursor * UI_DPI_FAC;
std::array<float2, 4> points;
- points[0] = link.fromsock ? socket_link_connection_location(*link.fromsock, link) : cursor;
- points[3] = link.tosock ? socket_link_connection_location(*link.tosock, link) : cursor;
+ points[0] = link.fromsock ?
+ socket_link_connection_location(*link.fromnode, *link.fromsock, link) :
+ cursor;
+ points[3] = link.tosock ? socket_link_connection_location(*link.tonode, *link.tosock, link) :
+ cursor;
calculate_inner_link_bezier_points(points);
return points;
}
diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc
index 07eecff320a..41f70a6d8cf 100644
--- a/source/blender/editors/space_node/node_add.cc
+++ b/source/blender/editors/space_node/node_add.cc
@@ -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;
@@ -367,6 +369,105 @@ 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;
+ }
+ /* By default, don't show the data-block selector since it's not usually necessary for assets. */
+ 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;
+
+ if (!add_node_group_asset(*C, *library_ref, handle, *op->reports)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ 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 OPERATOR_FINISHED;
+}
+
+void NODE_OT_add_group_asset(wmOperatorType *ot)
+{
+ ot->name = "Add Node Group Asset";
+ ot->description = "Add a node group asset to the active node tree";
+ 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 +494,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;
}
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index 98b2cacd162..5ae6573df7c 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -2146,7 +2146,7 @@ static void node_draw_basis(const bContext &C,
"");
UI_but_func_set(but, node_toggle_button_cb, &node, (void *)"NODE_OT_group_edit");
if (node.id) {
- UI_but_icon_indicator_number_set(but, node.id->us);
+ UI_but_icon_indicator_number_set(but, ID_REAL_USERS(node.id));
}
UI_block_emboss_set(&block, UI_EMBOSS);
}
diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc
index 48b3d711bdf..7982b47f363 100644
--- a/source/blender/editors/space_node/node_edit.cc
+++ b/source/blender/editors/space_node/node_edit.cc
@@ -89,8 +89,8 @@ struct CompoJob {
Depsgraph *compositor_depsgraph;
bNodeTree *localtree;
/* Jon system integration. */
- const short *stop;
- short *do_update;
+ const bool *stop;
+ bool *do_update;
float *progress;
};
@@ -166,7 +166,7 @@ static int compo_get_recalc_flags(const bContext *C)
}
/* called by compo, only to check job 'stop' value */
-static int compo_breakjob(void *cjv)
+static bool compo_breakjob(void *cjv)
{
CompoJob *cj = (CompoJob *)cjv;
@@ -250,8 +250,8 @@ static void compo_progressjob(void *cjv, float progress)
static void compo_startjob(void *cjv,
/* Cannot be const, this function implements wm_jobs_start_callback.
* NOLINTNEXTLINE: readability-non-const-parameter. */
- short *stop,
- short *do_update,
+ bool *stop,
+ bool *do_update,
float *progress)
{
CompoJob *cj = (CompoJob *)cjv;
@@ -502,6 +502,7 @@ void ED_node_shader_default(const bContext *C, ID *id)
}
ma->nodetree = ntreeCopyTree(bmain, ma_default->nodetree);
+ ma->nodetree->owner_id = &ma->id;
BKE_ntree_update_main_tree(bmain, ma->nodetree, nullptr);
}
else if (ELEM(GS(id->name), ID_WO, ID_LA)) {
@@ -736,26 +737,7 @@ void ED_node_set_active(
* - current image is not a Render Result or ViewerNode (want to keep looking at these) */
if (node->id != nullptr && GS(node->id->name) == ID_IM) {
Image *image = (Image *)node->id;
- wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
- LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
- const bScreen *screen = WM_window_get_active_screen(win);
- LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
- LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
- if (sl->spacetype != SPACE_IMAGE) {
- continue;
- }
- SpaceImage *sima = (SpaceImage *)sl;
- if (sima->pin) {
- continue;
- }
- if (sima->image &&
- ELEM(sima->image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
- continue;
- }
- ED_space_image_set(bmain, sima, image, true);
- }
- }
- }
+ ED_space_image_sync(bmain, image, true);
}
if (r_active_texture_changed) {
diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc
index aedceb1d368..329f78860a4 100644
--- a/source/blender/editors/space_node/node_group.cc
+++ b/source/blender/editors/space_node/node_group.cc
@@ -14,6 +14,7 @@
#include "BLI_linklist.h"
#include "BLI_listbase.h"
+#include "BLI_map.hh"
#include "BLI_math_vec_types.hh"
#include "BLI_string.h"
#include "BLI_vector.hh"
@@ -735,6 +736,74 @@ static int node_get_selected_minmax(
return totselect;
}
+/**
+ * Redirect a link that are connecting a non-selected node to selected one.
+ * Create new socket or reuse an existing one that was connected from the same input.
+ * The output sockets of group nodes usually have consciously given names so they have
+ * precedence over socket names the link points to.
+ *
+ * \param ntree: The node tree that the node group is being created from.
+ * \param ngroup: The node tree of the new node group.
+ * \param gnode: The new group node in the original tree.
+ * \param input_node: The input node of the new node group.
+ * \param link: The incoming link that needs to be altered.
+ * \param reusable_sockets: Map for input socket interface lookup.
+ */
+static void node_group_make_redirect_incoming_link(
+ bNodeTree &ntree,
+ bNodeTree *ngroup,
+ bNode *gnode,
+ bNode *input_node,
+ bNodeLink *link,
+ Map<bNodeSocket *, bNodeSocket *> &reusable_sockets)
+{
+ bNodeSocket *input_socket = reusable_sockets.lookup_default(link->fromsock, nullptr);
+ if (input_socket) {
+ /* The incoming link is from a socket that has already been linked to
+ * a socket interface of the input node.
+ * Change the source of the link to the previously created socket interface.
+ * Move the link into the node tree of the new group. */
+ link->fromnode = input_node;
+ link->fromsock = input_socket;
+ BLI_remlink(&ntree.links, link);
+ BLI_addtail(&ngroup->links, link);
+ }
+ else {
+ bNode *node_for_typeinfo = nullptr;
+ bNodeSocket *socket_for_typeinfo = nullptr;
+ /* Find a socket where typeinfo and name may come from. */
+ node_socket_skip_reroutes(
+ &ntree.links, link->tonode, link->tosock, &node_for_typeinfo, &socket_for_typeinfo);
+ bNodeSocket *socket_for_naming = socket_for_typeinfo;
+
+ /* Use the name of group node output sockets. */
+ if (ELEM(link->fromnode->type, NODE_GROUP_INPUT, NODE_GROUP, NODE_CUSTOM_GROUP)) {
+ socket_for_naming = link->fromsock;
+ }
+
+ bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocketWithName(ngroup,
+ node_for_typeinfo,
+ socket_for_typeinfo,
+ socket_for_naming->idname,
+ socket_for_naming->name);
+
+ /* Update the group node and interface sockets so the new interface socket can be linked. */
+ node_group_update(&ntree, gnode);
+ node_group_input_update(ngroup, input_node);
+
+ /* Create new internal link. */
+ bNodeSocket *input_sock = node_group_input_find_socket(input_node, iosock->identifier);
+ nodeAddLink(ngroup, input_node, input_sock, link->tonode, link->tosock);
+
+ /* Redirect external link. */
+ link->tonode = gnode;
+ link->tosock = node_group_find_input_socket(gnode, iosock->identifier);
+
+ /* Remember which interface socket the link has been redirected to. */
+ reusable_sockets.add_new(link->fromsock, input_sock);
+ }
+}
+
static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree, bNode *gnode)
{
Main *bmain = CTX_data_main(&C);
@@ -834,6 +903,10 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree,
output_node->locy = -offsety;
/* relink external sockets */
+
+ /* A map from link sources to input sockets already connected. */
+ Map<bNodeSocket *, bNodeSocket *> reusable_sockets;
+
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree.links) {
const bool fromselect = node_group_make_use_node(*link->fromnode, gnode);
const bool toselect = node_group_make_use_node(*link->tonode, gnode);
@@ -851,24 +924,8 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree,
continue;
}
- bNodeSocket *link_sock;
- bNode *link_node;
- node_socket_skip_reroutes(&ntree.links, link->tonode, link->tosock, &link_node, &link_sock);
- bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, link_node, link_sock);
-
- /* update the group node and interface node sockets,
- * so the new interface socket can be linked.
- */
- node_group_update(&ntree, gnode);
- node_group_input_update(ngroup, input_node);
-
- /* create new internal link */
- bNodeSocket *input_sock = node_group_input_find_socket(input_node, iosock->identifier);
- nodeAddLink(ngroup, input_node, input_sock, link->tonode, link->tosock);
-
- /* redirect external link */
- link->tonode = gnode;
- link->tosock = node_group_find_input_socket(gnode, iosock->identifier);
+ node_group_make_redirect_incoming_link(
+ ntree, ngroup, gnode, input_node, link, reusable_sockets);
}
else if (fromselect && !toselect) {
/* Remove hidden links to not create unconnected sockets in the interface. */
diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh
index 1c3026628a6..88be9a52c61 100644
--- a/source/blender/editors/space_node/node_intern.hh
+++ b/source/blender/editors/space_node/node_intern.hh
@@ -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
diff --git a/source/blender/editors/space_node/node_ops.cc b/source/blender/editors/space_node/node_ops.cc
index d45c33a3c59..104d1acf3b4 100644
--- a/source/blender/editors/space_node/node_ops.cc
+++ b/source/blender/editors/space_node/node_ops.cc
@@ -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);
diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc
index ac49115959c..ce0273eec81 100644
--- a/source/blender/editors/space_node/space_node.cc
+++ b/source/blender/editors/space_node/space_node.cc
@@ -181,7 +181,7 @@ void ED_node_tree_path_get(SpaceNode *snode, char *value)
value += strlen(path->display_name);
}
else {
- sprintf(value, "/%s", path->display_name);
+ BLI_sprintf(value, "/%s", path->display_name);
value += strlen(path->display_name) + 1;
}
}
@@ -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);
}