Blender  V3.3
node_geo_transfer_attribute.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include "BLI_generic_array.hh"
4 #include "BLI_kdopbvh.h"
5 #include "BLI_task.hh"
6 
7 #include "DNA_mesh_types.h"
8 #include "DNA_meshdata_types.h"
9 #include "DNA_pointcloud_types.h"
10 
11 #include "BKE_attribute_math.hh"
12 #include "BKE_bvhutils.h"
13 #include "BKE_mesh_runtime.h"
14 #include "BKE_mesh_sample.hh"
15 
16 #include "UI_interface.h"
17 #include "UI_resources.h"
18 
20 
21 #include "node_geometry_util.hh"
22 
24 
25 using namespace blender::bke::mesh_surface_sample;
26 
28 
30 {
31  b.add_input<decl::Geometry>(N_("Source"))
32  .supported_type({GEO_COMPONENT_TYPE_MESH,
36 
37  b.add_input<decl::Vector>(N_("Attribute")).hide_value().supports_field();
38  b.add_input<decl::Float>(N_("Attribute"), "Attribute_001").hide_value().supports_field();
39  b.add_input<decl::Color>(N_("Attribute"), "Attribute_002").hide_value().supports_field();
40  b.add_input<decl::Bool>(N_("Attribute"), "Attribute_003").hide_value().supports_field();
41  b.add_input<decl::Int>(N_("Attribute"), "Attribute_004").hide_value().supports_field();
42 
43  b.add_input<decl::Vector>(N_("Source Position"))
44  .implicit_field()
45  .make_available([](bNode &node) {
47  });
48  b.add_input<decl::Int>(N_("Index")).implicit_field().make_available([](bNode &node) {
49  node_storage(node).mode = GEO_NODE_ATTRIBUTE_TRANSFER_INDEX;
50  });
51 
52  b.add_output<decl::Vector>(N_("Attribute")).dependent_field({6, 7});
53  b.add_output<decl::Float>(N_("Attribute"), "Attribute_001").dependent_field({6, 7});
54  b.add_output<decl::Color>(N_("Attribute"), "Attribute_002").dependent_field({6, 7});
55  b.add_output<decl::Bool>(N_("Attribute"), "Attribute_003").dependent_field({6, 7});
56  b.add_output<decl::Int>(N_("Attribute"), "Attribute_004").dependent_field({6, 7});
57 }
58 
59 static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
60 {
61  const bNode &node = *static_cast<const bNode *>(ptr->data);
62  const NodeGeometryTransferAttribute &storage = node_storage(node);
64  storage.mode;
65 
66  uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
67  uiItemR(layout, ptr, "mapping", 0, "", ICON_NONE);
69  uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
70  }
71 }
72 
74 {
75  NodeGeometryTransferAttribute *data = MEM_cnew<NodeGeometryTransferAttribute>(__func__);
76  data->data_type = CD_PROP_FLOAT;
78  node->storage = data;
79 }
80 
82 {
83  const NodeGeometryTransferAttribute &storage = node_storage(*node);
84  const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type);
86  storage.mode;
87 
88  bNodeSocket *socket_geometry = (bNodeSocket *)node->inputs.first;
89  bNodeSocket *socket_vector = socket_geometry->next;
90  bNodeSocket *socket_float = socket_vector->next;
91  bNodeSocket *socket_color4f = socket_float->next;
92  bNodeSocket *socket_boolean = socket_color4f->next;
93  bNodeSocket *socket_int32 = socket_boolean->next;
94 
95  bNodeSocket *socket_positions = socket_int32->next;
96  bNodeSocket *socket_indices = socket_positions->next;
97 
98  nodeSetSocketAvailability(ntree, socket_vector, data_type == CD_PROP_FLOAT3);
99  nodeSetSocketAvailability(ntree, socket_float, data_type == CD_PROP_FLOAT);
100  nodeSetSocketAvailability(ntree, socket_color4f, data_type == CD_PROP_COLOR);
101  nodeSetSocketAvailability(ntree, socket_boolean, data_type == CD_PROP_BOOL);
102  nodeSetSocketAvailability(ntree, socket_int32, data_type == CD_PROP_INT32);
103 
106 
107  bNodeSocket *out_socket_vector = (bNodeSocket *)node->outputs.first;
108  bNodeSocket *out_socket_float = out_socket_vector->next;
109  bNodeSocket *out_socket_color4f = out_socket_float->next;
110  bNodeSocket *out_socket_boolean = out_socket_color4f->next;
111  bNodeSocket *out_socket_int32 = out_socket_boolean->next;
112 
113  nodeSetSocketAvailability(ntree, out_socket_vector, data_type == CD_PROP_FLOAT3);
114  nodeSetSocketAvailability(ntree, out_socket_float, data_type == CD_PROP_FLOAT);
115  nodeSetSocketAvailability(ntree, out_socket_color4f, data_type == CD_PROP_COLOR);
116  nodeSetSocketAvailability(ntree, out_socket_boolean, data_type == CD_PROP_BOOL);
117  nodeSetSocketAvailability(ntree, out_socket_int32, data_type == CD_PROP_INT32);
118 }
119 
121 {
122  const NodeDeclaration &declaration = *params.node_type().fixed_declaration;
123  search_link_ops_for_declarations(params, declaration.inputs().take_back(2));
124  search_link_ops_for_declarations(params, declaration.inputs().take_front(1));
125 
126  const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type(
127  (eNodeSocketDatatype)params.other_socket().type);
128  if (type && *type != CD_PROP_STRING) {
129  /* The input and output sockets have the same name. */
130  params.add_item(IFACE_("Attribute"), [type](LinkSearchOpParams &params) {
131  bNode &node = params.add_node("GeometryNodeAttributeTransfer");
132  node_storage(node).data_type = *type;
133  params.update_and_connect_available_socket(node, "Attribute");
134  });
135  }
136 }
137 
139  const VArray<float3> &positions,
140  const IndexMask mask,
141  const MutableSpan<int> r_indices,
142  const MutableSpan<float> r_distances_sq,
143  const MutableSpan<float3> r_positions)
144 {
145  BLI_assert(positions.size() >= r_indices.size());
146  BLI_assert(positions.size() >= r_distances_sq.size());
147  BLI_assert(positions.size() >= r_positions.size());
148 
149  for (const int i : mask) {
150  BVHTreeNearest nearest;
151  nearest.dist_sq = FLT_MAX;
152  const float3 position = positions[i];
154  tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data);
155  if (!r_indices.is_empty()) {
156  r_indices[i] = nearest.index;
157  }
158  if (!r_distances_sq.is_empty()) {
159  r_distances_sq[i] = nearest.dist_sq;
160  }
161  if (!r_positions.is_empty()) {
162  r_positions[i] = nearest.co;
163  }
164  }
165 }
166 
167 static void get_closest_pointcloud_points(const PointCloud &pointcloud,
168  const VArray<float3> &positions,
169  const IndexMask mask,
170  const MutableSpan<int> r_indices,
171  const MutableSpan<float> r_distances_sq)
172 {
173  BLI_assert(positions.size() >= r_indices.size());
174  BLI_assert(pointcloud.totpoint > 0);
175 
176  BVHTreeFromPointCloud tree_data;
177  BKE_bvhtree_from_pointcloud_get(&tree_data, &pointcloud, 2);
178 
179  for (const int i : mask) {
180  BVHTreeNearest nearest;
181  nearest.dist_sq = FLT_MAX;
182  const float3 position = positions[i];
184  tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data);
185  r_indices[i] = nearest.index;
186  if (!r_distances_sq.is_empty()) {
187  r_distances_sq[i] = nearest.dist_sq;
188  }
189  }
190 
191  free_bvhtree_from_pointcloud(&tree_data);
192 }
193 
194 static void get_closest_mesh_points(const Mesh &mesh,
195  const VArray<float3> &positions,
196  const IndexMask mask,
197  const MutableSpan<int> r_point_indices,
198  const MutableSpan<float> r_distances_sq,
199  const MutableSpan<float3> r_positions)
200 {
201  BLI_assert(mesh.totvert > 0);
202  BVHTreeFromMesh tree_data;
204  get_closest_in_bvhtree(tree_data, positions, mask, r_point_indices, r_distances_sq, r_positions);
205  free_bvhtree_from_mesh(&tree_data);
206 }
207 
208 static void get_closest_mesh_edges(const Mesh &mesh,
209  const VArray<float3> &positions,
210  const IndexMask mask,
211  const MutableSpan<int> r_edge_indices,
212  const MutableSpan<float> r_distances_sq,
213  const MutableSpan<float3> r_positions)
214 {
215  BLI_assert(mesh.totedge > 0);
216  BVHTreeFromMesh tree_data;
218  get_closest_in_bvhtree(tree_data, positions, mask, r_edge_indices, r_distances_sq, r_positions);
219  free_bvhtree_from_mesh(&tree_data);
220 }
221 
223  const VArray<float3> &positions,
224  const IndexMask mask,
225  const MutableSpan<int> r_looptri_indices,
226  const MutableSpan<float> r_distances_sq,
227  const MutableSpan<float3> r_positions)
228 {
229  BLI_assert(mesh.totpoly > 0);
230  BVHTreeFromMesh tree_data;
233  tree_data, positions, mask, r_looptri_indices, r_distances_sq, r_positions);
234  free_bvhtree_from_mesh(&tree_data);
235 }
236 
238  const VArray<float3> &positions,
239  const IndexMask mask,
240  const MutableSpan<int> r_poly_indices,
241  const MutableSpan<float> r_distances_sq,
242  const MutableSpan<float3> r_positions)
243 {
244  BLI_assert(mesh.totpoly > 0);
245 
246  Array<int> looptri_indices(positions.size());
247  get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, r_distances_sq, r_positions);
248 
251 
252  for (const int i : mask) {
253  const MLoopTri &looptri = looptris[looptri_indices[i]];
254  r_poly_indices[i] = looptri.poly;
255  }
256 }
257 
258 /* The closest corner is defined to be the closest corner on the closest face. */
259 static void get_closest_mesh_corners(const Mesh &mesh,
260  const VArray<float3> &positions,
261  const IndexMask mask,
262  const MutableSpan<int> r_corner_indices,
263  const MutableSpan<float> r_distances_sq,
264  const MutableSpan<float3> r_positions)
265 {
266  BLI_assert(mesh.totloop > 0);
267  Array<int> poly_indices(positions.size());
268  get_closest_mesh_polygons(mesh, positions, mask, poly_indices, {}, {});
269 
270  for (const int i : mask) {
271  const float3 position = positions[i];
272  const int poly_index = poly_indices[i];
273  const MPoly &poly = mesh.mpoly[poly_index];
274 
275  /* Find the closest vertex in the polygon. */
276  float min_distance_sq = FLT_MAX;
277  const MVert *closest_mvert;
278  int closest_loop_index = 0;
279  for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
280  const MLoop &loop = mesh.mloop[loop_index];
281  const int vertex_index = loop.v;
282  const MVert &mvert = mesh.mvert[vertex_index];
283  const float distance_sq = math::distance_squared(position, float3(mvert.co));
284  if (distance_sq < min_distance_sq) {
285  min_distance_sq = distance_sq;
286  closest_loop_index = loop_index;
287  closest_mvert = &mvert;
288  }
289  }
290  if (!r_corner_indices.is_empty()) {
291  r_corner_indices[i] = closest_loop_index;
292  }
293  if (!r_positions.is_empty()) {
294  r_positions[i] = closest_mvert->co;
295  }
296  if (!r_distances_sq.is_empty()) {
297  r_distances_sq[i] = min_distance_sq;
298  }
299  }
300 }
301 
302 template<typename T>
304  const IndexMask mask,
305  const Span<int> indices,
306  const MutableSpan<T> dst)
307 {
308  if (src.is_empty()) {
309  return;
310  }
311  for (const int i : mask) {
312  dst[i] = src[indices[i]];
313  }
314 }
315 
316 template<typename T>
318  const IndexMask mask,
319  const VArray<int> &indices,
320  const MutableSpan<T> dst)
321 {
322  if (src.is_empty()) {
323  return;
324  }
325  const int max_index = src.size() - 1;
326  threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) {
327  for (const int i : range) {
328  const int index = mask[i];
329  dst[index] = src[std::clamp(indices[index], 0, max_index)];
330  }
331  });
332 }
333 
334 template<typename T>
336  const VArray<T> &src_2,
337  const Span<float> distances_1,
338  const Span<float> distances_2,
339  const IndexMask mask,
340  const Span<int> indices_1,
341  const Span<int> indices_2,
342  const MutableSpan<T> dst)
343 {
344  if (src_1.is_empty() || src_2.is_empty()) {
345  return;
346  }
347  for (const int i : mask) {
348  if (distances_1[i] < distances_2[i]) {
349  dst[i] = src_1[indices_1[i]];
350  }
351  else {
352  dst[i] = src_2[indices_2[i]];
353  }
354  }
355 }
356 
357 static bool component_is_available(const GeometrySet &geometry,
359  const eAttrDomain domain)
360 {
361  if (!geometry.has(type)) {
362  return false;
363  }
365  if (component.is_empty()) {
366  return false;
367  }
368  return component.attribute_domain_size(domain) != 0;
369 }
370 
377  GeometrySet source_;
378  GField src_field_;
379 
387 
388  fn::MFSignature signature_;
389 
390  std::optional<GeometryComponentFieldContext> source_context_;
391  std::unique_ptr<FieldEvaluator> source_evaluator_;
392  const GVArray *source_data_;
393 
394  public:
396  : source_(std::move(geometry)), src_field_(std::move(src_field))
397  {
398  source_.ensure_owns_direct_data();
399  signature_ = this->create_signature();
400  this->set_signature(&signature_);
401  this->evaluate_source_field();
402  }
403 
405  {
406  blender::fn::MFSignatureBuilder signature{"Attribute Transfer Nearest Interpolated"};
407  signature.single_input<float3>("Position");
408  signature.single_output("Attribute", src_field_.cpp_type());
409  return signature.build();
410  }
411 
413  {
414  const VArray<float3> &positions = params.readonly_single_input<float3>(0, "Position");
415  GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Attribute");
416 
417  const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>();
418  BLI_assert(mesh_component.has_mesh());
419  const Mesh &mesh = *mesh_component.get_for_read();
420  BLI_assert(mesh.totpoly > 0);
421 
422  /* Find closest points on the mesh surface. */
423  Array<int> looptri_indices(mask.min_array_size());
424  Array<float3> sampled_positions(mask.min_array_size());
425  get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, {}, sampled_positions);
426 
427  MeshAttributeInterpolator interp(&mesh, mask, sampled_positions, looptri_indices);
428  interp.sample_data(*source_data_, domain_, eAttributeMapMode::INTERPOLATED, dst);
429  }
430 
431  private:
432  void evaluate_source_field()
433  {
434  const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>();
435  source_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_});
436  const int domain_num = mesh_component.attribute_domain_size(domain_);
437  source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_num);
438  source_evaluator_->add(src_field_);
439  source_evaluator_->evaluate();
440  source_data_ = &source_evaluator_->get_evaluated(0);
441  }
442 };
443 
450  GeometrySet source_;
451  GField src_field_;
452  eAttrDomain domain_;
453 
454  fn::MFSignature signature_;
455 
456  bool use_mesh_;
457  bool use_points_;
458 
459  /* Store data from the source as a virtual array, since we may only access a few indices. */
460  std::optional<GeometryComponentFieldContext> mesh_context_;
461  std::unique_ptr<FieldEvaluator> mesh_evaluator_;
462  const GVArray *mesh_data_;
463 
464  std::optional<GeometryComponentFieldContext> point_context_;
465  std::unique_ptr<FieldEvaluator> point_evaluator_;
466  const GVArray *point_data_;
467 
468  public:
470  : source_(std::move(geometry)), src_field_(std::move(src_field)), domain_(domain)
471  {
472  source_.ensure_owns_direct_data();
473  signature_ = this->create_signature();
474  this->set_signature(&signature_);
475 
476  this->use_mesh_ = component_is_available(source_, GEO_COMPONENT_TYPE_MESH, domain_);
477  this->use_points_ = component_is_available(source_, GEO_COMPONENT_TYPE_POINT_CLOUD, domain_);
478 
479  this->evaluate_source_field();
480  }
481 
483  {
484  blender::fn::MFSignatureBuilder signature{"Attribute Transfer Nearest"};
485  signature.single_input<float3>("Position");
486  signature.single_output("Attribute", src_field_.cpp_type());
487  return signature.build();
488  }
489 
491  {
492  const VArray<float3> &positions = params.readonly_single_input<float3>(0, "Position");
493  GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Attribute");
494 
495  if (!use_mesh_ && !use_points_) {
496  dst.type().value_initialize_indices(dst.data(), mask);
497  return;
498  }
499 
500  const Mesh *mesh = use_mesh_ ? source_.get_mesh_for_read() : nullptr;
501  const PointCloud *pointcloud = use_points_ ? source_.get_pointcloud_for_read() : nullptr;
502 
503  const int tot_samples = mask.min_array_size();
504 
505  Array<int> point_indices;
506  Array<float> point_distances;
507 
508  /* Depending on where what domain the source attribute lives, these indices are either vertex,
509  * corner, edge or polygon indices. */
510  Array<int> mesh_indices;
511  Array<float> mesh_distances;
512 
513  /* If there is a point cloud, find the closest points. */
514  if (use_points_) {
515  point_indices.reinitialize(tot_samples);
516  if (use_mesh_) {
517  point_distances.reinitialize(tot_samples);
518  }
519  get_closest_pointcloud_points(*pointcloud, positions, mask, point_indices, point_distances);
520  }
521 
522  /* If there is a mesh, find the closest mesh elements. */
523  if (use_mesh_) {
524  mesh_indices.reinitialize(tot_samples);
525  if (use_points_) {
526  mesh_distances.reinitialize(tot_samples);
527  }
528  switch (domain_) {
529  case ATTR_DOMAIN_POINT: {
530  get_closest_mesh_points(*mesh, positions, mask, mesh_indices, mesh_distances, {});
531  break;
532  }
533  case ATTR_DOMAIN_EDGE: {
534  get_closest_mesh_edges(*mesh, positions, mask, mesh_indices, mesh_distances, {});
535  break;
536  }
537  case ATTR_DOMAIN_FACE: {
538  get_closest_mesh_polygons(*mesh, positions, mask, mesh_indices, mesh_distances, {});
539  break;
540  }
541  case ATTR_DOMAIN_CORNER: {
542  get_closest_mesh_corners(*mesh, positions, mask, mesh_indices, mesh_distances, {});
543  break;
544  }
545  default: {
546  break;
547  }
548  }
549  }
550 
551  attribute_math::convert_to_static_type(dst.type(), [&](auto dummy) {
552  using T = decltype(dummy);
553  if (use_mesh_ && use_points_) {
554  VArray<T> src_mesh = mesh_data_->typed<T>();
555  VArray<T> src_point = point_data_->typed<T>();
556  copy_with_indices_and_comparison(src_mesh,
557  src_point,
558  mesh_distances,
559  point_distances,
560  mask,
561  mesh_indices,
562  point_indices,
563  dst.typed<T>());
564  }
565  else if (use_points_) {
566  VArray<T> src_point = point_data_->typed<T>();
567  copy_with_indices(src_point, mask, point_indices, dst.typed<T>());
568  }
569  else if (use_mesh_) {
570  VArray<T> src_mesh = mesh_data_->typed<T>();
571  copy_with_indices(src_mesh, mask, mesh_indices, dst.typed<T>());
572  }
573  });
574  }
575 
576  private:
577  void evaluate_source_field()
578  {
579  if (use_mesh_) {
581  const int domain_num = mesh.attribute_domain_size(domain_);
582  mesh_context_.emplace(GeometryComponentFieldContext(mesh, domain_));
583  mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_num);
584  mesh_evaluator_->add(src_field_);
585  mesh_evaluator_->evaluate();
586  mesh_data_ = &mesh_evaluator_->get_evaluated(0);
587  }
588 
589  if (use_points_) {
591  const int domain_num = points.attribute_domain_size(domain_);
592  point_context_.emplace(GeometryComponentFieldContext(points, domain_));
593  point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, domain_num);
594  point_evaluator_->add(src_field_);
595  point_evaluator_->evaluate();
596  point_data_ = &point_evaluator_->get_evaluated(0);
597  }
598  }
599 };
600 
602  const eAttrDomain domain)
603 {
604  /* Choose the other component based on a consistent order, rather than some more complicated
605  * heuristic. This is the same order visible in the spreadsheet and used in the ray-cast node. */
606  static const Array<GeometryComponentType> supported_types = {GEO_COMPONENT_TYPE_MESH,
610  for (const GeometryComponentType src_type : supported_types) {
611  if (component_is_available(geometry, src_type, domain)) {
612  return geometry.get_component_for_read(src_type);
613  }
614  }
615 
616  return nullptr;
617 }
618 
625  GeometrySet src_geometry_;
626  GField src_field_;
627  eAttrDomain domain_;
628 
629  fn::MFSignature signature_;
630 
631  std::optional<GeometryComponentFieldContext> geometry_context_;
632  std::unique_ptr<FieldEvaluator> evaluator_;
633  const GVArray *src_data_ = nullptr;
634 
635  public:
636  IndexTransferFunction(GeometrySet geometry, GField src_field, const eAttrDomain domain)
637  : src_geometry_(std::move(geometry)), src_field_(std::move(src_field)), domain_(domain)
638  {
639  src_geometry_.ensure_owns_direct_data();
640 
641  signature_ = this->create_signature();
642  this->set_signature(&signature_);
643 
644  this->evaluate_field();
645  }
646 
648  {
649  fn::MFSignatureBuilder signature{"Attribute Transfer Index"};
650  signature.single_input<int>("Index");
651  signature.single_output("Attribute", src_field_.cpp_type());
652  return signature.build();
653  }
654 
656  {
657  const GeometryComponent *component = find_source_component(src_geometry_, domain_);
658  if (component == nullptr) {
659  return;
660  }
661  const int domain_num = component->attribute_domain_size(domain_);
662  geometry_context_.emplace(GeometryComponentFieldContext(*component, domain_));
663  evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_num);
664  evaluator_->add(src_field_);
665  evaluator_->evaluate();
666  src_data_ = &evaluator_->get_evaluated(0);
667  }
668 
670  {
671  const VArray<int> &indices = params.readonly_single_input<int>(0, "Index");
672  GMutableSpan dst = params.uninitialized_single_output(1, "Attribute");
673 
674  const CPPType &type = dst.type();
675  if (src_data_ == nullptr) {
676  type.value_initialize_indices(dst.data(), mask);
677  return;
678  }
679 
681  using T = decltype(dummy);
682  copy_with_indices_clamped(src_data_->typed<T>(), mask, indices, dst.typed<T>());
683  });
684  }
685 };
686 
688 {
689  switch (data_type) {
690  case CD_PROP_FLOAT:
691  return params.extract_input<Field<float>>("Attribute_001");
692  case CD_PROP_FLOAT3:
693  return params.extract_input<Field<float3>>("Attribute");
694  case CD_PROP_COLOR:
695  return params.extract_input<Field<ColorGeometry4f>>("Attribute_002");
696  case CD_PROP_BOOL:
697  return params.extract_input<Field<bool>>("Attribute_003");
698  case CD_PROP_INT32:
699  return params.extract_input<Field<int>>("Attribute_004");
700  default:
702  }
703  return {};
704 }
705 
707 {
708  switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) {
709  case CD_PROP_FLOAT: {
710  params.set_output("Attribute_001", Field<float>(field));
711  break;
712  }
713  case CD_PROP_FLOAT3: {
714  params.set_output("Attribute", Field<float3>(field));
715  break;
716  }
717  case CD_PROP_COLOR: {
718  params.set_output("Attribute_002", Field<ColorGeometry4f>(field));
719  break;
720  }
721  case CD_PROP_BOOL: {
722  params.set_output("Attribute_003", Field<bool>(field));
723  break;
724  }
725  case CD_PROP_INT32: {
726  params.set_output("Attribute_004", Field<int>(field));
727  break;
728  }
729  default:
730  break;
731  }
732 }
733 
735 {
736  GeometrySet geometry = params.extract_input<GeometrySet>("Source");
737  const NodeGeometryTransferAttribute &storage = node_storage(params.node());
739  storage.mode;
740  const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type);
741  const eAttrDomain domain = static_cast<eAttrDomain>(storage.domain);
742 
743  GField field = get_input_attribute_field(params, data_type);
744 
745  auto return_default = [&]() {
746  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
747  using T = decltype(dummy);
748  output_attribute_field(params, fn::make_constant_field<T>(T()));
749  });
750  };
751 
752  GField output_field;
753  switch (mapping) {
755  const Mesh *mesh = geometry.get_mesh_for_read();
756  if (mesh == nullptr) {
757  if (!geometry.is_empty()) {
758  params.error_message_add(NodeWarningType::Error,
759  TIP_("The source geometry must contain a mesh"));
760  }
761  return return_default();
762  }
763  if (mesh->totpoly == 0) {
764  /* Don't add a warning for empty meshes. */
765  if (mesh->totvert != 0) {
766  params.error_message_add(NodeWarningType::Error,
767  TIP_("The source mesh must have faces"));
768  }
769  return return_default();
770  }
771  auto fn = std::make_unique<NearestInterpolatedTransferFunction>(std::move(geometry),
772  std::move(field));
773  auto op = std::make_shared<FieldOperation>(
774  FieldOperation(std::move(fn), {params.extract_input<Field<float3>>("Source Position")}));
775  output_field = GField(std::move(op));
776  break;
777  }
779  if (geometry.has_curves() && !geometry.has_mesh() && !geometry.has_pointcloud()) {
780  params.error_message_add(NodeWarningType::Error,
781  TIP_("The source geometry must contain a mesh or a point cloud"));
782  return return_default();
783  }
784  auto fn = std::make_unique<NearestTransferFunction>(
785  std::move(geometry), std::move(field), domain);
786  auto op = std::make_shared<FieldOperation>(
787  FieldOperation(std::move(fn), {params.extract_input<Field<float3>>("Source Position")}));
788  output_field = GField(std::move(op));
789  break;
790  }
792  Field<int> indices = params.extract_input<Field<int>>("Index");
793  auto fn = std::make_unique<IndexTransferFunction>(
794  std::move(geometry), std::move(field), domain);
795  auto op = std::make_shared<FieldOperation>(
796  FieldOperation(std::move(fn), {std::move(indices)}));
797  output_field = GField(std::move(op));
798  break;
799  }
800  }
801 
802  output_attribute_field(params, std::move(output_field));
803 }
804 
805 } // namespace blender::nodes::node_geo_transfer_attribute_cc
806 
808 {
810 
811  static bNodeType ntype;
812 
814  &ntype, GEO_NODE_TRANSFER_ATTRIBUTE, "Transfer Attribute", NODE_CLASS_ATTRIBUTE);
817  node_type_storage(&ntype,
818  "NodeGeometryTransferAttribute",
825  nodeRegisterType(&ntype);
826 }
eAttrDomain
Definition: BKE_attribute.h:25
@ ATTR_DOMAIN_POINT
Definition: BKE_attribute.h:27
@ ATTR_DOMAIN_FACE
Definition: BKE_attribute.h:29
@ ATTR_DOMAIN_CORNER
Definition: BKE_attribute.h:30
@ ATTR_DOMAIN_EDGE
Definition: BKE_attribute.h:28
BVHTree * BKE_bvhtree_from_pointcloud_get(struct BVHTreeFromPointCloud *data, const struct PointCloud *pointcloud, int tree_type)
void free_bvhtree_from_mesh(struct BVHTreeFromMesh *data)
Definition: bvhutils.cc:1410
@ BVHTREE_FROM_EDGES
Definition: BKE_bvhutils.h:71
@ BVHTREE_FROM_LOOPTRI
Definition: BKE_bvhutils.h:73
@ BVHTREE_FROM_VERTS
Definition: BKE_bvhutils.h:70
void free_bvhtree_from_pointcloud(struct BVHTreeFromPointCloud *data)
Definition: bvhutils.cc:1451
BVHTree * BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data, const struct Mesh *mesh, BVHCacheType bvh_cache_type, int tree_type)
Definition: bvhutils.cc:1213
GeometryComponentType
@ GEO_COMPONENT_TYPE_MESH
@ GEO_COMPONENT_TYPE_POINT_CLOUD
@ GEO_COMPONENT_TYPE_INSTANCES
@ GEO_COMPONENT_TYPE_CURVE
const struct MLoopTri * BKE_mesh_runtime_looptri_ensure(const struct Mesh *mesh)
int BKE_mesh_runtime_looptri_len(const struct Mesh *mesh)
void node_type_update(struct bNodeType *ntype, void(*updatefunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4443
#define NODE_STORAGE_FUNCS(StorageT)
Definition: BKE_node.h:1563
void nodeSetSocketAvailability(struct bNodeTree *ntree, struct bNodeSocket *sock, bool is_available)
Definition: node.cc:3664
void node_type_init(struct bNodeType *ntype, void(*initfunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4390
#define GEO_NODE_TRANSFER_ATTRIBUTE
Definition: BKE_node.h:1467
void node_type_storage(struct bNodeType *ntype, const char *storagename, void(*freefunc)(struct bNode *node), void(*copyfunc)(struct bNodeTree *dest_ntree, struct bNode *dest_node, const struct bNode *src_node))
Definition: node.cc:4426
#define NODE_CLASS_ATTRIBUTE
Definition: BKE_node.h:360
void nodeRegisterType(struct bNodeType *ntype)
Definition: node.cc:1357
#define BLI_assert_unreachable()
Definition: BLI_assert.h:93
#define BLI_assert(a)
Definition: BLI_assert.h:46
int BLI_bvhtree_find_nearest(BVHTree *tree, const float co[3], BVHTreeNearest *nearest, BVHTree_NearestPointCallback callback, void *userdata)
Definition: BLI_kdopbvh.c:1616
#define UNUSED(x)
#define TIP_(msgid)
#define IFACE_(msgid)
static uint8 component(Color32 c, uint i)
Definition: ColorBlock.cpp:108
eCustomDataType
@ CD_PROP_FLOAT
@ CD_PROP_FLOAT3
@ CD_PROP_COLOR
@ CD_PROP_INT32
@ CD_PROP_BOOL
@ CD_PROP_STRING
eNodeSocketDatatype
GeometryNodeAttributeTransferMode
@ GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED
@ GEO_NODE_ATTRIBUTE_TRANSFER_INDEX
@ GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
int attribute_domain_size(eAttrDomain domain) const
Definition: geometry_set.cc:63
const Mesh * get_for_read() const
void reinitialize(const int64_t new_size)
Definition: BLI_array.hh:387
VArray< T > typed() const
constexpr int64_t size() const
Definition: BLI_span.hh:511
constexpr bool is_empty() const
Definition: BLI_span.hh:519
const CPPType & cpp_type() const
Definition: FN_field.hh:122
void set_signature(const MFSignature *signature)
const MFSignature & signature() const
Span< SocketDeclarationPtr > inputs() const
void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
IndexTransferFunction(GeometrySet geometry, GField src_field, const eAttrDomain domain)
void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
NearestTransferFunction(GeometrySet geometry, GField src_field, eAttrDomain domain)
void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
OperationNode * node
SyclQueue void void * src
void * tree
bNodeTree * ntree
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_gpu_kernel_postfix int ccl_global int * indices
ccl_device_inline float2 interp(const float2 &a, const float2 &b, float t)
Definition: math_float2.h:232
ccl_device_inline float4 mask(const int4 &mask, const float4 &a)
Definition: math_float4.h:513
#define T
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
static int domain_num(const CurvesGeometry &curves, const eAttrDomain domain)
eCustomDataType cpp_type_to_custom_data_type(const blender::CPPType &type)
Definition: customdata.cc:5337
T distance_squared(const vec_base< T, Size > &a, const vec_base< T, Size > &b)
void copy_with_indices(const VArray< T > &src, const IndexMask mask, const Span< int > indices, const MutableSpan< T > dst)
static void get_closest_mesh_looptris(const Mesh &mesh, const VArray< float3 > &positions, const IndexMask mask, const MutableSpan< int > r_looptri_indices, const MutableSpan< float > r_distances_sq, const MutableSpan< float3 > r_positions)
static void get_closest_mesh_corners(const Mesh &mesh, const VArray< float3 > &positions, const IndexMask mask, const MutableSpan< int > r_corner_indices, const MutableSpan< float > r_distances_sq, const MutableSpan< float3 > r_positions)
static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data, const VArray< float3 > &positions, const IndexMask mask, const MutableSpan< int > r_indices, const MutableSpan< float > r_distances_sq, const MutableSpan< float3 > r_positions)
void copy_with_indices_clamped(const VArray< T > &src, const IndexMask mask, const VArray< int > &indices, const MutableSpan< T > dst)
static void get_closest_mesh_edges(const Mesh &mesh, const VArray< float3 > &positions, const IndexMask mask, const MutableSpan< int > r_edge_indices, const MutableSpan< float > r_distances_sq, const MutableSpan< float3 > r_positions)
static bool component_is_available(const GeometrySet &geometry, const GeometryComponentType type, const eAttrDomain domain)
static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
static void get_closest_mesh_points(const Mesh &mesh, const VArray< float3 > &positions, const IndexMask mask, const MutableSpan< int > r_point_indices, const MutableSpan< float > r_distances_sq, const MutableSpan< float3 > r_positions)
static const GeometryComponent * find_source_component(const GeometrySet &geometry, const eAttrDomain domain)
static void node_gather_link_searches(GatherLinkSearchOpParams &params)
static void node_declare(NodeDeclarationBuilder &b)
static GField get_input_attribute_field(GeoNodeExecParams &params, const eCustomDataType data_type)
void copy_with_indices_and_comparison(const VArray< T > &src_1, const VArray< T > &src_2, const Span< float > distances_1, const Span< float > distances_2, const IndexMask mask, const Span< int > indices_1, const Span< int > indices_2, const MutableSpan< T > dst)
static void node_update(bNodeTree *ntree, bNode *node)
static void get_closest_mesh_polygons(const Mesh &mesh, const VArray< float3 > &positions, const IndexMask mask, const MutableSpan< int > r_poly_indices, const MutableSpan< float > r_distances_sq, const MutableSpan< float3 > r_positions)
static void output_attribute_field(GeoNodeExecParams &params, GField field)
static void get_closest_pointcloud_points(const PointCloud &pointcloud, const VArray< float3 > &positions, const IndexMask mask, const MutableSpan< int > r_indices, const MutableSpan< float > r_distances_sq)
static void node_init(bNodeTree *UNUSED(tree), bNode *node)
std::optional< eCustomDataType > node_data_type_to_custom_data_type(const eNodeSocketDatatype type)
void search_link_ops_for_declarations(GatherLinkSearchOpParams &params, Span< SocketDeclarationPtr > declarations)
void parallel_for(IndexRange range, int64_t grain_size, const Function &function)
Definition: BLI_task.hh:51
vec_base< float, 3 > float3
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
static void node_init(const struct bContext *C, bNodeTree *ntree, bNode *node)
Definition: node.cc:1082
MutableSpan< float3 > positions
void register_node_type_geo_transfer_attribute()
void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass)
void node_copy_standard_storage(bNodeTree *UNUSED(dest_ntree), bNode *dest_node, const bNode *src_node)
Definition: node_util.c:55
void node_free_standard_storage(bNode *node)
Definition: node_util.c:43
struct BVHTree * tree
Definition: BKE_bvhutils.h:50
BVHTree_NearestPointCallback nearest_callback
Definition: BKE_bvhutils.h:53
struct BVHTree * tree
Definition: BKE_bvhutils.h:231
BVHTree_NearestPointCallback nearest_callback
Definition: BKE_bvhutils.h:233
float co[3]
Definition: BLI_kdopbvh.h:43
const PointCloud * get_pointcloud_for_read() const
void ensure_owns_direct_data()
bool has(const GeometryComponentType component_type) const
const GeometryComponent * get_component_for_read(GeometryComponentType component_type) const
const Mesh * get_mesh_for_read() const
bool is_empty() const
bool has_mesh() const
bool has_curves() const
bool has_pointcloud() const
unsigned int poly
unsigned int v
float co[3]
struct MVert * mvert
int totedge
int totvert
struct MLoop * mloop
int totpoly
int totloop
struct MPoly * mpoly
void * data
Definition: RNA_types.h:38
struct bNodeSocket * next
Defines a node type.
Definition: BKE_node.h:226
NodeGeometryExecFunction geometry_node_execute
Definition: BKE_node.h:316
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition: BKE_node.h:335
void(* draw_buttons)(struct uiLayout *, struct bContext *C, struct PointerRNA *ptr)
Definition: BKE_node.h:244
NodeDeclareFunction declare
Definition: BKE_node.h:324
#define N_(msgid)
PointerRNA * ptr
Definition: wm_files.c:3480