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