391 lines
11 KiB
C++
391 lines
11 KiB
C++
/*
|
|
* 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<DSocket, fn::MFSocket *> 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<const InputSocketRef *> dsockets,
|
|
Span<fn::MFInputSocket *> 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<const OutputSocketRef *> dsockets,
|
|
Span<fn::MFOutputSocket *> 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<const SocketRef *>(),
|
|
node.inputs().cast<fn::MFSocket *>());
|
|
this->add_try_match(*dnode.context(),
|
|
dnode->outputs().cast<const SocketRef *>(),
|
|
node.outputs().cast<fn::MFSocket *>());
|
|
}
|
|
|
|
void add_try_match(const DTreeContext &context,
|
|
Span<const InputSocketRef *> dsockets,
|
|
Span<fn::MFInputSocket *> sockets)
|
|
{
|
|
this->add_try_match(
|
|
context, dsockets.cast<const SocketRef *>(), sockets.cast<fn::MFSocket *>());
|
|
}
|
|
|
|
void add_try_match(const DTreeContext &context,
|
|
Span<const OutputSocketRef *> dsockets,
|
|
Span<fn::MFOutputSocket *> sockets)
|
|
{
|
|
this->add_try_match(
|
|
context, dsockets.cast<const SocketRef *>(), sockets.cast<fn::MFSocket *>());
|
|
}
|
|
|
|
void add_try_match(const DTreeContext &context,
|
|
Span<const SocketRef *> dsockets,
|
|
Span<fn::MFSocket *> 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<fn::MFInputSocket *> lookup(const DInputSocket &dsocket)
|
|
{
|
|
return sockets_by_dsocket_.lookup(dsocket).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_.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<typename T, typename... Args> T &construct_fn(Args &&... args)
|
|
{
|
|
BLI_STATIC_ASSERT((std::is_base_of_v<fn::MultiFunction, T>), "");
|
|
void *buffer = common_.scope.linear_allocator().allocate(sizeof(T), alignof(T));
|
|
T *fn = new (buffer) T(std::forward<Args>(args)...);
|
|
common_.scope.add(destruct_ptr<T>(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<typename T> T *socket_default_value()
|
|
{
|
|
return static_cast<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)
|
|
{
|
|
this->construct_generator_fn<fn::CustomMF_Constant<T>>(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<fn::CustomMF_GenericConstant>(type, value);
|
|
}
|
|
|
|
template<typename T, typename... Args> void construct_generator_fn(Args &&... args)
|
|
{
|
|
const fn::MultiFunction &fn = this->construct_fn<T>(std::forward<Args>(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<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_->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<DNode, const fn::MultiFunction *>;
|
|
MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope);
|
|
|
|
} // namespace blender::nodes
|