/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #pragma once /** \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 "NOD_type_callbacks.hh" #include "BLI_multi_value_map.hh" #include "BLI_resource_scope.hh" namespace blender::nodes { /** * A MFNetworkTreeMap maps various components of a node tree 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_; MultiValueMap sockets_by_dsocket_; public: MFNetworkTreeMap(const DerivedNodeTree &tree, fn::MFNetwork &network) : tree_(tree), network_(network) { } 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_.lookup(dsocket).is_empty()); sockets_by_dsocket_.add(dsocket, &socket); } void add(const DInputSocket &dsocket, fn::MFInputSocket &socket) { sockets_by_dsocket_.add(dsocket, &socket); } void add(const DOutputSocket &dsocket, fn::MFOutputSocket &socket) { /* There can be at most one matching output socket. */ BLI_assert(sockets_by_dsocket_.lookup(dsocket).is_empty()); sockets_by_dsocket_.add(dsocket, &socket); } void add(const DTreeContext &context, Span dsockets, Span sockets) { assert_same_size(dsockets, sockets); for (int i : dsockets.index_range()) { this->add(DInputSocket(&context, dsockets[i]), *sockets[i]); } } void add(const DTreeContext &context, Span dsockets, Span sockets) { assert_same_size(dsockets, sockets); for (int i : dsockets.index_range()) { this->add(DOutputSocket(&context, dsockets[i]), *sockets[i]); } } void add_try_match(const DNode &dnode, fn::MFNode &node) { this->add_try_match(*dnode.context(), dnode->inputs().cast(), node.inputs().cast()); this->add_try_match(*dnode.context(), dnode->outputs().cast(), node.outputs().cast()); } void add_try_match(const DTreeContext &context, Span dsockets, Span sockets) { this->add_try_match( context, dsockets.cast(), sockets.cast()); } void add_try_match(const DTreeContext &context, Span dsockets, Span sockets) { this->add_try_match( context, dsockets.cast(), sockets.cast()); } void add_try_match(const DTreeContext &context, Span dsockets, Span sockets) { int used_sockets = 0; for (const SocketRef *dsocket : dsockets) { if (!dsocket->is_available()) { continue; } if (!socket_is_mf_data_socket(*dsocket->typeinfo())) { continue; } fn::MFSocket *socket = sockets[used_sockets]; this->add(DSocket(&context, dsocket), *socket); used_sockets++; } } fn::MFOutputSocket &lookup(const DOutputSocket &dsocket) { return sockets_by_dsocket_.lookup(dsocket)[0]->as_output(); } Span lookup(const DInputSocket &dsocket) { return sockets_by_dsocket_.lookup(dsocket).cast(); } fn::MFInputSocket &lookup_dummy(const DInputSocket &dsocket) { Span 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_.lookup(dsocket).is_empty(); } }; /** * This data is necessary throughout the generation of a MFNetwork from a node tree. */ struct CommonMFNetworkBuilderData { ResourceScope &scope; 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. */ ResourceScope &resource_scope() { return common_.scope; } /** * Constructs a new function that will live at least as long as the MFNetwork. */ template T &construct_fn(Args &&...args) { BLI_STATIC_ASSERT((std::is_base_of_v), ""); void *buffer = common_.scope.linear_allocator().allocate(sizeof(T), alignof(T)); T *fn = new (buffer) T(std::forward(args)...); common_.scope.add(destruct_ptr(fn), fn->name().c_str()); 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: bNodeSocket *bsocket_; fn::MFOutputSocket *built_socket_ = nullptr; public: SocketMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DSocket &dsocket) : MFNetworkBuilderBase(common), bsocket_(dsocket->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 T *socket_default_value() { return static_cast(bsocket_->default_value); } /** * Builds a function node for that socket that outputs the given constant value. */ template void set_constant_value(T value) { this->construct_generator_fn>(std::move(value)); } void set_constant_value(const CPPType &type, const void *value) { /* The value has live as long as the generated mf network. */ this->construct_generator_fn(type, value); } template void construct_generator_fn(Args &&...args) { const fn::MultiFunction &fn = this->construct_fn(std::forward(args)...); 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: DNode dnode_; public: NodeMFNetworkBuilder(CommonMFNetworkBuilderData &common, 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 T &construct_and_set_matching_fn(Args &&...args) { T &function = this->construct_fn(std::forward(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_->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, ResourceScope &scope); using MultiFunctionByNode = Map; MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope); } // namespace blender::nodes