diff options
Diffstat (limited to 'source/blender/nodes')
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); |