From 1f30f41af885aeb50c1d173a0303921a09dac421 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 19 Feb 2024 13:32:01 +0100 Subject: [PATCH] Geometry Nodes: align input and output sockets in simulation and repeat zone nodes This changes the drawing of the zone nodes to align corresponding input and output sockets. The resulting nodes are smaller and it's easier to see how data is passed through them. Drawing aligned sockets is already technically supported for quite a while already, but we haven't used it so far. Using them for zone nodes seems to provide benefits only. How we use aligned sockets in other nodes still has to be discussed more. This patch only changes run-time data. It doesn't affect what is written to .blend files. In the node declaration, aligned sockets are created by tagging a socket so that it is aligned with the previous socket. This is a bit different from what we had before where a single socket declaration would be used for an input and output socket. I think the approach used here works better, especially in a potential future scenario where the input and output socket has a different type. Pull Request: https://projects.blender.org/blender/blender/pulls/118335 --- .../blender/editors/space_node/node_draw.cc | 41 ++++++++++++++----- source/blender/nodes/NOD_node_declaration.hh | 13 ++++++ .../nodes/geometry/nodes/node_geo_repeat.cc | 12 ++++-- .../geometry/nodes/node_geo_simulation.cc | 12 ++++-- .../blender/nodes/intern/node_declaration.cc | 21 +++++++++- 5 files changed, 78 insertions(+), 21 deletions(-) diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 896c4e9fb3e..63dae4ff9ce 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -614,17 +614,36 @@ static Vector node_build_item_data(bNode &node) if (const nodes::SocketDeclaration *socket_decl = dynamic_cast(item_decl->get())) { - switch (socket_decl->in_out) { - case SOCK_IN: - BLI_assert(input != input_end); - result.append({socket_decl, *input, nullptr}); - ++input; - break; - case SOCK_OUT: - BLI_assert(output != output_end); - result.append({socket_decl, nullptr, *output}); - ++output; - break; + if (socket_decl->align_with_previous_socket) { + NodeInterfaceItemData &last_item = result.last(); + switch (socket_decl->in_out) { + case SOCK_IN: + BLI_assert(input != input_end); + BLI_assert(last_item.input == nullptr); + last_item.input = *input; + ++input; + break; + case SOCK_OUT: + BLI_assert(output != output_end); + BLI_assert(last_item.output == nullptr); + last_item.output = *output; + ++output; + break; + } + } + else { + switch (socket_decl->in_out) { + case SOCK_IN: + BLI_assert(input != input_end); + result.append({socket_decl, *input, nullptr}); + ++input; + break; + case SOCK_OUT: + BLI_assert(output != output_end); + result.append({socket_decl, nullptr, *output}); + ++output; + break; + } } ++item_decl; } diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index 6e4fce48bd6..643f156d6a4 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -179,6 +179,8 @@ class SocketDeclaration : public ItemDeclaration { bool is_unavailable = false; bool is_attribute_name = false; bool is_default_link_socket = false; + /** Puts this socket on the same line as the previous one in the UI. */ + bool align_with_previous_socket = false; InputSocketFieldType input_field_type = InputSocketFieldType::None; OutputFieldDependency output_field_dependency; @@ -356,6 +358,12 @@ class BaseSocketDeclarationBuilder { */ BaseSocketDeclarationBuilder &make_available(std::function fn); + /** + * Puts this socket on the same row as the previous socket. This only works when one of them is + * an input and the other is an output. + */ + BaseSocketDeclarationBuilder &align_with_previous(bool value = true); + int input_index() const { BLI_assert(decl_in_base_ != nullptr); @@ -459,6 +467,10 @@ class NodeDeclaration { * outputs | buttons | inputs order. Panels are only supported when using custom socket order. */ bool use_custom_socket_order = false; + /** Usually output sockets come before input sockets currently. Only some specific nodes are + * exempt from that rule for now. */ + bool allow_any_socket_order = false; + /** * True if any context was used to build this declaration. */ @@ -521,6 +533,7 @@ class NodeDeclarationBuilder { void finalize(); void use_custom_socket_order(bool enable = true); + void allow_any_socket_order(bool enable = true); template typename DeclType::Builder &add_input(StringRef name, StringRef identifier = ""); diff --git a/source/blender/nodes/geometry/nodes/node_geo_repeat.cc b/source/blender/nodes/geometry/nodes/node_geo_repeat.cc index b860e1058f0..ea64436aa18 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_repeat.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_repeat.cc @@ -20,6 +20,8 @@ NODE_STORAGE_FUNCS(NodeGeometryRepeatInput); static void node_declare(NodeDeclarationBuilder &b) { + b.use_custom_socket_order(); + b.allow_any_socket_order(); b.add_input("Iterations").min(0).default_value(1); const bNode *node = b.node_or_null(); @@ -36,7 +38,7 @@ static void node_declare(NodeDeclarationBuilder &b) const StringRef name = item.name ? item.name : ""; const std::string identifier = RepeatItemsAccessor::socket_identifier_for_item(item); auto &input_decl = b.add_input(socket_type, name, identifier); - auto &output_decl = b.add_output(socket_type, name, identifier); + auto &output_decl = b.add_output(socket_type, name, identifier).align_with_previous(); if (socket_type_supports_fields(socket_type)) { input_decl.supports_field(); output_decl.dependent_field({input_decl.input_index()}); @@ -45,7 +47,7 @@ static void node_declare(NodeDeclarationBuilder &b) } } b.add_input("", "__extend__"); - b.add_output("", "__extend__"); + b.add_output("", "__extend__").align_with_previous(); } static void node_init(bNodeTree * /*tree*/, bNode *node) @@ -98,6 +100,8 @@ NODE_STORAGE_FUNCS(NodeGeometryRepeatOutput); static void node_declare(NodeDeclarationBuilder &b) { + b.use_custom_socket_order(); + b.allow_any_socket_order(); const bNode *node = b.node_or_null(); if (node) { const NodeGeometryRepeatOutput &storage = node_storage(*node); @@ -107,7 +111,7 @@ static void node_declare(NodeDeclarationBuilder &b) const StringRef name = item.name ? item.name : ""; const std::string identifier = RepeatItemsAccessor::socket_identifier_for_item(item); auto &input_decl = b.add_input(socket_type, name, identifier); - auto &output_decl = b.add_output(socket_type, name, identifier); + auto &output_decl = b.add_output(socket_type, name, identifier).align_with_previous(); if (socket_type_supports_fields(socket_type)) { input_decl.supports_field(); output_decl.dependent_field({input_decl.input_index()}); @@ -115,7 +119,7 @@ static void node_declare(NodeDeclarationBuilder &b) } } b.add_input("", "__extend__"); - b.add_output("", "__extend__"); + b.add_output("", "__extend__").align_with_previous(); } static void node_init(bNodeTree * /*tree*/, bNode *node) diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation.cc index c63e22688f3..c66d4400ac5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation.cc @@ -341,6 +341,8 @@ class LazyFunctionForSimulationInputNode final : public LazyFunction { static void node_declare(NodeDeclarationBuilder &b) { + b.use_custom_socket_order(); + b.allow_any_socket_order(); b.add_output("Delta Time"); const bNode *node = b.node_or_null(); @@ -362,14 +364,14 @@ static void node_declare(NodeDeclarationBuilder &b) const StringRef name = item.name; const std::string identifier = SimulationItemsAccessor::socket_identifier_for_item(item); auto &input_decl = b.add_input(socket_type, name, identifier); - auto &output_decl = b.add_output(socket_type, name, identifier); + auto &output_decl = b.add_output(socket_type, name, identifier).align_with_previous(); if (socket_type_supports_fields(socket_type)) { input_decl.supports_field(); output_decl.dependent_field({input_decl.input_index()}); } } b.add_input("", "__extend__"); - b.add_output("", "__extend__"); + b.add_output("", "__extend__").align_with_previous(); } static void node_init(bNodeTree * /*tree*/, bNode *node) @@ -681,6 +683,8 @@ class LazyFunctionForSimulationOutputNode final : public LazyFunction { static void node_declare(NodeDeclarationBuilder &b) { + b.use_custom_socket_order(); + b.allow_any_socket_order(); b.add_input("Skip").description( "Forward the output of the simulation input node directly to the output node and ignore " "the nodes in the simulation zone"); @@ -698,14 +702,14 @@ static void node_declare(NodeDeclarationBuilder &b) const StringRef name = item.name; const std::string identifier = SimulationItemsAccessor::socket_identifier_for_item(item); auto &input_decl = b.add_input(socket_type, name, identifier); - auto &output_decl = b.add_output(socket_type, name, identifier); + auto &output_decl = b.add_output(socket_type, name, identifier).align_with_previous(); if (socket_type_supports_fields(socket_type)) { input_decl.supports_field(); output_decl.dependent_field({input_decl.input_index()}); } } b.add_input("", "__extend__"); - b.add_output("", "__extend__"); + b.add_output("", "__extend__").align_with_previous(); } static void node_init(bNodeTree * /*tree*/, bNode *node) diff --git a/source/blender/nodes/intern/node_declaration.cc b/source/blender/nodes/intern/node_declaration.cc index 6c9308391f0..b20d67746d2 100644 --- a/source/blender/nodes/intern/node_declaration.cc +++ b/source/blender/nodes/intern/node_declaration.cc @@ -123,6 +123,12 @@ void NodeDeclarationBuilder::use_custom_socket_order(bool enable) declaration_.use_custom_socket_order = enable; } +void NodeDeclarationBuilder::allow_any_socket_order(bool enable) +{ + BLI_assert(declaration_.use_custom_socket_order); + declaration_.allow_any_socket_order = enable; +} + Span NodeDeclaration::sockets(eNodeSocketInOut in_out) const { if (in_out == SOCK_IN) { @@ -203,7 +209,7 @@ bool NodeDeclaration::is_valid() const if (const SocketDeclaration *socket_decl = dynamic_cast( item_decl.get())) { - if (state.item_type != NODE_INTERFACE_SOCKET) { + if (state.item_type != NODE_INTERFACE_SOCKET && !this->allow_any_socket_order) { std::cout << "Socket added after panel" << std::endl; return false; } @@ -213,7 +219,7 @@ bool NodeDeclaration::is_valid() const /* Start of input sockets. */ state.socket_in_out = SOCK_IN; } - if (socket_decl->in_out != state.socket_in_out) { + if (socket_decl->in_out != state.socket_in_out && !this->allow_any_socket_order) { std::cout << "Output socket added after input socket" << std::endl; return false; } @@ -812,6 +818,17 @@ BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::make_available( return *this; } +BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::align_with_previous(const bool value) +{ + if (decl_in_base_) { + decl_in_base_->align_with_previous_socket = value; + } + if (decl_out_base_) { + decl_out_base_->align_with_previous_socket = value; + } + return *this; +} + OutputFieldDependency OutputFieldDependency::ForFieldSource() { OutputFieldDependency field_dependency;