diff options
Diffstat (limited to 'source/blender/nodes')
41 files changed, 1172 insertions, 86 deletions
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index e06a3c98016..5f61d13a3af 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -193,6 +193,7 @@ set(SRC intern/node_multi_function.cc intern/node_socket.cc intern/node_socket_declarations.cc + intern/socket_search_link.cc intern/node_tree_ref.cc intern/node_util.c @@ -214,6 +215,7 @@ set(SRC NOD_shader.h NOD_socket.h NOD_socket_declarations.hh + NOD_socket_search_link.hh NOD_socket_declarations_geometry.hh NOD_static_types.h NOD_texture.h diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index a0bb47daef2..a0b8e237f19 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -16,12 +16,12 @@ #pragma once +#include "BKE_node.h" + #ifdef __cplusplus extern "C" { #endif -#include "BKE_node.h" - extern struct bNodeTreeType *ntreeType_Geometry; void register_node_tree_type_geo(void); diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index cccaf93a7d7..af2130ec452 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -16,6 +16,7 @@ #pragma once +#include <functional> #include <type_traits> #include "BLI_string_ref.hh" @@ -23,6 +24,8 @@ #include "DNA_node_types.h" +struct bNode; + namespace blender::nodes { class NodeDeclarationBuilder; @@ -84,6 +87,9 @@ class SocketDeclaration { std::string name_; std::string identifier_; std::string description_; + /** Defined by whether the socket is part of the node's input or + * output socket declaration list. Included here for convenience. */ + eNodeSocketInOut in_out_; bool hide_label_ = false; bool hide_value_ = false; bool compact_ = false; @@ -95,19 +101,36 @@ class SocketDeclaration { InputSocketFieldType input_field_type_ = InputSocketFieldType::None; OutputFieldDependency output_field_dependency_; + /** Utility method to make the socket available if there is a straightforward way to do so. */ + std::function<void(bNode &)> make_available_fn_; + friend NodeDeclarationBuilder; template<typename SocketDecl> friend class SocketDeclarationBuilder; public: virtual ~SocketDeclaration() = default; - virtual bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const = 0; + virtual bNodeSocket &build(bNodeTree &ntree, bNode &node) const = 0; virtual bool matches(const bNodeSocket &socket) const = 0; virtual bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const; + /** + * Determine if a new socket described by this declaration could have a valid connection + * the other socket. + */ + virtual bool can_connect(const bNodeSocket &socket) const = 0; + + /** + * Change the node such that the socket will become visible. The node type's update method + * should be called afterwards. + * \note Note that this is not necessarily implemented for all node types. + */ + void make_available(bNode &node) const; + StringRefNull name() const; StringRefNull description() const; StringRefNull identifier() const; + eNodeSocketInOut in_out() const; bool is_attribute_name() const; bool is_default_link_socket() const; @@ -216,6 +239,18 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { std::move(input_dependencies)); return *(Self *)this; } + + /** + * Pass a function that sets properties on the node required to make the corresponding socket + * available, if it is not available on the default state of the node. The function is allowed to + * make other sockets unavailable, since it is meant to be called when the node is first added. + * The node type's update function is called afterwards. + */ + Self &make_available(std::function<void(bNode &)> fn) + { + decl_->make_available_fn_ = std::move(fn); + return *(Self *)this; + } }; using SocketDeclarationPtr = std::unique_ptr<SocketDeclaration>; @@ -233,6 +268,7 @@ class NodeDeclaration { Span<SocketDeclarationPtr> inputs() const; Span<SocketDeclarationPtr> outputs() const; + Span<SocketDeclarationPtr> sockets(eNodeSocketInOut in_out) const; bool is_function_node() const { @@ -268,7 +304,7 @@ class NodeDeclarationBuilder { template<typename DeclType> typename DeclType::Builder &add_socket(StringRef name, StringRef identifier, - Vector<SocketDeclarationPtr> &r_decls); + eNodeSocketInOut in_out); }; /* -------------------------------------------------------------------- */ @@ -361,6 +397,11 @@ inline StringRefNull SocketDeclaration::identifier() const return identifier_; } +inline eNodeSocketInOut SocketDeclaration::in_out() const +{ + return in_out_; +} + inline StringRefNull SocketDeclaration::description() const { return description_; @@ -386,6 +427,13 @@ inline const OutputFieldDependency &SocketDeclaration::output_field_dependency() return output_field_dependency_; } +inline void SocketDeclaration::make_available(bNode &node) const +{ + if (make_available_fn_) { + make_available_fn_(node); + } +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -401,28 +449,34 @@ template<typename DeclType> inline typename DeclType::Builder &NodeDeclarationBuilder::add_input(StringRef name, StringRef identifier) { - return this->add_socket<DeclType>(name, identifier, declaration_.inputs_); + return this->add_socket<DeclType>(name, identifier, SOCK_IN); } template<typename DeclType> inline typename DeclType::Builder &NodeDeclarationBuilder::add_output(StringRef name, StringRef identifier) { - return this->add_socket<DeclType>(name, identifier, declaration_.outputs_); + return this->add_socket<DeclType>(name, identifier, SOCK_OUT); } template<typename DeclType> -inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket( - StringRef name, StringRef identifier, Vector<SocketDeclarationPtr> &r_decls) +inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket(StringRef name, + StringRef identifier, + eNodeSocketInOut in_out) { static_assert(std::is_base_of_v<SocketDeclaration, DeclType>); using Builder = typename DeclType::Builder; + + Vector<SocketDeclarationPtr> &declarations = in_out == SOCK_IN ? declaration_.inputs_ : + declaration_.outputs_; + std::unique_ptr<DeclType> socket_decl = std::make_unique<DeclType>(); std::unique_ptr<Builder> socket_decl_builder = std::make_unique<Builder>(); socket_decl_builder->decl_ = &*socket_decl; socket_decl->name_ = name; socket_decl->identifier_ = identifier.is_empty() ? name : identifier; - r_decls.append(std::move(socket_decl)); + socket_decl->in_out_ = in_out; + declarations.append(std::move(socket_decl)); Builder &socket_decl_builder_ref = *socket_decl_builder; builders_.append(std::move(socket_decl_builder)); return socket_decl_builder_ref; @@ -444,6 +498,14 @@ inline Span<SocketDeclarationPtr> NodeDeclaration::outputs() const return outputs_; } +inline Span<SocketDeclarationPtr> NodeDeclaration::sockets(eNodeSocketInOut in_out) const +{ + if (in_out == SOCK_IN) { + return inputs_; + } + return outputs_; +} + /** \} */ } // namespace blender::nodes diff --git a/source/blender/nodes/NOD_socket_declarations.hh b/source/blender/nodes/NOD_socket_declarations.hh index 1b78a09d6a8..3fb21a4263d 100644 --- a/source/blender/nodes/NOD_socket_declarations.hh +++ b/source/blender/nodes/NOD_socket_declarations.hh @@ -39,9 +39,10 @@ class Float : public SocketDeclaration { public: using Builder = FloatBuilder; - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; + bool can_connect(const bNodeSocket &socket) const override; }; class FloatBuilder : public SocketDeclarationBuilder<Float> { @@ -66,9 +67,10 @@ class Int : public SocketDeclaration { public: using Builder = IntBuilder; - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; + bool can_connect(const bNodeSocket &socket) const override; }; class IntBuilder : public SocketDeclarationBuilder<Int> { @@ -93,9 +95,10 @@ class Vector : public SocketDeclaration { public: using Builder = VectorBuilder; - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; + bool can_connect(const bNodeSocket &socket) const override; }; class VectorBuilder : public SocketDeclarationBuilder<Vector> { @@ -117,8 +120,9 @@ class Bool : public SocketDeclaration { public: using Builder = BoolBuilder; - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; + bool can_connect(const bNodeSocket &socket) const override; }; class BoolBuilder : public SocketDeclarationBuilder<Bool> { @@ -137,8 +141,9 @@ class Color : public SocketDeclaration { public: using Builder = ColorBuilder; - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; + bool can_connect(const bNodeSocket &socket) const override; }; class ColorBuilder : public SocketDeclarationBuilder<Color> { @@ -157,8 +162,9 @@ class String : public SocketDeclaration { public: using Builder = StringBuilder; - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; + bool can_connect(const bNodeSocket &socket) const override; }; class StringBuilder : public SocketDeclarationBuilder<String> { @@ -173,9 +179,10 @@ class IDSocketDeclaration : public SocketDeclaration { public: IDSocketDeclaration(const char *idname); - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; + bool can_connect(const bNodeSocket &socket) const override; }; class Object : public IDSocketDeclaration { @@ -222,8 +229,9 @@ class Shader : public SocketDeclaration { public: using Builder = ShaderBuilder; - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; + bool can_connect(const bNodeSocket &socket) const; }; class ShaderBuilder : public SocketDeclarationBuilder<Shader> { diff --git a/source/blender/nodes/NOD_socket_declarations_geometry.hh b/source/blender/nodes/NOD_socket_declarations_geometry.hh index 3c919729da9..0ce07da22ff 100644 --- a/source/blender/nodes/NOD_socket_declarations_geometry.hh +++ b/source/blender/nodes/NOD_socket_declarations_geometry.hh @@ -35,8 +35,9 @@ class Geometry : public SocketDeclaration { public: using Builder = GeometryBuilder; - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; + bool can_connect(const bNodeSocket &socket) const override; Span<GeometryComponentType> supported_types() const; bool only_realized_data() const; diff --git a/source/blender/nodes/NOD_socket_search_link.hh b/source/blender/nodes/NOD_socket_search_link.hh new file mode 100644 index 00000000000..b7594561dc4 --- /dev/null +++ b/source/blender/nodes/NOD_socket_search_link.hh @@ -0,0 +1,151 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include <functional> + +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "DNA_node_types.h" /* Necessary for eNodeSocketInOut. */ + +#include "NOD_node_declaration.hh" + +struct bContext; + +namespace blender::nodes { + +/** + * Parameters for the operation operation of adding a node after the link drag search menu closes. + */ +class LinkSearchOpParams { + private: + /** + * Keep track of the nodes added by the callback, so they can be selected or moved afterwards. + */ + Vector<bNode *> &added_nodes_; + + public: + const bContext &C; + bNodeTree &node_tree; + /** + * The node that contains the #socket. + */ + bNode &node; + /** + * The existing socket to connect any added nodes to. Might be an input or output socket. + */ + bNodeSocket &socket; + + LinkSearchOpParams(const bContext &C, + bNodeTree &node_tree, + bNode &node, + bNodeSocket &socket, + Vector<bNode *> &added_nodes) + : added_nodes_(added_nodes), C(C), node_tree(node_tree), node(node), socket(socket) + { + } + + bNode &add_node(StringRef idname); + bNode &add_node(const bNodeType &type); + /** + * Find a socket with the given name (correctly checks for inputs and outputs) + * and connect it to the socket the link drag started from (#socket). + */ + void connect_available_socket(bNode &new_node, StringRef socket_name); + /** + * Like #connect_available_socket, but also calls the node's update function. + */ + void update_and_connect_available_socket(bNode &new_node, StringRef socket_name); +}; + +struct SocketLinkOperation { + using LinkSocketFn = std::function<void(LinkSearchOpParams &link_params)>; + + std::string name; + LinkSocketFn fn; + int weight = 0; +}; + +class GatherLinkSearchOpParams { + /** The current node type. */ + const bNodeType &node_type_; + + const bNodeTree &node_tree_; + + const bNodeSocket &other_socket_; + + /* The operations currently being built. Owned by the caller. */ + Vector<SocketLinkOperation> &items_; + + public: + GatherLinkSearchOpParams(const bNodeType &node_type, + const bNodeTree &node_tree, + const bNodeSocket &other_socket, + Vector<SocketLinkOperation> &items) + : node_type_(node_type), node_tree_(node_tree), other_socket_(other_socket), items_(items) + { + } + + /** + * The node on the other side of the dragged link. + */ + const bNodeSocket &other_socket() const; + + /** + * The node tree the user is editing when the search menu is created. + */ + const bNodeTree &node_tree() const; + + /** + * The type of the node in the current callback. + */ + const bNodeType &node_type() const; + + /** + * Whether to list the input or output sockets of the node. + */ + eNodeSocketInOut in_out() const; + + /** + * \param weight: Used to customize the order when multiple search items match. + * + * \warning When creating lambdas for the #fn argument, be careful not to capture this class + * itself, since it is temporary. That is why we tend to use the same variable name for this + * class (`params`) that we do for the argument to `LinkSocketFn`. + */ + void add_item(std::string socket_name, SocketLinkOperation::LinkSocketFn fn, int weight = 0); +}; + +/** + * This callback can be used for a node type when a few things are true about its inputs. + * To avoid creating more boilerplate, it is the default callback for node types. + * - Either all declared sockets are visible in the default state of the node, *OR* the node's + * type's declaration has been extended with #make_available functions for those sockets. + * + * If a node type does not meet these criteria, the function will do nothing in a release build. + * In a debug build, an assert will most likely be hit. + * + * \note For nodes with the deprecated #bNodeSocketTemplate instead of a declaration, + * these criteria do not apply and the function just tries its best without asserting. + */ +void search_link_ops_for_basic_node(GatherLinkSearchOpParams ¶ms); + +void search_link_ops_for_declarations(GatherLinkSearchOpParams ¶ms, + Span<SocketDeclarationPtr> declarations); + +} // namespace blender::nodes diff --git a/source/blender/nodes/composite/node_composite_util.cc b/source/blender/nodes/composite/node_composite_util.cc index 9ea98d2a867..1262dfad11f 100644 --- a/source/blender/nodes/composite/node_composite_util.cc +++ b/source/blender/nodes/composite/node_composite_util.cc @@ -21,6 +21,8 @@ * \ingroup nodes */ +#include "NOD_socket_search_link.hh" + #include "node_composite_util.hh" bool cmp_node_poll_default(bNodeType *UNUSED(ntype), @@ -52,4 +54,5 @@ void cmp_node_type_base(bNodeType *ntype, int type, const char *name, short ncla ntype->poll = cmp_node_poll_default; ntype->updatefunc = cmp_node_update_default; ntype->insert_link = node_insert_link_default; + ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node; } diff --git a/source/blender/nodes/function/node_function_util.cc b/source/blender/nodes/function/node_function_util.cc index ac48322c269..83f5b571695 100644 --- a/source/blender/nodes/function/node_function_util.cc +++ b/source/blender/nodes/function/node_function_util.cc @@ -17,6 +17,8 @@ #include "node_function_util.hh" #include "node_util.h" +#include "NOD_socket_search_link.hh" + static bool fn_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree, const char **r_disabled_hint) @@ -34,4 +36,5 @@ void fn_node_type_base(bNodeType *ntype, int type, const char *name, short nclas node_type_base(ntype, type, name, nclass, flag); ntype->poll = fn_node_poll_default; ntype->insert_link = node_insert_link_default; + ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node; } diff --git a/source/blender/nodes/function/nodes/node_fn_random_value.cc b/source/blender/nodes/function/nodes/node_fn_random_value.cc index e2efae68001..b053482c99d 100644 --- a/source/blender/nodes/function/nodes/node_fn_random_value.cc +++ b/source/blender/nodes/function/nodes/node_fn_random_value.cc @@ -19,6 +19,8 @@ #include "node_function_util.hh" +#include "NOD_socket_search_link.hh" + #include "UI_interface.h" #include "UI_resources.h" @@ -43,7 +45,8 @@ static void fn_node_random_value_declare(NodeDeclarationBuilder &b) .max(1.0f) .default_value(0.5f) .subtype(PROP_FACTOR) - .supports_field(); + .supports_field() + .make_available([](bNode &node) { node_storage(node).data_type = CD_PROP_BOOL; }); b.add_input<decl::Int>(N_("ID")).implicit_field(); b.add_input<decl::Int>(N_("Seed")).default_value(0).min(-10000).max(10000).supports_field(); @@ -97,6 +100,55 @@ static void fn_node_random_value_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, sock_out_bool, data_type == CD_PROP_BOOL); } +static std::optional<CustomDataType> node_type_from_other_socket(const bNodeSocket &socket) +{ + switch (socket.type) { + case SOCK_FLOAT: + return CD_PROP_FLOAT; + case SOCK_BOOLEAN: + return CD_PROP_BOOL; + case SOCK_INT: + return CD_PROP_INT32; + case SOCK_VECTOR: + return CD_PROP_FLOAT3; + case SOCK_RGBA: + return CD_PROP_COLOR; + default: + return {}; + } +} + +static void fn_node_random_value_gather_link_search(GatherLinkSearchOpParams ¶ms) +{ + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + const std::optional<CustomDataType> type = node_type_from_other_socket(params.other_socket()); + if (!type) { + return; + } + if (params.in_out() == SOCK_IN) { + if (ELEM(*type, CD_PROP_INT32, CD_PROP_FLOAT3, CD_PROP_FLOAT)) { + params.add_item(IFACE_("Min"), [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("FunctionNodeRandomValue"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Min"); + }); + params.add_item(IFACE_("Max"), [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("FunctionNodeRandomValue"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Max"); + }); + } + search_link_ops_for_declarations(params, declaration.inputs().take_back(3)); + } + else { + params.add_item(IFACE_("Value"), [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("FunctionNodeRandomValue"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Value"); + }); + } +} + class RandomVectorFunction : public fn::MultiFunction { public: RandomVectorFunction() @@ -297,6 +349,7 @@ void register_node_type_fn_random_value() ntype.draw_buttons = blender::nodes::fn_node_random_value_layout; ntype.declare = blender::nodes::fn_node_random_value_declare; ntype.build_multi_function = blender::nodes::fn_node_random_value_build_multi_function; + ntype.gather_link_search_ops = blender::nodes::fn_node_random_value_gather_link_search; node_type_storage( &ntype, "NodeRandomValue", node_free_standard_storage, node_copy_standard_storage); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index 3f4089807bc..89ab4d9961e 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -84,15 +84,16 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa func(calldata, NODE_CLASS_LAYOUT, N_("Layout")); } -static bool geometry_node_tree_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link) +static bool geometry_node_tree_validate_link(eNodeSocketDatatype type_a, + eNodeSocketDatatype type_b) { /* Geometry, string, object, material, texture and collection sockets can only be connected to * themselves. The other types can be converted between each other. */ - if (ELEM(link->fromsock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT) && - ELEM(link->tosock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT)) { + if (ELEM(type_a, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT) && + ELEM(type_b, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT)) { return true; } - return (link->tosock->type == link->fromsock->type); + return type_a == type_b; } static bool geometry_node_tree_socket_type_valid(bNodeTreeType *UNUSED(ntreetype), diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc index ebe678c6de8..49991a40c1b 100644 --- a/source/blender/nodes/geometry/node_geometry_util.cc +++ b/source/blender/nodes/geometry/node_geometry_util.cc @@ -24,6 +24,8 @@ #include "BKE_mesh_runtime.h" #include "BKE_pointcloud.h" +#include "NOD_socket_search_link.hh" + namespace blender::nodes { using bke::GeometryInstanceGroup; @@ -49,6 +51,31 @@ void update_attribute_input_socket_availabilities(bNodeTree &ntree, } } +std::optional<CustomDataType> node_data_type_to_custom_data_type(const eNodeSocketDatatype type) +{ + switch (type) { + case SOCK_FLOAT: + return CD_PROP_FLOAT; + case SOCK_VECTOR: + return CD_PROP_FLOAT3; + case SOCK_RGBA: + return CD_PROP_COLOR; + case SOCK_BOOLEAN: + return CD_PROP_BOOL; + case SOCK_INT: + return CD_PROP_INT32; + case SOCK_STRING: + return CD_PROP_STRING; + default: + return {}; + } +} + +std::optional<CustomDataType> node_socket_to_custom_data_type(const bNodeSocket &socket) +{ + return node_data_type_to_custom_data_type(static_cast<eNodeSocketDatatype>(socket.type)); +} + } // namespace blender::nodes bool geo_node_poll_default(bNodeType *UNUSED(ntype), @@ -67,4 +94,5 @@ void geo_node_type_base(bNodeType *ntype, int type, const char *name, short ncla node_type_base(ntype, type, name, nclass, flag); ntype->poll = geo_node_poll_default; ntype->insert_link = node_insert_link_default; + ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node; } diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index cf731427841..3376b75d05b 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -133,4 +133,7 @@ void curve_create_default_rotation_attribute(Span<float3> tangents, Span<float3> normals, MutableSpan<float3> rotations); +std::optional<CustomDataType> node_data_type_to_custom_data_type(eNodeSocketDatatype type); +std::optional<CustomDataType> node_socket_to_custom_data_type(const bNodeSocket &socket); + } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index cf20ddacca7..22a9e4a0c33 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -19,6 +19,8 @@ #include "BKE_attribute_math.hh" +#include "NOD_socket_search_link.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_attribute_capture_cc { @@ -92,6 +94,36 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, out_socket_value_int32, data_type == CD_PROP_INT32); } +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const bNodeType &node_type = params.node_type(); + if (params.other_socket().type == SOCK_GEOMETRY) { + params.add_item(IFACE_("Geometry"), [node_type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); + params.connect_available_socket(node, "Geometry"); + }); + } + + const std::optional<CustomDataType> type = node_data_type_to_custom_data_type( + (eNodeSocketDatatype)params.other_socket().type); + if (type) { + if (params.in_out() == SOCK_OUT) { + params.add_item(IFACE_("Attribute"), [node_type, type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Attribute"); + }); + } + else { + params.add_item(IFACE_("Value"), [node_type, type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Value"); + }); + } + } +} + static void try_capture_field_on_geometry(GeometryComponent &component, const AttributeIDRef &attribute_id, const AttributeDomain domain, @@ -216,5 +248,6 @@ void register_node_type_geo_attribute_capture() ntype.declare = file_ns::node_declare; ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.draw_buttons = file_ns::node_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc index 082ea87853e..d6662e4e637 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc @@ -24,12 +24,24 @@ namespace blender::nodes::node_geo_attribute_domain_size_cc { static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Geometry>("Geometry"); - b.add_output<decl::Int>("Point Count"); - b.add_output<decl::Int>("Edge Count"); - b.add_output<decl::Int>("Face Count"); - b.add_output<decl::Int>("Face Corner Count"); - b.add_output<decl::Int>("Spline Count"); - b.add_output<decl::Int>("Instance Count"); + b.add_output<decl::Int>("Point Count").make_available([](bNode &node) { + node.custom1 = GEO_COMPONENT_TYPE_MESH; + }); + b.add_output<decl::Int>("Edge Count").make_available([](bNode &node) { + node.custom1 = GEO_COMPONENT_TYPE_MESH; + }); + b.add_output<decl::Int>("Face Count").make_available([](bNode &node) { + node.custom1 = GEO_COMPONENT_TYPE_MESH; + }); + b.add_output<decl::Int>("Face Corner Count").make_available([](bNode &node) { + node.custom1 = GEO_COMPONENT_TYPE_MESH; + }); + b.add_output<decl::Int>("Spline Count").make_available([](bNode &node) { + node.custom1 = GEO_COMPONENT_TYPE_CURVE; + }); + b.add_output<decl::Int>("Instance Count").make_available([](bNode &node) { + node.custom1 = GEO_COMPONENT_TYPE_INSTANCES; + }); } static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc index c754f70d323..b79125d43d1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc @@ -22,6 +22,8 @@ #include "BLI_math_base_safe.h" +#include "NOD_socket_search_link.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_attribute_statistic_cc { @@ -112,6 +114,54 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, socket_vector_variance, data_type == CD_PROP_FLOAT3); } +static std::optional<CustomDataType> node_type_from_other_socket(const bNodeSocket &socket) +{ + switch (socket.type) { + case SOCK_FLOAT: + case SOCK_BOOLEAN: + case SOCK_INT: + return CD_PROP_FLOAT; + case SOCK_VECTOR: + case SOCK_RGBA: + return CD_PROP_FLOAT3; + default: + return {}; + } +} + +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const bNodeType &node_type = params.node_type(); + const std::optional<CustomDataType> type = node_type_from_other_socket(params.other_socket()); + if (params.in_out() == SOCK_IN) { + if (params.other_socket().type == SOCK_GEOMETRY) { + params.add_item(IFACE_("Geometry"), [node_type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); + params.connect_available_socket(node, "Geometry"); + }); + } + if (type) { + params.add_item(IFACE_("Attribute"), [&](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); + node.custom1 = *type; + params.update_and_connect_available_socket(node, "Attribute"); + }); + } + } + else if (type) { + /* Only use the first 8 declarations since we set the type automatically. */ + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + for (const SocketDeclarationPtr &socket_decl : declaration.outputs().take_front(8)) { + StringRefNull name = socket_decl->name(); + params.add_item(IFACE_(name.c_str()), [node_type, name, type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); + node.custom1 = *type; + params.update_and_connect_available_socket(node, name); + }); + } + } +} + template<typename T> static T compute_sum(const Span<T> data) { return std::accumulate(data.begin(), data.end(), T()); @@ -359,5 +409,6 @@ void register_node_type_geo_attribute_statistic() node_type_update(&ntype, file_ns::node_update); ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.draw_buttons = file_ns::node_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index 7fbcc47c708..a438c1d6086 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -32,7 +32,12 @@ NODE_STORAGE_FUNCS(NodeGeometryCurveFillet) static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input<decl::Int>(N_("Count")).default_value(1).min(1).max(1000).supports_field(); + b.add_input<decl::Int>(N_("Count")) + .default_value(1) + .min(1) + .max(1000) + .supports_field() + .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_FILLET_POLY; }); b.add_input<decl::Float>(N_("Radius")) .min(0.0f) .max(FLT_MAX) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc index b08fcd86059..70abf4c64a7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc @@ -56,7 +56,9 @@ static void node_declare(NodeDeclarationBuilder &b) .subtype(PROP_DISTANCE) .description(N_("Distance of the points from the origin")); b.add_output<decl::Geometry>(N_("Curve")); - b.add_output<decl::Vector>(N_("Center")); + b.add_output<decl::Vector>(N_("Center")).make_available([](bNode &node) { + node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS; + }); } static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc index 41f92c7d0c8..07bd7cec766 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc @@ -17,6 +17,9 @@ #include "BKE_spline.hh" #include "UI_interface.h" #include "UI_resources.h" + +#include "NOD_socket_search_link.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_curve_primitive_quadrilaterial_cc { @@ -142,6 +145,44 @@ static void node_update(bNodeTree *ntree, bNode *node) } } +class SocketSearchOp { + public: + std::string socket_name; + GeometryNodeCurvePrimitiveQuadMode quad_mode; + void operator()(LinkSearchOpParams ¶ms) + { + bNode &node = params.add_node("GeometryNodeCurvePrimitiveQuadrilateral"); + node_storage(node).mode = quad_mode; + params.update_and_connect_available_socket(node, socket_name); + } +}; + +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + if (params.in_out() == SOCK_OUT) { + if (params.other_socket().type == SOCK_GEOMETRY) { + params.add_item(IFACE_("Curve"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeCurvePrimitiveQuadrilateral"); + params.connect_available_socket(node, "Curve"); + }); + } + } + else { + params.add_item(IFACE_("Width"), + SocketSearchOp{"Width", GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE}); + params.add_item(IFACE_("Height"), + SocketSearchOp{"Height", GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE}); + params.add_item(IFACE_("Bottom Width"), + SocketSearchOp{"Bottom Width", GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_TRAPEZOID}); + params.add_item(IFACE_("Top Width"), + SocketSearchOp{"Top Width", GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_TRAPEZOID}); + params.add_item(IFACE_("Offset"), + SocketSearchOp{"Offset", GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_PARALLELOGRAM}); + params.add_item(IFACE_("Point 1"), + SocketSearchOp{"Point 1", GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_POINTS}); + } +} + static void create_rectangle_curve(MutableSpan<float3> positions, const float height, const float width) @@ -271,5 +312,6 @@ void register_node_type_geo_curve_primitive_quadrilateral() "NodeGeometryCurvePrimitiveQuad", node_free_standard_storage, node_copy_standard_storage); + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc index 57e08a91211..eff760266f5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -32,9 +32,17 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_input<decl::Geometry>(N_("Curve")) .only_realized_data() .supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input<decl::Float>(N_("Factor")).min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field(); - b.add_input<decl::Float>(N_("Length")).min(0.0f).subtype(PROP_DISTANCE).supports_field(); - + b.add_input<decl::Float>(N_("Factor")) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .supports_field() + .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_FACTOR; }); + b.add_input<decl::Float>(N_("Length")) + .min(0.0f) + .subtype(PROP_DISTANCE) + .supports_field() + .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_LENGTH; }); b.add_output<decl::Vector>(N_("Position")).dependent_field(); b.add_output<decl::Vector>(N_("Tangent")).dependent_field(); b.add_output<decl::Vector>(N_("Normal")).dependent_field(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 2f9dfa8158b..0e9425246cb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -47,8 +47,18 @@ NODE_STORAGE_FUNCS(NodeGeometryCurveToPoints) static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input<decl::Int>(N_("Count")).default_value(10).min(2).max(100000); - b.add_input<decl::Float>(N_("Length")).default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); + b.add_input<decl::Int>(N_("Count")) + .default_value(10) + .min(2) + .max(100000) + .make_available( + [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_RESAMPLE_COUNT; }); + b.add_input<decl::Float>(N_("Length")) + .default_value(0.1f) + .min(0.001f) + .subtype(PROP_DISTANCE) + .make_available( + [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_RESAMPLE_LENGTH; }); b.add_output<decl::Geometry>(N_("Points")); b.add_output<decl::Vector>(N_("Tangent")).field_source(); b.add_output<decl::Vector>(N_("Normal")).field_source(); @@ -401,6 +411,5 @@ void register_node_type_geo_curve_to_points() &ntype, "NodeGeometryCurveToPoints", node_free_standard_storage, node_copy_standard_storage); node_type_init(&ntype, file_ns::node_init); node_type_update(&ntype, file_ns::node_update); - nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 449d0d14092..c6908cb8ed0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -20,6 +20,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "NOD_socket_search_link.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_curve_trim_cc { @@ -31,21 +33,29 @@ NODE_STORAGE_FUNCS(NodeGeometryCurveTrim) static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); - b.add_input<decl::Float>(N_("Start")).min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field(); + b.add_input<decl::Float>(N_("Start")) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_FACTOR; }) + .supports_field(); b.add_input<decl::Float>(N_("End")) .min(0.0f) .max(1.0f) .default_value(1.0f) .subtype(PROP_FACTOR) + .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_FACTOR; }) .supports_field(); b.add_input<decl::Float>(N_("Start"), "Start_001") .min(0.0f) .subtype(PROP_DISTANCE) + .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_LENGTH; }) .supports_field(); b.add_input<decl::Float>(N_("End"), "End_001") .min(0.0f) .default_value(1.0f) .subtype(PROP_DISTANCE) + .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_LENGTH; }) .supports_field(); b.add_output<decl::Geometry>(N_("Curve")); } @@ -80,6 +90,36 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, end_len, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); } +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + class SocketSearchOp { + public: + StringRef socket_name; + GeometryNodeCurveSampleMode mode; + void operator()(LinkSearchOpParams ¶ms) + { + bNode &node = params.add_node("GeometryNodeTrimCurve"); + node_storage(node).mode = mode; + params.update_and_connect_available_socket(node, socket_name); + } + }; + + if (params.in_out() == SOCK_OUT) { + params.add_item(IFACE_("Curve"), SocketSearchOp{"Curve", GEO_NODE_CURVE_SAMPLE_FACTOR}); + } + else { + params.add_item(IFACE_("Curve"), SocketSearchOp{"Curve", GEO_NODE_CURVE_SAMPLE_FACTOR}); + if (params.other_socket().type == SOCK_FLOAT) { + params.add_item(IFACE_("Start (Factor)"), + SocketSearchOp{"Start", GEO_NODE_CURVE_SAMPLE_FACTOR}); + params.add_item(IFACE_("End (Factor)"), SocketSearchOp{"End", GEO_NODE_CURVE_SAMPLE_FACTOR}); + params.add_item(IFACE_("Start (Length)"), + SocketSearchOp{"Start", GEO_NODE_CURVE_SAMPLE_LENGTH}); + params.add_item(IFACE_("End (Length)"), SocketSearchOp{"End", GEO_NODE_CURVE_SAMPLE_LENGTH}); + } + } +} + struct TrimLocation { /* Control point index at the start side of the trim location. */ int left_index; @@ -574,5 +614,6 @@ void register_node_type_geo_curve_trim() &ntype, "NodeGeometryCurveTrim", node_free_standard_storage, node_copy_standard_storage); node_type_init(&ntype, file_ns::node_init); node_type_update(&ntype, file_ns::node_update); + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc index 05830098cc2..389dc278197 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -23,6 +23,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "NOD_socket_search_link.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_mesh_primitive_line_cc { @@ -100,6 +102,40 @@ static void node_update(bNodeTree *ntree, bNode *node) count_mode == GEO_NODE_MESH_LINE_COUNT_TOTAL); } +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + if (params.in_out() == SOCK_OUT) { + search_link_ops_for_declarations(params, declaration.outputs()); + return; + } + params.add_item(IFACE_("Count"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeMeshLine"); + node_storage(node).mode = GEO_NODE_MESH_LINE_MODE_OFFSET; + params.connect_available_socket(node, "Count"); + }); + params.add_item(IFACE_("Resolution"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeMeshLine"); + node_storage(node).mode = GEO_NODE_MESH_LINE_MODE_OFFSET; + node_storage(node).count_mode = GEO_NODE_MESH_LINE_COUNT_RESOLUTION; + params.connect_available_socket(node, "Resolution"); + }); + params.add_item(IFACE_("Start Location"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeMeshLine"); + params.connect_available_socket(node, "Start Location"); + }); + params.add_item(IFACE_("Offset"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeMeshLine"); + params.connect_available_socket(node, "Offset"); + }); + /* The last socket is reused in end points mode. */ + params.add_item(IFACE_("End Location"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeMeshLine"); + node_storage(node).mode = GEO_NODE_MESH_LINE_MODE_END_POINTS; + params.connect_available_socket(node, "Offset"); + }); +} + static void node_geo_exec(GeoNodeExecParams params) { const NodeGeometryMeshLine &storage = node_storage(params.node()); @@ -194,5 +230,6 @@ void register_node_type_geo_mesh_primitive_line() &ntype, "NodeGeometryMeshLine", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.draw_buttons = file_ns::node_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index c356e1cf22b..744cce6d445 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -36,8 +36,19 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Geometry>(N_("Points")); b.add_input<decl::Float>(N_("Density")).default_value(1.0f).min(0.0f); - b.add_input<decl::Float>(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f); + b.add_input<decl::Float>(N_("Voxel Size")) + .default_value(0.3f) + .min(0.01f) + .subtype(PROP_DISTANCE) + .make_available([](bNode &node) { + node_storage(node).resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE; + }); + b.add_input<decl::Float>(N_("Voxel Amount")) + .default_value(64.0f) + .min(0.0f) + .make_available([](bNode &node) { + node_storage(node).resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT; + }); b.add_input<decl::Float>(N_("Radius")) .default_value(0.5f) .min(0.0f) diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index c33654b3f20..8968675c1ed 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -23,6 +23,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "NOD_socket_search_link.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_raycast_cc { @@ -110,6 +112,25 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, out_socket_int32, data_type == CD_PROP_INT32); } +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + search_link_ops_for_declarations(params, declaration.inputs().take_front(1)); + search_link_ops_for_declarations(params, declaration.inputs().take_back(3)); + search_link_ops_for_declarations(params, declaration.outputs().take_front(4)); + + const std::optional<CustomDataType> type = node_data_type_to_custom_data_type( + (eNodeSocketDatatype)params.other_socket().type); + if (type) { + /* The input and output sockets have the same name. */ + params.add_item(IFACE_("Attribute"), [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeRaycast"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Attribute"); + }); + } +} + static eAttributeMapMode get_map_mode(GeometryNodeRaycastMapMode map_mode) { switch (map_mode) { @@ -437,5 +458,6 @@ void register_node_type_geo_raycast() ntype.declare = file_ns::node_declare; ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.draw_buttons = file_ns::node_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index 912552c7ce9..e30b11907e8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -57,9 +57,14 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_input<decl::Float>(N_("Text Box Height")) .default_value(0.0f) .min(0.0f) - .subtype(PROP_DISTANCE); + .subtype(PROP_DISTANCE) + .make_available([](bNode &node) { + node_storage(node).overflow = GEO_NODE_STRING_TO_CURVES_MODE_SCALE_TO_FIT; + }); b.add_output<decl::Geometry>(N_("Curves")); - b.add_output<decl::String>(N_("Remainder")); + b.add_output<decl::String>(N_("Remainder")).make_available([](bNode &node) { + node_storage(node).overflow = GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW; + }); } static void node_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index 146407e0c33..d22522fe087 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -24,6 +24,8 @@ #include "BKE_material.h" +#include "NOD_socket_search_link.hh" + #include "FN_multi_function_signature.hh" namespace blender::nodes::node_geo_switch_cc { @@ -122,6 +124,35 @@ static void node_update(bNodeTree *ntree, bNode *node) } } +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + if (params.in_out() == SOCK_OUT) { + params.add_item(IFACE_("Output"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeSwitch"); + node_storage(node).input_type = params.socket.type; + params.update_and_connect_available_socket(node, "Output"); + }); + } + else { + if (params.other_socket().type == SOCK_BOOLEAN) { + params.add_item(IFACE_("Switch"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeSwitch"); + params.connect_available_socket(node, "Start"); + }); + } + params.add_item(IFACE_("False"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeSwitch"); + node_storage(node).input_type = params.socket.type; + params.update_and_connect_available_socket(node, "False"); + }); + params.add_item(IFACE_("True"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeSwitch"); + node_storage(node).input_type = params.socket.type; + params.update_and_connect_available_socket(node, "True"); + }); + } +} + template<typename T> class SwitchFieldsFunction : public fn::MultiFunction { public: SwitchFieldsFunction() @@ -303,6 +334,7 @@ void register_node_type_geo_switch() node_type_storage(&ntype, "NodeSwitch", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.geometry_node_execute_supports_laziness = true; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc index cfa6b752993..007373929f4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -31,6 +31,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "NOD_socket_search_link.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_transfer_attribute_cc { @@ -54,8 +56,14 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_input<decl::Bool>(N_("Attribute"), "Attribute_003").hide_value().supports_field(); b.add_input<decl::Int>(N_("Attribute"), "Attribute_004").hide_value().supports_field(); - b.add_input<decl::Vector>(N_("Source Position")).implicit_field(); - b.add_input<decl::Int>(N_("Index")).implicit_field(); + b.add_input<decl::Vector>(N_("Source Position")) + .implicit_field() + .make_available([](bNode &node) { + node_storage(node).mode = GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED; + }); + b.add_input<decl::Int>(N_("Index")).implicit_field().make_available([](bNode &node) { + node_storage(node).mode = GEO_NODE_ATTRIBUTE_TRANSFER_INDEX; + }); b.add_output<decl::Vector>(N_("Attribute")).dependent_field({6, 7}); b.add_output<decl::Float>(N_("Attribute"), "Attribute_001").dependent_field({6, 7}); @@ -126,6 +134,30 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, out_socket_int32, data_type == CD_PROP_INT32); } +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + if (params.other_socket().type == SOCK_GEOMETRY) { + params.add_item(IFACE_("Target"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeAttributeTransfer"); + params.connect_available_socket(node, "Target"); + }); + } + + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + search_link_ops_for_declarations(params, declaration.inputs().take_back(2)); + + const std::optional<CustomDataType> type = node_data_type_to_custom_data_type( + (eNodeSocketDatatype)params.other_socket().type); + if (type) { + /* The input and output sockets have the same name. */ + params.add_item(IFACE_("Attribute"), [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeAttributeTransfer"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Attribute"); + }); + } +} + static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data, const VArray<float3> &positions, const IndexMask mask, @@ -812,5 +844,6 @@ void register_node_type_geo_transfer_attribute() ntype.declare = file_ns::node_declare; ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.draw_buttons = file_ns::node_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc index ad9737ac24b..18f2fa4cd36 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc @@ -14,9 +14,16 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BKE_context.h" + #include "UI_interface.h" #include "UI_resources.h" +#include "ED_node.h" +#include "ED_spreadsheet.h" + +#include "NOD_socket_search_link.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_viewer_cc { @@ -80,6 +87,52 @@ static void node_update(bNodeTree *ntree, bNode *node) } } +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + auto set_active_fn = [](LinkSearchOpParams ¶ms, bNode &viewer_node) { + /* Set this new viewer node active in spreadsheet editors. */ + SpaceNode *snode = CTX_wm_space_node(¶ms.C); + Main *bmain = CTX_data_main(¶ms.C); + ED_node_set_active(bmain, snode, ¶ms.node_tree, &viewer_node, nullptr); + ED_spreadsheet_context_paths_set_geometry_node(bmain, snode, &viewer_node); + }; + + const std::optional<CustomDataType> type = node_socket_to_custom_data_type( + params.other_socket()); + if (params.in_out() == SOCK_OUT) { + /* The viewer node only has inputs. */ + return; + } + if (params.other_socket().type == SOCK_GEOMETRY) { + params.add_item(IFACE_("Geometry"), [set_active_fn](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeViewer"); + params.connect_available_socket(node, "Geometry"); + set_active_fn(params, node); + }); + } + if (type && + ELEM(type, CD_PROP_FLOAT, CD_PROP_BOOL, CD_PROP_INT32, CD_PROP_FLOAT3, CD_PROP_COLOR)) { + params.add_item(IFACE_("Value"), [type, set_active_fn](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeViewer"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Value"); + + /* If the source node has a geometry socket, connect it to the new viewer node as well. */ + LISTBASE_FOREACH (bNodeSocket *, socket, ¶ms.node.outputs) { + if (socket->type == SOCK_GEOMETRY && !(socket->flag & (SOCK_UNAVAIL | SOCK_HIDDEN))) { + nodeAddLink(¶ms.node_tree, + ¶ms.node, + socket, + &node, + static_cast<bNodeSocket *>(node.inputs.first)); + } + } + + set_active_fn(params, node); + }); + } +} + } // namespace blender::nodes::node_geo_viewer_cc void register_node_type_geo_viewer() @@ -95,5 +148,6 @@ void register_node_type_geo_viewer() node_type_init(&ntype, file_ns::node_init); ntype.declare = file_ns::node_declare; ntype.draw_buttons_ex = file_ns::node_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc index 04a1a8e04b8..0819b401941 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -42,8 +42,19 @@ NODE_STORAGE_FUNCS(NodeGeometryVolumeToMesh) static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Geometry>(N_("Volume")).supported_type(GEO_COMPONENT_TYPE_VOLUME); - b.add_input<decl::Float>(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f); + b.add_input<decl::Float>(N_("Voxel Size")) + .default_value(0.3f) + .min(0.01f) + .subtype(PROP_DISTANCE) + .make_available([](bNode &node) { + node_storage(node).resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE; + }); + b.add_input<decl::Float>(N_("Voxel Amount")) + .default_value(64.0f) + .min(0.0f) + .make_available([](bNode &node) { + node_storage(node).resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT; + }); b.add_input<decl::Float>(N_("Threshold")).default_value(0.1f).min(0.0f); b.add_input<decl::Float>(N_("Adaptivity")).min(0.0f).max(1.0f).subtype(PROP_FACTOR); b.add_output<decl::Geometry>(N_("Mesh")); diff --git a/source/blender/nodes/intern/node_declaration.cc b/source/blender/nodes/intern/node_declaration.cc index 7a19acd2510..75d47cfd386 100644 --- a/source/blender/nodes/intern/node_declaration.cc +++ b/source/blender/nodes/intern/node_declaration.cc @@ -51,7 +51,9 @@ bNodeSocket &SocketDeclaration::update_or_build(bNodeTree &ntree, bNodeSocket &socket) const { /* By default just rebuild. */ - return this->build(ntree, node, (eNodeSocketInOut)socket.in_out); + BLI_assert(socket.in_out == in_out_); + UNUSED_VARS_NDEBUG(socket); + return this->build(ntree, node); } void SocketDeclaration::set_common_flags(bNodeSocket &socket) const diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index dbc4f489a88..d83c05b38a1 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -191,7 +191,6 @@ static void refresh_socket_list(bNodeTree &ntree, bNode &node, ListBase &sockets, Span<SocketDeclarationPtr> socket_decls, - const eNodeSocketInOut in_out, const bool do_id_user) { Vector<bNodeSocket *> old_sockets = sockets; @@ -210,7 +209,7 @@ static void refresh_socket_list(bNodeTree &ntree, bNodeSocket *new_socket = nullptr; if (old_socket_with_same_identifier == nullptr) { /* Create a completely new socket. */ - new_socket = &socket_decl->build(ntree, node, in_out); + new_socket = &socket_decl->build(ntree, node); } else { STRNCPY(old_socket_with_same_identifier->name, socket_decl->name().c_str()); @@ -258,8 +257,8 @@ static void refresh_node(bNodeTree &ntree, blender::nodes::NodeDeclaration &node_decl, bool do_id_user) { - refresh_socket_list(ntree, node, node.inputs, node_decl.inputs(), SOCK_IN, do_id_user); - refresh_socket_list(ntree, node, node.outputs, node_decl.outputs(), SOCK_OUT, do_id_user); + refresh_socket_list(ntree, node, node.inputs, node_decl.inputs(), do_id_user); + refresh_socket_list(ntree, node, node.outputs, node_decl.outputs(), do_id_user); } void node_verify_sockets(bNodeTree *ntree, bNode *node, bool do_id_user) diff --git a/source/blender/nodes/intern/node_socket_declarations.cc b/source/blender/nodes/intern/node_socket_declarations.cc index 1795cc339e7..4fef5b96e9f 100644 --- a/source/blender/nodes/intern/node_socket_declarations.cc +++ b/source/blender/nodes/intern/node_socket_declarations.cc @@ -23,6 +23,52 @@ namespace blender::nodes::decl { +/** + * \note This function only deals with declarations, not the field status of existing nodes. If the + * field status of existing nodes was stored on the sockets, an improvement would be to check the + * existing socket's current status instead of the declaration. + */ +static bool field_types_are_compatible(const SocketDeclaration &input, + const SocketDeclaration &output) +{ + if (output.output_field_dependency().field_type() == OutputSocketFieldType::FieldSource) { + if (input.input_field_type() == InputSocketFieldType::None) { + return false; + } + } + return true; +} + +static bool sockets_can_connect(const SocketDeclaration &socket_decl, + const bNodeSocket &other_socket) +{ + /* Input sockets cannot connect to input sockets, outputs cannot connect to outputs. */ + if (socket_decl.in_out() == other_socket.in_out) { + return false; + } + + if (other_socket.declaration) { + if (socket_decl.in_out() == SOCK_IN) { + if (!field_types_are_compatible(socket_decl, *other_socket.declaration)) { + return false; + } + } + else { + if (!field_types_are_compatible(*other_socket.declaration, socket_decl)) { + return false; + } + } + } + + return true; +} + +static bool basic_types_can_connect(const SocketDeclaration &UNUSED(socket_decl), + const bNodeSocket &other_socket) +{ + return ELEM(other_socket.type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA); +} + static void modify_subtype_except_for_storage(bNodeSocket &socket, int new_subtype) { const char *idname = nodeStaticSocketType(socket.type, new_subtype); @@ -35,10 +81,10 @@ static void modify_subtype_except_for_storage(bNodeSocket &socket, int new_subty /** \name #Float * \{ */ -bNodeSocket &Float::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +bNodeSocket &Float::build(bNodeTree &ntree, bNode &node) const { bNodeSocket &socket = *nodeAddStaticSocket( - &ntree, &node, in_out, SOCK_FLOAT, subtype_, identifier_.c_str(), name_.c_str()); + &ntree, &node, in_out_, SOCK_FLOAT, subtype_, identifier_.c_str(), name_.c_str()); this->set_common_flags(socket); bNodeSocketValueFloat &value = *(bNodeSocketValueFloat *)socket.default_value; value.min = soft_min_value_; @@ -68,10 +114,19 @@ bool Float::matches(const bNodeSocket &socket) const return true; } +bool Float::can_connect(const bNodeSocket &socket) const +{ + if (!sockets_can_connect(*this, socket)) { + return false; + } + return basic_types_can_connect(*this, socket); +} + bNodeSocket &Float::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const { if (socket.type != SOCK_FLOAT) { - return this->build(ntree, node, (eNodeSocketInOut)socket.in_out); + BLI_assert(socket.in_out == in_out_); + return this->build(ntree, node); } if (socket.typeinfo->subtype != subtype_) { modify_subtype_except_for_storage(socket, subtype_); @@ -90,10 +145,10 @@ bNodeSocket &Float::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket & /** \name #Int * \{ */ -bNodeSocket &Int::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +bNodeSocket &Int::build(bNodeTree &ntree, bNode &node) const { bNodeSocket &socket = *nodeAddStaticSocket( - &ntree, &node, in_out, SOCK_INT, subtype_, identifier_.c_str(), name_.c_str()); + &ntree, &node, in_out_, SOCK_INT, subtype_, identifier_.c_str(), name_.c_str()); this->set_common_flags(socket); bNodeSocketValueInt &value = *(bNodeSocketValueInt *)socket.default_value; value.min = soft_min_value_; @@ -123,10 +178,19 @@ bool Int::matches(const bNodeSocket &socket) const return true; } +bool Int::can_connect(const bNodeSocket &socket) const +{ + if (!sockets_can_connect(*this, socket)) { + return false; + } + return basic_types_can_connect(*this, socket); +} + bNodeSocket &Int::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const { if (socket.type != SOCK_INT) { - return this->build(ntree, node, (eNodeSocketInOut)socket.in_out); + BLI_assert(socket.in_out == in_out_); + return this->build(ntree, node); } if (socket.typeinfo->subtype != subtype_) { modify_subtype_except_for_storage(socket, subtype_); @@ -145,10 +209,10 @@ bNodeSocket &Int::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &so /** \name #Vector * \{ */ -bNodeSocket &Vector::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +bNodeSocket &Vector::build(bNodeTree &ntree, bNode &node) const { bNodeSocket &socket = *nodeAddStaticSocket( - &ntree, &node, in_out, SOCK_VECTOR, subtype_, identifier_.c_str(), name_.c_str()); + &ntree, &node, in_out_, SOCK_VECTOR, subtype_, identifier_.c_str(), name_.c_str()); this->set_common_flags(socket); bNodeSocketValueVector &value = *(bNodeSocketValueVector *)socket.default_value; copy_v3_v3(value.value, default_value_); @@ -171,10 +235,19 @@ bool Vector::matches(const bNodeSocket &socket) const return true; } +bool Vector::can_connect(const bNodeSocket &socket) const +{ + if (!sockets_can_connect(*this, socket)) { + return false; + } + return basic_types_can_connect(*this, socket); +} + bNodeSocket &Vector::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const { if (socket.type != SOCK_VECTOR) { - return this->build(ntree, node, (eNodeSocketInOut)socket.in_out); + BLI_assert(socket.in_out == in_out_); + return this->build(ntree, node); } if (socket.typeinfo->subtype != subtype_) { modify_subtype_except_for_storage(socket, subtype_); @@ -192,10 +265,10 @@ bNodeSocket &Vector::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket /** \name #Bool * \{ */ -bNodeSocket &Bool::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +bNodeSocket &Bool::build(bNodeTree &ntree, bNode &node) const { bNodeSocket &socket = *nodeAddStaticSocket( - &ntree, &node, in_out, SOCK_BOOLEAN, PROP_NONE, identifier_.c_str(), name_.c_str()); + &ntree, &node, in_out_, SOCK_BOOLEAN, PROP_NONE, identifier_.c_str(), name_.c_str()); this->set_common_flags(socket); bNodeSocketValueBoolean &value = *(bNodeSocketValueBoolean *)socket.default_value; value.value = default_value_; @@ -213,16 +286,24 @@ bool Bool::matches(const bNodeSocket &socket) const return true; } +bool Bool::can_connect(const bNodeSocket &socket) const +{ + if (!sockets_can_connect(*this, socket)) { + return false; + } + return basic_types_can_connect(*this, socket); +} + /** \} */ /* -------------------------------------------------------------------- */ /** \name #Color * \{ */ -bNodeSocket &Color::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +bNodeSocket &Color::build(bNodeTree &ntree, bNode &node) const { bNodeSocket &socket = *nodeAddStaticSocket( - &ntree, &node, in_out, SOCK_RGBA, PROP_NONE, identifier_.c_str(), name_.c_str()); + &ntree, &node, in_out_, SOCK_RGBA, PROP_NONE, identifier_.c_str(), name_.c_str()); this->set_common_flags(socket); bNodeSocketValueRGBA &value = *(bNodeSocketValueRGBA *)socket.default_value; copy_v4_v4(value.value, default_value_); @@ -245,16 +326,24 @@ bool Color::matches(const bNodeSocket &socket) const return true; } +bool Color::can_connect(const bNodeSocket &socket) const +{ + if (!sockets_can_connect(*this, socket)) { + return false; + } + return basic_types_can_connect(*this, socket); +} + /** \} */ /* -------------------------------------------------------------------- */ /** \name #String * \{ */ -bNodeSocket &String::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +bNodeSocket &String::build(bNodeTree &ntree, bNode &node) const { bNodeSocket &socket = *nodeAddStaticSocket( - &ntree, &node, in_out, SOCK_STRING, PROP_NONE, identifier_.c_str(), name_.c_str()); + &ntree, &node, in_out_, SOCK_STRING, PROP_NONE, identifier_.c_str(), name_.c_str()); STRNCPY(((bNodeSocketValueString *)socket.default_value)->value, default_value_.c_str()); this->set_common_flags(socket); return socket; @@ -271,18 +360,21 @@ bool String::matches(const bNodeSocket &socket) const return true; } +bool String::can_connect(const bNodeSocket &socket) const +{ + return sockets_can_connect(*this, socket) && socket.type == SOCK_STRING; +} + /** \} */ /* -------------------------------------------------------------------- */ /** \name #IDSocketDeclaration * \{ */ -bNodeSocket &IDSocketDeclaration::build(bNodeTree &ntree, - bNode &node, - eNodeSocketInOut in_out) const +bNodeSocket &IDSocketDeclaration::build(bNodeTree &ntree, bNode &node) const { bNodeSocket &socket = *nodeAddSocket( - &ntree, &node, in_out, idname_, identifier_.c_str(), name_.c_str()); + &ntree, &node, in_out_, idname_, identifier_.c_str(), name_.c_str()); this->set_common_flags(socket); return socket; } @@ -298,12 +390,18 @@ bool IDSocketDeclaration::matches(const bNodeSocket &socket) const return true; } +bool IDSocketDeclaration::can_connect(const bNodeSocket &socket) const +{ + return sockets_can_connect(*this, socket) && STREQ(socket.idname, idname_); +} + bNodeSocket &IDSocketDeclaration::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const { if (StringRef(socket.idname) != idname_) { - return this->build(ntree, node, (eNodeSocketInOut)socket.in_out); + BLI_assert(socket.in_out == in_out_); + return this->build(ntree, node); } this->set_common_flags(socket); return socket; @@ -315,10 +413,10 @@ bNodeSocket &IDSocketDeclaration::update_or_build(bNodeTree &ntree, /** \name #Geometry * \{ */ -bNodeSocket &Geometry::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +bNodeSocket &Geometry::build(bNodeTree &ntree, bNode &node) const { bNodeSocket &socket = *nodeAddSocket( - &ntree, &node, in_out, "NodeSocketGeometry", identifier_.c_str(), name_.c_str()); + &ntree, &node, in_out_, "NodeSocketGeometry", identifier_.c_str(), name_.c_str()); this->set_common_flags(socket); return socket; } @@ -334,6 +432,11 @@ bool Geometry::matches(const bNodeSocket &socket) const return true; } +bool Geometry::can_connect(const bNodeSocket &socket) const +{ + return sockets_can_connect(*this, socket) && socket.type == SOCK_GEOMETRY; +} + Span<GeometryComponentType> Geometry::supported_types() const { return supported_types_; @@ -380,10 +483,10 @@ GeometryBuilder &GeometryBuilder::only_instances(bool value) /** \name #Shader * \{ */ -bNodeSocket &Shader::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +bNodeSocket &Shader::build(bNodeTree &ntree, bNode &node) const { bNodeSocket &socket = *nodeAddSocket( - &ntree, &node, in_out, "NodeSocketShader", identifier_.c_str(), name_.c_str()); + &ntree, &node, in_out_, "NodeSocketShader", identifier_.c_str(), name_.c_str()); this->set_common_flags(socket); return socket; } @@ -399,6 +502,18 @@ bool Shader::matches(const bNodeSocket &socket) const return true; } +bool Shader::can_connect(const bNodeSocket &socket) const +{ + if (!sockets_can_connect(*this, socket)) { + return false; + } + /* Basic types can convert to shaders, but not the other way around. */ + if (in_out_ == SOCK_IN) { + return ELEM(socket.type, SOCK_VECTOR, SOCK_RGBA, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN); + } + return socket.type == SOCK_SHADER; +} + /** \} */ } // namespace blender::nodes::decl diff --git a/source/blender/nodes/intern/socket_search_link.cc b/source/blender/nodes/intern/socket_search_link.cc new file mode 100644 index 00000000000..48f8106e58b --- /dev/null +++ b/source/blender/nodes/intern/socket_search_link.cc @@ -0,0 +1,199 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_set.hh" + +#include "BKE_node.h" + +#include "UI_interface.h" + +#include "NOD_node_declaration.hh" +#include "NOD_socket_search_link.hh" + +namespace blender::nodes { + +void GatherLinkSearchOpParams::add_item(std::string socket_name, + SocketLinkOperation::LinkSocketFn fn, + const int weight) +{ + + std::string name = std::string(node_type_.ui_name) + " " + UI_MENU_ARROW_SEP + socket_name; + + items_.append({std::move(name), std::move(fn), weight}); +} + +const bNodeSocket &GatherLinkSearchOpParams::other_socket() const +{ + return other_socket_; +} + +const bNodeTree &GatherLinkSearchOpParams::node_tree() const +{ + return node_tree_; +} + +const bNodeType &GatherLinkSearchOpParams::node_type() const +{ + return node_type_; +} + +eNodeSocketInOut GatherLinkSearchOpParams::in_out() const +{ + return other_socket_.in_out == SOCK_IN ? SOCK_OUT : SOCK_IN; +} + +void LinkSearchOpParams::connect_available_socket(bNode &new_node, StringRef socket_name) +{ + const eNodeSocketInOut in_out = socket.in_out == SOCK_IN ? SOCK_OUT : SOCK_IN; + bNodeSocket *new_node_socket = bke::node_find_enabled_socket(new_node, in_out, socket_name); + if (new_node_socket == nullptr) { + /* If the socket isn't found, some node's search gather functions probably aren't configured + * properly. It's likely enough that it's worth avoiding a crash in a release build though. */ + BLI_assert_unreachable(); + return; + } + nodeAddLink(&node_tree, &new_node, new_node_socket, &node, &socket); +} + +bNode &LinkSearchOpParams::add_node(StringRef idname) +{ + std::string idname_str = idname; + bNode *node = nodeAddNode(&C, &node_tree, idname_str.c_str()); + BLI_assert(node != nullptr); + added_nodes_.append(node); + return *node; +} + +bNode &LinkSearchOpParams::add_node(const bNodeType &node_type) +{ + return this->add_node(node_type.idname); +} + +void LinkSearchOpParams::update_and_connect_available_socket(bNode &new_node, + StringRef socket_name) +{ + if (new_node.typeinfo->updatefunc) { + new_node.typeinfo->updatefunc(&node_tree, &new_node); + } + this->connect_available_socket(new_node, socket_name); +} + +void search_link_ops_for_declarations(GatherLinkSearchOpParams ¶ms, + Span<SocketDeclarationPtr> declarations) +{ + const bNodeType &node_type = params.node_type(); + + const SocketDeclaration *main_socket = nullptr; + Vector<const SocketDeclaration *> connectable_sockets; + + Set<StringRef> socket_names; + for (const int i : declarations.index_range()) { + const SocketDeclaration &socket = *declarations[i]; + if (!socket_names.add(socket.name())) { + /* Don't add sockets with the same name to the search. Needed to support being called from + * #search_link_ops_for_basic_node, which should have "okay" behavior for nodes with + * duplicate socket names. */ + continue; + } + if (!socket.can_connect(params.other_socket())) { + continue; + } + if (socket.is_default_link_socket() || main_socket == nullptr) { + /* Either the first connectable or explicitly tagged socket is the main socket. */ + main_socket = &socket; + } + connectable_sockets.append(&socket); + } + for (const int i : connectable_sockets.index_range()) { + const SocketDeclaration &socket = *connectable_sockets[i]; + /* Give non-main sockets a lower weight so that they don't show up at the top of the search + * when they are not explicitly searched for. The -1 is used to make sure that the first socket + * has a smaller weight than zero so that it does not have the same weight as the main socket. + * Negative weights are used to avoid making the heighest weight dependent on the number of + * sockets. */ + const int weight = (&socket == main_socket) ? 0 : -1 - i; + params.add_item( + socket.name(), + [&node_type, &socket](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); + socket.make_available(node); + params.update_and_connect_available_socket(node, socket.name()); + }, + weight); + } +} + +static void search_link_ops_for_socket_templates(GatherLinkSearchOpParams ¶ms, + const bNodeSocketTemplate *templates, + const eNodeSocketInOut in_out) +{ + const bNodeType &node_type = params.node_type(); + const bNodeTreeType &node_tree_type = *params.node_tree().typeinfo; + + Set<StringRef> socket_names; + for (const bNodeSocketTemplate *socket_template = templates; socket_template->type != -1; + socket_template++) { + eNodeSocketDatatype from = (eNodeSocketDatatype)socket_template->type; + eNodeSocketDatatype to = (eNodeSocketDatatype)params.other_socket().type; + if (in_out == SOCK_IN) { + std::swap(from, to); + } + if (node_tree_type.validate_link && !node_tree_type.validate_link(from, to)) { + continue; + } + if (!socket_names.add(socket_template->name)) { + /* See comment in #search_link_ops_for_declarations. */ + continue; + } + + params.add_item( + socket_template->name, [socket_template, node_type, in_out](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); + bNodeSocket *new_node_socket = bke::node_find_enabled_socket( + node, in_out, socket_template->name); + if (new_node_socket != nullptr) { + /* Rely on the way #nodeAddLink switches in/out if necessary. */ + nodeAddLink(¶ms.node_tree, ¶ms.node, ¶ms.socket, &node, new_node_socket); + } + }); + } +} + +void search_link_ops_for_basic_node(GatherLinkSearchOpParams ¶ms) +{ + const bNodeType &node_type = params.node_type(); + + if (node_type.declare) { + if (node_type.declaration_is_dynamic) { + /* Dynamic declarations (whatever they end up being) aren't supported + * by this function, but still avoid a crash in release builds. */ + BLI_assert_unreachable(); + return; + } + + const NodeDeclaration &declaration = *node_type.fixed_declaration; + + search_link_ops_for_declarations(params, declaration.sockets(params.in_out())); + } + else if (node_type.inputs && params.in_out() == SOCK_IN) { + search_link_ops_for_socket_templates(params, node_type.inputs, SOCK_IN); + } + else if (node_type.outputs && params.in_out() == SOCK_OUT) { + search_link_ops_for_socket_templates(params, node_type.outputs, SOCK_OUT); + } +} + +} // namespace blender::nodes diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c index 40023ca80d8..c3b5236373c 100644 --- a/source/blender/nodes/shader/node_shader_tree.c +++ b/source/blender/nodes/shader/node_shader_tree.c @@ -170,12 +170,12 @@ static void update(bNodeTree *ntree) } } -static bool shader_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link) +static bool shader_validate_link(eNodeSocketDatatype from, eNodeSocketDatatype to) { /* Can't connect shader into other socket types, other way around is fine * since it will be interpreted as emission. */ - if (link->fromsock->type == SOCK_SHADER) { - return (link->tosock->type == SOCK_SHADER); + if (from == SOCK_SHADER) { + return to == SOCK_SHADER; } return true; } diff --git a/source/blender/nodes/shader/node_shader_util.cc b/source/blender/nodes/shader/node_shader_util.cc index 8b7e72fbcc9..f2464d4c1b4 100644 --- a/source/blender/nodes/shader/node_shader_util.cc +++ b/source/blender/nodes/shader/node_shader_util.cc @@ -25,6 +25,8 @@ #include "node_shader_util.h" +#include "NOD_socket_search_link.hh" + #include "node_exec.h" bool sh_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree, const char **r_disabled_hint) @@ -54,12 +56,14 @@ void sh_node_type_base( ntype->poll = sh_node_poll_default; ntype->insert_link = node_insert_link_default; + ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node; } void sh_fn_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag) { sh_node_type_base(ntype, type, name, nclass, flag); ntype->poll = sh_fn_poll_default; + ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node; } /* ****** */ diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc index a10124460ff..51c94b10c8b 100644 --- a/source/blender/nodes/shader/nodes/node_shader_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_math.cc @@ -24,6 +24,7 @@ #include "node_shader_util.h" #include "NOD_math_functions.hh" +#include "NOD_socket_search_link.hh" /* **************** SCALAR MATH ******************** */ @@ -44,6 +45,15 @@ static void sh_node_math_declare(NodeDeclarationBuilder &b) b.add_output<decl::Float>(N_("Value")); }; +static void sh_node_math_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + /* For now, do something very basic (only exposing "Add", and a single "Value" socket). */ + params.add_item(IFACE_("Value"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("ShaderNodeMath"); + params.update_and_connect_available_socket(node, "Value"); + }); +} + } // namespace blender::nodes static const char *gpu_shader_get_name(int mode) @@ -171,6 +181,7 @@ void register_node_type_sh_math() node_type_gpu(&ntype, gpu_shader_math); node_type_update(&ntype, node_math_update); ntype.build_multi_function = sh_node_math_build_multi_function; + ntype.gather_link_search_ops = blender::nodes::sh_node_math_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc index 9504b0dc9f0..97ac199bc03 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc @@ -29,7 +29,10 @@ static void sh_node_tex_musgrave_declare(NodeDeclarationBuilder &b) { b.is_function_node(); b.add_input<decl::Vector>(N_("Vector")).hide_value().implicit_field(); - b.add_input<decl::Float>(N_("W")).min(-1000.0f).max(1000.0f); + b.add_input<decl::Float>(N_("W")).min(-1000.0f).max(1000.0f).make_available([](bNode &node) { + /* Default to 1 instead of 4, because it is much faster. */ + node_storage(node).dimensions = 1; + }); b.add_input<decl::Float>(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); b.add_input<decl::Float>(N_("Detail")).min(0.0f).max(15.0f).default_value(2.0f); b.add_input<decl::Float>(N_("Dimension")).min(0.0f).max(1000.0f).default_value(2.0f); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc index e3764120663..d7f21853a01 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc @@ -29,7 +29,10 @@ static void sh_node_tex_noise_declare(NodeDeclarationBuilder &b) { b.is_function_node(); b.add_input<decl::Vector>(N_("Vector")).implicit_field(); - b.add_input<decl::Float>(N_("W")).min(-1000.0f).max(1000.0f); + b.add_input<decl::Float>(N_("W")).min(-1000.0f).max(1000.0f).make_available([](bNode &node) { + /* Default to 1 instead of 4, because it is much faster. */ + node_storage(node).dimensions = 1; + }); b.add_input<decl::Float>(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); b.add_input<decl::Float>(N_("Detail")).min(0.0f).max(15.0f).default_value(2.0f); b.add_input<decl::Float>(N_("Roughness")) diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc index 6968a7483b6..8a1170de304 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc @@ -29,14 +29,22 @@ static void sh_node_tex_voronoi_declare(NodeDeclarationBuilder &b) { b.is_function_node(); b.add_input<decl::Vector>(N_("Vector")).hide_value().implicit_field(); - b.add_input<decl::Float>(N_("W")).min(-1000.0f).max(1000.0f); + b.add_input<decl::Float>(N_("W")).min(-1000.0f).max(1000.0f).make_available([](bNode &node) { + /* Default to 1 instead of 4, because it is much faster. */ + node_storage(node).dimensions = 1; + }); b.add_input<decl::Float>(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); b.add_input<decl::Float>(N_("Smoothness")) .min(0.0f) .max(1.0f) .default_value(1.0f) - .subtype(PROP_FACTOR); - b.add_input<decl::Float>(N_("Exponent")).min(0.0f).max(32.0f).default_value(0.5f); + .subtype(PROP_FACTOR) + .make_available([](bNode &node) { node_storage(node).feature = SHD_VORONOI_SMOOTH_F1; }); + b.add_input<decl::Float>(N_("Exponent")) + .min(0.0f) + .max(32.0f) + .default_value(0.5f) + .make_available([](bNode &node) { node_storage(node).distance = SHD_VORONOI_MINKOWSKI; }); b.add_input<decl::Float>(N_("Randomness")) .min(0.0f) .max(1.0f) @@ -45,8 +53,13 @@ static void sh_node_tex_voronoi_declare(NodeDeclarationBuilder &b) b.add_output<decl::Float>(N_("Distance")).no_muted_links(); b.add_output<decl::Color>(N_("Color")).no_muted_links(); b.add_output<decl::Vector>(N_("Position")).no_muted_links(); - b.add_output<decl::Float>(N_("W")).no_muted_links(); - b.add_output<decl::Float>(N_("Radius")).no_muted_links(); + b.add_output<decl::Float>(N_("W")).no_muted_links().make_available([](bNode &node) { + /* Default to 1 instead of 4, because it is much faster. */ + node_storage(node).dimensions = 1; + }); + b.add_output<decl::Float>(N_("Radius")).no_muted_links().make_available([](bNode &node) { + node_storage(node).feature = SHD_VORONOI_N_SPHERE_RADIUS; + }); }; } // namespace blender::nodes diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc index f2dfd2e6968..0e72cee38e7 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc @@ -27,7 +27,10 @@ static void sh_node_tex_white_noise_declare(NodeDeclarationBuilder &b) { b.is_function_node(); b.add_input<decl::Vector>(N_("Vector")).min(-10000.0f).max(10000.0f).implicit_field(); - b.add_input<decl::Float>(N_("W")).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("W")).min(-10000.0f).max(10000.0f).make_available([](bNode &node) { + /* Default to 1 instead of 4, because it is faster. */ + node.custom1 = 1; + }); b.add_output<decl::Float>(N_("Value")); b.add_output<decl::Color>(N_("Color")); }; diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc index 7f87332ba1b..70a0d47e1b9 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc @@ -24,6 +24,7 @@ #include "node_shader_util.h" #include "NOD_math_functions.hh" +#include "NOD_socket_search_link.hh" namespace blender::nodes { @@ -38,6 +39,15 @@ static void sh_node_vector_math_declare(NodeDeclarationBuilder &b) b.add_output<decl::Float>(N_("Value")); }; +static void sh_node_vector_math_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + /* For now, do something very basic (only exposing "Add", and a single "Vector" socket). */ + params.add_item(IFACE_("Vector"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("ShaderNodeVectorMath"); + params.update_and_connect_available_socket(node, "Vector"); + }); +} + } // namespace blender::nodes static const char *gpu_shader_get_name(int mode) @@ -289,6 +299,7 @@ void register_node_type_sh_vect_math() node_type_gpu(&ntype, gpu_shader_vector_math); node_type_update(&ntype, node_shader_update_vector_math); ntype.build_multi_function = sh_node_vector_math_build_multi_function; + ntype.gather_link_search_ops = blender::nodes::sh_node_vector_math_gather_link_searches; nodeRegisterType(&ntype); } |