Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2020-07-17 12:36:59 +0300
committerJacques Lucke <jacques@blender.org>2020-07-17 12:36:59 +0300
commit5910dbdbf7dc7999597a680f81c261fa7bbcf011 (patch)
treeedc962ebd6b11d89d3c29e571caeee0dcbe4ad79 /source/blender/nodes
parent0a40c671b0b5a6735bd0137ebcb3ab7568ae4481 (diff)
Nodes: move some code from blenkernel directory to nodes
This also introduces the `blender::nodes` namespace. Eventually, we want to move most/all of the node implementation files into this namespace. The reason for this file-move is that the code fits much better into the `nodes` directory than in the `blenkernel` directory.
Diffstat (limited to 'source/blender/nodes')
-rw-r--r--source/blender/nodes/CMakeLists.txt6
-rw-r--r--source/blender/nodes/NOD_derived_node_tree.hh517
-rw-r--r--source/blender/nodes/NOD_node_tree_multi_function.hh388
-rw-r--r--source/blender/nodes/NOD_node_tree_ref.hh445
-rw-r--r--source/blender/nodes/function/node_function_util.hh2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_boolean_math.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_combine_strings.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_float_compare.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_group_instance_id.cc6
-rw-r--r--source/blender/nodes/intern/derived_node_tree.cc441
-rw-r--r--source/blender/nodes/intern/node_socket.cc14
-rw-r--r--source/blender/nodes/intern/node_tree_multi_function.cc344
-rw-r--r--source/blender/nodes/intern/node_tree_ref.cc177
-rw-r--r--source/blender/nodes/shader/node_shader_util.h2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_map_range.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_math.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_valToRgb.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_value.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_math.cc4
21 files changed, 2345 insertions, 27 deletions
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index f644cdc134a..bb2541fd7a6 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -273,9 +273,12 @@ set(SRC
texture/node_texture_tree.c
texture/node_texture_util.c
+ intern/derived_node_tree.cc
intern/node_common.c
intern/node_exec.c
intern/node_socket.cc
+ intern/node_tree_multi_function.cc
+ intern/node_tree_ref.cc
intern/node_util.c
composite/node_composite_util.h
@@ -286,7 +289,10 @@ set(SRC
NOD_common.h
NOD_composite.h
+ NOD_derived_node_tree.hh
NOD_function.h
+ NOD_node_tree_multi_function.hh
+ NOD_node_tree_ref.hh
NOD_shader.h
NOD_simulation.h
NOD_socket.h
diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh
new file mode 100644
index 00000000000..24144496c92
--- /dev/null
+++ b/source/blender/nodes/NOD_derived_node_tree.hh
@@ -0,0 +1,517 @@
+/*
+ * 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.
+ */
+
+#ifndef __NOD_DERIVED_NODE_TREE_HH__
+#define __NOD_DERIVED_NODE_TREE_HH__
+
+/** \file
+ * \ingroup nodes
+ *
+ * DerivedNodeTree provides a flattened view on a bNodeTree, i.e. node groups are inlined. It
+ * builds on top of NodeTreeRef and supports similar queries efficiently.
+ *
+ * Every inlined node remembers its path to the parent ("call stack").
+ *
+ * Unlinked group node inputs are handled separately from other sockets.
+ *
+ * There is a dot graph exporter for debugging purposes.
+ */
+
+#include "NOD_node_tree_ref.hh"
+
+namespace blender::nodes {
+
+class DSocket;
+class DInputSocket;
+class DOutputSocket;
+class DNode;
+class DParentNode;
+class DGroupInput;
+class DerivedNodeTree;
+
+class DSocket : NonCopyable, NonMovable {
+ protected:
+ DNode *node_;
+ const SocketRef *socket_ref_;
+ uint id_;
+
+ friend DerivedNodeTree;
+
+ public:
+ const DNode &node() const;
+
+ uint id() const;
+ uint index() const;
+
+ bool is_input() const;
+ bool is_output() const;
+
+ const DSocket &as_base() const;
+ const DInputSocket &as_input() const;
+ const DOutputSocket &as_output() const;
+
+ PointerRNA *rna() const;
+ StringRefNull idname() const;
+ StringRefNull name() const;
+
+ const SocketRef &socket_ref() const;
+ bNodeSocket *bsocket() const;
+
+ bool is_available() const;
+};
+
+class DInputSocket : public DSocket {
+ private:
+ Vector<DOutputSocket *> linked_sockets_;
+ Vector<DGroupInput *> linked_group_inputs_;
+
+ friend DerivedNodeTree;
+
+ public:
+ const InputSocketRef &socket_ref() const;
+
+ Span<const DOutputSocket *> linked_sockets() const;
+ Span<const DGroupInput *> linked_group_inputs() const;
+
+ bool is_linked() const;
+};
+
+class DOutputSocket : public DSocket {
+ private:
+ Vector<DInputSocket *> linked_sockets_;
+
+ friend DerivedNodeTree;
+
+ public:
+ const OutputSocketRef &socket_ref() const;
+ Span<const DInputSocket *> linked_sockets() const;
+};
+
+class DGroupInput : NonCopyable, NonMovable {
+ private:
+ const InputSocketRef *socket_ref_;
+ DParentNode *parent_;
+ Vector<DInputSocket *> linked_sockets_;
+ uint id_;
+
+ friend DerivedNodeTree;
+
+ public:
+ const InputSocketRef &socket_ref() const;
+ bNodeSocket *bsocket() const;
+ const DParentNode *parent() const;
+ Span<const DInputSocket *> linked_sockets() const;
+ uint id() const;
+ StringRefNull name() const;
+};
+
+class DNode : NonCopyable, NonMovable {
+ private:
+ const NodeRef *node_ref_;
+ DParentNode *parent_;
+
+ Span<DInputSocket *> inputs_;
+ Span<DOutputSocket *> outputs_;
+
+ uint id_;
+
+ friend DerivedNodeTree;
+
+ public:
+ const NodeRef &node_ref() const;
+ const DParentNode *parent() const;
+
+ Span<const DInputSocket *> inputs() const;
+ Span<const DOutputSocket *> outputs() const;
+
+ const DInputSocket &input(uint index) const;
+ const DOutputSocket &output(uint index) const;
+
+ const DInputSocket &input(uint index, StringRef expected_name) const;
+ const DOutputSocket &output(uint index, StringRef expected_name) const;
+
+ uint id() const;
+
+ PointerRNA *rna() const;
+ StringRefNull idname() const;
+ StringRefNull name() const;
+
+ private:
+ void destruct_with_sockets();
+};
+
+class DParentNode : NonCopyable, NonMovable {
+ private:
+ const NodeRef *node_ref_;
+ DParentNode *parent_;
+ uint id_;
+
+ friend DerivedNodeTree;
+
+ public:
+ const DParentNode *parent() const;
+ const NodeRef &node_ref() const;
+ uint id() const;
+};
+
+using NodeTreeRefMap = Map<bNodeTree *, std::unique_ptr<const NodeTreeRef>>;
+
+class DerivedNodeTree : NonCopyable, NonMovable {
+ private:
+ LinearAllocator<> allocator_;
+ bNodeTree *btree_;
+ Vector<DNode *> nodes_by_id_;
+ Vector<DGroupInput *> group_inputs_;
+ Vector<DParentNode *> parent_nodes_;
+
+ Vector<DSocket *> sockets_by_id_;
+ Vector<DInputSocket *> input_sockets_;
+ Vector<DOutputSocket *> output_sockets_;
+
+ Map<const bNodeType *, Vector<DNode *>> nodes_by_type_;
+
+ public:
+ DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs);
+ ~DerivedNodeTree();
+
+ Span<const DNode *> nodes() const;
+ Span<const DNode *> nodes_by_type(StringRefNull idname) const;
+ Span<const DNode *> nodes_by_type(const bNodeType *nodetype) const;
+
+ Span<const DSocket *> sockets() const;
+ Span<const DInputSocket *> input_sockets() const;
+ Span<const DOutputSocket *> output_sockets() const;
+
+ Span<const DGroupInput *> group_inputs() const;
+
+ std::string to_dot() const;
+
+ private:
+ /* Utility functions used during construction. */
+ void insert_nodes_and_links_in_id_order(const NodeTreeRef &tree_ref,
+ DParentNode *parent,
+ Vector<DNode *> &all_nodes);
+ DNode &create_node(const NodeRef &node_ref,
+ DParentNode *parent,
+ MutableSpan<DSocket *> r_sockets_map);
+ void expand_groups(Vector<DNode *> &all_nodes,
+ Vector<DGroupInput *> &all_group_inputs,
+ Vector<DParentNode *> &all_parent_nodes,
+ NodeTreeRefMap &node_tree_refs);
+ void expand_group_node(DNode &group_node,
+ Vector<DNode *> &all_nodes,
+ Vector<DGroupInput *> &all_group_inputs,
+ Vector<DParentNode *> &all_parent_nodes,
+ NodeTreeRefMap &node_tree_refs);
+ void create_group_inputs_for_unlinked_inputs(DNode &node,
+ Vector<DGroupInput *> &all_group_inputs);
+ void relink_group_inputs(const NodeTreeRef &group_ref,
+ Span<DNode *> nodes_by_id,
+ DNode &group_node);
+ void relink_group_outputs(const NodeTreeRef &group_ref,
+ Span<DNode *> nodes_by_id,
+ DNode &group_node);
+ void remove_expanded_group_interfaces(Vector<DNode *> &all_nodes);
+ void remove_unused_group_inputs(Vector<DGroupInput *> &all_group_inputs);
+ void store_in_this_and_init_ids(Vector<DNode *> &&all_nodes,
+ Vector<DGroupInput *> &&all_group_inputs,
+ Vector<DParentNode *> &&all_parent_nodes);
+};
+
+/* --------------------------------------------------------------------
+ * DSocket inline methods.
+ */
+
+inline const DNode &DSocket::node() const
+{
+ return *node_;
+}
+
+inline uint DSocket::id() const
+{
+ return id_;
+}
+
+inline uint DSocket::index() const
+{
+ return socket_ref_->index();
+}
+
+inline bool DSocket::is_input() const
+{
+ return socket_ref_->is_input();
+}
+
+inline bool DSocket::is_output() const
+{
+ return socket_ref_->is_output();
+}
+
+inline const DSocket &DSocket::as_base() const
+{
+ return *this;
+}
+
+inline const DInputSocket &DSocket::as_input() const
+{
+ return *(DInputSocket *)this;
+}
+
+inline const DOutputSocket &DSocket::as_output() const
+{
+ return *(DOutputSocket *)this;
+}
+
+inline PointerRNA *DSocket::rna() const
+{
+ return socket_ref_->rna();
+}
+
+inline StringRefNull DSocket::idname() const
+{
+ return socket_ref_->idname();
+}
+
+inline StringRefNull DSocket::name() const
+{
+ return socket_ref_->name();
+}
+
+inline const SocketRef &DSocket::socket_ref() const
+{
+ return *socket_ref_;
+}
+
+inline bNodeSocket *DSocket::bsocket() const
+{
+ return socket_ref_->bsocket();
+}
+
+inline bool DSocket::is_available() const
+{
+ return (socket_ref_->bsocket()->flag & SOCK_UNAVAIL) == 0;
+}
+
+/* --------------------------------------------------------------------
+ * DInputSocket inline methods.
+ */
+
+inline const InputSocketRef &DInputSocket::socket_ref() const
+{
+ return socket_ref_->as_input();
+}
+
+inline Span<const DOutputSocket *> DInputSocket::linked_sockets() const
+{
+ return linked_sockets_;
+}
+
+inline Span<const DGroupInput *> DInputSocket::linked_group_inputs() const
+{
+ return linked_group_inputs_;
+}
+
+inline bool DInputSocket::is_linked() const
+{
+ return linked_sockets_.size() > 0 || linked_group_inputs_.size() > 0;
+}
+
+/* --------------------------------------------------------------------
+ * DOutputSocket inline methods.
+ */
+
+inline const OutputSocketRef &DOutputSocket::socket_ref() const
+{
+ return socket_ref_->as_output();
+}
+
+inline Span<const DInputSocket *> DOutputSocket::linked_sockets() const
+{
+ return linked_sockets_;
+}
+
+/* --------------------------------------------------------------------
+ * DGroupInput inline methods.
+ */
+
+inline const InputSocketRef &DGroupInput::socket_ref() const
+{
+ return *socket_ref_;
+}
+
+inline bNodeSocket *DGroupInput::bsocket() const
+{
+ return socket_ref_->bsocket();
+}
+
+inline const DParentNode *DGroupInput::parent() const
+{
+ return parent_;
+}
+
+inline Span<const DInputSocket *> DGroupInput::linked_sockets() const
+{
+ return linked_sockets_;
+}
+
+inline uint DGroupInput::id() const
+{
+ return id_;
+}
+
+inline StringRefNull DGroupInput::name() const
+{
+ return socket_ref_->name();
+}
+
+/* --------------------------------------------------------------------
+ * DNode inline methods.
+ */
+
+inline const NodeRef &DNode::node_ref() const
+{
+ return *node_ref_;
+}
+
+inline const DParentNode *DNode::parent() const
+{
+ return parent_;
+}
+
+inline Span<const DInputSocket *> DNode::inputs() const
+{
+ return inputs_;
+}
+
+inline Span<const DOutputSocket *> DNode::outputs() const
+{
+ return outputs_;
+}
+
+inline const DInputSocket &DNode::input(uint index) const
+{
+ return *inputs_[index];
+}
+
+inline const DOutputSocket &DNode::output(uint index) const
+{
+ return *outputs_[index];
+}
+
+inline const DInputSocket &DNode::input(uint index, StringRef expected_name) const
+{
+ const DInputSocket &socket = *inputs_[index];
+ BLI_assert(socket.name() == expected_name);
+ UNUSED_VARS_NDEBUG(expected_name);
+ return socket;
+}
+
+inline const DOutputSocket &DNode::output(uint index, StringRef expected_name) const
+{
+ const DOutputSocket &socket = *outputs_[index];
+ BLI_assert(socket.name() == expected_name);
+ UNUSED_VARS_NDEBUG(expected_name);
+ return socket;
+}
+
+inline uint DNode::id() const
+{
+ return id_;
+}
+
+inline PointerRNA *DNode::rna() const
+{
+ return node_ref_->rna();
+}
+
+inline StringRefNull DNode::idname() const
+{
+ return node_ref_->idname();
+}
+
+inline StringRefNull DNode::name() const
+{
+ return node_ref_->name();
+}
+
+/* --------------------------------------------------------------------
+ * DParentNode inline methods.
+ */
+
+inline const DParentNode *DParentNode::parent() const
+{
+ return parent_;
+}
+
+inline const NodeRef &DParentNode::node_ref() const
+{
+ return *node_ref_;
+}
+
+inline uint DParentNode::id() const
+{
+ return id_;
+}
+
+/* --------------------------------------------------------------------
+ * DerivedNodeTree inline methods.
+ */
+
+inline Span<const DNode *> DerivedNodeTree::nodes() const
+{
+ return nodes_by_id_;
+}
+
+inline Span<const DNode *> DerivedNodeTree::nodes_by_type(StringRefNull idname) const
+{
+ const bNodeType *nodetype = nodeTypeFind(idname.data());
+ return this->nodes_by_type(nodetype);
+}
+
+inline Span<const DNode *> DerivedNodeTree::nodes_by_type(const bNodeType *nodetype) const
+{
+ const Vector<DNode *> *nodes = nodes_by_type_.lookup_ptr(nodetype);
+ if (nodes == nullptr) {
+ return {};
+ }
+ else {
+ return *nodes;
+ }
+}
+
+inline Span<const DSocket *> DerivedNodeTree::sockets() const
+{
+ return sockets_by_id_;
+}
+
+inline Span<const DInputSocket *> DerivedNodeTree::input_sockets() const
+{
+ return input_sockets_;
+}
+
+inline Span<const DOutputSocket *> DerivedNodeTree::output_sockets() const
+{
+ return output_sockets_;
+}
+
+inline Span<const DGroupInput *> DerivedNodeTree::group_inputs() const
+{
+ return group_inputs_;
+}
+
+} // namespace blender::nodes
+
+#endif /* __NOD_DERIVED_NODE_TREE_HH__ */
diff --git a/source/blender/nodes/NOD_node_tree_multi_function.hh b/source/blender/nodes/NOD_node_tree_multi_function.hh
new file mode 100644
index 00000000000..d40a630c9f2
--- /dev/null
+++ b/source/blender/nodes/NOD_node_tree_multi_function.hh
@@ -0,0 +1,388 @@
+/*
+ * 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.
+ */
+
+#ifndef __NOD_NODE_TREE_FUNCTION_HH__
+#define __NOD_NODE_TREE_FUNCTION_HH__
+
+/** \file
+ * \ingroup nodes
+ *
+ * This file allows you to generate a multi-function network from a user-generated node tree.
+ */
+
+#include "FN_multi_function_builder.hh"
+#include "FN_multi_function_network.hh"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "BLI_resource_collector.hh"
+
+namespace blender::nodes {
+
+/* Maybe this should be moved to BKE_node.h. */
+inline bool is_multi_function_data_socket(const bNodeSocket *bsocket)
+{
+ if (bsocket->typeinfo->get_mf_data_type != nullptr) {
+ BLI_assert(bsocket->typeinfo->expand_in_mf_network != nullptr);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * A MFNetworkTreeMap maps various components of a DerivedNodeTree to components of a
+ * fn::MFNetwork. This is necessary for further processing of a multi-function network that has
+ * been generated from a node tree.
+ */
+class MFNetworkTreeMap {
+ private:
+ /**
+ * Store by id instead of using a hash table to avoid unnecessary hash table lookups.
+ *
+ * Input sockets in a node tree can have multiple corresponding sockets in the generated
+ * MFNetwork. This is because nodes are allowed to expand into multiple multi-function nodes.
+ */
+ const DerivedNodeTree &tree_;
+ fn::MFNetwork &network_;
+ Array<Vector<fn::MFSocket *, 1>> sockets_by_dsocket_id_;
+ Array<fn::MFOutputSocket *> socket_by_group_input_id_;
+
+ public:
+ MFNetworkTreeMap(const DerivedNodeTree &tree, fn::MFNetwork &network)
+ : tree_(tree),
+ network_(network),
+ sockets_by_dsocket_id_(tree.sockets().size()),
+ socket_by_group_input_id_(tree.group_inputs().size(), nullptr)
+ {
+ }
+
+ const DerivedNodeTree &tree() const
+ {
+ return tree_;
+ }
+
+ const fn::MFNetwork &network() const
+ {
+ return network_;
+ }
+
+ fn::MFNetwork &network()
+ {
+ return network_;
+ }
+
+ void add(const DSocket &dsocket, fn::MFSocket &socket)
+ {
+ BLI_assert(dsocket.is_input() == socket.is_input());
+ BLI_assert(dsocket.is_input() || sockets_by_dsocket_id_[dsocket.id()].size() == 0);
+ sockets_by_dsocket_id_[dsocket.id()].append(&socket);
+ }
+
+ void add(const DInputSocket &dsocket, fn::MFInputSocket &socket)
+ {
+ sockets_by_dsocket_id_[dsocket.id()].append(&socket);
+ }
+
+ void add(const DOutputSocket &dsocket, fn::MFOutputSocket &socket)
+ {
+ /* There can be at most one matching output socket. */
+ BLI_assert(sockets_by_dsocket_id_[dsocket.id()].size() == 0);
+ sockets_by_dsocket_id_[dsocket.id()].append(&socket);
+ }
+
+ void add(Span<const DInputSocket *> dsockets, Span<fn::MFInputSocket *> sockets)
+ {
+ assert_same_size(dsockets, sockets);
+ for (uint i : dsockets.index_range()) {
+ this->add(*dsockets[i], *sockets[i]);
+ }
+ }
+
+ void add(Span<const DOutputSocket *> dsockets, Span<fn::MFOutputSocket *> sockets)
+ {
+ assert_same_size(dsockets, sockets);
+ for (uint i : dsockets.index_range()) {
+ this->add(*dsockets[i], *sockets[i]);
+ }
+ }
+
+ void add(const DGroupInput &group_input, fn::MFOutputSocket &socket)
+ {
+ BLI_assert(socket_by_group_input_id_[group_input.id()] == nullptr);
+ socket_by_group_input_id_[group_input.id()] = &socket;
+ }
+
+ void add_try_match(const DNode &dnode, fn::MFNode &node)
+ {
+ this->add_try_match(dnode.inputs(), node.inputs());
+ this->add_try_match(dnode.outputs(), node.outputs());
+ }
+
+ void add_try_match(Span<const DSocket *> dsockets, Span<fn::MFSocket *> sockets)
+ {
+ uint used_sockets = 0;
+ for (const DSocket *dsocket : dsockets) {
+ if (!dsocket->is_available()) {
+ continue;
+ }
+ if (!is_multi_function_data_socket(dsocket->bsocket())) {
+ continue;
+ }
+ fn::MFSocket *socket = sockets[used_sockets];
+ this->add(*dsocket, *socket);
+ used_sockets++;
+ }
+ }
+
+ fn::MFOutputSocket &lookup(const DGroupInput &group_input)
+ {
+ fn::MFOutputSocket *socket = socket_by_group_input_id_[group_input.id()];
+ BLI_assert(socket != nullptr);
+ return *socket;
+ }
+
+ fn::MFOutputSocket &lookup(const DOutputSocket &dsocket)
+ {
+ auto &sockets = sockets_by_dsocket_id_[dsocket.id()];
+ BLI_assert(sockets.size() == 1);
+ return sockets[0]->as_output();
+ }
+
+ Span<fn::MFInputSocket *> lookup(const DInputSocket &dsocket)
+ {
+ return sockets_by_dsocket_id_[dsocket.id()].as_span().cast<fn::MFInputSocket *>();
+ }
+
+ fn::MFInputSocket &lookup_dummy(const DInputSocket &dsocket)
+ {
+ Span<fn::MFInputSocket *> sockets = this->lookup(dsocket);
+ BLI_assert(sockets.size() == 1);
+ fn::MFInputSocket &socket = *sockets[0];
+ BLI_assert(socket.node().is_dummy());
+ return socket;
+ }
+
+ fn::MFOutputSocket &lookup_dummy(const DOutputSocket &dsocket)
+ {
+ fn::MFOutputSocket &socket = this->lookup(dsocket);
+ BLI_assert(socket.node().is_dummy());
+ return socket;
+ }
+
+ bool is_mapped(const DSocket &dsocket) const
+ {
+ return sockets_by_dsocket_id_[dsocket.id()].size() >= 1;
+ }
+};
+
+/**
+ * This data is necessary throughout the generation of a MFNetwork from a node tree.
+ */
+struct CommonMFNetworkBuilderData {
+ ResourceCollector &resources;
+ fn::MFNetwork &network;
+ MFNetworkTreeMap &network_map;
+ const DerivedNodeTree &tree;
+};
+
+class MFNetworkBuilderBase {
+ protected:
+ CommonMFNetworkBuilderData &common_;
+
+ public:
+ MFNetworkBuilderBase(CommonMFNetworkBuilderData &common) : common_(common)
+ {
+ }
+
+ /**
+ * Returns the network that is currently being built.
+ */
+ fn::MFNetwork &network()
+ {
+ return common_.network;
+ }
+
+ /**
+ * Returns the map between the node tree and the multi-function network that is being built.
+ */
+ MFNetworkTreeMap &network_map()
+ {
+ return common_.network_map;
+ }
+
+ /**
+ * Returns a resource collector that will only be destructed after the multi-function network is
+ * destructed.
+ */
+ ResourceCollector &resources()
+ {
+ return common_.resources;
+ }
+
+ /**
+ * Constructs a new function that will live at least as long as the MFNetwork.
+ */
+ template<typename T, typename... Args> T &construct_fn(Args &&... args)
+ {
+ BLI_STATIC_ASSERT((std::is_base_of_v<fn::MultiFunction, T>), "");
+ void *buffer = common_.resources.linear_allocator().allocate(sizeof(T), alignof(T));
+ T *fn = new (buffer) T(std::forward<Args>(args)...);
+ common_.resources.add(destruct_ptr<T>(fn), fn->name().data());
+ return *fn;
+ }
+};
+
+/**
+ * This class is used by socket implementations to define how an unlinked input socket is handled
+ * in a multi-function network.
+ */
+class SocketMFNetworkBuilder : public MFNetworkBuilderBase {
+ private:
+ const DSocket *dsocket_ = nullptr;
+ const DGroupInput *group_input_ = nullptr;
+ bNodeSocket *bsocket_;
+ fn::MFOutputSocket *built_socket_ = nullptr;
+
+ public:
+ SocketMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DSocket &dsocket)
+ : MFNetworkBuilderBase(common), dsocket_(&dsocket), bsocket_(dsocket.bsocket())
+ {
+ }
+
+ SocketMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DGroupInput &group_input)
+ : MFNetworkBuilderBase(common), group_input_(&group_input), bsocket_(group_input.bsocket())
+ {
+ }
+
+ /**
+ * Returns the socket that is currently being built.
+ */
+ bNodeSocket &bsocket()
+ {
+ return *bsocket_;
+ }
+
+ /**
+ * Utility method that returns bsocket->default_value for the current socket.
+ */
+ template<typename T> T *socket_default_value()
+ {
+ return (T *)bsocket_->default_value;
+ }
+
+ /**
+ * Builds a function node for that socket that outputs the given constant value.
+ */
+ template<typename T> void set_constant_value(T value)
+ {
+ const fn::MultiFunction &fn = this->construct_fn<fn::CustomMF_Constant<T>>(std::move(value));
+ this->set_generator_fn(fn);
+ }
+
+ /**
+ * Uses the first output of the given multi-function as value of the socket.
+ */
+ void set_generator_fn(const fn::MultiFunction &fn)
+ {
+ fn::MFFunctionNode &node = common_.network.add_function(fn);
+ this->set_socket(node.output(0));
+ }
+
+ /**
+ * Define a multi-function socket that outputs the value of the bsocket.
+ */
+ void set_socket(fn::MFOutputSocket &socket)
+ {
+ built_socket_ = &socket;
+ }
+
+ fn::MFOutputSocket *built_socket()
+ {
+ return built_socket_;
+ }
+};
+
+/**
+ * This class is used by node implementations to define how a user-level node expands into
+ * multi-function nodes internally.
+ */
+class NodeMFNetworkBuilder : public MFNetworkBuilderBase {
+ private:
+ const DNode &dnode_;
+
+ public:
+ NodeMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DNode &dnode)
+ : MFNetworkBuilderBase(common), dnode_(dnode)
+ {
+ }
+
+ /**
+ * Tells the builder to build a function that corresponds to the node that is being built. It
+ * will try to match up sockets.
+ */
+ template<typename T, typename... Args> T &construct_and_set_matching_fn(Args &&... args)
+ {
+ T &function = this->construct_fn<T>(std::forward<Args>(args)...);
+ this->set_matching_fn(function);
+ return function;
+ }
+
+ const fn::MultiFunction &get_not_implemented_fn()
+ {
+ return this->get_default_fn("Not Implemented (" + dnode_.name() + ")");
+ }
+
+ const fn::MultiFunction &get_default_fn(StringRef name);
+
+ const void set_not_implemented()
+ {
+ this->set_matching_fn(this->get_not_implemented_fn());
+ }
+
+ /**
+ * Tells the builder that the given function corresponds to the node that is being built. It will
+ * try to match up sockets. For that it skips unavailable and non-data sockets.
+ */
+ void set_matching_fn(const fn::MultiFunction &function)
+ {
+ fn::MFFunctionNode &node = common_.network.add_function(function);
+ common_.network_map.add_try_match(dnode_, node);
+ }
+
+ /**
+ * Returns the node that is currently being built.
+ */
+ bNode &bnode()
+ {
+ return *dnode_.node_ref().bnode();
+ }
+
+ /**
+ * Returns the node that is currently being built.
+ */
+ const DNode &dnode() const
+ {
+ return dnode_;
+ }
+};
+
+MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
+ const DerivedNodeTree &tree,
+ ResourceCollector &resources);
+
+} // namespace blender::nodes
+
+#endif /* __NOD_NODE_TREE_FUNCTION_HH__ */
diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh
new file mode 100644
index 00000000000..b5e4a768bd3
--- /dev/null
+++ b/source/blender/nodes/NOD_node_tree_ref.hh
@@ -0,0 +1,445 @@
+/*
+ * 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.
+ */
+
+#ifndef __NOD_NODE_TREE_REF_HH__
+#define __NOD_NODE_TREE_REF_HH__
+
+/** \file
+ * \ingroup nodes
+ *
+ * NodeTreeRef makes querying information about a bNodeTree more efficient. It is an immutable data
+ * structure. It should not be used after anymore, after the underlying node tree changed.
+ *
+ * The following queries are supported efficiently:
+ * - socket -> index of socket
+ * - socket -> directly linked sockets
+ * - socket -> linked sockets when skipping reroutes
+ * - socket -> node
+ * - socket/node -> rna pointer
+ * - node -> inputs/outputs
+ * - node -> tree
+ * - tree -> all nodes
+ * - tree -> all (input/output) sockets
+ * - idname -> nodes
+ *
+ * Every socket has an id. The id-space is shared between input and output sockets.
+ * When storing data per socket, it is often better to use the id as index into an array, instead
+ * of a hash table.
+ *
+ * Every node has an id as well. The same rule regarding hash tables applies.
+ *
+ * There is an utility to export this data structure as graph in dot format.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_linear_allocator.hh"
+#include "BLI_map.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_timeit.hh"
+#include "BLI_utility_mixins.hh"
+#include "BLI_vector.hh"
+
+#include "BKE_node.h"
+
+#include "DNA_node_types.h"
+
+#include "RNA_access.h"
+
+namespace blender::nodes {
+
+class SocketRef;
+class InputSocketRef;
+class OutputSocketRef;
+class NodeRef;
+class NodeTreeRef;
+
+class SocketRef : NonCopyable, NonMovable {
+ protected:
+ NodeRef *node_;
+ bNodeSocket *bsocket_;
+ bool is_input_;
+ uint id_;
+ uint index_;
+ PointerRNA rna_;
+ Vector<SocketRef *> linked_sockets_;
+ Vector<SocketRef *> directly_linked_sockets_;
+
+ friend NodeTreeRef;
+
+ public:
+ Span<const SocketRef *> linked_sockets() const;
+ Span<const SocketRef *> directly_linked_sockets() const;
+ bool is_linked() const;
+
+ const NodeRef &node() const;
+ const NodeTreeRef &tree() const;
+
+ uint id() const;
+ uint index() const;
+
+ bool is_input() const;
+ bool is_output() const;
+
+ const SocketRef &as_base() const;
+ const InputSocketRef &as_input() const;
+ const OutputSocketRef &as_output() const;
+
+ PointerRNA *rna() const;
+
+ StringRefNull idname() const;
+ StringRefNull name() const;
+
+ bNodeSocket *bsocket() const;
+ bNode *bnode() const;
+ bNodeTree *btree() const;
+};
+
+class InputSocketRef final : public SocketRef {
+ public:
+ Span<const OutputSocketRef *> linked_sockets() const;
+ Span<const OutputSocketRef *> directly_linked_sockets() const;
+};
+
+class OutputSocketRef final : public SocketRef {
+ public:
+ Span<const InputSocketRef *> linked_sockets() const;
+ Span<const InputSocketRef *> directly_linked_sockets() const;
+};
+
+class NodeRef : NonCopyable, NonMovable {
+ private:
+ NodeTreeRef *tree_;
+ bNode *bnode_;
+ PointerRNA rna_;
+ uint id_;
+ Vector<InputSocketRef *> inputs_;
+ Vector<OutputSocketRef *> outputs_;
+
+ friend NodeTreeRef;
+
+ public:
+ const NodeTreeRef &tree() const;
+
+ Span<const InputSocketRef *> inputs() const;
+ Span<const OutputSocketRef *> outputs() const;
+
+ const InputSocketRef &input(uint index) const;
+ const OutputSocketRef &output(uint index) const;
+
+ bNode *bnode() const;
+ bNodeTree *btree() const;
+
+ PointerRNA *rna() const;
+ StringRefNull idname() const;
+ StringRefNull name() const;
+
+ uint id() const;
+
+ bool is_reroute_node() const;
+ bool is_group_node() const;
+ bool is_group_input_node() const;
+ bool is_group_output_node() const;
+};
+
+class NodeTreeRef : NonCopyable, NonMovable {
+ private:
+ LinearAllocator<> allocator_;
+ bNodeTree *btree_;
+ Vector<NodeRef *> nodes_by_id_;
+ Vector<SocketRef *> sockets_by_id_;
+ Vector<InputSocketRef *> input_sockets_;
+ Vector<OutputSocketRef *> output_sockets_;
+ Map<const bNodeType *, Vector<NodeRef *>> nodes_by_type_;
+
+ public:
+ NodeTreeRef(bNodeTree *btree);
+ ~NodeTreeRef();
+
+ Span<const NodeRef *> nodes() const;
+ Span<const NodeRef *> nodes_by_type(StringRefNull idname) const;
+ Span<const NodeRef *> nodes_by_type(const bNodeType *nodetype) const;
+
+ Span<const SocketRef *> sockets() const;
+ Span<const InputSocketRef *> input_sockets() const;
+ Span<const OutputSocketRef *> output_sockets() const;
+
+ bNodeTree *btree() const;
+
+ std::string to_dot() const;
+
+ private:
+ /* Utility functions used during construction. */
+ InputSocketRef &find_input_socket(Map<bNode *, NodeRef *> &node_mapping,
+ bNode *bnode,
+ bNodeSocket *bsocket);
+ OutputSocketRef &find_output_socket(Map<bNode *, NodeRef *> &node_mapping,
+ bNode *bnode,
+ bNodeSocket *bsocket);
+ void find_targets_skipping_reroutes(OutputSocketRef &socket_ref, Vector<SocketRef *> &r_targets);
+};
+
+/* --------------------------------------------------------------------
+ * SocketRef inline methods.
+ */
+
+inline Span<const SocketRef *> SocketRef::linked_sockets() const
+{
+ return linked_sockets_;
+}
+
+inline Span<const SocketRef *> SocketRef::directly_linked_sockets() const
+{
+ return directly_linked_sockets_;
+}
+
+inline bool SocketRef::is_linked() const
+{
+ return linked_sockets_.size() > 0;
+}
+
+inline const NodeRef &SocketRef::node() const
+{
+ return *node_;
+}
+
+inline const NodeTreeRef &SocketRef::tree() const
+{
+ return node_->tree();
+}
+
+inline uint SocketRef::id() const
+{
+ return id_;
+}
+
+inline uint SocketRef::index() const
+{
+ return index_;
+}
+
+inline bool SocketRef::is_input() const
+{
+ return is_input_;
+}
+
+inline bool SocketRef::is_output() const
+{
+ return !is_input_;
+}
+
+inline const SocketRef &SocketRef::as_base() const
+{
+ return *this;
+}
+
+inline const InputSocketRef &SocketRef::as_input() const
+{
+ BLI_assert(this->is_input());
+ return *(const InputSocketRef *)this;
+}
+
+inline const OutputSocketRef &SocketRef::as_output() const
+{
+ BLI_assert(this->is_output());
+ return *(const OutputSocketRef *)this;
+}
+
+inline PointerRNA *SocketRef::rna() const
+{
+ return const_cast<PointerRNA *>(&rna_);
+}
+
+inline StringRefNull SocketRef::idname() const
+{
+ return bsocket_->idname;
+}
+
+inline StringRefNull SocketRef::name() const
+{
+ return bsocket_->name;
+}
+
+inline bNodeSocket *SocketRef::bsocket() const
+{
+ return bsocket_;
+}
+
+inline bNode *SocketRef::bnode() const
+{
+ return node_->bnode();
+}
+
+inline bNodeTree *SocketRef::btree() const
+{
+ return node_->btree();
+}
+
+/* --------------------------------------------------------------------
+ * InputSocketRef inline methods.
+ */
+
+inline Span<const OutputSocketRef *> InputSocketRef::linked_sockets() const
+{
+ return linked_sockets_.as_span().cast<const OutputSocketRef *>();
+}
+
+inline Span<const OutputSocketRef *> InputSocketRef::directly_linked_sockets() const
+{
+ return directly_linked_sockets_.as_span().cast<const OutputSocketRef *>();
+}
+
+/* --------------------------------------------------------------------
+ * OutputSocketRef inline methods.
+ */
+
+inline Span<const InputSocketRef *> OutputSocketRef::linked_sockets() const
+{
+ return linked_sockets_.as_span().cast<const InputSocketRef *>();
+}
+
+inline Span<const InputSocketRef *> OutputSocketRef::directly_linked_sockets() const
+{
+ return directly_linked_sockets_.as_span().cast<const InputSocketRef *>();
+}
+
+/* --------------------------------------------------------------------
+ * NodeRef inline methods.
+ */
+
+inline const NodeTreeRef &NodeRef::tree() const
+{
+ return *tree_;
+}
+
+inline Span<const InputSocketRef *> NodeRef::inputs() const
+{
+ return inputs_;
+}
+
+inline Span<const OutputSocketRef *> NodeRef::outputs() const
+{
+ return outputs_;
+}
+
+inline const InputSocketRef &NodeRef::input(uint index) const
+{
+ return *inputs_[index];
+}
+
+inline const OutputSocketRef &NodeRef::output(uint index) const
+{
+ return *outputs_[index];
+}
+
+inline bNode *NodeRef::bnode() const
+{
+ return bnode_;
+}
+
+inline bNodeTree *NodeRef::btree() const
+{
+ return tree_->btree();
+}
+
+inline PointerRNA *NodeRef::rna() const
+{
+ return const_cast<PointerRNA *>(&rna_);
+}
+
+inline StringRefNull NodeRef::idname() const
+{
+ return bnode_->idname;
+}
+
+inline StringRefNull NodeRef::name() const
+{
+ return bnode_->name;
+}
+
+inline uint NodeRef::id() const
+{
+ return id_;
+}
+
+inline bool NodeRef::is_reroute_node() const
+{
+ return bnode_->type == NODE_REROUTE;
+}
+
+inline bool NodeRef::is_group_node() const
+{
+ return bnode_->type == NODE_GROUP;
+}
+
+inline bool NodeRef::is_group_input_node() const
+{
+ return bnode_->type == NODE_GROUP_INPUT;
+}
+
+inline bool NodeRef::is_group_output_node() const
+{
+ return bnode_->type == NODE_GROUP_OUTPUT;
+}
+
+/* --------------------------------------------------------------------
+ * NodeRef inline methods.
+ */
+
+inline Span<const NodeRef *> NodeTreeRef::nodes() const
+{
+ return nodes_by_id_;
+}
+
+inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(StringRefNull idname) const
+{
+ const bNodeType *nodetype = nodeTypeFind(idname.data());
+ return this->nodes_by_type(nodetype);
+}
+
+inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(const bNodeType *nodetype) const
+{
+ const Vector<NodeRef *> *nodes = nodes_by_type_.lookup_ptr(nodetype);
+ if (nodes == nullptr) {
+ return {};
+ }
+ else {
+ return *nodes;
+ }
+}
+
+inline Span<const SocketRef *> NodeTreeRef::sockets() const
+{
+ return sockets_by_id_;
+}
+
+inline Span<const InputSocketRef *> NodeTreeRef::input_sockets() const
+{
+ return input_sockets_;
+}
+
+inline Span<const OutputSocketRef *> NodeTreeRef::output_sockets() const
+{
+ return output_sockets_;
+}
+
+inline bNodeTree *NodeTreeRef::btree() const
+{
+ return btree_;
+}
+
+} // namespace blender::nodes
+
+#endif /* __NOD_NODE_TREE_REF_HH__ */
diff --git a/source/blender/nodes/function/node_function_util.hh b/source/blender/nodes/function/node_function_util.hh
index 938cb5dd593..8e09ab0f24f 100644
--- a/source/blender/nodes/function/node_function_util.hh
+++ b/source/blender/nodes/function/node_function_util.hh
@@ -27,11 +27,11 @@
#include "DNA_node_types.h"
#include "BKE_node.h"
-#include "BKE_node_tree_multi_function.hh"
#include "BLT_translation.h"
#include "NOD_function.h"
+#include "NOD_node_tree_multi_function.hh"
#include "node_util.h"
diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
index 3a145311a08..231771abbfa 100644
--- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
+++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
@@ -71,7 +71,7 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &bnode)
return blender::fn::dummy_multi_function;
}
-static void node_boolean_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder)
+static void node_boolean_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode());
builder.set_matching_fn(fn);
diff --git a/source/blender/nodes/function/nodes/node_fn_combine_strings.cc b/source/blender/nodes/function/nodes/node_fn_combine_strings.cc
index a880933bc12..a545c4f0749 100644
--- a/source/blender/nodes/function/nodes/node_fn_combine_strings.cc
+++ b/source/blender/nodes/function/nodes/node_fn_combine_strings.cc
@@ -28,7 +28,7 @@ static bNodeSocketTemplate fn_node_combine_strings_out[] = {
};
static void fn_node_combine_strings_expand_in_mf_network(
- blender::bke::NodeMFNetworkBuilder &builder)
+ blender::nodes::NodeMFNetworkBuilder &builder)
{
static blender::fn::CustomMF_SI_SI_SO<std::string, std::string, std::string> combine_fn{
"Combine Strings", [](const std::string &a, const std::string &b) { return a + b; }};
diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc
index fb2c4d88caf..f8bd9a30940 100644
--- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc
+++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc
@@ -90,7 +90,7 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &node)
return blender::fn::dummy_multi_function;
}
-static void node_float_compare_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder)
+static void node_float_compare_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode());
builder.set_matching_fn(fn);
diff --git a/source/blender/nodes/function/nodes/node_fn_group_instance_id.cc b/source/blender/nodes/function/nodes/node_fn_group_instance_id.cc
index c61c941ee0d..1e22cde721d 100644
--- a/source/blender/nodes/function/nodes/node_fn_group_instance_id.cc
+++ b/source/blender/nodes/function/nodes/node_fn_group_instance_id.cc
@@ -22,11 +22,11 @@ static bNodeSocketTemplate fn_node_group_instance_id_out[] = {
};
static void fn_node_group_instance_id_expand_in_mf_network(
- blender::bke::NodeMFNetworkBuilder &builder)
+ blender::nodes::NodeMFNetworkBuilder &builder)
{
- const blender::bke::DNode &node = builder.dnode();
+ const blender::nodes::DNode &node = builder.dnode();
std::string id = "/";
- for (const blender::bke::DParentNode *parent = node.parent(); parent;
+ for (const blender::nodes::DParentNode *parent = node.parent(); parent;
parent = parent->parent()) {
id = "/" + parent->node_ref().name() + id;
}
diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc
new file mode 100644
index 00000000000..daec67b53a9
--- /dev/null
+++ b/source/blender/nodes/intern/derived_node_tree.cc
@@ -0,0 +1,441 @@
+/*
+ * 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 "NOD_derived_node_tree.hh"
+
+#include "BLI_dot_export.hh"
+
+#define UNINITIALIZED_ID UINT32_MAX
+
+namespace blender::nodes {
+
+static const NodeTreeRef &get_tree_ref(NodeTreeRefMap &node_tree_refs, bNodeTree *btree)
+{
+ return *node_tree_refs.lookup_or_add_cb(btree,
+ [&]() { return std::make_unique<NodeTreeRef>(btree); });
+}
+
+DerivedNodeTree::DerivedNodeTree(bNodeTree *btree, NodeTreeRefMap &node_tree_refs) : btree_(btree)
+{
+ const NodeTreeRef &main_tree_ref = get_tree_ref(node_tree_refs, btree);
+
+ Vector<DNode *> all_nodes;
+ Vector<DGroupInput *> all_group_inputs;
+ Vector<DParentNode *> all_parent_nodes;
+
+ this->insert_nodes_and_links_in_id_order(main_tree_ref, nullptr, all_nodes);
+ this->expand_groups(all_nodes, all_group_inputs, all_parent_nodes, node_tree_refs);
+ this->remove_expanded_group_interfaces(all_nodes);
+ this->remove_unused_group_inputs(all_group_inputs);
+ this->store_in_this_and_init_ids(
+ std::move(all_nodes), std::move(all_group_inputs), std::move(all_parent_nodes));
+}
+
+BLI_NOINLINE void DerivedNodeTree::insert_nodes_and_links_in_id_order(const NodeTreeRef &tree_ref,
+ DParentNode *parent,
+ Vector<DNode *> &all_nodes)
+{
+ Array<DSocket *, 64> sockets_map(tree_ref.sockets().size());
+
+ /* Insert nodes. */
+ for (const NodeRef *node_ref : tree_ref.nodes()) {
+ DNode &node = this->create_node(*node_ref, parent, sockets_map);
+ all_nodes.append(&node);
+ }
+
+ /* Insert links. */
+ for (const NodeRef *node_ref : tree_ref.nodes()) {
+ for (const InputSocketRef *to_socket_ref : node_ref->inputs()) {
+ DInputSocket *to_socket = (DInputSocket *)sockets_map[to_socket_ref->id()];
+ for (const OutputSocketRef *from_socket_ref : to_socket_ref->linked_sockets()) {
+ DOutputSocket *from_socket = (DOutputSocket *)sockets_map[from_socket_ref->id()];
+ to_socket->linked_sockets_.append(from_socket);
+ from_socket->linked_sockets_.append(to_socket);
+ }
+ }
+ }
+}
+
+DNode &DerivedNodeTree::create_node(const NodeRef &node_ref,
+ DParentNode *parent,
+ MutableSpan<DSocket *> r_sockets_map)
+{
+ DNode &node = *allocator_.construct<DNode>();
+ node.node_ref_ = &node_ref;
+ node.parent_ = parent;
+ node.id_ = UNINITIALIZED_ID;
+
+ node.inputs_ = allocator_.construct_elements_and_pointer_array<DInputSocket>(
+ node_ref.inputs().size());
+ node.outputs_ = allocator_.construct_elements_and_pointer_array<DOutputSocket>(
+ node_ref.outputs().size());
+
+ for (uint i : node.inputs_.index_range()) {
+ const InputSocketRef &socket_ref = node_ref.input(i);
+ DInputSocket &socket = *node.inputs_[i];
+
+ socket.id_ = UNINITIALIZED_ID;
+ socket.node_ = &node;
+ socket.socket_ref_ = &socket_ref;
+
+ r_sockets_map[socket_ref.id()] = &socket;
+ }
+
+ for (uint i : node.outputs_.index_range()) {
+ const OutputSocketRef &socket_ref = node_ref.output(i);
+ DOutputSocket &socket = *node.outputs_[i];
+
+ socket.id_ = UNINITIALIZED_ID;
+ socket.node_ = &node;
+ socket.socket_ref_ = &socket_ref;
+
+ r_sockets_map[socket_ref.id()] = &socket;
+ }
+
+ return node;
+}
+
+BLI_NOINLINE void DerivedNodeTree::expand_groups(Vector<DNode *> &all_nodes,
+ Vector<DGroupInput *> &all_group_inputs,
+ Vector<DParentNode *> &all_parent_nodes,
+ NodeTreeRefMap &node_tree_refs)
+{
+ for (uint i = 0; i < all_nodes.size(); i++) {
+ DNode &node = *all_nodes[i];
+ if (node.node_ref_->is_group_node()) {
+ this->expand_group_node(node, all_nodes, all_group_inputs, all_parent_nodes, node_tree_refs);
+ }
+ }
+}
+
+BLI_NOINLINE void DerivedNodeTree::expand_group_node(DNode &group_node,
+ Vector<DNode *> &all_nodes,
+ Vector<DGroupInput *> &all_group_inputs,
+ Vector<DParentNode *> &all_parent_nodes,
+ NodeTreeRefMap &node_tree_refs)
+{
+ const NodeRef &group_node_ref = *group_node.node_ref_;
+ BLI_assert(group_node_ref.is_group_node());
+
+ bNodeTree *btree = (bNodeTree *)group_node_ref.bnode()->id;
+ if (btree == nullptr) {
+ return;
+ }
+
+ const NodeTreeRef &group_ref = get_tree_ref(node_tree_refs, btree);
+
+ DParentNode &parent = *allocator_.construct<DParentNode>();
+ parent.id_ = all_parent_nodes.append_and_get_index(&parent);
+ parent.parent_ = group_node.parent_;
+ parent.node_ref_ = &group_node_ref;
+
+ this->insert_nodes_and_links_in_id_order(group_ref, &parent, all_nodes);
+ Span<DNode *> new_nodes_by_id = all_nodes.as_span().take_back(group_ref.nodes().size());
+
+ this->create_group_inputs_for_unlinked_inputs(group_node, all_group_inputs);
+ this->relink_group_inputs(group_ref, new_nodes_by_id, group_node);
+ this->relink_group_outputs(group_ref, new_nodes_by_id, group_node);
+}
+
+BLI_NOINLINE void DerivedNodeTree::create_group_inputs_for_unlinked_inputs(
+ DNode &node, Vector<DGroupInput *> &all_group_inputs)
+{
+ for (DInputSocket *input_socket : node.inputs_) {
+ if (input_socket->is_linked()) {
+ continue;
+ }
+
+ DGroupInput &group_input = *allocator_.construct<DGroupInput>();
+ group_input.id_ = UNINITIALIZED_ID;
+ group_input.socket_ref_ = &input_socket->socket_ref();
+ group_input.parent_ = node.parent_;
+
+ group_input.linked_sockets_.append(input_socket);
+ input_socket->linked_group_inputs_.append(&group_input);
+ all_group_inputs.append(&group_input);
+ }
+}
+
+BLI_NOINLINE void DerivedNodeTree::relink_group_inputs(const NodeTreeRef &group_ref,
+ Span<DNode *> nodes_by_id,
+ DNode &group_node)
+{
+ Span<const NodeRef *> node_refs = group_ref.nodes_by_type("NodeGroupInput");
+ if (node_refs.size() == 0) {
+ return;
+ }
+ /* TODO: Pick correct group input node if there are more than one. */
+ const NodeRef &input_node_ref = *node_refs[0];
+ DNode &input_node = *nodes_by_id[input_node_ref.id()];
+
+ uint input_amount = group_node.inputs().size();
+ BLI_assert(input_amount == input_node_ref.outputs().size() - 1);
+
+ for (uint input_index : IndexRange(input_amount)) {
+ DInputSocket *outside_group = group_node.inputs_[input_index];
+ DOutputSocket *inside_group = input_node.outputs_[input_index];
+
+ for (DOutputSocket *outside_connected : outside_group->linked_sockets_) {
+ outside_connected->linked_sockets_.remove_first_occurrence_and_reorder(outside_group);
+ }
+
+ for (DGroupInput *outside_connected : outside_group->linked_group_inputs_) {
+ outside_connected->linked_sockets_.remove_first_occurrence_and_reorder(outside_group);
+ }
+
+ for (DInputSocket *inside_connected : inside_group->linked_sockets_) {
+ inside_connected->linked_sockets_.remove_first_occurrence_and_reorder(inside_group);
+
+ for (DOutputSocket *outside_connected : outside_group->linked_sockets_) {
+ inside_connected->linked_sockets_.append(outside_connected);
+ outside_connected->linked_sockets_.append(inside_connected);
+ }
+
+ for (DGroupInput *outside_connected : outside_group->linked_group_inputs_) {
+ inside_connected->linked_group_inputs_.append(outside_connected);
+ outside_connected->linked_sockets_.append(inside_connected);
+ }
+ }
+
+ inside_group->linked_sockets_.clear();
+ outside_group->linked_sockets_.clear();
+ outside_group->linked_group_inputs_.clear();
+ }
+}
+
+BLI_NOINLINE void DerivedNodeTree::relink_group_outputs(const NodeTreeRef &group_ref,
+ Span<DNode *> nodes_by_id,
+ DNode &group_node)
+{
+ Span<const NodeRef *> node_refs = group_ref.nodes_by_type("NodeGroupOutput");
+ if (node_refs.size() == 0) {
+ return;
+ }
+ /* TODO: Pick correct group output node if there are more than one. */
+ const NodeRef &output_node_ref = *node_refs[0];
+ DNode &output_node = *nodes_by_id[output_node_ref.id()];
+
+ uint output_amount = group_node.outputs().size();
+ BLI_assert(output_amount == output_node_ref.inputs().size() - 1);
+
+ for (uint output_index : IndexRange(output_amount)) {
+ DOutputSocket *outside_group = group_node.outputs_[output_index];
+ DInputSocket *inside_group = output_node.inputs_[output_index];
+
+ for (DInputSocket *outside_connected : outside_group->linked_sockets_) {
+ outside_connected->linked_sockets_.remove_first_occurrence_and_reorder(outside_group);
+ }
+
+ for (DOutputSocket *inside_connected : inside_group->linked_sockets_) {
+ inside_connected->linked_sockets_.remove_first_occurrence_and_reorder(inside_group);
+
+ for (DInputSocket *outside_connected : outside_group->linked_sockets_) {
+ inside_connected->linked_sockets_.append(outside_connected);
+ outside_connected->linked_sockets_.append(inside_connected);
+ }
+ }
+
+ for (DGroupInput *inside_connected : inside_group->linked_group_inputs_) {
+ inside_connected->linked_sockets_.remove_first_occurrence_and_reorder(inside_group);
+
+ for (DInputSocket *outside_connected : outside_group->linked_sockets_) {
+ inside_connected->linked_sockets_.append(outside_connected);
+ outside_connected->linked_group_inputs_.append(inside_connected);
+ }
+ }
+
+ outside_group->linked_sockets_.clear();
+ inside_group->linked_sockets_.clear();
+ }
+}
+
+BLI_NOINLINE void DerivedNodeTree::remove_expanded_group_interfaces(Vector<DNode *> &all_nodes)
+{
+ int index = 0;
+ while (index < all_nodes.size()) {
+ DNode &node = *all_nodes[index];
+ const NodeRef &node_ref = *node.node_ref_;
+ if (node_ref.is_group_node() ||
+ (node.parent_ != nullptr &&
+ (node_ref.is_group_input_node() || node_ref.is_group_output_node()))) {
+ all_nodes.remove_and_reorder(index);
+ node.destruct_with_sockets();
+ }
+ else {
+ index++;
+ }
+ }
+}
+
+BLI_NOINLINE void DerivedNodeTree::remove_unused_group_inputs(
+ Vector<DGroupInput *> &all_group_inputs)
+{
+ int index = 0;
+ while (index < all_group_inputs.size()) {
+ DGroupInput &group_input = *all_group_inputs[index];
+ if (group_input.linked_sockets_.is_empty()) {
+ all_group_inputs.remove_and_reorder(index);
+ group_input.~DGroupInput();
+ }
+ else {
+ index++;
+ }
+ }
+}
+
+void DNode::destruct_with_sockets()
+{
+ for (DInputSocket *socket : inputs_) {
+ socket->~DInputSocket();
+ }
+ for (DOutputSocket *socket : outputs_) {
+ socket->~DOutputSocket();
+ }
+ this->~DNode();
+}
+
+BLI_NOINLINE void DerivedNodeTree::store_in_this_and_init_ids(
+ Vector<DNode *> &&all_nodes,
+ Vector<DGroupInput *> &&all_group_inputs,
+ Vector<DParentNode *> &&all_parent_nodes)
+{
+ nodes_by_id_ = std::move(all_nodes);
+ group_inputs_ = std::move(all_group_inputs);
+ parent_nodes_ = std::move(all_parent_nodes);
+
+ for (uint node_index : nodes_by_id_.index_range()) {
+ DNode *node = nodes_by_id_[node_index];
+ node->id_ = node_index;
+
+ const bNodeType *nodetype = node->node_ref_->bnode()->typeinfo;
+ nodes_by_type_.lookup_or_add_default(nodetype).append(node);
+
+ for (DInputSocket *socket : node->inputs_) {
+ socket->id_ = sockets_by_id_.append_and_get_index(socket);
+ input_sockets_.append(socket);
+ }
+ for (DOutputSocket *socket : node->outputs_) {
+ socket->id_ = sockets_by_id_.append_and_get_index(socket);
+ output_sockets_.append(socket);
+ }
+ }
+
+ for (uint i : group_inputs_.index_range()) {
+ group_inputs_[i]->id_ = i;
+ }
+}
+
+DerivedNodeTree::~DerivedNodeTree()
+{
+ for (DInputSocket *socket : input_sockets_) {
+ socket->~DInputSocket();
+ }
+ for (DOutputSocket *socket : output_sockets_) {
+ socket->~DOutputSocket();
+ }
+ for (DNode *node : nodes_by_id_) {
+ node->~DNode();
+ }
+ for (DGroupInput *group_input : group_inputs_) {
+ group_input->~DGroupInput();
+ }
+ for (DParentNode *parent : parent_nodes_) {
+ parent->~DParentNode();
+ }
+}
+
+static dot::Cluster *get_cluster_for_parent(dot::DirectedGraph &graph,
+ Map<const DParentNode *, dot::Cluster *> &clusters,
+ const DParentNode *parent)
+{
+ if (parent == nullptr) {
+ return nullptr;
+ }
+ return clusters.lookup_or_add_cb(parent, [&]() {
+ dot::Cluster *parent_cluster = get_cluster_for_parent(graph, clusters, parent->parent());
+ bNodeTree *btree = (bNodeTree *)parent->node_ref().bnode()->id;
+ dot::Cluster *new_cluster = &graph.new_cluster(parent->node_ref().name() + " / " +
+ StringRef(btree->id.name + 2));
+ new_cluster->set_parent_cluster(parent_cluster);
+ return new_cluster;
+ });
+}
+
+std::string DerivedNodeTree::to_dot() const
+{
+ dot::DirectedGraph digraph;
+ digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
+
+ Map<const DNode *, dot::NodeWithSocketsRef> dot_nodes;
+ Map<const DGroupInput *, dot::NodeWithSocketsRef> dot_group_inputs;
+ Map<const DParentNode *, dot::Cluster *> dot_clusters;
+
+ for (const DNode *node : nodes_by_id_) {
+ dot::Node &dot_node = digraph.new_node("");
+ dot_node.set_background_color("white");
+
+ Vector<std::string> input_names;
+ for (const DInputSocket *socket : node->inputs()) {
+ input_names.append(socket->name());
+ }
+ Vector<std::string> output_names;
+ for (const DOutputSocket *socket : node->outputs()) {
+ output_names.append(socket->name());
+ }
+
+ dot_nodes.add_new(node,
+ dot::NodeWithSocketsRef(dot_node, node->name(), input_names, output_names));
+
+ dot::Cluster *cluster = get_cluster_for_parent(digraph, dot_clusters, node->parent());
+ dot_node.set_parent_cluster(cluster);
+ }
+
+ for (const DGroupInput *group_input : group_inputs_) {
+ dot::Node &dot_node = digraph.new_node("");
+ dot_node.set_background_color("white");
+
+ std::string group_input_name = group_input->name();
+ dot_group_inputs.add_new(
+ group_input, dot::NodeWithSocketsRef(dot_node, "Group Input", {}, {group_input_name}));
+
+ dot::Cluster *cluster = get_cluster_for_parent(digraph, dot_clusters, group_input->parent());
+ dot_node.set_parent_cluster(cluster);
+ }
+
+ for (const DNode *to_node : nodes_by_id_) {
+ dot::NodeWithSocketsRef &to_dot_node = dot_nodes.lookup(to_node);
+
+ for (const DInputSocket *to_socket : to_node->inputs()) {
+ for (const DOutputSocket *from_socket : to_socket->linked_sockets()) {
+ const DNode *from_node = &from_socket->node();
+ dot::NodeWithSocketsRef &from_dot_node = dot_nodes.lookup(from_node);
+
+ digraph.new_edge(from_dot_node.output(from_socket->index()),
+ to_dot_node.input(to_socket->index()));
+ }
+ for (const DGroupInput *group_input : to_socket->linked_group_inputs()) {
+ dot::NodeWithSocketsRef &from_dot_node = dot_group_inputs.lookup(group_input);
+
+ digraph.new_edge(from_dot_node.output(0), to_dot_node.input(to_socket->index()));
+ }
+ }
+ }
+
+ digraph.set_random_cluster_bgcolors();
+ return digraph.to_dot_string();
+}
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc
index 02124465dda..3a82438a211 100644
--- a/source/blender/nodes/intern/node_socket.cc
+++ b/source/blender/nodes/intern/node_socket.cc
@@ -34,13 +34,13 @@
#include "BKE_lib_id.h"
#include "BKE_node.h"
-#include "BKE_node_tree_multi_function.hh"
#include "RNA_access.h"
#include "RNA_types.h"
#include "MEM_guardedalloc.h"
+#include "NOD_node_tree_multi_function.hh"
#include "NOD_socket.h"
struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
@@ -517,7 +517,7 @@ static bNodeSocketType *make_socket_type_bool()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE);
socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<bool>(); };
- socktype->expand_in_mf_network = [](blender::bke::SocketMFNetworkBuilder &builder) {
+ socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
bool value = builder.socket_default_value<bNodeSocketValueBoolean>()->value;
builder.set_constant_value(value);
};
@@ -528,7 +528,7 @@ static bNodeSocketType *make_socket_type_float(PropertySubType subtype)
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_FLOAT, subtype);
socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<float>(); };
- socktype->expand_in_mf_network = [](blender::bke::SocketMFNetworkBuilder &builder) {
+ socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
float value = builder.socket_default_value<bNodeSocketValueFloat>()->value;
builder.set_constant_value(value);
};
@@ -539,7 +539,7 @@ static bNodeSocketType *make_socket_type_int(PropertySubType subtype)
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_INT, subtype);
socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<int>(); };
- socktype->expand_in_mf_network = [](blender::bke::SocketMFNetworkBuilder &builder) {
+ socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
int value = builder.socket_default_value<bNodeSocketValueInt>()->value;
builder.set_constant_value(value);
};
@@ -552,7 +552,7 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype)
socktype->get_mf_data_type = []() {
return blender::fn::MFDataType::ForSingle<blender::float3>();
};
- socktype->expand_in_mf_network = [](blender::bke::SocketMFNetworkBuilder &builder) {
+ socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
blender::float3 value = builder.socket_default_value<bNodeSocketValueVector>()->value;
builder.set_constant_value(value);
};
@@ -565,7 +565,7 @@ static bNodeSocketType *make_socket_type_rgba()
socktype->get_mf_data_type = []() {
return blender::fn::MFDataType::ForSingle<blender::Color4f>();
};
- socktype->expand_in_mf_network = [](blender::bke::SocketMFNetworkBuilder &builder) {
+ socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
blender::Color4f value = builder.socket_default_value<bNodeSocketValueRGBA>()->value;
builder.set_constant_value(value);
};
@@ -576,7 +576,7 @@ static bNodeSocketType *make_socket_type_string()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_STRING, PROP_NONE);
socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<std::string>(); };
- socktype->expand_in_mf_network = [](blender::bke::SocketMFNetworkBuilder &builder) {
+ socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
std::string value = builder.socket_default_value<bNodeSocketValueString>()->value;
builder.set_constant_value(value);
};
diff --git a/source/blender/nodes/intern/node_tree_multi_function.cc b/source/blender/nodes/intern/node_tree_multi_function.cc
new file mode 100644
index 00000000000..f77b19354a4
--- /dev/null
+++ b/source/blender/nodes/intern/node_tree_multi_function.cc
@@ -0,0 +1,344 @@
+/*
+ * 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 "NOD_node_tree_multi_function.hh"
+
+#include "BLI_color.hh"
+#include "BLI_float3.hh"
+
+namespace blender::nodes {
+
+/* Maybe this should be moved to BKE_node.h. */
+static std::optional<fn::MFDataType> try_get_multi_function_data_type_of_socket(
+ const bNodeSocket *bsocket)
+{
+ if (bsocket->typeinfo->get_mf_data_type == nullptr) {
+ return {};
+ }
+ return bsocket->typeinfo->get_mf_data_type();
+}
+
+const fn::MultiFunction &NodeMFNetworkBuilder::get_default_fn(StringRef name)
+{
+ Vector<fn::MFDataType, 10> input_types;
+ Vector<fn::MFDataType, 10> output_types;
+
+ for (const DInputSocket *dsocket : dnode_.inputs()) {
+ if (dsocket->is_available()) {
+ std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
+ dsocket->bsocket());
+ if (data_type.has_value()) {
+ input_types.append(*data_type);
+ }
+ }
+ }
+ for (const DOutputSocket *dsocket : dnode_.outputs()) {
+ if (dsocket->is_available()) {
+ std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
+ dsocket->bsocket());
+ if (data_type.has_value()) {
+ output_types.append(*data_type);
+ }
+ }
+ }
+
+ const fn::MultiFunction &fn = this->construct_fn<fn::CustomMF_DefaultOutput>(
+ name, input_types, output_types);
+ return fn;
+}
+
+static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &dnode)
+{
+ constexpr uint stack_capacity = 10;
+
+ Vector<fn::MFDataType, stack_capacity> input_types;
+ Vector<StringRef, stack_capacity> input_names;
+ Vector<const DInputSocket *, stack_capacity> input_dsockets;
+
+ for (const DInputSocket *dsocket : dnode.inputs()) {
+ if (dsocket->is_available()) {
+ std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
+ dsocket->bsocket());
+ if (data_type.has_value()) {
+ input_types.append(*data_type);
+ input_names.append(dsocket->name());
+ input_dsockets.append(dsocket);
+ }
+ }
+ }
+
+ Vector<fn::MFDataType, stack_capacity> output_types;
+ Vector<StringRef, stack_capacity> output_names;
+ Vector<const DOutputSocket *, stack_capacity> output_dsockets;
+
+ for (const DOutputSocket *dsocket : dnode.outputs()) {
+ if (dsocket->is_available()) {
+ std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket(
+ dsocket->bsocket());
+ if (data_type.has_value()) {
+ output_types.append(*data_type);
+ output_names.append(dsocket->name());
+ output_dsockets.append(dsocket);
+ }
+ }
+ }
+
+ fn::MFDummyNode &dummy_node = common.network.add_dummy(
+ dnode.name(), input_types, output_types, input_names, output_names);
+
+ common.network_map.add(input_dsockets, dummy_node.inputs());
+ common.network_map.add(output_dsockets, dummy_node.outputs());
+}
+
+static bool has_data_sockets(const DNode &dnode)
+{
+ for (const DInputSocket *socket : dnode.inputs()) {
+ if (is_multi_function_data_socket(socket->bsocket())) {
+ return true;
+ }
+ }
+ for (const DOutputSocket *socket : dnode.outputs()) {
+ if (is_multi_function_data_socket(socket->bsocket())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Expands all function nodes in the multi-function network. Nodes that don't have an expand
+ * function, but do have data sockets, will get corresponding dummy nodes.
+ */
+static void insert_nodes(CommonMFNetworkBuilderData &common)
+{
+ for (const DNode *dnode : common.tree.nodes()) {
+ const bNodeType *node_type = dnode->node_ref().bnode()->typeinfo;
+ if (node_type->expand_in_mf_network != nullptr) {
+ NodeMFNetworkBuilder builder{common, *dnode};
+ node_type->expand_in_mf_network(builder);
+ }
+ else if (has_data_sockets(*dnode)) {
+ insert_dummy_node(common, *dnode);
+ }
+ }
+}
+
+static void insert_group_inputs(CommonMFNetworkBuilderData &common)
+{
+ for (const DGroupInput *group_input : common.tree.group_inputs()) {
+ bNodeSocket *bsocket = group_input->bsocket();
+ if (is_multi_function_data_socket(bsocket)) {
+ bNodeSocketType *socktype = bsocket->typeinfo;
+ BLI_assert(socktype->expand_in_mf_network != nullptr);
+
+ SocketMFNetworkBuilder builder{common, *group_input};
+ socktype->expand_in_mf_network(builder);
+
+ fn::MFOutputSocket *from_socket = builder.built_socket();
+ BLI_assert(from_socket != nullptr);
+ common.network_map.add(*group_input, *from_socket);
+ }
+ }
+}
+
+static fn::MFOutputSocket *try_find_origin(CommonMFNetworkBuilderData &common,
+ const DInputSocket &to_dsocket)
+{
+ Span<const DOutputSocket *> from_dsockets = to_dsocket.linked_sockets();
+ Span<const DGroupInput *> from_group_inputs = to_dsocket.linked_group_inputs();
+ uint total_linked_amount = from_dsockets.size() + from_group_inputs.size();
+ BLI_assert(total_linked_amount <= 1);
+
+ if (total_linked_amount == 0) {
+ return nullptr;
+ }
+
+ if (from_dsockets.size() == 1) {
+ const DOutputSocket &from_dsocket = *from_dsockets[0];
+ if (!from_dsocket.is_available()) {
+ return nullptr;
+ }
+ if (is_multi_function_data_socket(from_dsocket.bsocket())) {
+ return &common.network_map.lookup(from_dsocket);
+ }
+ return nullptr;
+ }
+ else {
+ const DGroupInput &from_group_input = *from_group_inputs[0];
+ if (is_multi_function_data_socket(from_group_input.bsocket())) {
+ return &common.network_map.lookup(from_group_input);
+ }
+ return nullptr;
+ }
+}
+
+using ImplicitConversionsMap =
+ Map<std::pair<fn::MFDataType, fn::MFDataType>, const fn::MultiFunction *>;
+
+template<typename From, typename To>
+static void add_implicit_conversion(ImplicitConversionsMap &map)
+{
+ static fn::CustomMF_Convert<From, To> function;
+ map.add({fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>()}, &function);
+}
+
+template<typename From, typename To, typename ConversionF>
+static void add_implicit_conversion(ImplicitConversionsMap &map,
+ StringRef name,
+ ConversionF conversion)
+{
+ static fn::CustomMF_SI_SO<From, To> function{name, conversion};
+ map.add({fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>()}, &function);
+}
+
+static ImplicitConversionsMap get_implicit_conversions()
+{
+ ImplicitConversionsMap conversions;
+ add_implicit_conversion<float, int32_t>(conversions);
+ add_implicit_conversion<float, float3>(conversions);
+ add_implicit_conversion<int32_t, float>(conversions);
+ add_implicit_conversion<float3, float>(
+ conversions, "Vector Length", [](float3 a) { return a.length(); });
+ add_implicit_conversion<int32_t, float3>(
+ conversions, "int32 to float3", [](int32_t a) { return float3((float)a); });
+ add_implicit_conversion<float3, Color4f>(
+ conversions, "float3 to Color4f", [](float3 a) { return Color4f(a.x, a.y, a.z, 1.0f); });
+ add_implicit_conversion<Color4f, float3>(
+ conversions, "Color4f to float3", [](Color4f a) { return float3(a.r, a.g, a.b); });
+ return conversions;
+}
+
+static const fn::MultiFunction *try_get_conversion_function(fn::MFDataType from, fn::MFDataType to)
+{
+ static const ImplicitConversionsMap conversions = get_implicit_conversions();
+ const fn::MultiFunction *function = conversions.lookup_default({from, to}, nullptr);
+ return function;
+}
+
+static fn::MFOutputSocket &insert_default_value_for_type(CommonMFNetworkBuilderData &common,
+ fn::MFDataType type)
+{
+ const fn::MultiFunction *default_fn;
+ if (type.is_single()) {
+ default_fn = &common.resources.construct<fn::CustomMF_GenericConstant>(
+ AT, type.single_type(), type.single_type().default_value());
+ }
+ else {
+ default_fn = &common.resources.construct<fn::CustomMF_GenericConstantArray>(
+ AT, fn::GSpan(type.vector_base_type()));
+ }
+
+ fn::MFNode &node = common.network.add_function(*default_fn);
+ return node.output(0);
+}
+
+static void insert_links(CommonMFNetworkBuilderData &common)
+{
+ for (const DInputSocket *to_dsocket : common.tree.input_sockets()) {
+ if (!to_dsocket->is_available()) {
+ continue;
+ }
+ if (!to_dsocket->is_linked()) {
+ continue;
+ }
+ if (!is_multi_function_data_socket(to_dsocket->bsocket())) {
+ continue;
+ }
+
+ Span<fn::MFInputSocket *> to_sockets = common.network_map.lookup(*to_dsocket);
+ BLI_assert(to_sockets.size() >= 1);
+ fn::MFDataType to_type = to_sockets[0]->data_type();
+
+ fn::MFOutputSocket *from_socket = try_find_origin(common, *to_dsocket);
+ if (from_socket == nullptr) {
+ from_socket = &insert_default_value_for_type(common, to_type);
+ }
+
+ fn::MFDataType from_type = from_socket->data_type();
+
+ if (from_type != to_type) {
+ const fn::MultiFunction *conversion_fn = try_get_conversion_function(from_type, to_type);
+ if (conversion_fn != nullptr) {
+ fn::MFNode &node = common.network.add_function(*conversion_fn);
+ common.network.add_link(*from_socket, node.input(0));
+ from_socket = &node.output(0);
+ }
+ else {
+ from_socket = &insert_default_value_for_type(common, to_type);
+ }
+ }
+
+ for (fn::MFInputSocket *to_socket : to_sockets) {
+ common.network.add_link(*from_socket, *to_socket);
+ }
+ }
+}
+
+static void insert_unlinked_input(CommonMFNetworkBuilderData &common, const DInputSocket &dsocket)
+{
+ bNodeSocket *bsocket = dsocket.bsocket();
+ bNodeSocketType *socktype = bsocket->typeinfo;
+ BLI_assert(socktype->expand_in_mf_network != nullptr);
+
+ SocketMFNetworkBuilder builder{common, dsocket};
+ socktype->expand_in_mf_network(builder);
+
+ fn::MFOutputSocket *from_socket = builder.built_socket();
+ BLI_assert(from_socket != nullptr);
+
+ for (fn::MFInputSocket *to_socket : common.network_map.lookup(dsocket)) {
+ common.network.add_link(*from_socket, *to_socket);
+ }
+}
+
+static void insert_unlinked_inputs(CommonMFNetworkBuilderData &common)
+{
+ Vector<const DInputSocket *> unlinked_data_inputs;
+ for (const DInputSocket *dsocket : common.tree.input_sockets()) {
+ if (dsocket->is_available()) {
+ if (is_multi_function_data_socket(dsocket->bsocket())) {
+ if (!dsocket->is_linked()) {
+ insert_unlinked_input(common, *dsocket);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Expands all function nodes contained in the given node tree within the given multi-function
+ * network.
+ *
+ * Returns a mapping between the original node tree and the generated nodes/sockets for further
+ * processing.
+ */
+MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
+ const DerivedNodeTree &tree,
+ ResourceCollector &resources)
+{
+ MFNetworkTreeMap network_map{tree, network};
+
+ CommonMFNetworkBuilderData common{resources, network, network_map, tree};
+
+ insert_nodes(common);
+ insert_group_inputs(common);
+ insert_links(common);
+ insert_unlinked_inputs(common);
+
+ return network_map;
+}
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc
new file mode 100644
index 00000000000..186ca750f10
--- /dev/null
+++ b/source/blender/nodes/intern/node_tree_ref.cc
@@ -0,0 +1,177 @@
+/*
+ * 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 "NOD_node_tree_ref.hh"
+
+#include "BLI_dot_export.hh"
+
+namespace blender::nodes {
+
+NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree)
+{
+ Map<bNode *, NodeRef *> node_mapping;
+
+ LISTBASE_FOREACH (bNode *, bnode, &btree->nodes) {
+ NodeRef &node = *allocator_.construct<NodeRef>();
+
+ node.tree_ = this;
+ node.bnode_ = bnode;
+ node.id_ = nodes_by_id_.append_and_get_index(&node);
+ RNA_pointer_create(&btree->id, &RNA_Node, bnode, &node.rna_);
+
+ LISTBASE_FOREACH (bNodeSocket *, bsocket, &bnode->inputs) {
+ InputSocketRef &socket = *allocator_.construct<InputSocketRef>();
+ socket.node_ = &node;
+ socket.index_ = node.inputs_.append_and_get_index(&socket);
+ socket.is_input_ = true;
+ socket.bsocket_ = bsocket;
+ socket.id_ = sockets_by_id_.append_and_get_index(&socket);
+ RNA_pointer_create(&btree->id, &RNA_NodeSocket, bsocket, &socket.rna_);
+ }
+
+ LISTBASE_FOREACH (bNodeSocket *, bsocket, &bnode->outputs) {
+ OutputSocketRef &socket = *allocator_.construct<OutputSocketRef>();
+ socket.node_ = &node;
+ socket.index_ = node.outputs_.append_and_get_index(&socket);
+ socket.is_input_ = false;
+ socket.bsocket_ = bsocket;
+ socket.id_ = sockets_by_id_.append_and_get_index(&socket);
+ RNA_pointer_create(&btree->id, &RNA_NodeSocket, bsocket, &socket.rna_);
+ }
+
+ input_sockets_.extend(node.inputs_.as_span());
+ output_sockets_.extend(node.outputs_.as_span());
+
+ node_mapping.add_new(bnode, &node);
+ }
+
+ LISTBASE_FOREACH (bNodeLink *, blink, &btree->links) {
+ OutputSocketRef &from_socket = this->find_output_socket(
+ node_mapping, blink->fromnode, blink->fromsock);
+ InputSocketRef &to_socket = this->find_input_socket(
+ node_mapping, blink->tonode, blink->tosock);
+
+ from_socket.directly_linked_sockets_.append(&to_socket);
+ to_socket.directly_linked_sockets_.append(&from_socket);
+ }
+
+ for (OutputSocketRef *socket : output_sockets_) {
+ if (!socket->node_->is_reroute_node()) {
+ this->find_targets_skipping_reroutes(*socket, socket->linked_sockets_);
+ for (SocketRef *target : socket->linked_sockets_) {
+ target->linked_sockets_.append(socket);
+ }
+ }
+ }
+
+ for (NodeRef *node : nodes_by_id_) {
+ const bNodeType *nodetype = node->bnode_->typeinfo;
+ nodes_by_type_.lookup_or_add_default(nodetype).append(node);
+ }
+}
+
+NodeTreeRef::~NodeTreeRef()
+{
+ for (NodeRef *node : nodes_by_id_) {
+ node->~NodeRef();
+ }
+ for (InputSocketRef *socket : input_sockets_) {
+ socket->~InputSocketRef();
+ }
+ for (OutputSocketRef *socket : output_sockets_) {
+ socket->~OutputSocketRef();
+ }
+}
+
+InputSocketRef &NodeTreeRef::find_input_socket(Map<bNode *, NodeRef *> &node_mapping,
+ bNode *bnode,
+ bNodeSocket *bsocket)
+{
+ NodeRef *node = node_mapping.lookup(bnode);
+ for (SocketRef *socket : node->inputs_) {
+ if (socket->bsocket_ == bsocket) {
+ return *(InputSocketRef *)socket;
+ }
+ }
+ BLI_assert(false);
+ return *node->inputs_[0];
+}
+
+OutputSocketRef &NodeTreeRef::find_output_socket(Map<bNode *, NodeRef *> &node_mapping,
+ bNode *bnode,
+ bNodeSocket *bsocket)
+{
+ NodeRef *node = node_mapping.lookup(bnode);
+ for (SocketRef *socket : node->outputs_) {
+ if (socket->bsocket_ == bsocket) {
+ return *(OutputSocketRef *)socket;
+ }
+ }
+ BLI_assert(false);
+ return *node->outputs_[0];
+}
+
+void NodeTreeRef::find_targets_skipping_reroutes(OutputSocketRef &socket,
+ Vector<SocketRef *> &r_targets)
+{
+ for (SocketRef *direct_target : socket.directly_linked_sockets_) {
+ if (direct_target->node_->is_reroute_node()) {
+ this->find_targets_skipping_reroutes(*direct_target->node_->outputs_[0], r_targets);
+ }
+ else {
+ r_targets.append_non_duplicates(direct_target);
+ }
+ }
+}
+
+std::string NodeTreeRef::to_dot() const
+{
+ dot::DirectedGraph digraph;
+ digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
+
+ Map<const NodeRef *, dot::NodeWithSocketsRef> dot_nodes;
+
+ for (const NodeRef *node : nodes_by_id_) {
+ dot::Node &dot_node = digraph.new_node("");
+ dot_node.set_background_color("white");
+
+ Vector<std::string> input_names;
+ Vector<std::string> output_names;
+ for (const InputSocketRef *socket : node->inputs()) {
+ input_names.append(socket->name());
+ }
+ for (const OutputSocketRef *socket : node->outputs()) {
+ output_names.append(socket->name());
+ }
+
+ dot_nodes.add_new(node,
+ dot::NodeWithSocketsRef(dot_node, node->name(), input_names, output_names));
+ }
+
+ for (const OutputSocketRef *from_socket : output_sockets_) {
+ for (const InputSocketRef *to_socket : from_socket->directly_linked_sockets()) {
+ dot::NodeWithSocketsRef &from_dot_node = dot_nodes.lookup(&from_socket->node());
+ dot::NodeWithSocketsRef &to_dot_node = dot_nodes.lookup(&to_socket->node());
+
+ digraph.new_edge(from_dot_node.output(from_socket->index()),
+ to_dot_node.input(to_socket->index()));
+ }
+ }
+
+ return digraph.to_dot_string();
+}
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h
index 2aed485c8bc..b0ba1ea194f 100644
--- a/source/blender/nodes/shader/node_shader_util.h
+++ b/source/blender/nodes/shader/node_shader_util.h
@@ -73,7 +73,7 @@
#ifdef __cplusplus
# include "FN_multi_function_builder.hh"
-# include "BKE_node_tree_multi_function.hh"
+# include "NOD_node_tree_multi_function.hh"
# include "BLI_color.hh"
# include "BLI_float3.hh"
diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
index 3f8a516c175..b026499ec4b 100644
--- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
@@ -128,7 +128,7 @@ class MapRangeFunction : public blender::fn::MultiFunction {
}
};
-static void sh_node_map_range_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder)
+static void sh_node_map_range_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
bNode &bnode = builder.bnode();
bool clamp = bnode.custom1 != 0;
diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc
index c6762f17919..c7035da8c70 100644
--- a/source/blender/nodes/shader/nodes/node_shader_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_math.cc
@@ -147,7 +147,7 @@ static int gpu_shader_math(GPUMaterial *mat,
}
static const blender::fn::MultiFunction &get_base_multi_function(
- blender::bke::NodeMFNetworkBuilder &builder)
+ blender::nodes::NodeMFNetworkBuilder &builder)
{
const int mode = builder.bnode().custom1;
switch (mode) {
@@ -347,11 +347,11 @@ static const blender::fn::MultiFunction &get_base_multi_function(
}
}
-static void sh_node_math_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder)
+static void sh_node_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
const blender::fn::MultiFunction &base_function = get_base_multi_function(builder);
- const blender::bke::DNode &dnode = builder.dnode();
+ const blender::nodes::DNode &dnode = builder.dnode();
blender::fn::MFNetwork &network = builder.network();
blender::fn::MFFunctionNode &base_node = network.add_function(base_function);
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
index 8cab115c0b5..4b4b80ea1ad 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
@@ -89,7 +89,7 @@ class SeparateRGBFunction : public blender::fn::MultiFunction {
}
};
-static void sh_node_seprgb_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder)
+static void sh_node_seprgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
static SeparateRGBFunction fn;
builder.set_matching_fn(fn);
@@ -146,7 +146,7 @@ static int gpu_shader_combrgb(GPUMaterial *mat,
return GPU_stack_link(mat, node, "combine_rgb", in, out);
}
-static void sh_node_combrgb_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder)
+static void sh_node_combrgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::Color4f> fn{
"Combine RGB", [](float r, float g, float b) { return blender::Color4f(r, g, b, 1.0f); }};
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc
index d9af37ab46b..03e18acc245 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc
@@ -74,7 +74,7 @@ class MF_SeparateXYZ : public blender::fn::MultiFunction {
}
};
-static void sh_node_sepxyz_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder)
+static void sh_node_sepxyz_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
static MF_SeparateXYZ separate_fn;
builder.set_matching_fn(separate_fn);
@@ -113,7 +113,7 @@ static int gpu_shader_combxyz(GPUMaterial *mat,
return GPU_stack_link(mat, node, "combine_xyz", in, out);
}
-static void sh_node_combxyz_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder)
+static void sh_node_combxyz_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::float3> fn{
"Combine Vector", [](float x, float y, float z) { return blender::float3(x, y, z); }};
diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
index 76409c836d8..21f15bcf6f4 100644
--- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
@@ -157,7 +157,7 @@ class ColorBandFunction : public blender::fn::MultiFunction {
}
};
-static void sh_node_valtorgb_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder)
+static void sh_node_valtorgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
bNode &bnode = builder.bnode();
const ColorBand *color_band = (const ColorBand *)bnode.storage;
diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc
index 64701018d63..1d7c3f47233 100644
--- a/source/blender/nodes/shader/nodes/node_shader_value.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_value.cc
@@ -39,7 +39,7 @@ static int gpu_shader_value(GPUMaterial *mat,
return GPU_stack_link(mat, node, "set_value", in, out, link);
}
-static void sh_node_value_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder)
+static void sh_node_value_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
const bNodeSocket *bsocket = builder.dnode().output(0).bsocket();
const bNodeSocketValueFloat *value = (const bNodeSocketValueFloat *)bsocket->default_value;
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 8301ab4d855..c18ad8bb244 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
@@ -161,7 +161,7 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node
}
static const blender::fn::MultiFunction &get_multi_function(
- blender::bke::NodeMFNetworkBuilder &builder)
+ blender::nodes::NodeMFNetworkBuilder &builder)
{
using blender::float3;
@@ -271,7 +271,7 @@ static const blender::fn::MultiFunction &get_multi_function(
};
}
-static void sh_node_vector_math_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder)
+static void sh_node_vector_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
const blender::fn::MultiFunction &fn = get_multi_function(builder);
builder.set_matching_fn(fn);