/* * 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 "NOD_node_tree_multi_function.hh" #include "FN_multi_function_network_evaluation.hh" #include "BLI_color.hh" #include "BLI_float2.hh" #include "BLI_float3.hh" namespace blender::nodes { const fn::MultiFunction &NodeMFNetworkBuilder::get_default_fn(StringRef name) { Vector input_types; Vector output_types; for (const DInputSocket *dsocket : dnode_.inputs()) { if (dsocket->is_available()) { std::optional data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo); if (data_type.has_value()) { input_types.append(*data_type); } } } for (const DOutputSocket *dsocket : dnode_.outputs()) { if (dsocket->is_available()) { std::optional data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo); if (data_type.has_value()) { output_types.append(*data_type); } } } const fn::MultiFunction &fn = this->construct_fn( name, input_types, output_types); return fn; } static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &dnode) { constexpr int 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 = socket_mf_type_get(*dsocket->bsocket()->typeinfo); 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 = socket_mf_type_get(*dsocket->bsocket()->typeinfo); 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 (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) { return true; } } for (const DOutputSocket *socket : dnode.outputs()) { if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) { 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 (socket_is_mf_data_socket(*bsocket->typeinfo)) { 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(); int 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) { const DOutputSocket &from_dsocket = *from_dsockets[0]; if (!from_dsocket.is_available()) { return nullptr; } if (socket_is_mf_data_socket(*from_dsocket.bsocket()->typeinfo)) { return &common.network_map.lookup(from_dsocket); } return nullptr; } const DGroupInput &from_group_input = *from_group_inputs[0]; if (socket_is_mf_data_socket(*from_group_input.bsocket()->typeinfo)) { return &common.network_map.lookup(from_group_input); } return nullptr; } template static void add_implicit_conversion(DataTypeConversions &conversions) { static fn::CustomMF_Convert function; conversions.add(fn::MFDataType::ForSingle(), fn::MFDataType::ForSingle(), function); } template static void add_implicit_conversion(DataTypeConversions &conversions, StringRef name, ConversionF conversion) { static fn::CustomMF_SI_SO function{name, conversion}; conversions.add(fn::MFDataType::ForSingle(), fn::MFDataType::ForSingle(), function); } static DataTypeConversions create_implicit_conversions() { DataTypeConversions conversions; add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion( conversions, "float to Color4f", [](float a) { return Color4f(a, a, a, 1.0f); }); add_implicit_conversion( conversions, "float2 to float3", [](float2 a) { return float3(a.x, a.y, 0.0f); }); add_implicit_conversion( conversions, "float2 to float", [](float2 a) { return a.length(); }); add_implicit_conversion( conversions, "float2 to int32_t", [](float2 a) { return (int32_t)a.length(); }); add_implicit_conversion( conversions, "float2 to bool", [](float2 a) { return a.length_squared() == 0.0f; }); add_implicit_conversion( conversions, "float2 to Color4f", [](float2 a) { return Color4f(a.x, a.y, 0.0f, 1.0f); }); add_implicit_conversion( conversions, "float3 to boolean", [](float3 a) { return a.length_squared() == 0.0f; }); add_implicit_conversion( conversions, "Vector Length", [](float3 a) { return a.length(); }); add_implicit_conversion( conversions, "float3 to int32_t", [](float3 a) { return (int)a.length(); }); add_implicit_conversion(conversions); add_implicit_conversion( conversions, "float3 to Color4f", [](float3 a) { return Color4f(a.x, a.y, a.z, 1.0f); }); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion( conversions, "int32 to float2", [](int32_t a) { return float2((float)a); }); add_implicit_conversion( conversions, "int32 to float3", [](int32_t a) { return float3((float)a); }); add_implicit_conversion(conversions); add_implicit_conversion(conversions); add_implicit_conversion( conversions, "boolean to float2", [](bool a) { return (a) ? float2(1.0f) : float2(0.0f); }); add_implicit_conversion( conversions, "boolean to float3", [](bool a) { return (a) ? float3(1.0f) : float3(0.0f); }); add_implicit_conversion(conversions, "boolean to Color4f", [](bool a) { return (a) ? Color4f(1.0f, 1.0f, 1.0f, 1.0f) : Color4f(0.0f, 0.0f, 0.0f, 1.0f); }); add_implicit_conversion( conversions, "Color4f to float", [](Color4f a) { return rgb_to_grayscale(a); }); add_implicit_conversion( conversions, "Color4f to float2", [](Color4f a) { return float2(a.r, a.g); }); add_implicit_conversion( conversions, "Color4f to float3", [](Color4f a) { return float3(a.r, a.g, a.b); }); return conversions; } const DataTypeConversions &get_implicit_type_conversions() { static const DataTypeConversions conversions = create_implicit_conversions(); return conversions; } void DataTypeConversions::convert(const CPPType &from_type, const CPPType &to_type, const void *from_value, void *to_value) const { const fn::MultiFunction *fn = this->get_conversion(MFDataType::ForSingle(from_type), MFDataType::ForSingle(to_type)); BLI_assert(fn != nullptr); fn::MFContextBuilder context; fn::MFParamsBuilder params{*fn, 1}; params.add_readonly_single_input(fn::GSpan(from_type, from_value, 1)); params.add_uninitialized_single_output(fn::GMutableSpan(to_type, to_value, 1)); fn->call({0}, params, context); } 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 (!socket_is_mf_data_socket(*to_dsocket->bsocket()->typeinfo)) { 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 = get_implicit_type_conversions().get_conversion( 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 (socket_is_mf_data_socket(*dsocket->bsocket()->typeinfo)) { 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; } /** * A single node is allowed to expand into multiple nodes before evaluation. Depending on what * nodes it expands to, it belongs a different type of the ones below. */ enum class NodeExpandType { SingleFunctionNode, MultipleFunctionNodes, HasDummyNodes, }; /** * Checks how the given node expanded in the multi-function network. If it is only a single * function node, the corresponding function is returned as well. */ static NodeExpandType get_node_expand_type(MFNetworkTreeMap &network_map, const DNode &dnode, const fn::MultiFunction **r_single_function) { const fn::MFFunctionNode *single_function_node = nullptr; bool has_multiple_nodes = false; bool has_dummy_nodes = false; auto check_mf_node = [&](fn::MFNode &mf_node) { if (mf_node.is_function()) { if (single_function_node == nullptr) { single_function_node = &mf_node.as_function(); } if (&mf_node != single_function_node) { has_multiple_nodes = true; } } else { BLI_assert(mf_node.is_dummy()); has_dummy_nodes = true; } }; for (const DInputSocket *dsocket : dnode.inputs()) { if (dsocket->is_available()) { for (fn::MFInputSocket *mf_input : network_map.lookup(*dsocket)) { check_mf_node(mf_input->node()); } } } for (const DOutputSocket *dsocket : dnode.outputs()) { if (dsocket->is_available()) { fn::MFOutputSocket &mf_output = network_map.lookup(*dsocket); check_mf_node(mf_output.node()); } } if (has_dummy_nodes) { return NodeExpandType::HasDummyNodes; } if (has_multiple_nodes) { return NodeExpandType::MultipleFunctionNodes; } *r_single_function = &single_function_node->function(); return NodeExpandType::SingleFunctionNode; } static const fn::MultiFunction &create_function_for_node_that_expands_into_multiple( const DNode &dnode, fn::MFNetwork &network, MFNetworkTreeMap &network_map, ResourceCollector &resources) { Vector dummy_fn_inputs; for (const DInputSocket *dsocket : dnode.inputs()) { if (dsocket->is_available()) { MFDataType data_type = *socket_mf_type_get(*dsocket->typeinfo()); fn::MFOutputSocket &fn_input = network.add_input(data_type.to_string(), data_type); for (fn::MFInputSocket *mf_input : network_map.lookup(*dsocket)) { network.add_link(fn_input, *mf_input); dummy_fn_inputs.append(&fn_input); } } } Vector dummy_fn_outputs; for (const DOutputSocket *dsocket : dnode.outputs()) { if (dsocket->is_available()) { fn::MFOutputSocket &mf_output = network_map.lookup(*dsocket); MFDataType data_type = mf_output.data_type(); fn::MFInputSocket &fn_output = network.add_output(data_type.to_string(), data_type); network.add_link(mf_output, fn_output); dummy_fn_outputs.append(&fn_output); } } fn::MFNetworkEvaluator &fn_evaluator = resources.construct( __func__, std::move(dummy_fn_inputs), std::move(dummy_fn_outputs)); return fn_evaluator; } /** * Returns a single multi-function for every node that supports it. This makes it easier to reuse * the multi-function implementation of nodes in different contexts. */ MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceCollector &resources) { /* Build a network that nodes can insert themselves into. However, the individual nodes are not * connected. */ fn::MFNetwork &network = resources.construct(__func__); MFNetworkTreeMap network_map{tree, network}; MultiFunctionByNode functions_by_node; CommonMFNetworkBuilderData common{resources, network, network_map, tree}; for (const DNode *dnode : tree.nodes()) { const bNodeType *node_type = dnode->typeinfo(); if (node_type->expand_in_mf_network == nullptr) { /* This node does not have a multi-function implementation. */ continue; } NodeMFNetworkBuilder builder{common, *dnode}; node_type->expand_in_mf_network(builder); const fn::MultiFunction *single_function = nullptr; const NodeExpandType expand_type = get_node_expand_type(network_map, *dnode, &single_function); switch (expand_type) { case NodeExpandType::HasDummyNodes: { /* Dummy nodes cannot be executed, so skip them. */ break; } case NodeExpandType::SingleFunctionNode: { /* This is the common case. Most nodes just expand to a single function. */ functions_by_node.add_new(dnode, single_function); break; } case NodeExpandType::MultipleFunctionNodes: { /* If a node expanded into multiple functions, a new function has to be created that * combines those. */ const fn::MultiFunction &fn = create_function_for_node_that_expands_into_multiple( *dnode, network, network_map, resources); functions_by_node.add_new(dnode, &fn); break; } } } return functions_by_node; } } // namespace blender::nodes