/* SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include #include #include "BLI_string_ref.hh" #include "BLI_vector.hh" #include "DNA_node_types.h" struct bNode; namespace blender::nodes { class NodeDeclarationBuilder; enum class InputSocketFieldType { /** The input is required to be a single value. */ None, /** The input can be a field. */ IsSupported, /** The input can be a field and is a field implicitly if nothing is connected. */ Implicit, }; enum class OutputSocketFieldType { /** The output is always a single value. */ None, /** The output is always a field, independent of the inputs. */ FieldSource, /** If any input is a field, this output will be a field as well. */ DependentField, /** If any of a subset of inputs is a field, this out will be a field as well. * The subset is defined by the vector of indices. */ PartiallyDependent, }; /** * Contains information about how a node output's field state depends on inputs of the same node. */ class OutputFieldDependency { private: OutputSocketFieldType type_ = OutputSocketFieldType::None; Vector linked_input_indices_; public: static OutputFieldDependency ForFieldSource(); static OutputFieldDependency ForDataSource(); static OutputFieldDependency ForDependentField(); static OutputFieldDependency ForPartiallyDependentField(Vector indices); OutputSocketFieldType field_type() const; Span linked_input_indices() const; friend bool operator==(const OutputFieldDependency &a, const OutputFieldDependency &b); }; /** * Information about how a node interacts with fields. */ struct FieldInferencingInterface { Vector inputs; Vector outputs; }; /** * Describes a single input or output socket. This is subclassed for different socket types. */ class SocketDeclaration { protected: 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; bool is_multi_input_ = false; bool no_mute_links_ = false; bool is_unavailable_ = false; bool is_attribute_name_ = false; bool is_default_link_socket_ = false; InputSocketFieldType input_field_type_ = InputSocketFieldType::None; OutputFieldDependency output_field_dependency_; /** The priority of the input for determining the domain of the node. See * realtime_compositor::InputDescriptor for more information. */ int compositor_domain_priority_ = 0; /** This input expects a single value and can't operate on non-single values. See * realtime_compositor::InputDescriptor for more information. */ bool compositor_expects_single_value_ = false; /** Utility method to make the socket available if there is a straightforward way to do so. */ std::function make_available_fn_; friend NodeDeclarationBuilder; template friend class SocketDeclarationBuilder; public: virtual ~SocketDeclaration() = default; 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 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; InputSocketFieldType input_field_type() const; const OutputFieldDependency &output_field_dependency() const; int compositor_domain_priority() const; bool compositor_expects_single_value() const; protected: void set_common_flags(bNodeSocket &socket) const; bool matches_common_data(const bNodeSocket &socket) const; }; class BaseSocketDeclarationBuilder { public: virtual ~BaseSocketDeclarationBuilder() = default; }; /** * Wraps a #SocketDeclaration and provides methods to set it up correctly. * This is separate from #SocketDeclaration, because it allows separating the API used by nodes to * declare themselves from how the declaration is stored internally. */ template class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { protected: using Self = typename SocketDecl::Builder; static_assert(std::is_base_of_v); SocketDecl *decl_; friend class NodeDeclarationBuilder; public: Self &hide_label(bool value = true) { decl_->hide_label_ = value; return *(Self *)this; } Self &hide_value(bool value = true) { decl_->hide_value_ = value; return *(Self *)this; } Self &multi_input(bool value = true) { decl_->is_multi_input_ = value; return *(Self *)this; } Self &description(std::string value = "") { decl_->description_ = std::move(value); return *(Self *)this; } Self &no_muted_links(bool value = true) { decl_->no_mute_links_ = value; return *(Self *)this; } /** * Used for sockets that are always unavailable and should not be seen by the user. * Ideally, no new calls to this method should be added over time. */ Self &unavailable(bool value = true) { decl_->is_unavailable_ = value; return *(Self *)this; } Self &is_attribute_name(bool value = true) { decl_->is_attribute_name_ = value; return *(Self *)this; } Self &is_default_link_socket(bool value = true) { decl_->is_default_link_socket_ = value; return *(Self *)this; } /** The input socket allows passing in a field. */ Self &supports_field() { decl_->input_field_type_ = InputSocketFieldType::IsSupported; return *(Self *)this; } /** The input supports a field and is a field by default when nothing is connected. */ Self &implicit_field() { this->hide_value(); decl_->input_field_type_ = InputSocketFieldType::Implicit; return *(Self *)this; } /** The output is always a field, regardless of any inputs. */ Self &field_source() { decl_->output_field_dependency_ = OutputFieldDependency::ForFieldSource(); return *(Self *)this; } /** The output is a field if any of the inputs is a field. */ Self &dependent_field() { decl_->output_field_dependency_ = OutputFieldDependency::ForDependentField(); return *(Self *)this; } /** The output is a field if any of the inputs with indices in the given list is a field. */ Self &dependent_field(Vector input_dependencies) { decl_->output_field_dependency_ = OutputFieldDependency::ForPartiallyDependentField( std::move(input_dependencies)); return *(Self *)this; } /** The priority of the input for determining the domain of the node. See * realtime_compositor::InputDescriptor for more information. */ Self &compositor_domain_priority(int priority) { decl_->compositor_domain_priority_ = priority; return *(Self *)this; } /** This input expects a single value and can't operate on non-single values. See * realtime_compositor::InputDescriptor for more information. */ Self &compositor_expects_single_value(bool value = true) { decl_->compositor_expects_single_value_ = value; 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 fn) { decl_->make_available_fn_ = std::move(fn); return *(Self *)this; } }; using SocketDeclarationPtr = std::unique_ptr; class NodeDeclaration { private: Vector inputs_; Vector outputs_; bool is_function_node_ = false; friend NodeDeclarationBuilder; public: bool matches(const bNode &node) const; Span inputs() const; Span outputs() const; Span sockets(eNodeSocketInOut in_out) const; bool is_function_node() const { return is_function_node_; } MEM_CXX_CLASS_ALLOC_FUNCS("NodeDeclaration") }; class NodeDeclarationBuilder { private: NodeDeclaration &declaration_; Vector> builders_; public: NodeDeclarationBuilder(NodeDeclaration &declaration); /** * All inputs support fields, and all outputs are fields if any of the inputs is a field. * Calling field status definitions on each socket is unnecessary. Must be called before adding * any sockets. */ void is_function_node(bool value = true) { BLI_assert_msg(declaration_.inputs().is_empty() && declaration_.outputs().is_empty(), "is_function_node() must be called before any socket is created"); declaration_.is_function_node_ = value; } template typename DeclType::Builder &add_input(StringRef name, StringRef identifier = ""); template typename DeclType::Builder &add_output(StringRef name, StringRef identifier = ""); private: template typename DeclType::Builder &add_socket(StringRef name, StringRef identifier, eNodeSocketInOut in_out); }; /* -------------------------------------------------------------------- */ /** \name #OutputFieldDependency Inline Methods * \{ */ inline OutputFieldDependency OutputFieldDependency::ForFieldSource() { OutputFieldDependency field_dependency; field_dependency.type_ = OutputSocketFieldType::FieldSource; return field_dependency; } inline OutputFieldDependency OutputFieldDependency::ForDataSource() { OutputFieldDependency field_dependency; field_dependency.type_ = OutputSocketFieldType::None; return field_dependency; } inline OutputFieldDependency OutputFieldDependency::ForDependentField() { OutputFieldDependency field_dependency; field_dependency.type_ = OutputSocketFieldType::DependentField; return field_dependency; } inline OutputFieldDependency OutputFieldDependency::ForPartiallyDependentField(Vector indices) { OutputFieldDependency field_dependency; if (indices.is_empty()) { field_dependency.type_ = OutputSocketFieldType::None; } else { field_dependency.type_ = OutputSocketFieldType::PartiallyDependent; field_dependency.linked_input_indices_ = std::move(indices); } return field_dependency; } inline OutputSocketFieldType OutputFieldDependency::field_type() const { return type_; } inline Span OutputFieldDependency::linked_input_indices() const { return linked_input_indices_; } inline bool operator==(const OutputFieldDependency &a, const OutputFieldDependency &b) { return a.type_ == b.type_ && a.linked_input_indices_ == b.linked_input_indices_; } inline bool operator!=(const OutputFieldDependency &a, const OutputFieldDependency &b) { return !(a == b); } /** \} */ /* -------------------------------------------------------------------- */ /** \name #FieldInferencingInterface Inline Methods * \{ */ inline bool operator==(const FieldInferencingInterface &a, const FieldInferencingInterface &b) { return a.inputs == b.inputs && a.outputs == b.outputs; } inline bool operator!=(const FieldInferencingInterface &a, const FieldInferencingInterface &b) { return !(a == b); } /** \} */ /* -------------------------------------------------------------------- */ /** \name #SocketDeclaration Inline Methods * \{ */ inline StringRefNull SocketDeclaration::name() const { return name_; } inline StringRefNull SocketDeclaration::identifier() const { return identifier_; } inline eNodeSocketInOut SocketDeclaration::in_out() const { return in_out_; } inline StringRefNull SocketDeclaration::description() const { return description_; } inline bool SocketDeclaration::is_attribute_name() const { return is_attribute_name_; } inline bool SocketDeclaration::is_default_link_socket() const { return is_default_link_socket_; } inline InputSocketFieldType SocketDeclaration::input_field_type() const { return input_field_type_; } inline const OutputFieldDependency &SocketDeclaration::output_field_dependency() const { return output_field_dependency_; } inline int SocketDeclaration::compositor_domain_priority() const { return compositor_domain_priority_; } inline bool SocketDeclaration::compositor_expects_single_value() const { return compositor_expects_single_value_; } inline void SocketDeclaration::make_available(bNode &node) const { if (make_available_fn_) { make_available_fn_(node); } } /** \} */ /* -------------------------------------------------------------------- */ /** \name #NodeDeclarationBuilder Inline Methods * \{ */ inline NodeDeclarationBuilder::NodeDeclarationBuilder(NodeDeclaration &declaration) : declaration_(declaration) { } template inline typename DeclType::Builder &NodeDeclarationBuilder::add_input(StringRef name, StringRef identifier) { return this->add_socket(name, identifier, SOCK_IN); } template inline typename DeclType::Builder &NodeDeclarationBuilder::add_output(StringRef name, StringRef identifier) { return this->add_socket(name, identifier, SOCK_OUT); } template inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket(StringRef name, StringRef identifier, eNodeSocketInOut in_out) { static_assert(std::is_base_of_v); using Builder = typename DeclType::Builder; Vector &declarations = in_out == SOCK_IN ? declaration_.inputs_ : declaration_.outputs_; std::unique_ptr socket_decl = std::make_unique(); std::unique_ptr socket_decl_builder = std::make_unique(); socket_decl_builder->decl_ = &*socket_decl; socket_decl->name_ = name; socket_decl->identifier_ = identifier.is_empty() ? name : identifier; socket_decl->in_out_ = in_out; if (declaration_.is_function_node()) { socket_decl->input_field_type_ = InputSocketFieldType::IsSupported; socket_decl->output_field_dependency_ = OutputFieldDependency::ForDependentField(); } 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; } /** \} */ /* -------------------------------------------------------------------- */ /** \name #NodeDeclaration Inline Methods * \{ */ inline Span NodeDeclaration::inputs() const { return inputs_; } inline Span NodeDeclaration::outputs() const { return outputs_; } inline Span NodeDeclaration::sockets(eNodeSocketInOut in_out) const { if (in_out == SOCK_IN) { return inputs_; } return outputs_; } /** \} */ } // namespace blender::nodes