diff options
Diffstat (limited to 'source/blender/blenkernel/intern/node_tree_multi_function.cc')
-rw-r--r-- | source/blender/blenkernel/intern/node_tree_multi_function.cc | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/node_tree_multi_function.cc b/source/blender/blenkernel/intern/node_tree_multi_function.cc new file mode 100644 index 00000000000..4e505db9b9d --- /dev/null +++ b/source/blender/blenkernel/intern/node_tree_multi_function.cc @@ -0,0 +1,306 @@ +/* + * 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" + +#include "BLI_float3.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) { + if (is_multi_function_data_socket(from_dsockets[0]->bsocket())) { + return &common.network_map.lookup(*from_dsockets[0]); + } + else { + return nullptr; + } + } + else { + if (is_multi_function_data_socket(from_group_inputs[0]->bsocket())) { + return &common.network_map.lookup(*from_group_inputs[0]); + } + else { + return nullptr; + } + } +} + +static const fn::MultiFunction *try_get_conversion_function(fn::MFDataType from, fn::MFDataType to) +{ + if (from == fn::MFDataType::ForSingle<float>()) { + if (to == fn::MFDataType::ForSingle<int32_t>()) { + static fn::CustomMF_Convert<float, int32_t> function; + return &function; + } + if (to == fn::MFDataType::ForSingle<float3>()) { + static fn::CustomMF_Convert<float, float3> function; + return &function; + } + } + if (from == fn::MFDataType::ForSingle<float3>()) { + if (to == fn::MFDataType::ForSingle<float>()) { + static fn::CustomMF_SI_SO<float3, float> function{"Vector Length", + [](float3 a) { return a.length(); }}; + return &function; + } + } + if (from == fn::MFDataType::ForSingle<int32_t>()) { + if (to == fn::MFDataType::ForSingle<float>()) { + static fn::CustomMF_Convert<int32_t, float> function; + return &function; + } + if (to == fn::MFDataType::ForSingle<float3>()) { + static fn::CustomMF_SI_SO<int32_t, float3> function{ + "int32 to float3", [](int32_t a) { return float3((float)a); }}; + return &function; + } + } + + return nullptr; +} + +static fn::MFOutputSocket &insert_default_value_for_type(CommonMFNetworkBuilderData &common, + fn::MFDataType type) +{ + const fn::MultiFunction *default_fn; + if (type.is_single()) { + default_fn = &common.resources.construct<fn::CustomMF_GenericConstant>( + AT, type.single_type(), type.single_type().default_value()); + } + else { + default_fn = &common.resources.construct<fn::CustomMF_GenericConstantArray>( + AT, fn::GSpan(type.vector_base_type())); + } + + fn::MFNode &node = common.network.add_function(*default_fn); + return node.output(0); +} + +static void insert_links(CommonMFNetworkBuilderData &common) +{ + for (const DInputSocket *to_dsocket : common.tree.input_sockets()) { + if (!to_dsocket->is_available()) { + continue; + } + if (!to_dsocket->is_linked()) { + continue; + } + if (!is_multi_function_data_socket(to_dsocket->bsocket())) { + continue; + } + + Span<fn::MFInputSocket *> to_sockets = common.network_map.lookup(*to_dsocket); + BLI_assert(to_sockets.size() >= 1); + fn::MFDataType to_type = to_sockets[0]->data_type(); + + fn::MFOutputSocket *from_socket = try_find_origin(common, *to_dsocket); + if (from_socket == nullptr) { + from_socket = &insert_default_value_for_type(common, to_type); + } + + fn::MFDataType from_type = from_socket->data_type(); + + if (from_type != to_type) { + const fn::MultiFunction *conversion_fn = try_get_conversion_function(from_type, to_type); + if (conversion_fn != nullptr) { + fn::MFNode &node = common.network.add_function(*conversion_fn); + common.network.add_link(*from_socket, node.input(0)); + from_socket = &node.output(0); + } + else { + from_socket = &insert_default_value_for_type(common, to_type); + } + } + + for (fn::MFInputSocket *to_socket : to_sockets) { + common.network.add_link(*from_socket, *to_socket); + } + } +} + +static void insert_unlinked_input(CommonMFNetworkBuilderData &common, const DInputSocket &dsocket) +{ + bNodeSocket *bsocket = dsocket.bsocket(); + bNodeSocketType *socktype = bsocket->typeinfo; + BLI_assert(socktype->expand_in_mf_network != nullptr); + + SocketMFNetworkBuilder builder{common, dsocket}; + socktype->expand_in_mf_network(builder); + + fn::MFOutputSocket *from_socket = builder.built_socket(); + BLI_assert(from_socket != nullptr); + + for (fn::MFInputSocket *to_socket : common.network_map.lookup(dsocket)) { + common.network.add_link(*from_socket, *to_socket); + } +} + +static void insert_unlinked_inputs(CommonMFNetworkBuilderData &common) +{ + Vector<const DInputSocket *> unlinked_data_inputs; + for (const DInputSocket *dsocket : common.tree.input_sockets()) { + if (dsocket->is_available()) { + if (is_multi_function_data_socket(dsocket->bsocket())) { + if (!dsocket->is_linked()) { + insert_unlinked_input(common, *dsocket); + } + } + } + } +} + +/** + * Expands all function nodes contained in the given node tree within the given multi-function + * network. + * + * Returns a mapping between the original node tree and the generated nodes/sockets for further + * processing. + */ +MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network, + const DerivedNodeTree &tree, + ResourceCollector &resources) +{ + MFNetworkTreeMap network_map{tree, network}; + + CommonMFNetworkBuilderData common{resources, network, network_map, tree}; + + insert_nodes(common); + insert_group_inputs(common); + insert_links(common); + insert_unlinked_inputs(common); + + return network_map; +} + +} // namespace bke +} // namespace blender |