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:
Jacques Lucke 2020-07-07 18:23:33 +02:00
parent ff97545c50
commit 4990e4dd01
17 changed files with 1080 additions and 28 deletions

View File

@ -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;

View File

@ -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__ */

View File

@ -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

View File

@ -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

View File

@ -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__ */

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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")},

View File

@ -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));

View File

@ -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

View File

@ -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);
}