/* * 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 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 input_types; Vector input_names; Vector input_dsockets; for (const DInputSocket *dsocket : dnode.inputs()) { if (dsocket->is_available()) { std::optional 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 output_types; Vector output_names; Vector output_dsockets; for (const DOutputSocket *dsocket : dnode.outputs()) { if (dsocket->is_available()) { std::optional 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 from_dsockets = to_dsocket.linked_sockets(); Span 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()) { if (to == fn::MFDataType::ForSingle()) { static fn::CustomMF_Convert function; return &function; } if (to == fn::MFDataType::ForSingle()) { static fn::CustomMF_Convert function; return &function; } } if (from == fn::MFDataType::ForSingle()) { if (to == fn::MFDataType::ForSingle()) { static fn::CustomMF_SI_SO function{"Vector Length", [](float3 a) { return a.length(); }}; return &function; } } if (from == fn::MFDataType::ForSingle()) { if (to == fn::MFDataType::ForSingle()) { static fn::CustomMF_Convert function; return &function; } if (to == fn::MFDataType::ForSingle()) { static fn::CustomMF_SI_SO 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( AT, type.single_type(), type.single_type().default_value()); } else { default_fn = &common.resources.construct( 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 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 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