From 60a416bea47a625a666f38fa7b16103c5b3eb387 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 27 Sep 2025 14:58:43 +0200 Subject: [PATCH] Geometry Nodes: improve socket shapes for bundle and closure nodes Previously, the closure and bundle nodes were a bit restrictive when it comes to socket shapes. Especially the bundle nodes did not support customizing the socket shape at all, so they always worked with dynamic values. This was problematic, because it meant that e.g. the outputs of the Separate Bundle node looked like they couldn't be used as single values, and other similar issues. With this patch, the following is supported (a few aspects were supported before but now it all fits better together): * Support manually selecting socket shapes in Combine Bundle, Separate Bundle, Closure Input, Closure Output and Evaluate Closure nodes. * Automatic inferencing of shapes in all these nodes, as long as the socket shape is set to "auto". * A new "Define Signature" option can be enabled in the nodes. If enabled, linked nodes will also sync the socket shapes from that node. In the future, we also want to add support for naming the signature. Pull Request: https://projects.blender.org/blender/blender/pulls/145550 --- source/blender/blenkernel/BKE_node_runtime.hh | 19 +- .../node_tree_structure_type_inferencing.cc | 178 ++++++++++---- .../blenkernel/intern/node_tree_update.cc | 231 +++++++++++------- source/blender/editors/space_node/drawnode.cc | 6 + source/blender/makesdna/DNA_node_types.h | 42 +++- .../makesrna/intern/rna_node_socket.cc | 9 +- .../intern/rna_node_tree_interface.cc | 20 +- .../blender/makesrna/intern/rna_nodetree.cc | 141 +++++++++-- .../NOD_geometry_nodes_bundle_signature.hh | 44 +++- .../NOD_geometry_nodes_closure_signature.hh | 33 ++- source/blender/nodes/NOD_rna_define.hh | 4 + source/blender/nodes/NOD_trace_values.hh | 9 +- .../nodes/geometry/include/NOD_geo_closure.hh | 2 - .../nodes/geometry/nodes/node_geo_closure.cc | 32 ++- .../geometry/nodes/node_geo_combine_bundle.cc | 21 +- .../nodes/node_geo_evaluate_closure.cc | 33 ++- .../nodes/node_geo_separate_bundle.cc | 23 +- .../nodes/intern/geometry_nodes_bundle.cc | 99 ++++++-- .../nodes/intern/geometry_nodes_closure.cc | 149 +++++++---- source/blender/nodes/intern/sync_sockets.cc | 187 ++++++++------ source/blender/nodes/intern/trace_values.cc | 146 ++++++----- 21 files changed, 994 insertions(+), 434 deletions(-) diff --git a/source/blender/blenkernel/BKE_node_runtime.hh b/source/blender/blenkernel/BKE_node_runtime.hh index fc146e626a8..67628cc8307 100644 --- a/source/blender/blenkernel/BKE_node_runtime.hh +++ b/source/blender/blenkernel/BKE_node_runtime.hh @@ -173,12 +173,6 @@ class bNodeTreeRuntime : NonCopyable, NonMovable { std::unique_ptr field_inferencing_interface; /** Field status for every socket, accessed with #bNodeSocket::index_in_tree(). */ Array field_states; - /** - * Inferred structure type for every socket, accessed with #bNodeSocket::index_in_tree(). - * This is not necessarily the structure type that is displayed in the node editor. E.g. it may - * be Single for an unconnected field input. - */ - Array inferred_structure_types; /** Information about usage of anonymous attributes within the group. */ std::unique_ptr reference_lifetimes_info; std::unique_ptr gizmo_propagation; @@ -293,6 +287,17 @@ class bNodeSocketRuntime : NonCopyable, NonMovable { */ short total_inputs = 0; + /** + * Inferred structure type of the socket. This is not necessarily the same as the structure type + * that is displayed in the UI. For example, it would be #StructureType::Single for an unlinked + * input of the Math node, but the socket is displayed as #StructureType::Dynamic. + * + * This is stored on the socket instead of as array in #bNodeTreeRuntime because the data needs + * to stay attached to the socket even when the node tree changes. This is used when e.g. syncing + * a newly created Separate Bundle node to an existing Combine Bundle node. + */ + nodes::StructureType inferred_structure_type = nodes::StructureType::Dynamic; + /** * The location of the socket in the tree, calculated while drawing the nodes and invalid if the * node tree hasn't been drawn yet. In the node tree's "world space" (the same as @@ -1032,7 +1037,7 @@ inline bool bNodeSocket::is_icon_visible() const inline bool bNodeSocket::may_be_field() const { - return ELEM(this->owner_tree().runtime->inferred_structure_types[this->index_in_tree()], + return ELEM(this->runtime->inferred_structure_type, blender::nodes::StructureType::Field, blender::nodes::StructureType::Dynamic); } diff --git a/source/blender/blenkernel/intern/node_tree_structure_type_inferencing.cc b/source/blender/blenkernel/intern/node_tree_structure_type_inferencing.cc index 9d10810773e..10a60f50151 100644 --- a/source/blender/blenkernel/intern/node_tree_structure_type_inferencing.cc +++ b/source/blender/blenkernel/intern/node_tree_structure_type_inferencing.cc @@ -3,6 +3,8 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ #include "BLI_array_utils.hh" +#include "BLI_bit_span_ops.hh" +#include "BLI_bit_vector.hh" #include "BLI_stack.hh" #include "BLI_utildefines.h" @@ -129,19 +131,107 @@ static StructureType data_requirement_to_auto_structure_type(const DataRequireme return StructureType::Dynamic; } +static void find_auto_structure_type_sockets(const bNodeTree &tree, + bits::MutableBoundedBitSpan is_auto_structure_type) +{ + /* Handle group inputs. */ + for (const int i : tree.interface_inputs().index_range()) { + const bNodeTreeInterfaceSocket &io_socket = *tree.interface_inputs()[i]; + if (io_socket.structure_type != NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) { + continue; + } + for (const bNode *node : tree.group_input_nodes()) { + const bNodeSocket &socket = node->output_socket(i); + is_auto_structure_type[socket.index_in_tree()].set(); + } + } + + /* Handle group outputs. */ + if (const bNode *group_output_node = tree.group_output_node()) { + is_auto_structure_type.slice(group_output_node->input_socket_indices_in_tree().drop_back(1)) + .set_all(); + } + + /* Handle closure inputs and outputs. */ + const bke::bNodeZoneType *closure_zone_type = bke::zone_type_by_node_type(NODE_CLOSURE_OUTPUT); + for (const bNode *closure_input_node : tree.nodes_by_type("NodeClosureInput")) { + const auto *closure_output_node = closure_zone_type->get_corresponding_output( + tree, *closure_input_node); + if (!closure_output_node) { + continue; + } + const auto &storage = *static_cast(closure_output_node->storage); + for (const int i : IndexRange(storage.input_items.items_num)) { + const NodeClosureInputItem &item = storage.input_items.items[i]; + if (item.structure_type == NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) { + const bNodeSocket &socket = closure_input_node->output_socket(i); + is_auto_structure_type[socket.index_in_tree()].set(); + } + } + for (const int i : IndexRange(storage.output_items.items_num)) { + const NodeClosureOutputItem &item = storage.output_items.items[i]; + if (item.structure_type == NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) { + const bNodeSocket &socket = closure_output_node->input_socket(i); + is_auto_structure_type[socket.index_in_tree()].set(); + } + } + } + + /* Handle Evaluate Closure nodes. */ + for (const bNode *evaluate_closure_node : tree.nodes_by_type("NodeEvaluateClosure")) { + auto &storage = *static_cast(evaluate_closure_node->storage); + for (const int i : IndexRange(storage.input_items.items_num)) { + const NodeEvaluateClosureInputItem &item = storage.input_items.items[i]; + if (item.structure_type == NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) { + const bNodeSocket &socket = evaluate_closure_node->input_socket(i + 1); + is_auto_structure_type[socket.index_in_tree()].set(); + } + } + for (const int i : IndexRange(storage.output_items.items_num)) { + const NodeEvaluateClosureOutputItem &item = storage.output_items.items[i]; + if (item.structure_type == NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) { + const bNodeSocket &socket = evaluate_closure_node->output_socket(i); + is_auto_structure_type[socket.index_in_tree()].set(); + } + } + } + + /* Handle Combine Bundle nodes. */ + for (const bNode *node : tree.nodes_by_type("NodeCombineBundle")) { + auto &storage = *static_cast(node->storage); + for (const int i : IndexRange(storage.items_num)) { + const NodeCombineBundleItem &item = storage.items[i]; + if (item.structure_type == NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) { + const bNodeSocket &socket = node->input_socket(i); + is_auto_structure_type[socket.index_in_tree()].set(); + } + } + } + + /* Handle Separate Bundle nodes. */ + for (const bNode *node : tree.nodes_by_type("NodeSeparateBundle")) { + auto &storage = *static_cast(node->storage); + for (const int i : IndexRange(storage.items_num)) { + const NodeSeparateBundleItem &item = storage.items[i]; + if (item.structure_type == NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) { + const bNodeSocket &socket = node->output_socket(i); + is_auto_structure_type[socket.index_in_tree()].set(); + } + } + } +} + static void init_input_requirements(const bNodeTree &tree, + const bits::BoundedBitSpan is_auto_structure_type, MutableSpan input_requirements) { for (const bNode *node : tree.all_nodes()) { - if (ELEM(node->type_legacy, NODE_GROUP_OUTPUT, NODE_CLOSURE_OUTPUT)) { - for (const bNodeSocket *socket : node->input_sockets()) { - /* Inputs of these nodes have no requirements. */ - input_requirements[socket->index_in_all_inputs()] = DataRequirement::None; - } - continue; - } for (const bNodeSocket *socket : node->input_sockets()) { DataRequirement &requirement = input_requirements[socket->index_in_all_inputs()]; + if (is_auto_structure_type[socket->index_in_tree()]) { + requirement = DataRequirement::None; + continue; + } const nodes::SocketDeclaration *declaration = socket->runtime->declaration; if (!declaration) { requirement = DataRequirement::None; @@ -222,37 +312,27 @@ static void store_group_input_structure_types(const bNodeTree &tree, } } -static void store_closure_input_structure_types(const bNodeTree &tree, - const Span input_requirements, - MutableSpan structure_types) +static void store_auto_output_structure_types(const bNodeTree &tree, + const Span input_requirements, + const bits::BoundedBitSpan is_auto_structure_type, + MutableSpan structure_types) { - const bNodeTreeZones *zones = tree.zones(); - if (!zones) { - return; - } - for (const bNodeTreeZone *zone : zones->zones) { - const bNode *input_node = zone->input_node(); - const bNode *output_node = zone->output_node(); - if (!input_node || !output_node) { - continue; + const Span all_sockets = tree.all_sockets(); + bits::foreach_1_index(is_auto_structure_type, [&](const int i) { + const bNodeSocket &socket = *all_sockets[i]; + if (socket.is_input()) { + return; } - if (!output_node->is_type("NodeClosureOutput")) { - continue; + const bNode &node = socket.owner_node(); + if (node.is_group_input()) { + /* Group input nodes have special handling in #store_group_input_structure_types because + * corresponding sockets on all group input nodes should have the same structure type. */ + return; } - const auto *storage = static_cast(output_node->storage); - for (const int i : IndexRange(storage->input_items.items_num)) { - const NodeClosureInputItem &item = storage->input_items.items[i]; - const bNodeSocket &socket = input_node->output_socket(i); - StructureType &structure_type = structure_types[socket.index_in_tree()]; - if (item.structure_type != NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) { - structure_type = StructureType(item.structure_type); - continue; - } - const DataRequirement requirement = calc_output_socket_requirement(socket, - input_requirements); - structure_type = data_requirement_to_auto_structure_type(requirement); - } - } + + const DataRequirement requirement = calc_output_socket_requirement(socket, input_requirements); + structure_types[socket.index_in_tree()] = data_requirement_to_auto_structure_type(requirement); + }); } enum class ZoneInOutChange { @@ -601,6 +681,7 @@ static StructureType get_unconnected_input_structure_type( static void propagate_left_to_right(const bNodeTree &tree, const Span node_interfaces, const Span group_input_structure_types, + const bits::BoundedBitSpan is_auto_structure_type, MutableSpan structure_types) { for (const bNodeSocket *input : tree.all_input_sockets()) { @@ -638,10 +719,6 @@ static void propagate_left_to_right(const bNodeTree &tree, } continue; } - if (node->type_legacy == NODE_CLOSURE_INPUT) { - /* Initialized in #store_closure_input_structure_types already. */ - continue; - } for (const bNodeSocket *input : input_sockets) { if (!input->is_available()) { @@ -673,6 +750,10 @@ static void propagate_left_to_right(const bNodeTree &tree, if (!output.is_available() || !output.runtime->declaration) { continue; } + if (is_auto_structure_type[output.index_in_tree()]) { + /* Has been initialized in #store_auto_output_structure_types. */ + continue; + } const nodes::SocketDeclaration &declaration = *output.runtime->declaration; std::optional output_type; @@ -798,15 +879,21 @@ static StructureTypeInferenceResult calc_structure_type_interface(const bNodeTre } Array node_interfaces = calc_node_interfaces(tree); + bits::BitVector<> is_auto_structure_type(tree.all_sockets().size(), false); Array data_requirements(tree.all_input_sockets().size()); - init_input_requirements(tree, data_requirements); + find_auto_structure_type_sockets(tree, is_auto_structure_type); + init_input_requirements(tree, is_auto_structure_type, data_requirements); propagate_right_to_left(tree, node_interfaces, data_requirements); store_group_input_structure_types(tree, data_requirements, result.group_interface); - store_closure_input_structure_types(tree, data_requirements, result.socket_structure_types); - propagate_left_to_right( - tree, node_interfaces, result.group_interface.inputs, result.socket_structure_types); + store_auto_output_structure_types( + tree, data_requirements, is_auto_structure_type, result.socket_structure_types); + propagate_left_to_right(tree, + node_interfaces, + result.group_interface.inputs, + is_auto_structure_type, + result.socket_structure_types); store_group_output_structure_types( tree, node_interfaces, result.socket_structure_types, result.group_interface); @@ -824,7 +911,10 @@ static StructureTypeInferenceResult calc_structure_type_interface(const bNodeTre bool update_structure_type_interface(bNodeTree &tree) { StructureTypeInferenceResult result = calc_structure_type_interface(tree); - tree.runtime->inferred_structure_types = std::move(result.socket_structure_types); + for (const int i : tree.all_sockets().index_range()) { + const bNodeSocket &socket = *tree.all_sockets()[i]; + socket.runtime->inferred_structure_type = result.socket_structure_types[i]; + } if (tree.runtime->structure_type_interface && *tree.runtime->structure_type_interface == result.group_interface) { diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc index bfb9a090cba..2a729c0d402 100644 --- a/source/blender/blenkernel/intern/node_tree_update.cc +++ b/source/blender/blenkernel/intern/node_tree_update.cc @@ -931,16 +931,23 @@ class NodeTreeMainUpdater { } } - static int get_input_socket_shape(const SocketDeclaration &decl, - const StructureType structure_type) + static int get_socket_shape(const bNodeSocket &socket, + const bool use_inferred_structure_type = false) { - if (decl.identifier == "__extend__") { - return SOCK_DISPLAY_SHAPE_CIRCLE; - } - if (nodes::socket_type_always_single(decl.socket_type)) { + if (nodes::socket_type_always_single(socket.typeinfo->type)) { return SOCK_DISPLAY_SHAPE_LINE; } - switch (structure_type) { + const SocketDeclaration *decl = socket.runtime->declaration; + if (!decl) { + return SOCK_DISPLAY_SHAPE_CIRCLE; + } + if (decl->identifier == "__extend__") { + return SOCK_DISPLAY_SHAPE_CIRCLE; + } + const StructureType display_structure_type = use_inferred_structure_type ? + socket.runtime->inferred_structure_type : + decl->structure_type; + switch (display_structure_type) { case StructureType::Single: return SOCK_DISPLAY_SHAPE_LINE; case StructureType::Dynamic: @@ -956,36 +963,6 @@ class NodeTreeMainUpdater { return SOCK_DISPLAY_SHAPE_CIRCLE; } - static int get_output_socket_shape(const SocketDeclaration &decl, - const StructureType structure_type) - { - if (decl.identifier == "__extend__") { - return SOCK_DISPLAY_SHAPE_CIRCLE; - } - if (nodes::socket_type_always_single(decl.socket_type)) { - return SOCK_DISPLAY_SHAPE_LINE; - } - switch (structure_type) { - case StructureType::Single: { - return SOCK_DISPLAY_SHAPE_LINE; - } - case StructureType::Dynamic: { - return SOCK_DISPLAY_SHAPE_CIRCLE; - } - case StructureType::Field: { - return SOCK_DISPLAY_SHAPE_DIAMOND; - } - case StructureType::Grid: { - return SOCK_DISPLAY_SHAPE_VOLUME_GRID; - } - case StructureType::List: { - return SOCK_DISPLAY_SHAPE_LIST; - } - } - BLI_assert_unreachable(); - return SOCK_DISPLAY_SHAPE_CIRCLE; - } - void update_socket_shapes(bNodeTree &ntree) { ntree.ensure_topology_cache(); @@ -993,33 +970,89 @@ class NodeTreeMainUpdater { if (node->is_undefined()) { continue; } - /* For input/output nodes we use the inferred structure types. */ - if (node->is_group_input() || node->is_group_output() || - ELEM(node->type_legacy, NODE_CLOSURE_INPUT, NODE_CLOSURE_OUTPUT)) - { - for (bNodeSocket *socket : node->input_sockets()) { - socket->display_shape = get_input_socket_shape( - *socket->runtime->declaration, - ntree.runtime->inferred_structure_types[socket->index_in_tree()]); + const bke::bNodeZoneType *closure_zone_type = bke::zone_type_by_node_type( + NODE_CLOSURE_OUTPUT); + switch (node->type_legacy) { + case NODE_GROUP_OUTPUT: + case NODE_GROUP_INPUT: { + for (bNodeSocket *socket : node->input_sockets()) { + socket->display_shape = get_socket_shape(*socket, true); + } + for (bNodeSocket *socket : node->output_sockets()) { + socket->display_shape = get_socket_shape(*socket, true); + } + break; } - for (bNodeSocket *socket : node->output_sockets()) { - socket->display_shape = get_output_socket_shape( - *socket->runtime->declaration, - ntree.runtime->inferred_structure_types[socket->index_in_tree()]); + case NODE_COMBINE_BUNDLE: { + const auto &storage = *static_cast(node->storage); + for (const int i : IndexRange(storage.items_num)) { + const NodeCombineBundleItem &item = storage.items[i]; + bNodeSocket &socket = node->input_socket(i); + socket.display_shape = get_socket_shape( + socket, item.structure_type == NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO); + } + break; } - continue; - } - /* For other nodes we just use the static structure types defined in the declaration. */ - for (bNodeSocket *socket : node->input_sockets()) { - if (const SocketDeclaration *declaration = socket->runtime->declaration) { - socket->display_shape = get_input_socket_shape(*declaration, - declaration->structure_type); + case NODE_SEPARATE_BUNDLE: { + const auto &storage = *static_cast(node->storage); + for (const int i : IndexRange(storage.items_num)) { + const NodeSeparateBundleItem &item = storage.items[i]; + bNodeSocket &socket = node->output_socket(i); + socket.display_shape = get_socket_shape( + socket, item.structure_type == NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO); + } + break; } - } - for (bNodeSocket *socket : node->output_sockets()) { - if (const SocketDeclaration *declaration = socket->runtime->declaration) { - socket->display_shape = get_output_socket_shape(*declaration, - declaration->structure_type); + case NODE_CLOSURE_INPUT: { + if (const bNode *closure_output_node = closure_zone_type->get_corresponding_output( + ntree, *node)) + { + const auto &storage = *static_cast( + closure_output_node->storage); + for (const int i : IndexRange(storage.input_items.items_num)) { + const NodeClosureInputItem &item = storage.input_items.items[i]; + bNodeSocket &socket = node->output_socket(i); + socket.display_shape = get_socket_shape( + socket, item.structure_type == NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO); + } + } + break; + } + case NODE_CLOSURE_OUTPUT: { + const auto &storage = *static_cast(node->storage); + for (const int i : IndexRange(storage.output_items.items_num)) { + const NodeClosureOutputItem &item = storage.output_items.items[i]; + bNodeSocket &socket = node->input_socket(i); + socket.display_shape = get_socket_shape( + socket, item.structure_type == NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO); + } + break; + } + case NODE_EVALUATE_CLOSURE: { + const auto &storage = *static_cast(node->storage); + for (const int i : IndexRange(storage.input_items.items_num)) { + const NodeEvaluateClosureInputItem &item = storage.input_items.items[i]; + bNodeSocket &socket = node->input_socket(i + 1); + socket.display_shape = get_socket_shape( + socket, item.structure_type == NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO); + } + for (const int i : IndexRange(storage.output_items.items_num)) { + const NodeEvaluateClosureOutputItem &item = storage.output_items.items[i]; + bNodeSocket &socket = node->output_socket(i); + socket.display_shape = get_socket_shape( + socket, item.structure_type == NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO); + } + break; + } + default: { + /* For other nodes we just use the static structure types defined in the declaration. */ + for (bNodeSocket *socket : node->input_sockets()) { + socket->display_shape = get_socket_shape(*socket); + } + for (bNodeSocket *socket : node->output_sockets()) { + socket->display_shape = get_socket_shape(*socket); + } + break; } } } @@ -1373,14 +1406,6 @@ class NodeTreeMainUpdater { NodeLinkError{TIP_("Use node groups to reuse the same menu multiple times")}); continue; } - if (ntree.type == NTREE_GEOMETRY) { - if (this->is_invalid_field_link(*link)) { - link->flag &= ~NODE_LINK_VALID; - ntree.runtime->link_errors.add( - NodeLinkKey{*link}, NodeLinkError{TIP_("The node input does not support fields")}); - continue; - } - } const bNode &from_node = *link->fromnode; const bNode &to_node = *link->tonode; if (from_node.runtime->toposort_left_to_right_index > @@ -1421,25 +1446,67 @@ class NodeTreeMainUpdater { continue; } } + if (const char *error = this->get_structure_type_link_error(*link)) { + link->flag &= ~NODE_LINK_VALID; + ntree.runtime->link_errors.add(NodeLinkKey{*link}, NodeLinkError{error}); + continue; + } } } - bool is_invalid_field_link(const bNodeLink &link) + const char *get_structure_type_link_error(const bNodeLink &link) { - if (!link.fromsock->may_be_field()) { - return false; + const nodes::StructureType from_inferred_type = + link.fromsock->runtime->inferred_structure_type; + if (from_inferred_type == StructureType::Dynamic) { + /* Showing errors in this case results in many false positives in cases where Blender is not + * sure what the actual type is. */ + return nullptr; } - const nodes::SocketDeclaration *to_socket_decl = link.tosock->runtime->declaration; - if (!to_socket_decl) { - return false; + const int from_shape = link.fromsock->display_shape; + const int to_shape = link.tosock->display_shape; + switch (to_shape) { + case SOCK_DISPLAY_SHAPE_CIRCLE: { + return nullptr; + } + case SOCK_DISPLAY_SHAPE_LINE: { + if (from_shape == SOCK_DISPLAY_SHAPE_LINE) { + return nullptr; + } + if (from_inferred_type == StructureType::Single) { + return nullptr; + } + return TIP_("Input expects a single value"); + } + case SOCK_DISPLAY_SHAPE_DIAMOND: { + if (ELEM(from_shape, SOCK_DISPLAY_SHAPE_LINE, SOCK_DISPLAY_SHAPE_DIAMOND)) { + return nullptr; + } + if (ELEM(from_inferred_type, StructureType::Single, StructureType::Field)) { + return nullptr; + } + return TIP_("Input expects a field or single value"); + } + case SOCK_DISPLAY_SHAPE_VOLUME_GRID: { + if (from_shape == SOCK_DISPLAY_SHAPE_VOLUME_GRID) { + return nullptr; + } + if (from_inferred_type == StructureType::Grid) { + return nullptr; + } + return TIP_("Input expects a volume grid"); + } + case SOCK_DISPLAY_SHAPE_LIST: { + if (from_shape == SOCK_DISPLAY_SHAPE_LIST) { + return nullptr; + } + if (from_inferred_type == StructureType::List) { + return nullptr; + } + return TIP_("Input expects a list"); + } } - if (ELEM(to_socket_decl->structure_type, StructureType::Dynamic, StructureType::Field)) { - return false; - } - if (link.tonode->is_group_output() || link.tonode->is_type("NodeClosureOutput")) { - return false; - } - return true; + return nullptr; } bool check_if_output_changed(const bNodeTree &tree) diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 49a60faae52..5a573340f91 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -1134,6 +1134,12 @@ static void std_node_socket_draw( draw_node_socket_without_value(layout, sock, text); return; } + if (tree->type == NTREE_GEOMETRY && + ELEM(sock->display_shape, SOCK_DISPLAY_SHAPE_LIST, SOCK_DISPLAY_SHAPE_VOLUME_GRID)) + { + draw_node_socket_without_value(layout, sock, text); + return; + } const StringRefNull label = text; text = (sock->flag & SOCK_HIDE_LABEL) ? "" : text; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 04a7098ab23..ac349d75c17 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -2307,7 +2307,9 @@ typedef struct NodeClosureOutputItem { char *name; /** #eNodeSocketDatatype. */ short socket_type; - char _pad[2]; + /** #NodeSocketInterfaceStructureType. */ + int8_t structure_type; + char _pad[1]; int identifier; } NodeClosureOutputItem; @@ -2327,9 +2329,16 @@ typedef struct NodeClosureOutputItems { char _pad[4]; } NodeClosureOutputItems; +typedef enum NodeClosureFlag { + NODE_CLOSURE_FLAG_DEFINE_SIGNATURE = (1 << 0), +} NodeClosureFlag; + typedef struct NodeClosureOutput { NodeClosureInputItems input_items; NodeClosureOutputItems output_items; + /** #NodeClosureFlag. */ + uint8_t flag; + char _pad[7]; } NodeClosureOutput; typedef struct NodeEvaluateClosureInputItem { @@ -2352,6 +2361,10 @@ typedef struct NodeEvaluateClosureOutputItem { int identifier; } NodeEvaluateClosureOutputItem; +typedef enum NodeEvaluateClosureFlag { + NODE_EVALUATE_CLOSURE_FLAG_DEFINE_SIGNATURE = (1 << 0), +} NodeEvaluateClosureFlag; + typedef struct NodeEvaluateClosureInputItems { NodeEvaluateClosureInputItem *items; int items_num; @@ -2371,6 +2384,9 @@ typedef struct NodeEvaluateClosureOutputItems { typedef struct NodeEvaluateClosure { NodeEvaluateClosureInputItems input_items; NodeEvaluateClosureOutputItems output_items; + /** #NodeEvaluateClosureFlag. */ + uint8_t flag; + char _pad[7]; } NodeEvaluateClosure; typedef struct IndexSwitchItem { @@ -2491,30 +2507,46 @@ typedef struct NodeCombineBundleItem { char *name; int identifier; int16_t socket_type; - char _pad[2]; + /** #NodeSocketInterfaceStructureType. */ + int8_t structure_type; + char _pad[1]; } NodeCombineBundleItem; +typedef enum NodeCombineBundleFlag { + NODE_COMBINE_BUNDLE_FLAG_DEFINE_SIGNATURE = (1 << 0), +} NodeCombineBundleFlag; + typedef struct NodeCombineBundle { NodeCombineBundleItem *items; int items_num; int next_identifier; int active_index; - char _pad[4]; + /** #NodeCombineBundleFlag. */ + uint8_t flag; + char _pad[3]; } NodeCombineBundle; typedef struct NodeSeparateBundleItem { char *name; int identifier; int16_t socket_type; - char _pad[2]; + /** #NodeSocketInterfaceStructureType. */ + int8_t structure_type; + char _pad[1]; } NodeSeparateBundleItem; +typedef enum NodeSeparateBundleFlag { + NODE_SEPARATE_BUNDLE_FLAG_DEFINE_SIGNATURE = (1 << 0), +} NodeSeparateBundleFlag; + typedef struct NodeSeparateBundle { NodeSeparateBundleItem *items; int items_num; int next_identifier; int active_index; - char _pad[4]; + /** #NodeSeparateBundleFlag. */ + uint8_t flag; + char _pad[3]; } NodeSeparateBundle; typedef struct NodeFunctionFormatStringItem { diff --git a/source/blender/makesrna/intern/rna_node_socket.cc b/source/blender/makesrna/intern/rna_node_socket.cc index dc06972445b..2b1f61beb3a 100644 --- a/source/blender/makesrna/intern/rna_node_socket.cc +++ b/source/blender/makesrna/intern/rna_node_socket.cc @@ -279,15 +279,8 @@ static void rna_NodeSocket_type_set(PointerRNA *ptr, int value) static int rna_NodeSocket_inferred_structure_type_get(PointerRNA *ptr) { - bNodeTree *tree = reinterpret_cast(ptr->owner_id); bNodeSocket *socket = ptr->data_as(); - tree->ensure_topology_cache(); - if (tree->runtime->inferred_structure_types.size() != tree->all_sockets().size()) { - /* This cache is outdated or not available on this tree type. */ - return int(blender::nodes::StructureType::Dynamic); - } - const int index = socket->index_in_tree(); - return int(tree->runtime->inferred_structure_types[index]); + return int(socket->runtime->inferred_structure_type); } static void rna_NodeSocket_bl_idname_get(PointerRNA *ptr, char *value) diff --git a/source/blender/makesrna/intern/rna_node_tree_interface.cc b/source/blender/makesrna/intern/rna_node_tree_interface.cc index e6603f9572a..d4780c08334 100644 --- a/source/blender/makesrna/intern/rna_node_tree_interface.cc +++ b/source/blender/makesrna/intern/rna_node_tree_interface.cc @@ -97,6 +97,7 @@ static const EnumPropertyItem node_default_input_items[] = { # include "BLT_translation.hh" # include "NOD_node_declaration.hh" +# include "NOD_rna_define.hh" # include "NOD_socket.hh" # include "DNA_material_types.h" @@ -477,19 +478,14 @@ static void rna_NodeTreeInterfaceSocket_force_non_field_set(PointerRNA *ptr, con NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO; } -static const EnumPropertyItem *rna_NodeTreeInterfaceSocket_structure_type_itemf( - bContext * /*C*/, PointerRNA *ptr, PropertyRNA * /*prop*/, bool *r_free) +const EnumPropertyItem *rna_NodeSocket_structure_type_item_filter( + const bNodeTree *ntree, const eNodeSocketDatatype socket_type, bool *r_free) { - const bNodeTree *ntree = reinterpret_cast(ptr->owner_id); - const bNodeTreeInterfaceSocket *socket = static_cast( - ptr->data); if (!ntree) { return rna_enum_dummy_NULL_items; } - const bool is_geometry_nodes = ntree->type == NTREE_GEOMETRY; - const eNodeSocketDatatype socket_type = socket->socket_typeinfo()->type; const bool supports_fields = is_geometry_nodes && blender::nodes::socket_type_supports_fields(socket_type); const bool supports_grids = is_geometry_nodes && @@ -541,6 +537,16 @@ static const EnumPropertyItem *rna_NodeTreeInterfaceSocket_structure_type_itemf( return items; } +static const EnumPropertyItem *rna_NodeTreeInterfaceSocket_structure_type_itemf( + bContext * /*C*/, PointerRNA *ptr, PropertyRNA * /*prop*/, bool *r_free) +{ + const bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + const bNodeTreeInterfaceSocket *socket = static_cast( + ptr->data); + const eNodeSocketDatatype socket_type = socket->socket_typeinfo()->type; + return rna_NodeSocket_structure_type_item_filter(ntree, socket_type, r_free); +} + static const EnumPropertyItem *rna_NodeTreeInterfaceSocket_default_input_itemf( bContext * /*C*/, PointerRNA *ptr, PropertyRNA * /*prop*/, bool *r_free) { diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 4f14f8356df..a50f628e8de 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -659,6 +659,7 @@ static const EnumPropertyItem node_cryptomatte_layer_name_items[] = { # include "NOD_geo_viewer.hh" # include "NOD_geometry.hh" # include "NOD_geometry_nodes_lazy_function.hh" +# include "NOD_rna_define.hh" # include "NOD_shader.h" # include "NOD_socket.hh" # include "NOD_socket_items.hh" @@ -2353,26 +2354,6 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeDomain_attribute_domain_ return item_array; } -static const EnumPropertyItem *rna_structure_type_no_auto_itemf(bContext * /*C*/, - PointerRNA * /*ptr*/, - PropertyRNA * /*prop*/, - bool *r_free) -{ - EnumPropertyItem *items = nullptr; - int items_len = 0; - for (const EnumPropertyItem *item = rna_enum_node_socket_structure_type_items; item->identifier; - item++) - { - if (item->value != NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) { - RNA_enum_item_add(&items, &items_len, item); - } - } - RNA_enum_item_end(&items, &items_len); - - *r_free = true; - return items; -} - static StructRNA *rna_ShaderNode_register(Main *bmain, ReportList *reports, void *data, @@ -3837,6 +3818,20 @@ typename Accessor::ItemT *rna_Node_ItemArray_new_with_socket_and_name( return new_item; } +template +static const EnumPropertyItem *rna_Node_ItemArray_structure_type_itemf(bContext * /*C*/, + PointerRNA *ptr, + PropertyRNA * /*prop*/, + bool *r_free) +{ + using ItemT = typename Accessor::ItemT; + + const bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + const ItemT &item = *static_cast(ptr->data); + const eNodeSocketDatatype socket_type = Accessor::get_socket_type(item); + return rna_NodeSocket_structure_type_item_filter(ntree, socket_type, r_free); +} + static IndexSwitchItem *rna_NodeIndexSwitchItems_new(ID *id, bNode *node, Main *bmain) { IndexSwitchItem *new_item = blender::nodes::socket_items::add_item( @@ -7530,6 +7525,11 @@ static void rna_def_closure_input_item(BlenderRNA *brna) prop = RNA_def_property(srna, "structure_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_node_socket_structure_type_items); + RNA_def_property_enum_funcs( + prop, + nullptr, + nullptr, + "rna_Node_ItemArray_structure_type_itemf"); RNA_def_property_ui_text( prop, "Structure Type", @@ -7552,11 +7552,27 @@ static void rna_def_closure_input_items(BlenderRNA *brna) static void rna_def_closure_output_item(BlenderRNA *brna) { + PropertyRNA *prop; + StructRNA *srna = RNA_def_struct(brna, "NodeClosureOutputItem", nullptr); RNA_def_struct_ui_text(srna, "Closure Output Item", ""); RNA_def_struct_sdna(srna, "NodeClosureOutputItem"); rna_def_node_item_array_socket_item_common(srna, "ClosureOutputItemsAccessor", true); + + prop = RNA_def_property(srna, "structure_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_node_socket_structure_type_items); + RNA_def_property_enum_funcs( + prop, + nullptr, + nullptr, + "rna_Node_ItemArray_structure_type_itemf"); + RNA_def_property_ui_text( + prop, + "Structure Type", + "What kind of higher order types are expected to flow through this socket"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_Node_ItemArray_item_update"); } static void rna_def_closure_output_items(BlenderRNA *brna) @@ -7606,6 +7622,16 @@ static void def_closure_output(BlenderRNA *brna, StructRNA *srna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); RNA_def_property_update(prop, NC_NODE, nullptr); + + prop = RNA_def_property(srna, "define_signature", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_CLOSURE_FLAG_DEFINE_SIGNATURE); + RNA_def_property_ui_text( + prop, + "Define Signature", + "This zone defines a closure signature that should be used by other nodes"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); + RNA_def_property_update(prop, NC_NODE, "rna_Node_update"); } static void rna_def_geo_capture_attribute_item(BlenderRNA *brna) @@ -7690,7 +7716,11 @@ static void rna_def_evaluate_closure_input_item(BlenderRNA *brna) prop = RNA_def_property(srna, "structure_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_node_socket_structure_type_items); - RNA_def_property_enum_funcs(prop, nullptr, nullptr, "rna_structure_type_no_auto_itemf"); + RNA_def_property_enum_funcs( + prop, + nullptr, + nullptr, + "rna_Node_ItemArray_structure_type_itemf"); RNA_def_property_ui_text( prop, "Structure Type", @@ -7726,7 +7756,11 @@ static void rna_def_evaluate_closure_output_item(BlenderRNA *brna) prop = RNA_def_property(srna, "structure_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_node_socket_structure_type_items); - RNA_def_property_enum_funcs(prop, nullptr, nullptr, "rna_structure_type_no_auto_itemf"); + RNA_def_property_enum_funcs( + prop, + nullptr, + nullptr, + "rna_Node_ItemArray_structure_type_itemf"); RNA_def_property_ui_text( prop, "Structure Type", @@ -7787,6 +7821,17 @@ static void def_evaluate_closure(BlenderRNA *brna, StructRNA *srna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); RNA_def_property_update(prop, NC_NODE, nullptr); + + prop = RNA_def_property(srna, "define_signature", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "flag", NODE_EVALUATE_CLOSURE_FLAG_DEFINE_SIGNATURE); + RNA_def_property_ui_text( + prop, + "Define Signature", + "This node defines a closure signature that should be used by other nodes"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); + RNA_def_property_update(prop, NC_NODE, "rna_Node_update"); } static void rna_def_geo_bake_item(BlenderRNA *brna) @@ -7866,10 +7911,26 @@ static void rna_def_geo_bake(BlenderRNA *brna, StructRNA *srna) static void rna_def_combine_bundle_item(BlenderRNA *brna) { + PropertyRNA *prop; + StructRNA *srna = RNA_def_struct(brna, "NodeCombineBundleItem", nullptr); RNA_def_struct_ui_text(srna, "Combine Bundle Item", ""); rna_def_node_item_array_socket_item_common(srna, "CombineBundleItemsAccessor", true); + + prop = RNA_def_property(srna, "structure_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_node_socket_structure_type_items); + RNA_def_property_enum_funcs( + prop, + nullptr, + nullptr, + "rna_Node_ItemArray_structure_type_itemf"); + RNA_def_property_ui_text( + prop, + "Structure Type", + "What kind of higher order types are expected to flow through this socket"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_Node_ItemArray_item_update"); } static void rna_def_combine_bundle_items(BlenderRNA *brna) @@ -7905,14 +7966,40 @@ static void def_combine_bundle(BlenderRNA *brna, StructRNA *srna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); RNA_def_property_update(prop, NC_NODE, nullptr); + + prop = RNA_def_property(srna, "define_signature", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_COMBINE_BUNDLE_FLAG_DEFINE_SIGNATURE); + RNA_def_property_ui_text( + prop, + "Define Signature", + "This node defines a bundle signature that should be used by other nodes"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); + RNA_def_property_update(prop, NC_NODE, "rna_Node_update"); } static void rna_def_separate_bundle_item(BlenderRNA *brna) { + PropertyRNA *prop; + StructRNA *srna = RNA_def_struct(brna, "NodeSeparateBundleItem", nullptr); RNA_def_struct_ui_text(srna, "Separate Bundle Item", ""); rna_def_node_item_array_socket_item_common(srna, "SeparateBundleItemsAccessor", true); + + prop = RNA_def_property(srna, "structure_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_node_socket_structure_type_items); + RNA_def_property_enum_funcs( + prop, + nullptr, + nullptr, + "rna_Node_ItemArray_structure_type_itemf"); + RNA_def_property_ui_text( + prop, + "Structure Type", + "What kind of higher order types are expected to flow through this socket"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_Node_ItemArray_item_update"); } static void rna_def_separate_bundle_items(BlenderRNA *brna) @@ -7948,6 +8035,16 @@ static void def_separate_bundle(BlenderRNA *brna, StructRNA *srna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); RNA_def_property_update(prop, NC_NODE, nullptr); + + prop = RNA_def_property(srna, "define_signature", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_SEPARATE_BUNDLE_FLAG_DEFINE_SIGNATURE); + RNA_def_property_ui_text( + prop, + "Define Signature", + "This node defines a bundle signature that should be used by other nodes"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); + RNA_def_property_update(prop, NC_NODE, "rna_Node_update"); } static void rna_def_index_switch_item(BlenderRNA *brna) diff --git a/source/blender/nodes/NOD_geometry_nodes_bundle_signature.hh b/source/blender/nodes/NOD_geometry_nodes_bundle_signature.hh index e60d3f8f63a..a12e8333403 100644 --- a/source/blender/nodes/NOD_geometry_nodes_bundle_signature.hh +++ b/source/blender/nodes/NOD_geometry_nodes_bundle_signature.hh @@ -8,24 +8,21 @@ #include "BKE_node.hh" +#include "NOD_node_in_compute_context.hh" + namespace blender::nodes { struct BundleSignature { - struct Item { std::string key; const bke::bNodeSocketType *type = nullptr; + NodeSocketInterfaceStructureType structure_type; - uint64_t hash() const - { - return get_default_hash(this->key); - } - - BLI_STRUCT_EQUALITY_OPERATORS_1(Item, key) + BLI_STRUCT_EQUALITY_OPERATORS_3(Item, key, type, structure_type); }; struct ItemKeyGetter { - std::string operator()(const Item &item) + StringRefNull operator()(const Item &item) { return item.key; } @@ -33,12 +30,35 @@ struct BundleSignature { CustomIDVectorSet items; - bool matches_exactly(const BundleSignature &other) const; + friend bool operator==(const BundleSignature &a, const BundleSignature &b); + friend bool operator!=(const BundleSignature &a, const BundleSignature &b); - static bool all_matching_exactly(const Span signatures); + static BundleSignature from_combine_bundle_node(const bNode &node, + bool allow_auto_structure_type); + static BundleSignature from_separate_bundle_node(const bNode &node, + bool allow_auto_structure_type); - static BundleSignature from_combine_bundle_node(const bNode &node); - static BundleSignature from_separate_bundle_node(const bNode &node); + void set_auto_structure_types(); }; +/** + * Multiple bundle signatures that may be linked to a single node. + */ +struct LinkedBundleSignatures { + struct Item { + BundleSignature signature; + bool is_signature_definition = false; + SocketInContext source_socket; + }; + Vector items; + bool has_type_definition() const; + + std::optional get_merged_signature() const; +}; + +NodeSocketInterfaceStructureType get_structure_type_for_bundle_signature( + const bNodeSocket &socket, + const NodeSocketInterfaceStructureType stored_structure_type, + const bool allow_auto_structure_type); + } // namespace blender::nodes diff --git a/source/blender/nodes/NOD_geometry_nodes_closure_signature.hh b/source/blender/nodes/NOD_geometry_nodes_closure_signature.hh index 902f1695705..8017d9d5144 100644 --- a/source/blender/nodes/NOD_geometry_nodes_closure_signature.hh +++ b/source/blender/nodes/NOD_geometry_nodes_closure_signature.hh @@ -8,6 +8,8 @@ #include "BLI_vector_set.hh" +#include "NOD_node_in_compute_context.hh" + namespace blender::nodes { /** Describes the names and types of the inputs and outputs of a closure. */ @@ -16,7 +18,9 @@ class ClosureSignature { struct Item { std::string key; const bke::bNodeSocketType *type = nullptr; - std::optional structure_type = std::nullopt; + NodeSocketInterfaceStructureType structure_type; + + BLI_STRUCT_EQUALITY_OPERATORS_3(Item, key, type, structure_type); }; struct ItemKeyGetter { @@ -32,11 +36,30 @@ class ClosureSignature { std::optional find_input_index(StringRef key) const; std::optional find_output_index(StringRef key) const; - bool matches_exactly(const ClosureSignature &other) const; - static bool all_matching_exactly(Span signatures); + friend bool operator==(const ClosureSignature &a, const ClosureSignature &b); + friend bool operator!=(const ClosureSignature &a, const ClosureSignature &b); - static ClosureSignature from_closure_output_node(const bNode &node); - static ClosureSignature from_evaluate_closure_node(const bNode &node); + static ClosureSignature from_closure_output_node(const bNode &node, + bool allow_auto_structure_type); + static ClosureSignature from_evaluate_closure_node(const bNode &node, + bool allow_auto_structure_type); + + void set_auto_structure_types(); +}; + +/** + * Multiple closure signatures that may be linked to a single node. + */ +struct LinkedClosureSignatures { + struct Item { + ClosureSignature signature; + bool define_signature = false; + SocketInContext socket; + }; + Vector items; + bool has_type_definition() const; + + std::optional get_merged_signature() const; }; } // namespace blender::nodes diff --git a/source/blender/nodes/NOD_rna_define.hh b/source/blender/nodes/NOD_rna_define.hh index 11eb264aa70..960d96f6018 100644 --- a/source/blender/nodes/NOD_rna_define.hh +++ b/source/blender/nodes/NOD_rna_define.hh @@ -8,6 +8,8 @@ #include "BLI_function_ref.hh" +#include "DNA_node_types.h" + #include "RNA_define.hh" #include "WM_types.hh" /* For notifier defines */ @@ -16,6 +18,8 @@ void rna_Node_update(Main *bmain, Scene *scene, PointerRNA *ptr); void rna_Node_socket_update(Main *bmain, Scene *scene, PointerRNA *ptr); void rna_Node_update_relations(Main *bmain, Scene *scne, PointerRNA *ptr); void rna_Node_Viewer_shortcut_node_set(PointerRNA *ptr, PropertyRNA *prop, int value); +const EnumPropertyItem *rna_NodeSocket_structure_type_item_filter( + const bNodeTree *ntree, const eNodeSocketDatatype socket_type, bool *r_free); namespace blender::nodes { diff --git a/source/blender/nodes/NOD_trace_values.hh b/source/blender/nodes/NOD_trace_values.hh index 5053fd89a41..de308423bf5 100644 --- a/source/blender/nodes/NOD_trace_values.hh +++ b/source/blender/nodes/NOD_trace_values.hh @@ -13,6 +13,7 @@ #include "NOD_geometry_nodes_bundle_signature.hh" #include "NOD_geometry_nodes_closure_location.hh" #include "NOD_geometry_nodes_closure_signature.hh" +#include "NOD_node_in_compute_context.hh" namespace blender::nodes { @@ -27,19 +28,19 @@ namespace blender::nodes { bke::ComputeContextCache &compute_context_cache, const std::optional &source_location); -Vector gather_linked_target_bundle_signatures( +LinkedBundleSignatures gather_linked_target_bundle_signatures( const ComputeContext *bundle_socket_context, const bNodeSocket &bundle_socket, bke::ComputeContextCache &compute_context_cache); -Vector gather_linked_origin_bundle_signatures( +LinkedBundleSignatures gather_linked_origin_bundle_signatures( const ComputeContext *bundle_socket_context, const bNodeSocket &bundle_socket, bke::ComputeContextCache &compute_context_cache); -Vector gather_linked_target_closure_signatures( +LinkedClosureSignatures gather_linked_target_closure_signatures( const ComputeContext *closure_socket_context, const bNodeSocket &closure_socket, bke::ComputeContextCache &compute_context_cache); -Vector gather_linked_origin_closure_signatures( +LinkedClosureSignatures gather_linked_origin_closure_signatures( const ComputeContext *closure_socket_context, const bNodeSocket &closure_socket, bke::ComputeContextCache &compute_context_cache); diff --git a/source/blender/nodes/geometry/include/NOD_geo_closure.hh b/source/blender/nodes/geometry/include/NOD_geo_closure.hh index cc7f1f2256a..55eca82f0c0 100644 --- a/source/blender/nodes/geometry/include/NOD_geo_closure.hh +++ b/source/blender/nodes/geometry/include/NOD_geo_closure.hh @@ -258,7 +258,6 @@ struct EvaluateClosureInputItemsAccessor : public socket_items::SocketItemsAcces auto *storage = static_cast(node.storage); item.socket_type = socket_type; item.identifier = storage->input_items.next_identifier++; - item.structure_type = NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_DYNAMIC; socket_items::set_item_name_and_make_unique( node, item, name); } @@ -334,7 +333,6 @@ struct EvaluateClosureOutputItemsAccessor : public socket_items::SocketItemsAcce auto *storage = static_cast(node.storage); item.socket_type = socket_type; item.identifier = storage->output_items.next_identifier++; - item.structure_type = NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_DYNAMIC; socket_items::set_item_name_and_make_unique( node, item, name); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_closure.cc b/source/blender/nodes/geometry/nodes/node_geo_closure.cc index 047ec81bd29..4e2e8b60a76 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_closure.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_closure.cc @@ -42,15 +42,23 @@ static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *current_no layout->use_property_split_set(true); layout->use_property_decorate_set(false); + PointerRNA output_node_ptr = RNA_pointer_create_discrete(&ntree.id, &RNA_Node, &output_node); + layout->op("node.sockets_sync", "Sync", ICON_FILE_REFRESH); + layout->prop(&output_node_ptr, "define_signature", UI_ITEM_NONE, std::nullopt, ICON_NONE); if (current_node->type_legacy == NODE_CLOSURE_INPUT) { if (uiLayout *panel = layout->panel(C, "input_items", false, TIP_("Input Items"))) { socket_items::ui::draw_items_list_with_operators( C, panel, ntree, output_node); socket_items::ui::draw_active_item_props( ntree, output_node, [&](PointerRNA *item_ptr) { + const auto &item = *item_ptr->data_as(); + panel->use_property_split_set(true); + panel->use_property_decorate_set(false); panel->prop(item_ptr, "socket_type", UI_ITEM_NONE, std::nullopt, ICON_NONE); - panel->prop(item_ptr, "structure_type", UI_ITEM_NONE, std::nullopt, ICON_NONE); + if (!socket_type_always_single(eNodeSocketDatatype(item.socket_type))) { + panel->prop(item_ptr, "structure_type", UI_ITEM_NONE, "Shape", ICON_NONE); + } }); } } @@ -60,7 +68,13 @@ static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *current_no C, panel, ntree, output_node); socket_items::ui::draw_active_item_props( ntree, output_node, [&](PointerRNA *item_ptr) { + const auto &item = *item_ptr->data_as(); + panel->use_property_split_set(true); + panel->use_property_decorate_set(false); panel->prop(item_ptr, "socket_type", UI_ITEM_NONE, std::nullopt, ICON_NONE); + if (!socket_type_always_single(eNodeSocketDatatype(item.socket_type))) { + panel->prop(item_ptr, "structure_type", UI_ITEM_NONE, "Shape", ICON_NONE); + } }); } } @@ -83,7 +97,13 @@ static void node_declare(NodeDeclarationBuilder &b) const NodeClosureInputItem &item = output_storage.input_items.items[i]; const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); const std::string identifier = ClosureInputItemsAccessor::socket_identifier_for_item(item); - b.add_output(socket_type, item.name, identifier); + auto &decl = b.add_output(socket_type, item.name, identifier); + if (item.structure_type != NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) { + decl.structure_type(StructureType(item.structure_type)); + } + else { + decl.structure_type(StructureType::Dynamic); + } } } } @@ -149,7 +169,13 @@ static void node_declare(NodeDeclarationBuilder &b) const NodeClosureOutputItem &item = storage.output_items.items[i]; const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); const std::string identifier = ClosureOutputItemsAccessor::socket_identifier_for_item(item); - b.add_input(socket_type, item.name, identifier).supports_field(); + auto &decl = b.add_input(socket_type, item.name, identifier).supports_field(); + if (item.structure_type != NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) { + decl.structure_type(StructureType(item.structure_type)); + } + else { + decl.structure_type(StructureType::Dynamic); + } } } b.add_input("", "__extend__"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_combine_bundle.cc b/source/blender/nodes/geometry/nodes/node_geo_combine_bundle.cc index f753399f7ce..f061fd5aeec 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_combine_bundle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_combine_bundle.cc @@ -35,10 +35,16 @@ static void node_declare(NodeDeclarationBuilder &b) const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); const StringRef name = item.name ? item.name : ""; const std::string identifier = CombineBundleItemsAccessor::socket_identifier_for_item(item); - b.add_input(socket_type, name, identifier) - .socket_name_ptr(&tree->id, CombineBundleItemsAccessor::item_srna, &item, "name") - .supports_field() - .structure_type(StructureType::Dynamic); + auto &decl = b.add_input(socket_type, name, identifier) + .socket_name_ptr( + &tree->id, CombineBundleItemsAccessor::item_srna, &item, "name") + .supports_field(); + if (item.structure_type != NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) { + decl.structure_type(StructureType(item.structure_type)); + } + else { + decl.structure_type(StructureType::Dynamic); + } } } b.add_input("", "__extend__"); @@ -89,12 +95,19 @@ static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *node_ptr) bNode &node = *static_cast(node_ptr->data); layout->op("node.sockets_sync", "Sync", ICON_FILE_REFRESH); + layout->prop(node_ptr, "define_signature", UI_ITEM_NONE, std::nullopt, ICON_NONE); if (uiLayout *panel = layout->panel(C, "bundle_items", false, TIP_("Bundle Items"))) { socket_items::ui::draw_items_list_with_operators( C, panel, ntree, node); socket_items::ui::draw_active_item_props( ntree, node, [&](PointerRNA *item_ptr) { + const auto &item = *item_ptr->data_as(); + panel->use_property_split_set(true); + panel->use_property_decorate_set(false); panel->prop(item_ptr, "socket_type", UI_ITEM_NONE, "Type", ICON_NONE); + if (!socket_type_always_single(eNodeSocketDatatype(item.socket_type))) { + panel->prop(item_ptr, "structure_type", UI_ITEM_NONE, "Shape", ICON_NONE); + } }); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_evaluate_closure.cc b/source/blender/nodes/geometry/nodes/node_geo_evaluate_closure.cc index e4cadb06fda..bd1983d6a62 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_evaluate_closure.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_evaluate_closure.cc @@ -39,8 +39,13 @@ static void node_declare(NodeDeclarationBuilder &b) const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); const std::string identifier = EvaluateClosureOutputItemsAccessor::socket_identifier_for_item(item); - panel.add_output(socket_type, item.name, identifier) - .structure_type(StructureType(item.structure_type)); + auto &decl = panel.add_output(socket_type, item.name, identifier); + if (item.structure_type != NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) { + decl.structure_type(StructureType(item.structure_type)); + } + else { + decl.structure_type(StructureType::Dynamic); + } } panel.add_output("", "__extend__"); for (const int i : IndexRange(storage.input_items.items_num)) { @@ -48,8 +53,13 @@ static void node_declare(NodeDeclarationBuilder &b) const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); const std::string identifier = EvaluateClosureInputItemsAccessor::socket_identifier_for_item( item); - panel.add_input(socket_type, item.name, identifier) - .structure_type(StructureType(item.structure_type)); + auto &decl = panel.add_input(socket_type, item.name, identifier); + if (item.structure_type != NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) { + decl.structure_type(StructureType(item.structure_type)); + } + else { + decl.structure_type(StructureType::Dynamic); + } } panel.add_input("", "__extend__"); } @@ -109,14 +119,20 @@ static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) layout->use_property_decorate_set(false); layout->op("node.sockets_sync", "Sync", ICON_FILE_REFRESH); + layout->prop(ptr, "define_signature", UI_ITEM_NONE, std::nullopt, ICON_NONE); if (uiLayout *panel = layout->panel(C, "input_items", false, IFACE_("Input Items"))) { socket_items::ui::draw_items_list_with_operators( C, panel, tree, node); socket_items::ui::draw_active_item_props( tree, node, [&](PointerRNA *item_ptr) { + const auto &item = *item_ptr->data_as(); + panel->use_property_split_set(true); + panel->use_property_decorate_set(false); panel->prop(item_ptr, "socket_type", UI_ITEM_NONE, std::nullopt, ICON_NONE); - panel->prop(item_ptr, "structure_type", UI_ITEM_NONE, std::nullopt, ICON_NONE); + if (!socket_type_always_single(eNodeSocketDatatype(item.socket_type))) { + panel->prop(item_ptr, "structure_type", UI_ITEM_NONE, "Shape", ICON_NONE); + } }); } if (uiLayout *panel = layout->panel(C, "output_items", false, IFACE_("Output Items"))) { @@ -124,8 +140,13 @@ static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) C, panel, tree, node); socket_items::ui::draw_active_item_props( tree, node, [&](PointerRNA *item_ptr) { + const auto &item = *item_ptr->data_as(); + panel->use_property_split_set(true); + panel->use_property_decorate_set(false); panel->prop(item_ptr, "socket_type", UI_ITEM_NONE, std::nullopt, ICON_NONE); - panel->prop(item_ptr, "structure_type", UI_ITEM_NONE, std::nullopt, ICON_NONE); + if (!socket_type_always_single(eNodeSocketDatatype(item.socket_type))) { + panel->prop(item_ptr, "structure_type", UI_ITEM_NONE, "Shape", ICON_NONE); + } }); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_bundle.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_bundle.cc index 9f396008b14..3c0590055d4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_separate_bundle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_separate_bundle.cc @@ -39,11 +39,17 @@ static void node_declare(NodeDeclarationBuilder &b) const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); const StringRef name = item.name ? item.name : ""; const std::string identifier = SeparateBundleItemsAccessor::socket_identifier_for_item(item); - b.add_output(socket_type, name, identifier) - .socket_name_ptr(&tree->id, SeparateBundleItemsAccessor::item_srna, &item, "name") - .propagate_all() - .reference_pass_all() - .structure_type(StructureType::Dynamic); + auto &decl = b.add_output(socket_type, name, identifier) + .socket_name_ptr( + &tree->id, SeparateBundleItemsAccessor::item_srna, &item, "name") + .propagate_all() + .reference_pass_all(); + if (item.structure_type != NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) { + decl.structure_type(StructureType(item.structure_type)); + } + else { + decl.structure_type(StructureType::Dynamic); + } } } b.add_output("", "__extend__"); @@ -93,12 +99,19 @@ static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *node_ptr) bNode &node = *static_cast(node_ptr->data); layout->op("node.sockets_sync", "Sync", ICON_FILE_REFRESH); + layout->prop(node_ptr, "define_signature", UI_ITEM_NONE, std::nullopt, ICON_NONE); if (uiLayout *panel = layout->panel(C, "bundle_items", false, TIP_("Bundle Items"))) { socket_items::ui::draw_items_list_with_operators( C, panel, ntree, node); socket_items::ui::draw_active_item_props( ntree, node, [&](PointerRNA *item_ptr) { + const auto &item = *item_ptr->data_as(); + panel->use_property_split_set(true); + panel->use_property_decorate_set(false); panel->prop(item_ptr, "socket_type", UI_ITEM_NONE, "Type", ICON_NONE); + if (!socket_type_always_single(eNodeSocketDatatype(item.socket_type))) { + panel->prop(item_ptr, "structure_type", UI_ITEM_NONE, "Shape", ICON_NONE); + } }); } } diff --git a/source/blender/nodes/intern/geometry_nodes_bundle.cc b/source/blender/nodes/intern/geometry_nodes_bundle.cc index 09a4d3e699c..dd824d7ef96 100644 --- a/source/blender/nodes/intern/geometry_nodes_bundle.cc +++ b/source/blender/nodes/intern/geometry_nodes_bundle.cc @@ -14,33 +14,22 @@ namespace blender::nodes { -bool BundleSignature::matches_exactly(const BundleSignature &other) const +bool operator==(const BundleSignature &a, const BundleSignature &b) { - if (items.size() != other.items.size()) { - return false; - } - for (const Item &item : items) { - if (std::none_of(other.items.begin(), other.items.end(), [&](const Item &other_item) { - return item.key == other_item.key; - })) - { - return false; - } - } - return true; + return a.items.as_span() == b.items.as_span(); } -bool BundleSignature::all_matching_exactly(const Span signatures) +bool operator!=(const BundleSignature &a, const BundleSignature &b) { - if (signatures.is_empty()) { - return true; + return !(a == b); +} + +void BundleSignature::set_auto_structure_types() +{ + for (const BundleSignature::Item &item : this->items) { + const_cast(item).structure_type = + NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO; } - for (const BundleSignature &signature : signatures.drop_front(1)) { - if (!signatures[0].matches_exactly(signature)) { - return false; - } - } - return true; } BundlePtr Bundle::create() @@ -212,29 +201,87 @@ void Bundle::delete_self() MEM_delete(this); } -BundleSignature BundleSignature::from_combine_bundle_node(const bNode &node) +NodeSocketInterfaceStructureType get_structure_type_for_bundle_signature( + const bNodeSocket &socket, + const NodeSocketInterfaceStructureType stored_structure_type, + const bool allow_auto_structure_type) +{ + if (stored_structure_type != NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) { + return stored_structure_type; + } + if (allow_auto_structure_type) { + return NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO; + } + return NodeSocketInterfaceStructureType(socket.runtime->inferred_structure_type); +} + +BundleSignature BundleSignature::from_combine_bundle_node(const bNode &node, + const bool allow_auto_structure_type) { BLI_assert(node.is_type("NodeCombineBundle")); const auto &storage = *static_cast(node.storage); BundleSignature signature; for (const int i : IndexRange(storage.items_num)) { const NodeCombineBundleItem &item = storage.items[i]; + const bNodeSocket &socket = node.input_socket(i); if (const bke::bNodeSocketType *stype = bke::node_socket_type_find_static(item.socket_type)) { - signature.items.add({item.name, stype}); + const NodeSocketInterfaceStructureType structure_type = + get_structure_type_for_bundle_signature( + socket, + NodeSocketInterfaceStructureType(item.structure_type), + allow_auto_structure_type); + signature.items.add({item.name, stype, structure_type}); } } return signature; } -BundleSignature BundleSignature::from_separate_bundle_node(const bNode &node) +BundleSignature BundleSignature::from_separate_bundle_node(const bNode &node, + const bool allow_auto_structure_type) { BLI_assert(node.is_type("NodeSeparateBundle")); const auto &storage = *static_cast(node.storage); BundleSignature signature; for (const int i : IndexRange(storage.items_num)) { const NodeSeparateBundleItem &item = storage.items[i]; + const bNodeSocket &socket = node.output_socket(i); if (const bke::bNodeSocketType *stype = bke::node_socket_type_find_static(item.socket_type)) { - signature.items.add({item.name, stype}); + const NodeSocketInterfaceStructureType structure_type = + get_structure_type_for_bundle_signature( + socket, + NodeSocketInterfaceStructureType(item.structure_type), + allow_auto_structure_type); + signature.items.add({item.name, stype, structure_type}); + } + } + return signature; +} + +bool LinkedBundleSignatures::has_type_definition() const +{ + for (const Item &item : this->items) { + if (item.is_signature_definition) { + return true; + } + } + return false; +} + +std::optional LinkedBundleSignatures::get_merged_signature() const +{ + BundleSignature signature; + for (const Item &src_signature : this->items) { + for (const BundleSignature::Item &item : src_signature.signature.items) { + if (!signature.items.add(item)) { + const BundleSignature::Item &existing_item = *signature.items.lookup_key_ptr_as(item.key); + if (item.type->type != existing_item.type->type) { + return std::nullopt; + } + if (existing_item.structure_type != item.structure_type) { + const_cast(existing_item).structure_type = + NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_DYNAMIC; + } + } } } return signature; diff --git a/source/blender/nodes/intern/geometry_nodes_closure.cc b/source/blender/nodes/intern/geometry_nodes_closure.cc index 2acd6003048..6208efc31c2 100644 --- a/source/blender/nodes/intern/geometry_nodes_closure.cc +++ b/source/blender/nodes/intern/geometry_nodes_closure.cc @@ -4,6 +4,7 @@ #include "BKE_node_runtime.hh" +#include "NOD_geometry_nodes_bundle_signature.hh" #include "NOD_geometry_nodes_closure.hh" namespace blender::nodes { @@ -30,97 +31,137 @@ std::optional ClosureSignature::find_output_index(const StringRef key) cons return std::nullopt; } -static bool items_equal(const ClosureSignature::Item &a, const ClosureSignature::Item &b) +void ClosureSignature::set_auto_structure_types() { - if (a.key != b.key) { - return false; + for (const Item &item : this->inputs) { + const_cast(item).structure_type = NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO; } - if (a.type != b.type) { - return false; + for (const Item &item : this->outputs) { + const_cast(item).structure_type = NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO; } - if (a.structure_type.has_value() && b.structure_type.has_value()) { - if (*a.structure_type != *b.structure_type) { - return false; - } - } - return true; } -bool ClosureSignature::matches_exactly(const ClosureSignature &other) const +bool operator==(const ClosureSignature &a, const ClosureSignature &b) { - if (inputs.size() != other.inputs.size()) { - return false; - } - if (outputs.size() != other.outputs.size()) { - return false; - } - for (const Item &item : inputs) { - if (std::none_of(other.inputs.begin(), other.inputs.end(), [&](const Item &other_item) { - return items_equal(item, other_item); - })) - { - return false; - } - } - for (const Item &item : outputs) { - if (std::none_of(other.outputs.begin(), other.outputs.end(), [&](const Item &other_item) { - return items_equal(item, other_item); - })) - { - return false; - } - } - return true; + return a.inputs.as_span() == b.inputs.as_span() && a.outputs.as_span() == b.outputs.as_span(); } -bool ClosureSignature::all_matching_exactly(const Span signatures) +bool operator!=(const ClosureSignature &a, const ClosureSignature &b) { - if (signatures.is_empty()) { - return true; - } - for (const ClosureSignature &signature : signatures.drop_front(1)) { - if (!signatures[0].matches_exactly(signature)) { - return false; - } - } - return true; + return !(a == b); } -ClosureSignature ClosureSignature::from_closure_output_node(const bNode &node) +ClosureSignature ClosureSignature::from_closure_output_node(const bNode &node, + const bool allow_auto_structure_type) { BLI_assert(node.is_type("NodeClosureOutput")); + const bNodeTree &tree = node.owner_tree(); + const bNode *input_node = + bke::zone_type_by_node_type(node.type_legacy)->get_corresponding_input(tree, node); const auto &storage = *static_cast(node.storage); nodes::ClosureSignature signature; - for (const int i : IndexRange(storage.input_items.items_num)) { - const NodeClosureInputItem &item = storage.input_items.items[i]; - if (const bke::bNodeSocketType *stype = bke::node_socket_type_find_static(item.socket_type)) { - signature.inputs.add({item.name, stype}); + if (input_node) { + for (const int i : IndexRange(storage.input_items.items_num)) { + const NodeClosureInputItem &item = storage.input_items.items[i]; + const bNodeSocket &socket = input_node->output_socket(i); + if (const bke::bNodeSocketType *stype = bke::node_socket_type_find_static(item.socket_type)) + { + const NodeSocketInterfaceStructureType structure_type = + get_structure_type_for_bundle_signature( + socket, + NodeSocketInterfaceStructureType(item.structure_type), + allow_auto_structure_type); + signature.inputs.add({item.name, stype, structure_type}); + } } } for (const int i : IndexRange(storage.output_items.items_num)) { const NodeClosureOutputItem &item = storage.output_items.items[i]; + const bNodeSocket &socket = node.input_socket(i); if (const bke::bNodeSocketType *stype = bke::node_socket_type_find_static(item.socket_type)) { - signature.outputs.add({item.name, stype}); + const NodeSocketInterfaceStructureType structure_type = + get_structure_type_for_bundle_signature( + socket, + NodeSocketInterfaceStructureType(item.structure_type), + allow_auto_structure_type); + signature.outputs.add({item.name, stype, structure_type}); } } return signature; } -ClosureSignature ClosureSignature::from_evaluate_closure_node(const bNode &node) +ClosureSignature ClosureSignature::from_evaluate_closure_node(const bNode &node, + const bool allow_auto_structure_type) { BLI_assert(node.is_type("NodeEvaluateClosure")); const auto &storage = *static_cast(node.storage); nodes::ClosureSignature signature; for (const int i : IndexRange(storage.input_items.items_num)) { const NodeEvaluateClosureInputItem &item = storage.input_items.items[i]; + const bNodeSocket &socket = node.input_socket(i + 1); if (const bke::bNodeSocketType *stype = bke::node_socket_type_find_static(item.socket_type)) { - signature.inputs.add({item.name, stype, nodes::StructureType(item.structure_type)}); + const NodeSocketInterfaceStructureType structure_type = + get_structure_type_for_bundle_signature( + socket, + NodeSocketInterfaceStructureType(item.structure_type), + allow_auto_structure_type); + signature.inputs.add({item.name, stype, structure_type}); } } for (const int i : IndexRange(storage.output_items.items_num)) { const NodeEvaluateClosureOutputItem &item = storage.output_items.items[i]; + const bNodeSocket &socket = node.output_socket(i); if (const bke::bNodeSocketType *stype = bke::node_socket_type_find_static(item.socket_type)) { - signature.outputs.add({item.name, stype, nodes::StructureType(item.structure_type)}); + const NodeSocketInterfaceStructureType structure_type = + get_structure_type_for_bundle_signature( + socket, + NodeSocketInterfaceStructureType(item.structure_type), + allow_auto_structure_type); + signature.outputs.add({item.name, stype, structure_type}); + } + } + return signature; +} + +bool LinkedClosureSignatures::has_type_definition() const +{ + for (const Item &item : this->items) { + if (item.define_signature) { + return true; + } + } + return false; +} + +std::optional LinkedClosureSignatures::get_merged_signature() const +{ + ClosureSignature signature; + for (const Item &src_signature : this->items) { + for (const ClosureSignature::Item &item : src_signature.signature.inputs) { + if (!signature.inputs.add(item)) { + const ClosureSignature::Item &existing_item = *signature.inputs.lookup_key_ptr_as( + item.key); + if (existing_item.type->type != item.type->type) { + return std::nullopt; + } + if (existing_item.structure_type != item.structure_type) { + const_cast(existing_item).structure_type = + NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_DYNAMIC; + } + } + } + for (const ClosureSignature::Item &item : src_signature.signature.outputs) { + if (!signature.outputs.add(item)) { + const ClosureSignature::Item &existing_item = *signature.outputs.lookup_key_ptr_as( + item.key); + if (existing_item.type->type != item.type->type) { + return std::nullopt; + } + if (existing_item.structure_type != item.structure_type) { + const_cast(existing_item).structure_type = + NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_DYNAMIC; + } + } } } return signature; diff --git a/source/blender/nodes/intern/sync_sockets.cc b/source/blender/nodes/intern/sync_sockets.cc index 8e5afe19841..24e176b6832 100644 --- a/source/blender/nodes/intern/sync_sockets.cc +++ b/source/blender/nodes/intern/sync_sockets.cc @@ -68,19 +68,22 @@ static BundleSyncState get_sync_state_separate_bundle( bke::ComputeContextCache compute_context_cache; const ComputeContext *current_context = ed::space_node::compute_context_for_edittree_socket( snode, compute_context_cache, *src_bundle_socket); - const Vector source_signatures = gather_linked_origin_bundle_signatures( + const LinkedBundleSignatures linked_signatures = gather_linked_origin_bundle_signatures( current_context, *src_bundle_socket, compute_context_cache); - if (source_signatures.is_empty()) { + if (linked_signatures.items.is_empty()) { return {NodeSyncState::NoSyncSource}; } - if (!nodes::BundleSignature::all_matching_exactly(source_signatures)) { + std::optional merged_signature = linked_signatures.get_merged_signature(); + if (!merged_signature.has_value()) { return {NodeSyncState::ConflictingSyncSources}; } - const nodes::BundleSignature &source_signature = source_signatures[0]; + if (!linked_signatures.has_type_definition()) { + merged_signature->set_auto_structure_types(); + } const nodes::BundleSignature ¤t_signature = - nodes::BundleSignature::from_separate_bundle_node(separate_bundle_node); - if (!source_signature.matches_exactly(current_signature)) { - return {NodeSyncState::CanBeSynced, source_signature}; + nodes::BundleSignature::from_separate_bundle_node(separate_bundle_node, true); + if (*merged_signature != current_signature) { + return {NodeSyncState::CanBeSynced, std::move(merged_signature)}; } return {NodeSyncState::Synced}; } @@ -100,19 +103,22 @@ static BundleSyncState get_sync_state_combine_bundle( bke::ComputeContextCache compute_context_cache; const ComputeContext *current_context = ed::space_node::compute_context_for_edittree_socket( snode, compute_context_cache, *src_bundle_socket); - const Vector source_signatures = gather_linked_target_bundle_signatures( + const LinkedBundleSignatures linked_signatures = gather_linked_target_bundle_signatures( current_context, *src_bundle_socket, compute_context_cache); - if (source_signatures.is_empty()) { + if (linked_signatures.items.is_empty()) { return {NodeSyncState::NoSyncSource}; } - if (!nodes::BundleSignature::all_matching_exactly(source_signatures)) { + std::optional merged_signature = linked_signatures.get_merged_signature(); + if (!merged_signature.has_value()) { return {NodeSyncState::ConflictingSyncSources}; } - const nodes::BundleSignature &source_signature = source_signatures[0]; + if (!linked_signatures.has_type_definition()) { + merged_signature->set_auto_structure_types(); + } const nodes::BundleSignature ¤t_signature = - nodes::BundleSignature::from_combine_bundle_node(combine_bundle_node); - if (!source_signature.matches_exactly(current_signature)) { - return {NodeSyncState::CanBeSynced, source_signature}; + nodes::BundleSignature::from_combine_bundle_node(combine_bundle_node, true); + if (*merged_signature != current_signature) { + return {NodeSyncState::CanBeSynced, std::move(merged_signature)}; } return {NodeSyncState::Synced}; } @@ -131,20 +137,22 @@ static ClosureSyncState get_sync_state_closure_output( bke::ComputeContextCache compute_context_cache; const ComputeContext *current_context = ed::space_node::compute_context_for_edittree_socket( snode, compute_context_cache, *src_closure_socket); - const Vector source_signatures = - gather_linked_target_closure_signatures( - current_context, *src_closure_socket, compute_context_cache); - if (source_signatures.is_empty()) { + const LinkedClosureSignatures linked_signatures = gather_linked_target_closure_signatures( + current_context, *src_closure_socket, compute_context_cache); + if (linked_signatures.items.is_empty()) { return {NodeSyncState::NoSyncSource}; } - if (!nodes::ClosureSignature::all_matching_exactly(source_signatures)) { + std::optional merged_signature = linked_signatures.get_merged_signature(); + if (!merged_signature.has_value()) { return {NodeSyncState::ConflictingSyncSources}; } - const nodes::ClosureSignature &source_signature = source_signatures[0]; + if (!linked_signatures.has_type_definition()) { + merged_signature->set_auto_structure_types(); + } const nodes::ClosureSignature ¤t_signature = - nodes::ClosureSignature::from_closure_output_node(closure_output_node); - if (!source_signature.matches_exactly(current_signature)) { - return {NodeSyncState::CanBeSynced, source_signature}; + nodes::ClosureSignature::from_closure_output_node(closure_output_node, true); + if (*merged_signature != current_signature) { + return {NodeSyncState::CanBeSynced, merged_signature}; } return {NodeSyncState::Synced}; } @@ -163,20 +171,22 @@ static ClosureSyncState get_sync_state_evaluate_closure( bke::ComputeContextCache compute_context_cache; const ComputeContext *current_context = ed::space_node::compute_context_for_edittree_socket( snode, compute_context_cache, *src_closure_socket); - const Vector source_signatures = - gather_linked_origin_closure_signatures( - current_context, *src_closure_socket, compute_context_cache); - if (source_signatures.is_empty()) { + const LinkedClosureSignatures linked_signatures = gather_linked_origin_closure_signatures( + current_context, *src_closure_socket, compute_context_cache); + if (linked_signatures.items.is_empty()) { return {NodeSyncState::NoSyncSource}; } - if (!nodes::ClosureSignature::all_matching_exactly(source_signatures)) { + std::optional merged_signature = linked_signatures.get_merged_signature(); + if (!merged_signature.has_value()) { return {NodeSyncState::ConflictingSyncSources}; } - const nodes::ClosureSignature &source_signature = source_signatures[0]; + if (!linked_signatures.has_type_definition()) { + merged_signature->set_auto_structure_types(); + } const nodes::ClosureSignature ¤t_signature = - nodes::ClosureSignature::from_evaluate_closure_node(evaluate_closure_node); - if (!source_signature.matches_exactly(current_signature)) { - return {NodeSyncState::CanBeSynced, source_signature}; + nodes::ClosureSignature::from_evaluate_closure_node(evaluate_closure_node, true); + if (*merged_signature != current_signature) { + return {NodeSyncState::CanBeSynced, merged_signature}; } return {NodeSyncState::Synced}; } @@ -214,6 +224,7 @@ void sync_sockets_separate_bundle(SpaceNode &snode, NodeSeparateBundleItem &new_item = *nodes::socket_items::add_item_with_socket_type_and_name< nodes ::SeparateBundleItemsAccessor>( *snode.edittree, separate_bundle_node, item.type->type, item.key.c_str()); + new_item.structure_type = int(item.structure_type); if (const std::optional old_identifier = old_identifiers.lookup_try(item.key)) { new_item.identifier = *old_identifier; } @@ -254,6 +265,7 @@ void sync_sockets_combine_bundle(SpaceNode &snode, NodeCombineBundleItem &new_item = *nodes::socket_items::add_item_with_socket_type_and_name< nodes ::CombineBundleItemsAccessor>( *snode.edittree, combine_bundle_node, item.type->type, item.key.c_str()); + new_item.structure_type = int(item.structure_type); if (const std::optional old_identifier = old_identifiers.lookup_try(item.key)) { new_item.identifier = *old_identifier; } @@ -303,6 +315,7 @@ void sync_sockets_evaluate_closure(SpaceNode &snode, *nodes::socket_items::add_item_with_socket_type_and_name< nodes::EvaluateClosureInputItemsAccessor>( *snode.edittree, evaluate_closure_node, item.type->type, item.key.c_str()); + new_item.structure_type = int(item.structure_type); if (const std::optional old_identifier = old_input_identifiers.lookup_try(item.key)) { new_item.identifier = *old_identifier; } @@ -312,6 +325,7 @@ void sync_sockets_evaluate_closure(SpaceNode &snode, *nodes::socket_items::add_item_with_socket_type_and_name< nodes::EvaluateClosureOutputItemsAccessor>( *snode.edittree, evaluate_closure_node, item.type->type, item.key.c_str()); + new_item.structure_type = int(item.structure_type); if (const std::optional old_identifier = old_output_identifiers.lookup_try(item.key)) { new_item.identifier = *old_identifier; } @@ -361,9 +375,7 @@ void sync_sockets_closure(SpaceNode &snode, NodeClosureInputItem &new_item = *nodes::socket_items::add_item_with_socket_type_and_name( *snode.edittree, closure_output_node, item.type->type, item.key.c_str()); - if (item.structure_type) { - new_item.structure_type = int(*item.structure_type); - } + new_item.structure_type = int(item.structure_type); if (const std::optional old_identifier = old_input_identifiers.lookup_try(item.key)) { new_item.identifier = *old_identifier; } @@ -372,6 +384,7 @@ void sync_sockets_closure(SpaceNode &snode, NodeClosureOutputItem &new_item = *nodes::socket_items::add_item_with_socket_type_and_name< nodes::ClosureOutputItemsAccessor>( *snode.edittree, closure_output_node, item.type->type, item.key.c_str()); + new_item.structure_type = int(item.structure_type); if (const std::optional old_identifier = old_output_identifiers.lookup_try(item.key)) { new_item.identifier = *old_identifier; } @@ -413,20 +426,25 @@ static std::string get_bundle_sync_tooltip(const nodes::BundleSignature &old_sig Vector added_items; Vector removed_items; Vector changed_items; + bool order_changed = false; - for (const nodes::BundleSignature::Item &new_item : new_signature.items) { - if (const nodes::BundleSignature::Item *old_item = old_signature.items.lookup_key_ptr_as( - new_item.key)) - { - if (new_item.type->type != old_item->type->type) { - changed_items.append(new_item.key); - } - } - else { + for (const int new_item_i : new_signature.items.index_range()) { + const BundleSignature::Item &new_item = new_signature.items[new_item_i]; + const int old_item_i = old_signature.items.index_of_try_as(new_item.key); + if (old_item_i == -1) { added_items.append(new_item.key); } + else { + const BundleSignature::Item &old_item = old_signature.items[old_item_i]; + if (new_item != old_item) { + changed_items.append(new_item.key); + } + if (old_item_i != new_item_i) { + order_changed = true; + } + } } - for (const nodes::BundleSignature ::Item &old_item : old_signature.items) { + for (const nodes::BundleSignature::Item &old_item : old_signature.items) { if (!new_signature.items.contains_as(old_item.key)) { removed_items.append(old_item.key); } @@ -435,13 +453,16 @@ static std::string get_bundle_sync_tooltip(const nodes::BundleSignature &old_sig fmt::memory_buffer string_buffer; auto buf = fmt::appender(string_buffer); if (!added_items.is_empty()) { - fmt::format_to(buf, "{}: {}\n", TIP_("Add"), fmt::join(added_items, ", ")); + fmt::format_to(buf, "\u2022 {}: {}\n", TIP_("Add"), fmt::join(added_items, ", ")); } if (!removed_items.is_empty()) { - fmt::format_to(buf, "{}: {}\n", TIP_("Remove"), fmt::join(removed_items, ", ")); + fmt::format_to(buf, "\u2022 {}: {}\n", TIP_("Remove"), fmt::join(removed_items, ", ")); } if (!changed_items.is_empty()) { - fmt::format_to(buf, "{}: {}\n", TIP_("Change"), fmt::join(changed_items, ", ")); + fmt::format_to(buf, "\u2022 {}: {}\n", TIP_("Change"), fmt::join(changed_items, ", ")); + } + if (order_changed) { + fmt::format_to(buf, "\u2022 {}", TIP_("Reorder")); } fmt::format_to(buf, "\n{}", TIP_("Update based on linked bundle signature")); @@ -454,21 +475,27 @@ static std::string get_closure_sync_tooltip(const nodes::ClosureSignature &old_s Vector added_inputs; Vector removed_inputs; Vector changed_inputs; + bool input_order = false; Vector added_outputs; Vector removed_outputs; Vector changed_outputs; + bool output_order = false; - for (const nodes::ClosureSignature::Item &new_item : new_signature.inputs) { - if (const nodes::ClosureSignature::Item *old_item = old_signature.inputs.lookup_key_ptr_as( - new_item.key)) - { - if (new_item.type->type != old_item->type->type) { - changed_inputs.append(new_item.key); - } + for (const int new_item_i : new_signature.inputs.index_range()) { + const nodes::ClosureSignature::Item &new_item = new_signature.inputs[new_item_i]; + const int old_item_i = old_signature.inputs.index_of_try_as(new_item.key); + if (old_item_i == -1) { + added_inputs.append(new_item.key); } else { - added_inputs.append(new_item.key); + const nodes::ClosureSignature::Item &old_item = old_signature.inputs[old_item_i]; + if (new_item != old_item) { + changed_inputs.append(new_item.key); + } + if (old_item_i != new_item_i) { + input_order = true; + } } } for (const nodes::ClosureSignature::Item &old_item : old_signature.inputs) { @@ -476,16 +503,20 @@ static std::string get_closure_sync_tooltip(const nodes::ClosureSignature &old_s removed_inputs.append(old_item.key); } } - for (const nodes::ClosureSignature::Item &new_item : new_signature.outputs) { - if (const nodes::ClosureSignature::Item *old_item = old_signature.outputs.lookup_key_ptr_as( - new_item.key)) - { - if (new_item.type->type != old_item->type->type) { - changed_outputs.append(new_item.key); - } + for (const int new_item_i : new_signature.outputs.index_range()) { + const nodes::ClosureSignature::Item &new_item = new_signature.outputs[new_item_i]; + const int old_item_i = old_signature.outputs.index_of_try_as(new_item.key); + if (old_item_i == -1) { + added_outputs.append(new_item.key); } else { - added_outputs.append(new_item.key); + const nodes::ClosureSignature::Item &old_item = old_signature.outputs[old_item_i]; + if (new_item != old_item) { + changed_outputs.append(new_item.key); + } + if (old_item_i != new_item_i) { + output_order = true; + } } } for (const nodes::ClosureSignature::Item &old_item : old_signature.outputs) { @@ -497,22 +528,30 @@ static std::string get_closure_sync_tooltip(const nodes::ClosureSignature &old_s fmt::memory_buffer string_buffer; auto buf = fmt::appender(string_buffer); if (!added_inputs.is_empty()) { - fmt::format_to(buf, "{}: {}\n", TIP_("Add Inputs"), fmt::join(added_inputs, ", ")); + fmt::format_to(buf, "\u2022 {}: {}\n", TIP_("Add Inputs"), fmt::join(added_inputs, ", ")); } if (!removed_inputs.is_empty()) { - fmt::format_to(buf, "{}: {}\n", TIP_("Remove Inputs"), fmt::join(removed_inputs, ", ")); + fmt::format_to(buf, "\u2022 {}: {}\n", TIP_("Remove Inputs"), fmt::join(removed_inputs, ", ")); } if (!changed_inputs.is_empty()) { - fmt::format_to(buf, "{}: {}\n", TIP_("Change Inputs"), fmt::join(changed_inputs, ", ")); + fmt::format_to(buf, "\u2022 {}: {}\n", TIP_("Change Inputs"), fmt::join(changed_inputs, ", ")); + } + if (input_order) { + fmt::format_to(buf, "\u2022 {}\n", TIP_("Reorder Inputs")); } if (!added_outputs.is_empty()) { - fmt::format_to(buf, "{}: {}\n", TIP_("Add Outputs"), fmt::join(added_outputs, ", ")); + fmt::format_to(buf, "\u2022 {}: {}\n", TIP_("Add Outputs"), fmt::join(added_outputs, ", ")); } if (!removed_outputs.is_empty()) { - fmt::format_to(buf, "{}: {}\n", TIP_("Remove Outputs"), fmt::join(removed_outputs, ", ")); + fmt::format_to( + buf, "\u2022 {}: {}\n", TIP_("Remove Outputs"), fmt::join(removed_outputs, ", ")); } if (!changed_outputs.is_empty()) { - fmt::format_to(buf, "{}: {}\n", TIP_("Change Outputs"), fmt::join(changed_outputs, ", ")); + fmt::format_to( + buf, "\u2022 {}: {}\n", TIP_("Change Outputs"), fmt::join(changed_outputs, ", ")); + } + if (output_order) { + fmt::format_to(buf, "\u2022 {}\n", TIP_("Reorder Outputs")); } fmt::format_to(buf, "\n{}", TIP_("Update based on linked closure signature")); @@ -559,7 +598,7 @@ std::string sync_node_description_get(const bContext &C, const bNode &node) if (node.is_type("NodeSeparateBundle")) { const nodes::BundleSignature old_signature = nodes::BundleSignature::from_separate_bundle_node( - node); + node, true); if (const std::optional new_signature = get_sync_state_separate_bundle(*snode, node).source_signature) { @@ -568,7 +607,7 @@ std::string sync_node_description_get(const bContext &C, const bNode &node) } else if (node.is_type("NodeCombineBundle")) { const nodes::BundleSignature old_signature = nodes::BundleSignature::from_combine_bundle_node( - node); + node, true); if (const std::optional new_signature = get_sync_state_combine_bundle(*snode, node).source_signature) { @@ -577,7 +616,7 @@ std::string sync_node_description_get(const bContext &C, const bNode &node) } else if (node.is_type("NodeEvaluateClosure")) { const nodes::ClosureSignature old_signature = - nodes::ClosureSignature::from_evaluate_closure_node(node); + nodes::ClosureSignature::from_evaluate_closure_node(node, true); if (const std::optional new_signature = get_sync_state_evaluate_closure(*snode, node).source_signature) { @@ -586,7 +625,7 @@ std::string sync_node_description_get(const bContext &C, const bNode &node) } else if (node.is_type("NodeClosureOutput")) { const nodes::ClosureSignature old_signature = - nodes::ClosureSignature::from_closure_output_node(node); + nodes::ClosureSignature::from_closure_output_node(node, true); if (const std::optional new_signature = get_sync_state_closure_output(*snode, node).source_signature) { diff --git a/source/blender/nodes/intern/trace_values.cc b/source/blender/nodes/intern/trace_values.cc index 065445933e3..4351c9f5d35 100644 --- a/source/blender/nodes/intern/trace_values.cc +++ b/source/blender/nodes/intern/trace_values.cc @@ -570,82 +570,100 @@ static Vector find_origin_sockets_through_contexts( return found_origins.extract_vector(); } -Vector gather_linked_target_bundle_signatures( +LinkedBundleSignatures gather_linked_target_bundle_signatures( const ComputeContext *bundle_socket_context, const bNodeSocket &bundle_socket, bke::ComputeContextCache &compute_context_cache) { - const Vector target_sockets = find_target_sockets_through_contexts( + LinkedBundleSignatures result; + find_target_sockets_through_contexts( {bundle_socket_context, &bundle_socket}, compute_context_cache, - [](const SocketInContext &socket) { - return socket->is_input() && socket->owner_node().is_type("NodeSeparateBundle"); - }, - true); - Vector signatures; - for (const SocketInContext &target_socket : target_sockets) { - const NodeInContext &target_node = target_socket.owner_node(); - signatures.append(BundleSignature::from_separate_bundle_node(*target_node.node)); - } - return signatures; -} - -Vector gather_linked_origin_bundle_signatures( - const ComputeContext *bundle_socket_context, - const bNodeSocket &bundle_socket, - bke::ComputeContextCache &compute_context_cache) -{ - const Vector origin_sockets = find_origin_sockets_through_contexts( - {bundle_socket_context, &bundle_socket}, - compute_context_cache, - [](const SocketInContext &socket) { - return socket->is_output() && socket->owner_node().is_type("NodeCombineBundle"); - }, - true); - Vector signatures; - for (const SocketInContext &origin_socket : origin_sockets) { - const NodeInContext &origin_node = origin_socket.owner_node(); - signatures.append(BundleSignature::from_combine_bundle_node(*origin_node.node)); - } - return signatures; -} - -Vector gather_linked_target_closure_signatures( - const ComputeContext *closure_socket_context, - const bNodeSocket &closure_socket, - bke::ComputeContextCache &compute_context_cache) -{ - const Vector target_sockets = find_target_sockets_through_contexts( - {closure_socket_context, &closure_socket}, - compute_context_cache, - is_evaluate_closure_node_input, - true); - Vector signatures; - for (const SocketInContext &target_socket : target_sockets) { - const NodeInContext &target_node = target_socket.owner_node(); - signatures.append(ClosureSignature::from_evaluate_closure_node(*target_node.node)); - } - return signatures; -} - -Vector gather_linked_origin_closure_signatures( - const ComputeContext *closure_socket_context, - const bNodeSocket &closure_socket, - bke::ComputeContextCache &compute_context_cache) -{ - Vector signatures; - find_origin_sockets_through_contexts( - {closure_socket_context, &closure_socket}, - compute_context_cache, [&](const SocketInContext &socket) { - if (is_closure_zone_output_socket(socket)) { - signatures.append(ClosureSignature::from_closure_output_node(socket->owner_node())); + const bNode &node = socket->owner_node(); + if (socket->is_input() && node.is_type("NodeSeparateBundle")) { + const auto &storage = *static_cast(node.storage); + result.items.append({BundleSignature::from_separate_bundle_node(node, false), + bool(storage.flag & NODE_SEPARATE_BUNDLE_FLAG_DEFINE_SIGNATURE), + socket}); return true; } return false; }, true); - return signatures; + return result; +} + +LinkedBundleSignatures gather_linked_origin_bundle_signatures( + const ComputeContext *bundle_socket_context, + const bNodeSocket &bundle_socket, + bke::ComputeContextCache &compute_context_cache) +{ + LinkedBundleSignatures result; + find_origin_sockets_through_contexts( + {bundle_socket_context, &bundle_socket}, + compute_context_cache, + [&](const SocketInContext &socket) { + const bNode &node = socket->owner_node(); + if (socket->is_output() && node.is_type("NodeCombineBundle")) { + const auto &storage = *static_cast(node.storage); + result.items.append({BundleSignature::from_combine_bundle_node(node, false), + bool(storage.flag & NODE_COMBINE_BUNDLE_FLAG_DEFINE_SIGNATURE), + socket}); + return true; + } + return false; + }, + true); + return result; +} + +LinkedClosureSignatures gather_linked_target_closure_signatures( + const ComputeContext *closure_socket_context, + const bNodeSocket &closure_socket, + bke::ComputeContextCache &compute_context_cache) +{ + LinkedClosureSignatures result; + find_target_sockets_through_contexts( + {closure_socket_context, &closure_socket}, + compute_context_cache, + [&](const SocketInContext &socket) { + const bNode &node = socket->owner_node(); + if (is_evaluate_closure_node_input(socket)) { + const auto &storage = *static_cast(node.storage); + result.items.append({ClosureSignature::from_evaluate_closure_node(node, false), + bool(storage.flag & NODE_EVALUATE_CLOSURE_FLAG_DEFINE_SIGNATURE), + socket}); + return true; + } + return false; + }, + true); + return result; +} + +LinkedClosureSignatures gather_linked_origin_closure_signatures( + const ComputeContext *closure_socket_context, + const bNodeSocket &closure_socket, + bke::ComputeContextCache &compute_context_cache) +{ + LinkedClosureSignatures result; + find_origin_sockets_through_contexts( + {closure_socket_context, &closure_socket}, + compute_context_cache, + [&](const SocketInContext &socket) { + const bNode &node = socket->owner_node(); + if (is_closure_zone_output_socket(socket)) { + const auto &storage = *static_cast(node.storage); + result.items.append({ClosureSignature::from_closure_output_node(node, false), + bool(storage.flag & NODE_CLOSURE_FLAG_DEFINE_SIGNATURE), + socket}); + return true; + } + return false; + }, + true); + return result; } } // namespace blender::nodes