Nodes: support implicit conversions and incorrectly linked sockets
This commit is contained in:
		@@ -16,6 +16,8 @@
 | 
			
		||||
 | 
			
		||||
#include "BKE_node_tree_multi_function.hh"
 | 
			
		||||
 | 
			
		||||
#include "BLI_float3.hh"
 | 
			
		||||
 | 
			
		||||
namespace blender {
 | 
			
		||||
namespace bke {
 | 
			
		||||
 | 
			
		||||
@@ -136,11 +138,56 @@ static fn::MFOutputSocket *try_find_origin(CommonMFNetworkBuilderData &common,
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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<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;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  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)
 | 
			
		||||
@@ -149,24 +196,35 @@ static void insert_links(CommonMFNetworkBuilderData &common)
 | 
			
		||||
    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<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();
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
      /* 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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -202,6 +202,30 @@ template<typename Mut1> class CustomMF_SM : public MultiFunction {
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Generates a multi-function that converts between two types.
 | 
			
		||||
 */
 | 
			
		||||
template<typename From, typename To> class CustomMF_Convert : public MultiFunction {
 | 
			
		||||
 public:
 | 
			
		||||
  CustomMF_Convert()
 | 
			
		||||
  {
 | 
			
		||||
    std::string name = CPPType::get<From>().name() + " to " + CPPType::get<To>().name();
 | 
			
		||||
    MFSignatureBuilder signature = this->get_builder(std::move(name));
 | 
			
		||||
    signature.single_input<From>("Input");
 | 
			
		||||
    signature.single_output<To>("Output");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
 | 
			
		||||
  {
 | 
			
		||||
    VSpan<From> inputs = params.readonly_single_input<From>(0);
 | 
			
		||||
    MutableSpan<To> outputs = params.uninitialized_single_output<To>(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.
 | 
			
		||||
 
 | 
			
		||||
@@ -364,4 +364,23 @@ TEST(multi_function, CustomMF_GenericConstantArray)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(multi_function, CustomMF_Convert)
 | 
			
		||||
{
 | 
			
		||||
  CustomMF_Convert<float, int> fn;
 | 
			
		||||
 | 
			
		||||
  Array<float> inputs = {5.4f, 7.1f, 9.0f};
 | 
			
		||||
  Array<int> 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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user