From c7eada103c67021052f4c6a2340ca8f744cf17db Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 11 Jul 2020 18:02:06 +0200 Subject: Nodes: support implicit conversions and incorrectly linked sockets --- .../blenkernel/intern/node_tree_multi_function.cc | 76 +++++++++++++++++++--- .../blender/functions/FN_multi_function_builder.hh | 24 +++++++ tests/gtests/functions/FN_multi_function_test.cc | 19 ++++++ 3 files changed, 110 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/intern/node_tree_multi_function.cc b/source/blender/blenkernel/intern/node_tree_multi_function.cc index 02437d98732..1aaefd4a301 100644 --- a/source/blender/blenkernel/intern/node_tree_multi_function.cc +++ b/source/blender/blenkernel/intern/node_tree_multi_function.cc @@ -16,6 +16,8 @@ #include "BKE_node_tree_multi_function.hh" +#include "BLI_float3.hh" + namespace blender { namespace bke { @@ -136,37 +138,93 @@ static fn::MFOutputSocket *try_find_origin(CommonMFNetworkBuilderData &common, } if (from_dsockets.size() == 1) { - return &common.network_map.lookup(*from_dsockets[0]); + if (is_multi_function_data_socket(from_dsockets[0]->bsocket())) { + return &common.network_map.lookup(*from_dsockets[0]); + } + else { + return nullptr; + } } else { - return &common.network_map.lookup(*from_group_inputs[0]); + 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 (from == fn::MFDataType::ForSingle()) { + if (to == fn::MFDataType::ForSingle()) { + static fn::CustomMF_SI_SO function{"Vector Length", + [](float3 a) { return a.length(); }}; + 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 (!is_multi_function_data_socket(to_dsocket->bsocket())) { + if (!to_dsocket->is_linked()) { continue; } - - fn::MFOutputSocket *from_socket = try_find_origin(common, *to_dsocket); - if (from_socket == nullptr) { + 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(); - fn::MFDataType to_type = to_sockets[0]->data_type(); if (from_type != to_type) { - /* Todo: Try inserting implicit conversion. */ + 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); + continue; + } } for (fn::MFInputSocket *to_socket : to_sockets) { diff --git a/source/blender/functions/FN_multi_function_builder.hh b/source/blender/functions/FN_multi_function_builder.hh index 5905d1cc315..6e7efb21850 100644 --- a/source/blender/functions/FN_multi_function_builder.hh +++ b/source/blender/functions/FN_multi_function_builder.hh @@ -202,6 +202,30 @@ template class CustomMF_SM : public MultiFunction { } }; +/** + * Generates a multi-function that converts between two types. + */ +template class CustomMF_Convert : public MultiFunction { + public: + CustomMF_Convert() + { + std::string name = CPPType::get().name() + " to " + CPPType::get().name(); + MFSignatureBuilder signature = this->get_builder(std::move(name)); + signature.single_input("Input"); + signature.single_output("Output"); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + VSpan inputs = params.readonly_single_input(0); + MutableSpan outputs = params.uninitialized_single_output(1); + + for (uint i : mask) { + new ((void *)&outputs[i]) To(inputs[i]); + } + } +}; + /** * A multi-function that outputs the same value every time. The value is not owned by an instance * of this function. The caller is responsible for destructing and freeing the value. diff --git a/tests/gtests/functions/FN_multi_function_test.cc b/tests/gtests/functions/FN_multi_function_test.cc index 51115e4cd6b..2544f1c63b0 100644 --- a/tests/gtests/functions/FN_multi_function_test.cc +++ b/tests/gtests/functions/FN_multi_function_test.cc @@ -364,4 +364,23 @@ TEST(multi_function, CustomMF_GenericConstantArray) } } +TEST(multi_function, CustomMF_Convert) +{ + CustomMF_Convert fn; + + Array inputs = {5.4f, 7.1f, 9.0f}; + Array outputs{inputs.size(), 0}; + + MFParamsBuilder params(fn, inputs.size()); + params.add_readonly_single_input(inputs.as_span()); + params.add_uninitialized_single_output(outputs.as_mutable_span()); + + MFContextBuilder context; + fn.call({0, 2}, params, context); + + EXPECT_EQ(outputs[0], 5); + EXPECT_EQ(outputs[1], 0); + EXPECT_EQ(outputs[2], 9); +} + } // namespace blender::fn -- cgit v1.2.3