Nodes: Generate multi-function network from node tree
This adds new callbacks to `bNodeSocketType` and `bNodeType`. Those are used to generate a multi-function network from a node tree. Later, this network is evaluated on e.g. particle data. Reviewers: brecht Differential Revision: https://developer.blender.org/D8169
This commit is contained in:
parent
ff97545c50
commit
4990e4dd01
|
@ -101,6 +101,30 @@ typedef struct bNodeSocketTemplate {
|
|||
char identifier[64]; /* generated from name */
|
||||
} bNodeSocketTemplate;
|
||||
|
||||
/* Use `void *` for callbacks that require C++. This is rather ugly, but works well for now. This
|
||||
* would not be necessary if we would use bNodeSocketType and bNodeType only in C++ code.
|
||||
* However, achieving this requires quite a few changes currently. */
|
||||
#ifdef __cplusplus
|
||||
namespace blender {
|
||||
namespace bke {
|
||||
class SocketMFNetworkBuilder;
|
||||
class NodeMFNetworkBuilder;
|
||||
} // namespace bke
|
||||
namespace fn {
|
||||
class MFDataType;
|
||||
}
|
||||
} // namespace blender
|
||||
|
||||
using NodeExpandInMFNetworkFunction = void (*)(blender::bke::NodeMFNetworkBuilder &builder);
|
||||
using SocketGetMFDataTypeFunction = blender::fn::MFDataType (*)();
|
||||
using SocketExpandInMFNetworkFunction = void (*)(blender::bke::SocketMFNetworkBuilder &builder);
|
||||
|
||||
#else
|
||||
typedef void *NodeExpandInMFNetworkFunction;
|
||||
typedef void *SocketGetMFDataTypeFunction;
|
||||
typedef void *SocketExpandInMFNetworkFunction;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Defines a socket type.
|
||||
*
|
||||
|
@ -153,6 +177,11 @@ typedef struct bNodeSocketType {
|
|||
|
||||
/* Callback to free the socket type. */
|
||||
void (*free_self)(struct bNodeSocketType *stype);
|
||||
|
||||
/* Returns the multi-function data type of this socket type. */
|
||||
SocketGetMFDataTypeFunction get_mf_data_type;
|
||||
/* Expands the socket into a multi-function node that outputs the socket value. */
|
||||
SocketExpandInMFNetworkFunction expand_in_mf_network;
|
||||
} bNodeSocketType;
|
||||
|
||||
typedef void *(*NodeInitExecFunction)(struct bNodeExecContext *context,
|
||||
|
@ -267,6 +296,9 @@ typedef struct bNodeType {
|
|||
/* gpu */
|
||||
NodeGPUExecFunction gpufunc;
|
||||
|
||||
/* Expands the bNode into nodes in a multi-function network, which will be evaluated later on. */
|
||||
NodeExpandInMFNetworkFunction expand_in_mf_network;
|
||||
|
||||
/* RNA integration */
|
||||
ExtensionRNA rna_ext;
|
||||
} bNodeType;
|
||||
|
|
|
@ -0,0 +1,355 @@
|
|||
/*
|
||||
* 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 __BKE_NODE_TREE_FUNCTION_HH__
|
||||
#define __BKE_NODE_TREE_FUNCTION_HH__
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*
|
||||
* 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 "BKE_derived_node_tree.hh"
|
||||
|
||||
#include "BLI_resource_collector.hh"
|
||||
|
||||
namespace blender {
|
||||
namespace bke {
|
||||
|
||||
/* 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 bke::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.
|
||||
*/
|
||||
Array<Vector<fn::MFSocket *, 1>> m_sockets_by_dsocket_id;
|
||||
Array<fn::MFOutputSocket *> m_socket_by_group_input_id;
|
||||
|
||||
public:
|
||||
MFNetworkTreeMap(const DerivedNodeTree &tree)
|
||||
: m_sockets_by_dsocket_id(tree.sockets().size()),
|
||||
m_socket_by_group_input_id(tree.group_inputs().size(), nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void add(const DSocket &dsocket, fn::MFSocket &socket)
|
||||
{
|
||||
BLI_assert(dsocket.is_input() == socket.is_input());
|
||||
m_sockets_by_dsocket_id[dsocket.id()].append(&socket);
|
||||
}
|
||||
|
||||
void add(const DInputSocket &dsocket, fn::MFInputSocket &socket)
|
||||
{
|
||||
m_sockets_by_dsocket_id[dsocket.id()].append(&socket);
|
||||
}
|
||||
|
||||
void add(const DOutputSocket &dsocket, fn::MFOutputSocket &socket)
|
||||
{
|
||||
m_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(m_socket_by_group_input_id[group_input.id()] == nullptr);
|
||||
m_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 = m_socket_by_group_input_id[group_input.id()];
|
||||
BLI_assert(socket != nullptr);
|
||||
return *socket;
|
||||
}
|
||||
|
||||
fn::MFOutputSocket &lookup(const DOutputSocket &dsocket)
|
||||
{
|
||||
auto &sockets = m_sockets_by_dsocket_id[dsocket.id()];
|
||||
BLI_assert(sockets.size() == 1);
|
||||
return sockets[0]->as_output();
|
||||
}
|
||||
|
||||
Span<fn::MFInputSocket *> lookup(const DInputSocket &dsocket)
|
||||
{
|
||||
return m_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 m_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 &m_common;
|
||||
|
||||
public:
|
||||
MFNetworkBuilderBase(CommonMFNetworkBuilderData &common) : m_common(common)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the network that is currently being built.
|
||||
*/
|
||||
fn::MFNetwork &network()
|
||||
{
|
||||
return m_common.network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map between the node tree and the multi-function network that is being built.
|
||||
*/
|
||||
MFNetworkTreeMap &network_map()
|
||||
{
|
||||
return m_common.network_map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a resource collector that will only be destructed after the multi-function network is
|
||||
* destructed.
|
||||
*/
|
||||
ResourceCollector &resources()
|
||||
{
|
||||
return m_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 = m_common.resources.linear_allocator().allocate(sizeof(T), alignof(T));
|
||||
T *fn = new (buffer) T(std::forward<Args>(args)...);
|
||||
m_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 *m_dsocket = nullptr;
|
||||
const DGroupInput *m_group_input = nullptr;
|
||||
bNodeSocket *m_bsocket;
|
||||
fn::MFOutputSocket *m_built_socket = nullptr;
|
||||
|
||||
public:
|
||||
SocketMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DSocket &dsocket)
|
||||
: MFNetworkBuilderBase(common), m_dsocket(&dsocket), m_bsocket(dsocket.bsocket())
|
||||
{
|
||||
}
|
||||
|
||||
SocketMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DGroupInput &group_input)
|
||||
: MFNetworkBuilderBase(common), m_group_input(&group_input), m_bsocket(group_input.bsocket())
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the socket that is currently being built.
|
||||
*/
|
||||
bNodeSocket &bsocket()
|
||||
{
|
||||
return *m_bsocket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that returns bsocket->default_value for the current socket.
|
||||
*/
|
||||
template<typename T> T *socket_default_value()
|
||||
{
|
||||
return (T *)m_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 = m_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)
|
||||
{
|
||||
m_built_socket = &socket;
|
||||
}
|
||||
|
||||
fn::MFOutputSocket *built_socket()
|
||||
{
|
||||
return m_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 &m_node;
|
||||
|
||||
public:
|
||||
NodeMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DNode &node)
|
||||
: MFNetworkBuilderBase(common), m_node(node)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 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> void construct_and_set_matching_fn(Args &&... args)
|
||||
{
|
||||
const fn::MultiFunction &function = this->construct_fn<T>(std::forward<Args>(args)...);
|
||||
this->set_matching_fn(function);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = m_common.network.add_function(function);
|
||||
m_common.network_map.add_try_match(m_node, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node that is currently being built.
|
||||
*/
|
||||
bNode &bnode()
|
||||
{
|
||||
return *m_node.node_ref().bnode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node that is currently being built.
|
||||
*/
|
||||
const DNode &dnode() const
|
||||
{
|
||||
return m_node;
|
||||
}
|
||||
};
|
||||
|
||||
MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
|
||||
const DerivedNodeTree &tree,
|
||||
ResourceCollector &resources);
|
||||
|
||||
} // namespace bke
|
||||
} // namespace blender
|
||||
|
||||
#endif /* __BKE_NODE_TREE_FUNCTION_HH__ */
|
|
@ -27,6 +27,7 @@ set(INC
|
|||
../bmesh
|
||||
../depsgraph
|
||||
../draw
|
||||
../functions
|
||||
../gpencil_modifiers
|
||||
../gpu
|
||||
../ikplugin
|
||||
|
@ -187,6 +188,7 @@ set(SRC
|
|||
intern/multires_unsubdivide.c
|
||||
intern/nla.c
|
||||
intern/node.c
|
||||
intern/node_tree_multi_function.cc
|
||||
intern/node_tree_ref.cc
|
||||
intern/object.c
|
||||
intern/object_deform.c
|
||||
|
@ -356,6 +358,7 @@ set(SRC
|
|||
BKE_multires.h
|
||||
BKE_nla.h
|
||||
BKE_node.h
|
||||
BKE_node_tree_multi_function.hh
|
||||
BKE_node_tree_ref.hh
|
||||
BKE_object.h
|
||||
BKE_object_deform.h
|
||||
|
@ -424,6 +427,7 @@ set(LIB
|
|||
bf_bmesh
|
||||
bf_depsgraph
|
||||
bf_draw
|
||||
bf_functions
|
||||
bf_gpencil_modifiers
|
||||
bf_gpu
|
||||
bf_ikplugin
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
* 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 "BKE_node_tree_multi_function.hh"
|
||||
|
||||
namespace blender {
|
||||
namespace bke {
|
||||
|
||||
/* 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();
|
||||
}
|
||||
|
||||
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) {
|
||||
return &common.network_map.lookup(*from_dsockets[0]);
|
||||
}
|
||||
else {
|
||||
return &common.network_map.lookup(*from_group_inputs[0]);
|
||||
}
|
||||
}
|
||||
|
||||
static void insert_links(CommonMFNetworkBuilderData &common)
|
||||
{
|
||||
for (const DInputSocket *to_dsocket : common.tree.input_sockets()) {
|
||||
if (!to_dsocket->is_available()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_multi_function_data_socket(to_dsocket->bsocket())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fn::MFOutputSocket *from_socket = try_find_origin(common, *to_dsocket);
|
||||
if (from_socket == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Span<fn::MFInputSocket *> to_sockets = common.network_map.lookup(*to_dsocket);
|
||||
BLI_assert(to_sockets.size() >= 1);
|
||||
|
||||
fn::MFDataType from_type = from_socket->data_type();
|
||||
fn::MFDataType to_type = to_sockets[0]->data_type();
|
||||
|
||||
if (from_type != to_type) {
|
||||
/* Todo: Try inserting implicit conversion. */
|
||||
}
|
||||
|
||||
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};
|
||||
|
||||
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 bke
|
||||
} // namespace blender
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* 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 __BLI_RESOURCE_COLLECTOR_HH__
|
||||
#define __BLI_RESOURCE_COLLECTOR_HH__
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* A ResourceCollector holds an arbitrary set of resources, that will be destructed and/or freed
|
||||
* when the ResourceCollector is destructed. This is useful when some object has to take ownership
|
||||
* of other objects, but it does not know the type of those other objects.
|
||||
*
|
||||
* Resources owned by the ResourceCollector will be freed in reverse order. That allows resources
|
||||
* that are added later to depend on resources that have been added before.
|
||||
*/
|
||||
|
||||
#include "BLI_linear_allocator.hh"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
class ResourceCollector : NonCopyable, NonMovable {
|
||||
private:
|
||||
struct ResourceData {
|
||||
void *data;
|
||||
void (*free)(void *data);
|
||||
const char *debug_name;
|
||||
};
|
||||
|
||||
LinearAllocator<> m_allocator;
|
||||
Vector<ResourceData> m_resources;
|
||||
|
||||
public:
|
||||
ResourceCollector() = default;
|
||||
|
||||
~ResourceCollector()
|
||||
{
|
||||
/* Free in reversed order. */
|
||||
for (uint i = m_resources.size(); i--;) {
|
||||
ResourceData &data = m_resources[i];
|
||||
data.free(data.data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass ownership of the resource to the ResourceCollector. It will be destructed and freed when
|
||||
* the collector is destructed.
|
||||
*/
|
||||
template<typename T> void add(std::unique_ptr<T> resource, const char *name)
|
||||
{
|
||||
BLI_assert(resource.get() != nullptr);
|
||||
this->add(
|
||||
resource.release(),
|
||||
[](void *data) {
|
||||
T *typed_data = reinterpret_cast<T *>(data);
|
||||
delete typed_data;
|
||||
},
|
||||
name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass ownership of the resource to the ResourceCollector. It will be destructed when the
|
||||
* collector is destructed.
|
||||
*/
|
||||
template<typename T> void add(destruct_ptr<T> resource, const char *name)
|
||||
{
|
||||
BLI_assert(resource.get() != nullptr);
|
||||
this->add(
|
||||
resource.release(),
|
||||
[](void *data) {
|
||||
T *typed_data = reinterpret_cast<T *>(data);
|
||||
typed_data->~T();
|
||||
},
|
||||
name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass ownership of some resource to the ResourceCollector. The given free function will be
|
||||
* calld when the collector is destructed.
|
||||
*/
|
||||
void add(void *userdata, void (*free)(void *), const char *name)
|
||||
{
|
||||
ResourceData data;
|
||||
data.debug_name = name;
|
||||
data.data = userdata;
|
||||
data.free = free;
|
||||
m_resources.append(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to a linear allocator that is owned by the ResourcesCollector. Memory
|
||||
* allocated through this allocator will be freed when the collector is destructed.
|
||||
*/
|
||||
LinearAllocator<> &linear_allocator()
|
||||
{
|
||||
return m_allocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to construct an instance of type T that will be owned by the ResourceCollector.
|
||||
*/
|
||||
template<typename T, typename... Args> T &construct(const char *name, Args &&... args)
|
||||
{
|
||||
T *value = m_allocator.construct<T>(std::forward<Args>(args)...);
|
||||
this->add(destruct_ptr<T>(value), name);
|
||||
return *value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the names of all the resources that are owned by this ResourceCollector. This can be
|
||||
* useful for debugging.
|
||||
*/
|
||||
void print(StringRef name) const
|
||||
{
|
||||
if (m_resources.size() == 0) {
|
||||
std::cout << "\"" << name << "\" has no resources.\n";
|
||||
return;
|
||||
}
|
||||
else {
|
||||
std::cout << "Resources for \"" << name << "\":\n";
|
||||
for (const ResourceData &data : m_resources) {
|
||||
std::cout << " " << data.data << ": " << data.debug_name << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
|
||||
#endif /* __BLI_RESOURCE_COLLECTOR_HH__ */
|
|
@ -232,6 +232,7 @@ set(SRC
|
|||
BLI_quadric.h
|
||||
BLI_rand.h
|
||||
BLI_rect.h
|
||||
BLI_resource_collector.hh
|
||||
BLI_scanfill.h
|
||||
BLI_set.hh
|
||||
BLI_set_slots.hh
|
||||
|
|
|
@ -30,6 +30,7 @@ set(INC
|
|||
../blenlib
|
||||
../blentranslation
|
||||
../depsgraph
|
||||
../functions
|
||||
../gpu
|
||||
../imbuf
|
||||
../makesdna
|
||||
|
@ -192,7 +193,7 @@ set(SRC
|
|||
shader/nodes/node_shader_script.c
|
||||
shader/nodes/node_shader_sepcombHSV.c
|
||||
shader/nodes/node_shader_sepcombRGB.c
|
||||
shader/nodes/node_shader_sepcombXYZ.c
|
||||
shader/nodes/node_shader_sepcombXYZ.cc
|
||||
shader/nodes/node_shader_shaderToRgb.c
|
||||
shader/nodes/node_shader_squeeze.c
|
||||
shader/nodes/node_shader_subsurface_scattering.c
|
||||
|
@ -277,7 +278,7 @@ set(SRC
|
|||
intern/node_util.c
|
||||
|
||||
composite/node_composite_util.h
|
||||
function/node_function_util.h
|
||||
function/node_function_util.hh
|
||||
shader/node_shader_util.h
|
||||
simulation/node_simulation_util.h
|
||||
texture/node_texture_util.h
|
||||
|
@ -296,6 +297,7 @@ set(SRC
|
|||
)
|
||||
|
||||
set(LIB
|
||||
bf_functions
|
||||
)
|
||||
|
||||
if(WITH_PYTHON)
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "node_function_util.h"
|
||||
#include "node_function_util.hh"
|
||||
#include "node_util.h"
|
||||
|
||||
bool fn_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
#include "BLI_float3.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
@ -26,6 +27,7 @@
|
|||
#include "DNA_node_types.h"
|
||||
|
||||
#include "BKE_node.h"
|
||||
#include "BKE_node_tree_multi_function.hh"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
|
@ -33,6 +35,8 @@
|
|||
|
||||
#include "node_util.h"
|
||||
|
||||
#include "FN_multi_function_builder.hh"
|
||||
|
||||
void fn_node_type_base(
|
||||
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
|
||||
bool fn_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include "RNA_enum_types.h"
|
||||
|
||||
#include "node_function_util.h"
|
||||
#include "node_function_util.hh"
|
||||
|
||||
static bNodeSocketTemplate fn_node_boolean_math_in[] = {
|
||||
{SOCK_BOOLEAN, N_("Boolean")},
|
||||
|
@ -50,6 +50,33 @@ static void node_boolean_math_label(bNodeTree *UNUSED(ntree), bNode *node, char
|
|||
BLI_strncpy(label, IFACE_(name), maxlen);
|
||||
}
|
||||
|
||||
static const blender::fn::MultiFunction &get_multi_function(bNode &bnode)
|
||||
{
|
||||
static blender::fn::CustomMF_SI_SI_SO<bool, bool, bool> and_fn{
|
||||
"And", [](bool a, bool b) { return a && b; }};
|
||||
static blender::fn::CustomMF_SI_SI_SO<bool, bool, bool> or_fn{
|
||||
"Or", [](bool a, bool b) { return a || b; }};
|
||||
static blender::fn::CustomMF_SI_SO<bool, bool> not_fn{"Not", [](bool a) { return !a; }};
|
||||
|
||||
switch (bnode.custom1) {
|
||||
case NODE_BOOLEAN_MATH_AND:
|
||||
return and_fn;
|
||||
case NODE_BOOLEAN_MATH_OR:
|
||||
return or_fn;
|
||||
case NODE_BOOLEAN_MATH_NOT:
|
||||
return not_fn;
|
||||
}
|
||||
|
||||
BLI_assert(false);
|
||||
return blender::fn::dummy_multi_function;
|
||||
}
|
||||
|
||||
static void node_boolean_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder)
|
||||
{
|
||||
const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode());
|
||||
builder.set_matching_fn(fn);
|
||||
}
|
||||
|
||||
void register_node_type_fn_boolean_math()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
@ -58,5 +85,6 @@ void register_node_type_fn_boolean_math()
|
|||
node_type_socket_templates(&ntype, fn_node_boolean_math_in, fn_node_boolean_math_out);
|
||||
node_type_label(&ntype, node_boolean_math_label);
|
||||
node_type_update(&ntype, node_boolean_math_update);
|
||||
ntype.expand_in_mf_network = node_boolean_expand_in_mf_network;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,20 @@
|
|||
#include "node_function_util.h"
|
||||
/*
|
||||
* 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 "node_function_util.hh"
|
||||
|
||||
static bNodeSocketTemplate fn_node_combine_strings_in[] = {
|
||||
{SOCK_STRING, N_("A")},
|
||||
|
@ -11,11 +27,20 @@ static bNodeSocketTemplate fn_node_combine_strings_out[] = {
|
|||
{-1, ""},
|
||||
};
|
||||
|
||||
static void fn_node_combine_strings_expand_in_mf_network(
|
||||
blender::bke::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; }};
|
||||
builder.set_matching_fn(combine_fn);
|
||||
}
|
||||
|
||||
void register_node_type_fn_combine_strings()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
fn_node_type_base(&ntype, FN_NODE_COMBINE_STRINGS, "Combine Strings", 0, 0);
|
||||
node_type_socket_templates(&ntype, fn_node_combine_strings_in, fn_node_combine_strings_out);
|
||||
ntype.expand_in_mf_network = fn_node_combine_strings_expand_in_mf_network;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
|
|
|
@ -14,12 +14,14 @@
|
|||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "RNA_enum_types.h"
|
||||
|
||||
#include "node_function_util.h"
|
||||
#include "node_function_util.hh"
|
||||
|
||||
static bNodeSocketTemplate fn_node_float_compare_in[] = {
|
||||
{SOCK_FLOAT, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f},
|
||||
|
@ -54,6 +56,46 @@ static void node_float_compare_label(bNodeTree *UNUSED(ntree),
|
|||
BLI_strncpy(label, IFACE_(name), maxlen);
|
||||
}
|
||||
|
||||
static const blender::fn::MultiFunction &get_multi_function(bNode &node)
|
||||
{
|
||||
static blender::fn::CustomMF_SI_SI_SO<float, float, bool> less_than_fn{
|
||||
"Less Than", [](float a, float b) { return a < b; }};
|
||||
static blender::fn::CustomMF_SI_SI_SO<float, float, bool> less_equal_fn{
|
||||
"Less Equal", [](float a, float b) { return a <= b; }};
|
||||
static blender::fn::CustomMF_SI_SI_SO<float, float, bool> greater_than_fn{
|
||||
"Greater Than", [](float a, float b) { return a > b; }};
|
||||
static blender::fn::CustomMF_SI_SI_SO<float, float, bool> greater_equal_fn{
|
||||
"Greater Equal", [](float a, float b) { return a >= b; }};
|
||||
static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, bool> equal_fn{
|
||||
"Equal", [](float a, float b, float epsilon) { return std::abs(a - b) <= epsilon; }};
|
||||
static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, bool> not_equal_fn{
|
||||
"Not Equal", [](float a, float b, float epsilon) { return std::abs(a - b) > epsilon; }};
|
||||
|
||||
switch (node.custom1) {
|
||||
case NODE_FLOAT_COMPARE_LESS_THAN:
|
||||
return less_than_fn;
|
||||
case NODE_FLOAT_COMPARE_LESS_EQUAL:
|
||||
return less_equal_fn;
|
||||
case NODE_FLOAT_COMPARE_GREATER_THAN:
|
||||
return greater_than_fn;
|
||||
case NODE_FLOAT_COMPARE_GREATER_EQUAL:
|
||||
return greater_equal_fn;
|
||||
case NODE_FLOAT_COMPARE_EQUAL:
|
||||
return equal_fn;
|
||||
case NODE_FLOAT_COMPARE_NOT_EQUAL:
|
||||
return not_equal_fn;
|
||||
}
|
||||
|
||||
BLI_assert(false);
|
||||
return blender::fn::dummy_multi_function;
|
||||
}
|
||||
|
||||
static void node_float_compare_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder)
|
||||
{
|
||||
const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode());
|
||||
builder.set_matching_fn(fn);
|
||||
}
|
||||
|
||||
void register_node_type_fn_float_compare()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
@ -62,5 +104,6 @@ void register_node_type_fn_float_compare()
|
|||
node_type_socket_templates(&ntype, fn_node_float_compare_in, fn_node_float_compare_out);
|
||||
node_type_label(&ntype, node_float_compare_label);
|
||||
node_type_update(&ntype, node_float_compare_update);
|
||||
ntype.expand_in_mf_network = node_float_compare_expand_in_mf_network;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
|
|
|
@ -1,15 +1,45 @@
|
|||
#include "node_function_util.h"
|
||||
/*
|
||||
* 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 "node_function_util.hh"
|
||||
|
||||
static bNodeSocketTemplate fn_node_group_instance_id_out[] = {
|
||||
{SOCK_STRING, N_("Identifier")},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
static void fn_node_group_instance_id_expand_in_mf_network(
|
||||
blender::bke::NodeMFNetworkBuilder &builder)
|
||||
{
|
||||
const blender::bke::DNode &node = builder.dnode();
|
||||
std::string id = "/";
|
||||
for (const blender::bke::DParentNode *parent = node.parent(); parent;
|
||||
parent = parent->parent()) {
|
||||
id = "/" + parent->node_ref().name() + id;
|
||||
}
|
||||
builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<std::string>>(
|
||||
std::move(id));
|
||||
}
|
||||
|
||||
void register_node_type_fn_group_instance_id()
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
fn_node_type_base(&ntype, FN_NODE_GROUP_INSTANCE_ID, "Group Instance ID", 0, 0);
|
||||
node_type_socket_templates(&ntype, nullptr, fn_node_group_instance_id_out);
|
||||
ntype.expand_in_mf_network = fn_node_group_instance_id_expand_in_mf_network;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "node_function_util.h"
|
||||
#include "node_function_util.hh"
|
||||
|
||||
static bNodeSocketTemplate fn_node_switch_in[] = {
|
||||
{SOCK_BOOLEAN, N_("Switch")},
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
#include "DNA_node_types.h"
|
||||
|
||||
#include "BLI_color.hh"
|
||||
#include "BLI_float3.hh"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_string.h"
|
||||
|
@ -32,6 +34,7 @@
|
|||
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_node.h"
|
||||
#include "BKE_node_tree_multi_function.hh"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_types.h"
|
||||
|
@ -510,35 +513,105 @@ static bNodeSocketType *make_socket_type_control_flow(int type)
|
|||
return stype;
|
||||
}
|
||||
|
||||
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) {
|
||||
bool value = builder.socket_default_value<bNodeSocketValueBoolean>()->value;
|
||||
builder.set_constant_value(value);
|
||||
};
|
||||
return socktype;
|
||||
}
|
||||
|
||||
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) {
|
||||
float value = builder.socket_default_value<bNodeSocketValueFloat>()->value;
|
||||
builder.set_constant_value(value);
|
||||
};
|
||||
return socktype;
|
||||
}
|
||||
|
||||
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) {
|
||||
int value = builder.socket_default_value<bNodeSocketValueInt>()->value;
|
||||
builder.set_constant_value(value);
|
||||
};
|
||||
return socktype;
|
||||
}
|
||||
|
||||
static bNodeSocketType *make_socket_type_vector(PropertySubType subtype)
|
||||
{
|
||||
bNodeSocketType *socktype = make_standard_socket_type(SOCK_VECTOR, subtype);
|
||||
socktype->get_mf_data_type = []() {
|
||||
return blender::fn::MFDataType::ForSingle<blender::float3>();
|
||||
};
|
||||
socktype->expand_in_mf_network = [](blender::bke::SocketMFNetworkBuilder &builder) {
|
||||
blender::float3 value = builder.socket_default_value<bNodeSocketValueVector>()->value;
|
||||
builder.set_constant_value(value);
|
||||
};
|
||||
return socktype;
|
||||
}
|
||||
|
||||
static bNodeSocketType *make_socket_type_rgba()
|
||||
{
|
||||
bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE);
|
||||
socktype->get_mf_data_type = []() {
|
||||
return blender::fn::MFDataType::ForSingle<blender::Color4f>();
|
||||
};
|
||||
socktype->expand_in_mf_network = [](blender::bke::SocketMFNetworkBuilder &builder) {
|
||||
blender::Color4f value = builder.socket_default_value<bNodeSocketValueRGBA>()->value;
|
||||
builder.set_constant_value(value);
|
||||
};
|
||||
return socktype;
|
||||
}
|
||||
|
||||
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) {
|
||||
std::string value = builder.socket_default_value<bNodeSocketValueString>()->value;
|
||||
builder.set_constant_value(value);
|
||||
};
|
||||
return socktype;
|
||||
}
|
||||
|
||||
void register_standard_node_socket_types(void)
|
||||
{
|
||||
/* draw callbacks are set in drawnode.c to avoid bad-level calls */
|
||||
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_NONE));
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_UNSIGNED));
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_PERCENTAGE));
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_FACTOR));
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_ANGLE));
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_TIME));
|
||||
nodeRegisterSocketType(make_socket_type_float(PROP_NONE));
|
||||
nodeRegisterSocketType(make_socket_type_float(PROP_UNSIGNED));
|
||||
nodeRegisterSocketType(make_socket_type_float(PROP_PERCENTAGE));
|
||||
nodeRegisterSocketType(make_socket_type_float(PROP_FACTOR));
|
||||
nodeRegisterSocketType(make_socket_type_float(PROP_ANGLE));
|
||||
nodeRegisterSocketType(make_socket_type_float(PROP_TIME));
|
||||
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_INT, PROP_NONE));
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_INT, PROP_UNSIGNED));
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_INT, PROP_PERCENTAGE));
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_INT, PROP_FACTOR));
|
||||
nodeRegisterSocketType(make_socket_type_int(PROP_NONE));
|
||||
nodeRegisterSocketType(make_socket_type_int(PROP_UNSIGNED));
|
||||
nodeRegisterSocketType(make_socket_type_int(PROP_PERCENTAGE));
|
||||
nodeRegisterSocketType(make_socket_type_int(PROP_FACTOR));
|
||||
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE));
|
||||
nodeRegisterSocketType(make_socket_type_bool());
|
||||
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_NONE));
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_TRANSLATION));
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_DIRECTION));
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_VELOCITY));
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_ACCELERATION));
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_EULER));
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_XYZ));
|
||||
nodeRegisterSocketType(make_socket_type_vector(PROP_NONE));
|
||||
nodeRegisterSocketType(make_socket_type_vector(PROP_TRANSLATION));
|
||||
nodeRegisterSocketType(make_socket_type_vector(PROP_DIRECTION));
|
||||
nodeRegisterSocketType(make_socket_type_vector(PROP_VELOCITY));
|
||||
nodeRegisterSocketType(make_socket_type_vector(PROP_ACCELERATION));
|
||||
nodeRegisterSocketType(make_socket_type_vector(PROP_EULER));
|
||||
nodeRegisterSocketType(make_socket_type_vector(PROP_XYZ));
|
||||
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_RGBA, PROP_NONE));
|
||||
nodeRegisterSocketType(make_socket_type_rgba());
|
||||
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_STRING, PROP_NONE));
|
||||
nodeRegisterSocketType(make_socket_type_string());
|
||||
|
||||
nodeRegisterSocketType(make_standard_socket_type(SOCK_SHADER, PROP_NONE));
|
||||
|
||||
|
|
|
@ -70,6 +70,12 @@
|
|||
#include "GPU_uniformbuffer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
# include "FN_multi_function_builder.hh"
|
||||
|
||||
# include "BKE_node_tree_multi_function.hh"
|
||||
|
||||
# include "BLI_float3.hh"
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
|
|
@ -44,6 +44,42 @@ static int gpu_shader_sepxyz(GPUMaterial *mat,
|
|||
return GPU_stack_link(mat, node, "separate_xyz", in, out);
|
||||
}
|
||||
|
||||
class MF_SeparateXYZ : public blender::fn::MultiFunction {
|
||||
public:
|
||||
MF_SeparateXYZ()
|
||||
{
|
||||
blender::fn::MFSignatureBuilder signature = this->get_builder("Separate XYZ");
|
||||
signature.single_input<blender::float3>("XYZ");
|
||||
signature.single_output<float>("X");
|
||||
signature.single_output<float>("y");
|
||||
signature.single_output<float>("z");
|
||||
}
|
||||
|
||||
void call(blender::IndexMask mask,
|
||||
blender::fn::MFParams params,
|
||||
blender::fn::MFContext UNUSED(context)) const override
|
||||
{
|
||||
blender::fn::VSpan<blender::float3> vectors = params.readonly_single_input<blender::float3>(
|
||||
0, "XYZ");
|
||||
blender::MutableSpan<float> xs = params.uninitialized_single_output<float>(1, "X");
|
||||
blender::MutableSpan<float> ys = params.uninitialized_single_output<float>(2, "Y");
|
||||
blender::MutableSpan<float> zs = params.uninitialized_single_output<float>(3, "Z");
|
||||
|
||||
for (uint i : mask) {
|
||||
blender::float3 xyz = vectors[i];
|
||||
xs[i] = xyz.x;
|
||||
ys[i] = xyz.y;
|
||||
zs[i] = xyz.z;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_sepxyz_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder)
|
||||
{
|
||||
static MF_SeparateXYZ separate_fn;
|
||||
builder.set_matching_fn(separate_fn);
|
||||
}
|
||||
|
||||
void register_node_type_sh_sepxyz(void)
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
@ -51,6 +87,7 @@ void register_node_type_sh_sepxyz(void)
|
|||
sh_fn_node_type_base(&ntype, SH_NODE_SEPXYZ, "Separate XYZ", NODE_CLASS_CONVERTOR, 0);
|
||||
node_type_socket_templates(&ntype, sh_node_sepxyz_in, sh_node_sepxyz_out);
|
||||
node_type_gpu(&ntype, gpu_shader_sepxyz);
|
||||
ntype.expand_in_mf_network = sh_node_sepxyz_expand_in_mf_network;
|
||||
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
|
@ -76,6 +113,39 @@ static int gpu_shader_combxyz(GPUMaterial *mat,
|
|||
return GPU_stack_link(mat, node, "combine_xyz", in, out);
|
||||
}
|
||||
|
||||
class MF_CombineXYZ : public blender::fn::MultiFunction {
|
||||
public:
|
||||
MF_CombineXYZ()
|
||||
{
|
||||
blender::fn::MFSignatureBuilder signature = this->get_builder("Combine XYZ");
|
||||
signature.single_input<float>("X");
|
||||
signature.single_input<float>("Y");
|
||||
signature.single_input<float>("Z");
|
||||
signature.single_output<blender::float3>("XYZ");
|
||||
}
|
||||
|
||||
void call(blender::IndexMask mask,
|
||||
blender::fn::MFParams params,
|
||||
blender::fn::MFContext UNUSED(context)) const override
|
||||
{
|
||||
blender::fn::VSpan<float> xs = params.readonly_single_input<float>(0, "X");
|
||||
blender::fn::VSpan<float> ys = params.readonly_single_input<float>(1, "Y");
|
||||
blender::fn::VSpan<float> zs = params.readonly_single_input<float>(2, "Z");
|
||||
blender::MutableSpan<blender::float3> vectors =
|
||||
params.uninitialized_single_output<blender::float3>(3, "XYZ");
|
||||
|
||||
for (uint i : mask) {
|
||||
vectors[i] = {xs[i], ys[i], zs[i]};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void sh_node_combxyz_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder)
|
||||
{
|
||||
static MF_CombineXYZ combine_fn;
|
||||
builder.set_matching_fn(combine_fn);
|
||||
}
|
||||
|
||||
void register_node_type_sh_combxyz(void)
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
@ -83,6 +153,7 @@ void register_node_type_sh_combxyz(void)
|
|||
sh_fn_node_type_base(&ntype, SH_NODE_COMBXYZ, "Combine XYZ", NODE_CLASS_CONVERTOR, 0);
|
||||
node_type_socket_templates(&ntype, sh_node_combxyz_in, sh_node_combxyz_out);
|
||||
node_type_gpu(&ntype, gpu_shader_combxyz);
|
||||
ntype.expand_in_mf_network = sh_node_combxyz_expand_in_mf_network;
|
||||
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
Loading…
Reference in New Issue