Blender  V3.3
node_geo_duplicate_elements.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include "BLI_map.hh"
4 #include "BLI_noise.hh"
5 #include "BLI_span.hh"
6 #include "BLI_task.hh"
7 
8 #include "DNA_mesh_types.h"
9 #include "DNA_meshdata_types.h"
10 #include "DNA_pointcloud_types.h"
11 
12 #include "BKE_attribute_math.hh"
13 #include "BKE_curves.hh"
14 #include "BKE_mesh.h"
15 #include "BKE_pointcloud.h"
16 
17 #include "node_geometry_util.hh"
18 
19 #include "UI_interface.h"
20 #include "UI_resources.h"
21 
23 
25 
27 {
28  b.add_input<decl::Geometry>(N_("Geometry"));
29  b.add_input<decl::Bool>(N_("Selection")).hide_value().default_value(true).supports_field();
30  b.add_input<decl::Int>(N_("Amount"))
31  .min(0)
32  .default_value(1)
33  .supports_field()
34  .description(N_("The number of duplicates to create for each element"));
35 
36  b.add_output<decl::Geometry>(N_("Geometry"))
37  .description(N_("The duplicated geometry, not including the original geometry"));
38  b.add_output<decl::Int>(N_("Duplicate Index"))
39  .field_source()
40  .description(N_("The indices of the duplicates for each element"));
41 }
42 
44 {
45  NodeGeometryDuplicateElements *data = MEM_cnew<NodeGeometryDuplicateElements>(__func__);
46  data->domain = ATTR_DOMAIN_POINT;
47  node->storage = data;
48 }
49 
50 static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
51 {
52  uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
53 }
54 
57 };
58 
59 /* -------------------------------------------------------------------- */
64  const GeometrySet &geometry_set, const GeometryComponentType component_type)
65 {
68  {component_type}, component_type, false, attributes);
69  attributes.remove("id");
70  return attributes;
71 };
72 
73 static IndexRange range_for_offsets_index(const Span<int> offsets, const int index)
74 {
75  return {offsets[index], offsets[index + 1] - offsets[index]};
76 }
77 
79  const VArray<int> &counts)
80 {
81  Array<int> offsets(selection.size() + 1);
82  int total = 0;
83  for (const int i : selection.index_range()) {
84  offsets[i] = total;
85  total += std::max(counts[selection[i]], 0);
86  }
87  offsets.last() = total;
88  return offsets;
89 }
90 
91 /* Utility functions for threaded copying of attribute data where possible. */
92 template<typename T>
93 static void threaded_slice_fill(Span<int> offsets,
94  const IndexMask selection,
95  Span<T> src,
96  MutableSpan<T> dst)
97 {
98  BLI_assert(offsets.last() == dst.size());
99  BLI_assert(selection.size() == offsets.size() - 1);
100  threading::parallel_for(IndexRange(offsets.size() - 1), 512, [&](IndexRange range) {
101  for (const int i : range) {
102  dst.slice(range_for_offsets_index(offsets, i)).fill(src[selection[i]]);
103  }
104  });
105 }
106 
107 template<typename T>
108 static void threaded_mapped_copy(const Span<int> mapping, const Span<T> src, MutableSpan<T> dst)
109 {
110  threading::parallel_for(mapping.index_range(), 512, [&](IndexRange range) {
111  for (const int i : range) {
112  dst[i] = src[mapping[i]];
113  }
114  });
115 }
116 
117 static void copy_hashed_ids(const Span<int> src, const int hash, MutableSpan<int> dst)
118 {
119  for (const int i : src.index_range()) {
120  dst[i] = noise::hash(src[i], hash);
121  }
122 }
123 
124 static void threaded_id_offset_copy(const Span<int> offsets,
125  const Span<int> src,
126  MutableSpan<int> dst)
127 {
128  BLI_assert(offsets.last() == dst.size());
129  threading::parallel_for(IndexRange(offsets.size() - 1), 512, [&](IndexRange range) {
130  for (const int i : range) {
131  dst[offsets[i]] = src[i];
132  const int count = offsets[i + 1] - offsets[i];
133  if (count == 0) {
134  continue;
135  }
136  for (const int i_duplicate : IndexRange(1, count - 1)) {
137  dst[offsets[i] + i_duplicate] = noise::hash(src[i], i_duplicate);
138  }
139  }
140  });
141 }
142 
145  const eAttrDomain output_domain,
146  const IndexMask selection,
147  const IndexAttributes &attribute_outputs,
148  const Span<int> offsets)
149 {
150  SpanAttributeWriter<int> duplicate_indices = attributes.lookup_or_add_for_write_only_span<int>(
151  attribute_outputs.duplicate_index.get(), output_domain);
152  for (const int i : IndexRange(selection.size())) {
153  const IndexRange range = range_for_offsets_index(offsets, i);
154  MutableSpan<int> indices = duplicate_indices.span.slice(range);
155  for (const int i : indices.index_range()) {
156  indices[i] = i;
157  }
158  }
159  duplicate_indices.finish();
160 }
161 
166 static void copy_stable_id_point(const Span<int> offsets,
167  const bke::AttributeAccessor src_attributes,
168  bke::MutableAttributeAccessor dst_attributes)
169 {
170  GAttributeReader src_attribute = src_attributes.lookup("id");
171  if (!src_attribute) {
172  return;
173  }
174  GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
176  if (!dst_attribute) {
177  return;
178  }
179 
180  VArraySpan<int> src{src_attribute.varray.typed<int>()};
181  MutableSpan<int> dst = dst_attribute.span.typed<int>();
182  threaded_id_offset_copy(offsets, src, dst);
183  dst_attribute.finish();
184 }
185 
186 static void copy_attributes_without_id(GeometrySet &geometry_set,
187  const GeometryComponentType component_type,
188  const eAttrDomain domain,
189  const Span<int> offsets,
190  const IndexMask selection,
191  const bke::AttributeAccessor src_attributes,
192  bke::MutableAttributeAccessor dst_attributes)
193 {
195  geometry_set, component_type);
196 
197  for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
198  const AttributeIDRef attribute_id = entry.key;
199  GAttributeReader src_attribute = src_attributes.lookup(attribute_id);
200  if (!src_attribute || src_attribute.domain != domain) {
201  continue;
202  }
203  eAttrDomain out_domain = src_attribute.domain;
205  src_attribute.varray.type());
206  GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
207  attribute_id, out_domain, data_type);
208  if (!dst_attribute) {
209  continue;
210  }
211  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
212  using T = decltype(dummy);
213  VArraySpan<T> src = src_attribute.varray.typed<T>();
214  MutableSpan<T> dst = dst_attribute.span.typed<T>();
215  threaded_slice_fill<T>(offsets, selection, src, dst);
216  });
217  dst_attribute.finish();
218  }
219 }
220 
223 /* -------------------------------------------------------------------- */
231 static void copy_curve_attributes_without_id(const GeometrySet &geometry_set,
232  const bke::CurvesGeometry &src_curves,
233  const IndexMask selection,
234  const Span<int> curve_offsets,
235  bke::CurvesGeometry &dst_curves)
236 {
238  geometry_set, GEO_COMPONENT_TYPE_CURVE);
239 
240  for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
241  const AttributeIDRef attribute_id = entry.key;
242  GAttributeReader src_attribute = src_curves.attributes().lookup(attribute_id);
243  if (!src_attribute) {
244  continue;
245  }
246 
247  eAttrDomain out_domain = src_attribute.domain;
249  src_attribute.varray.type());
250  GSpanAttributeWriter dst_attribute =
252  attribute_id, out_domain, data_type);
253  if (!dst_attribute) {
254  continue;
255  }
256 
257  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
258  using T = decltype(dummy);
259  VArraySpan<T> src{src_attribute.varray.typed<T>()};
260  MutableSpan<T> dst = dst_attribute.span.typed<T>();
261 
262  switch (out_domain) {
263  case ATTR_DOMAIN_CURVE:
264  threaded_slice_fill<T>(curve_offsets, selection, src, dst);
265  break;
266  case ATTR_DOMAIN_POINT:
267  threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
268  for (const int i_selection : range) {
269  const int i_src_curve = selection[i_selection];
270  const Span<T> curve_src = src.slice(src_curves.points_for_curve(i_src_curve));
271  for (const int i_dst_curve : range_for_offsets_index(curve_offsets, i_selection)) {
272  dst.slice(dst_curves.points_for_curve(i_dst_curve)).copy_from(curve_src);
273  }
274  }
275  });
276  break;
277  default:
278  break;
279  }
280  });
281  dst_attribute.finish();
282  }
283 }
284 
291 static void copy_stable_id_curves(const bke::CurvesGeometry &src_curves,
292  const IndexMask selection,
293  const Span<int> curve_offsets,
294  bke::CurvesGeometry &dst_curves)
295 {
296  GAttributeReader src_attribute = src_curves.attributes().lookup("id");
297  if (!src_attribute) {
298  return;
299  }
300  GSpanAttributeWriter dst_attribute =
303  if (!dst_attribute) {
304  return;
305  }
306 
307  VArraySpan<int> src{src_attribute.varray.typed<int>()};
308  MutableSpan<int> dst = dst_attribute.span.typed<int>();
309 
310  threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
311  for (const int i_selection : range) {
312  const int i_src_curve = selection[i_selection];
313  const Span<int> curve_src = src.slice(src_curves.points_for_curve(i_src_curve));
314  const IndexRange duplicates_range = range_for_offsets_index(curve_offsets, i_selection);
315  for (const int i_duplicate : IndexRange(duplicates_range.size()).drop_front(1)) {
316  const int i_dst_curve = duplicates_range[i_duplicate];
317  copy_hashed_ids(
318  curve_src, i_duplicate, dst.slice(dst_curves.points_for_curve(i_dst_curve)));
319  }
320  }
321  });
322  dst_attribute.finish();
323 }
324 
325 static void duplicate_curves(GeometrySet &geometry_set,
326  const Field<int> &count_field,
327  const Field<bool> &selection_field,
328  const IndexAttributes &attribute_outputs)
329 {
330  if (!geometry_set.has_curves()) {
331  geometry_set.remove_geometry_during_modify();
332  return;
333  }
336 
337  const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
338  const Curves &curves_id = *src_component.get_for_read();
339  const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
340 
341  GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE};
342  FieldEvaluator evaluator{field_context, curves.curves_num()};
343  evaluator.add(count_field);
344  evaluator.set_selection(selection_field);
345  evaluator.evaluate();
346  const VArray<int> counts = evaluator.get_evaluated<int>(0);
347  const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
348 
349  /* The offset in the result curve domain at every selected input curve. */
350  Array<int> curve_offsets(selection.size() + 1);
351  Array<int> point_offsets(selection.size() + 1);
352 
353  int dst_curves_num = 0;
354  int dst_points_num = 0;
355  for (const int i_curve : selection.index_range()) {
356  const int count = std::max(counts[selection[i_curve]], 0);
357  curve_offsets[i_curve] = dst_curves_num;
358  point_offsets[i_curve] = dst_points_num;
359  dst_curves_num += count;
360  dst_points_num += count * curves.points_for_curve(selection[i_curve]).size();
361  }
362  curve_offsets.last() = dst_curves_num;
363  point_offsets.last() = dst_points_num;
364 
365  Curves *new_curves_id = bke::curves_new_nomain(dst_points_num, dst_curves_num);
366  bke::curves_copy_parameters(curves_id, *new_curves_id);
367  bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry);
368  MutableSpan<int> all_dst_offsets = new_curves.offsets_for_write();
369 
370  threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
371  for (const int i_selection : range) {
372  const int i_src_curve = selection[i_selection];
373  const IndexRange src_curve_range = curves.points_for_curve(i_src_curve);
374  const IndexRange dst_curves_range = range_for_offsets_index(curve_offsets, i_selection);
375  MutableSpan<int> dst_offsets = all_dst_offsets.slice(dst_curves_range);
376  for (const int i_duplicate : IndexRange(dst_curves_range.size())) {
377  dst_offsets[i_duplicate] = point_offsets[i_selection] +
378  src_curve_range.size() * i_duplicate;
379  }
380  }
381  });
382  all_dst_offsets.last() = dst_points_num;
383 
384  copy_curve_attributes_without_id(geometry_set, curves, selection, curve_offsets, new_curves);
385 
386  copy_stable_id_curves(curves, selection, curve_offsets, new_curves);
387 
388  if (attribute_outputs.duplicate_index) {
389  create_duplicate_index_attribute(new_curves.attributes_for_write(),
391  selection,
392  attribute_outputs,
393  curve_offsets);
394  }
395 
396  geometry_set.replace_curves(new_curves_id);
397 }
398 
401 /* -------------------------------------------------------------------- */
410  const Span<int> edge_mapping,
411  const Span<int> vert_mapping,
412  const Span<int> loop_mapping,
413  const Span<int> offsets,
414  const IndexMask selection,
415  const bke::AttributeAccessor src_attributes,
416  bke::MutableAttributeAccessor dst_attributes)
417 {
419  geometry_set, GEO_COMPONENT_TYPE_MESH);
420 
421  for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
422  const AttributeIDRef attribute_id = entry.key;
423  GAttributeReader src_attribute = src_attributes.lookup(attribute_id);
424  if (!src_attribute) {
425  continue;
426  }
427 
428  eAttrDomain out_domain = src_attribute.domain;
430  src_attribute.varray.type());
431  GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
432  attribute_id, out_domain, data_type);
433  if (!dst_attribute) {
434  continue;
435  }
436 
437  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
438  using T = decltype(dummy);
439  VArraySpan<T> src{src_attribute.varray.typed<T>()};
440  MutableSpan<T> dst = dst_attribute.span.typed<T>();
441 
442  switch (out_domain) {
443  case ATTR_DOMAIN_FACE:
444  threaded_slice_fill<T>(offsets, selection, src, dst);
445  break;
446  case ATTR_DOMAIN_EDGE:
447  threaded_mapped_copy<T>(edge_mapping, src, dst);
448  break;
449  case ATTR_DOMAIN_POINT:
450  threaded_mapped_copy<T>(vert_mapping, src, dst);
451  break;
452  case ATTR_DOMAIN_CORNER:
453  threaded_mapped_copy<T>(loop_mapping, src, dst);
454  break;
455  default:
456  break;
457  }
458  });
459  dst_attribute.finish();
460  }
461 }
462 
470 static void copy_stable_id_faces(const Mesh &mesh,
471  const IndexMask selection,
472  const Span<int> poly_offsets,
473  const Span<int> vert_mapping,
474  const bke::AttributeAccessor src_attributes,
475  bke::MutableAttributeAccessor dst_attributes)
476 {
477  GAttributeReader src_attribute = src_attributes.lookup("id");
478  if (!src_attribute) {
479  return;
480  }
481  GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
483  if (!dst_attribute) {
484  return;
485  }
486 
487  VArraySpan<int> src{src_attribute.varray.typed<int>()};
488  MutableSpan<int> dst = dst_attribute.span.typed<int>();
489 
491  int loop_index = 0;
492  for (const int i_poly : selection.index_range()) {
493  const IndexRange range = range_for_offsets_index(poly_offsets, i_poly);
494  if (range.size() == 0) {
495  continue;
496  }
497  const MPoly &source = polys[i_poly];
498  for ([[maybe_unused]] const int i_duplicate : IndexRange(range.size())) {
499  for ([[maybe_unused]] const int i_loops : IndexRange(source.totloop)) {
500  if (i_duplicate == 0) {
501  dst[loop_index] = src[vert_mapping[loop_index]];
502  }
503  else {
504  dst[loop_index] = noise::hash(src[vert_mapping[loop_index]], i_duplicate);
505  }
506  loop_index++;
507  }
508  }
509  }
510 
511  dst_attribute.finish();
512 }
513 
514 static void duplicate_faces(GeometrySet &geometry_set,
515  const Field<int> &count_field,
516  const Field<bool> &selection_field,
517  const IndexAttributes &attribute_outputs)
518 {
519  if (!geometry_set.has_mesh()) {
520  geometry_set.remove_geometry_during_modify();
521  return;
522  }
524 
525  const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>();
526  const Mesh &mesh = *src_component.get_for_read();
531 
532  GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_FACE};
533  FieldEvaluator evaluator(field_context, polys.size());
534  evaluator.add(count_field);
535  evaluator.set_selection(selection_field);
536  evaluator.evaluate();
537  const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
538  const VArray<int> counts = evaluator.get_evaluated<int>(0);
539 
540  int total_polys = 0;
541  int total_loops = 0;
542  Array<int> offsets(selection.size() + 1);
543  for (const int i_selection : selection.index_range()) {
544  const int count = std::max(counts[selection[i_selection]], 0);
545  offsets[i_selection] = total_polys;
546  total_polys += count;
547  total_loops += count * polys[selection[i_selection]].totloop;
548  }
549  offsets[selection.size()] = total_polys;
550 
551  Mesh *new_mesh = BKE_mesh_new_nomain(total_loops, total_loops, 0, total_loops, total_polys);
552  MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert);
553  MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge);
554  MutableSpan<MLoop> new_loops(new_mesh->mloop, new_mesh->totloop);
555  MutableSpan<MPoly> new_poly(new_mesh->mpoly, new_mesh->totpoly);
556 
557  Array<int> vert_mapping(new_verts.size());
558  Array<int> edge_mapping(new_edges.size());
559  Array<int> loop_mapping(new_loops.size());
560 
561  int poly_index = 0;
562  int loop_index = 0;
563  for (const int i_selection : selection.index_range()) {
564  const IndexRange poly_range = range_for_offsets_index(offsets, i_selection);
565 
566  const MPoly &source = polys[selection[i_selection]];
567  for ([[maybe_unused]] const int i_duplicate : IndexRange(poly_range.size())) {
568  new_poly[poly_index] = source;
569  new_poly[poly_index].loopstart = loop_index;
570  for (const int i_loops : IndexRange(source.totloop)) {
571  const MLoop &current_loop = loops[source.loopstart + i_loops];
572  loop_mapping[loop_index] = source.loopstart + i_loops;
573  new_verts[loop_index] = verts[current_loop.v];
574  vert_mapping[loop_index] = current_loop.v;
575  new_edges[loop_index] = edges[current_loop.e];
576  edge_mapping[loop_index] = current_loop.e;
577  new_edges[loop_index].v1 = loop_index;
578  if (i_loops + 1 != source.totloop) {
579  new_edges[loop_index].v2 = loop_index + 1;
580  }
581  else {
582  new_edges[loop_index].v2 = new_poly[poly_index].loopstart;
583  }
584  new_loops[loop_index].v = loop_index;
585  new_loops[loop_index].e = loop_index;
586  loop_index++;
587  }
588  poly_index++;
589  }
590  }
591 
592  copy_face_attributes_without_id(geometry_set,
593  edge_mapping,
594  vert_mapping,
595  loop_mapping,
596  offsets,
597  selection,
599  bke::mesh_attributes_for_write(*new_mesh));
600 
602  selection,
603  offsets,
604  vert_mapping,
606  bke::mesh_attributes_for_write(*new_mesh));
607 
608  if (attribute_outputs.duplicate_index) {
611  selection,
612  attribute_outputs,
613  offsets);
614  }
615 
616  geometry_set.replace_mesh(new_mesh);
617 }
618 
621 /* -------------------------------------------------------------------- */
630  const Span<int> point_mapping,
631  const Span<int> offsets,
632  const IndexMask selection,
633  const bke::AttributeAccessor src_attributes,
634  bke::MutableAttributeAccessor dst_attributes)
635 {
637  geometry_set, GEO_COMPONENT_TYPE_MESH);
638 
639  for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
640  const AttributeIDRef attribute_id = entry.key;
641  GAttributeReader src_attribute = src_attributes.lookup(attribute_id);
642  if (!src_attribute) {
643  continue;
644  }
645 
646  const eAttrDomain out_domain = src_attribute.domain;
648  src_attribute.varray.type());
649  GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
650  attribute_id, out_domain, data_type);
651  if (!dst_attribute) {
652  continue;
653  }
654  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
655  using T = decltype(dummy);
656  VArraySpan<T> src{src_attribute.varray.typed<T>()};
657  MutableSpan<T> dst = dst_attribute.span.typed<T>();
658 
659  switch (out_domain) {
660  case ATTR_DOMAIN_EDGE:
661  threaded_slice_fill<T>(offsets, selection, src, dst);
662  break;
663  case ATTR_DOMAIN_POINT:
664  threaded_mapped_copy<T>(point_mapping, src, dst);
665  break;
666  default:
667  break;
668  }
669  });
670  dst_attribute.finish();
671  }
672 }
673 
678 static void copy_stable_id_edges(const Mesh &mesh,
679  const IndexMask selection,
680  const Span<int> edge_offsets,
681  const bke::AttributeAccessor src_attributes,
682  bke::MutableAttributeAccessor dst_attributes)
683 {
684  GAttributeReader src_attribute = src_attributes.lookup("id");
685  if (!src_attribute) {
686  return;
687  }
688  GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
690  if (!dst_attribute) {
691  return;
692  }
693 
695 
696  VArraySpan<int> src{src_attribute.varray.typed<int>()};
697  MutableSpan<int> dst = dst_attribute.span.typed<int>();
698  threading::parallel_for(IndexRange(selection.size()), 1024, [&](IndexRange range) {
699  for (const int i_selection : range) {
700  const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_selection);
701  if (edge_range.size() == 0) {
702  continue;
703  }
704  const MEdge &edge = edges[selection[i_selection]];
705  const IndexRange vert_range = {edge_range.start() * 2, edge_range.size() * 2};
706 
707  dst[vert_range[0]] = src[edge.v1];
708  dst[vert_range[1]] = src[edge.v2];
709  for (const int i_duplicate : IndexRange(1, edge_range.size() - 1)) {
710  dst[vert_range[i_duplicate * 2]] = noise::hash(src[edge.v1], i_duplicate);
711  dst[vert_range[i_duplicate * 2 + 1]] = noise::hash(src[edge.v2], i_duplicate);
712  }
713  }
714  });
715  dst_attribute.finish();
716 }
717 
718 static void duplicate_edges(GeometrySet &geometry_set,
719  const Field<int> &count_field,
720  const Field<bool> &selection_field,
721  const IndexAttributes &attribute_outputs)
722 {
723  if (!geometry_set.has_mesh()) {
724  geometry_set.remove_geometry_during_modify();
725  return;
726  };
727  const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>();
728  const Mesh &mesh = *src_component.get_for_read();
731 
732  GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_EDGE};
733  FieldEvaluator evaluator{field_context, edges.size()};
734  evaluator.add(count_field);
735  evaluator.set_selection(selection_field);
736  evaluator.evaluate();
737  const VArray<int> counts = evaluator.get_evaluated<int>(0);
738  const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
739 
740  Array<int> edge_offsets = accumulate_counts_to_offsets(selection, counts);
741 
742  Mesh *new_mesh = BKE_mesh_new_nomain(edge_offsets.last() * 2, edge_offsets.last(), 0, 0, 0);
743  MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert);
744  MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge);
745 
746  Array<int> vert_orig_indices(edge_offsets.last() * 2);
747  threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
748  for (const int i_selection : range) {
749  const MEdge &edge = edges[selection[i_selection]];
750  const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_selection);
751  const IndexRange vert_range(edge_range.start() * 2, edge_range.size() * 2);
752 
753  for (const int i_duplicate : IndexRange(edge_range.size())) {
754  vert_orig_indices[vert_range[i_duplicate * 2]] = edge.v1;
755  vert_orig_indices[vert_range[i_duplicate * 2 + 1]] = edge.v2;
756  }
757  }
758  });
759 
760  threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
761  for (const int i_selection : range) {
762  const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_selection);
763  const IndexRange vert_range(edge_range.start() * 2, edge_range.size() * 2);
764  for (const int i_duplicate : IndexRange(edge_range.size())) {
765  MEdge &new_edge = new_edges[edge_range[i_duplicate]];
766  new_edge.v1 = vert_range[i_duplicate * 2];
767  new_edge.v2 = vert_range[i_duplicate * 2] + 1;
768  new_edge.flag = ME_LOOSEEDGE;
769  }
770  }
771  });
772 
773  copy_edge_attributes_without_id(geometry_set,
774  vert_orig_indices,
775  edge_offsets,
776  selection,
778  bke::mesh_attributes_for_write(*new_mesh));
779 
781  selection,
782  edge_offsets,
784  bke::mesh_attributes_for_write(*new_mesh));
785 
786  if (attribute_outputs.duplicate_index) {
789  selection,
790  attribute_outputs,
791  edge_offsets);
792  }
793 
794  geometry_set.replace_mesh(new_mesh);
795 }
796 
799 /* -------------------------------------------------------------------- */
803 static void duplicate_points_curve(GeometrySet &geometry_set,
804  const Field<int> &count_field,
805  const Field<bool> &selection_field,
806  const IndexAttributes &attribute_outputs)
807 {
808  const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
809  const Curves &src_curves_id = *src_component.get_for_read();
810  const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
811  if (src_curves.points_num() == 0) {
812  return;
813  }
814 
815  GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT};
816  FieldEvaluator evaluator{field_context, src_curves.points_num()};
817  evaluator.add(count_field);
818  evaluator.set_selection(selection_field);
819  evaluator.evaluate();
820  const VArray<int> counts = evaluator.get_evaluated<int>(0);
821  const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
822 
823  Array<int> offsets = accumulate_counts_to_offsets(selection, counts);
824  const int dst_num = offsets.last();
825 
826  Array<int> point_to_curve_map(src_curves.points_num());
827  threading::parallel_for(src_curves.curves_range(), 1024, [&](const IndexRange range) {
828  for (const int i_curve : range) {
829  const IndexRange points = src_curves.points_for_curve(i_curve);
830  point_to_curve_map.as_mutable_span().slice(points).fill(i_curve);
831  }
832  });
833 
834  Curves *new_curves_id = bke::curves_new_nomain(dst_num, dst_num);
835  bke::curves_copy_parameters(src_curves_id, *new_curves_id);
836  bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry);
837  MutableSpan<int> new_curve_offsets = new_curves.offsets_for_write();
838  for (const int i : new_curves.curves_range()) {
839  new_curve_offsets[i] = i;
840  }
841  new_curve_offsets.last() = dst_num;
842 
844  geometry_set, GEO_COMPONENT_TYPE_CURVE);
845 
846  for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
847  const AttributeIDRef attribute_id = entry.key;
848  GAttributeReader src_attribute = src_component.attributes()->lookup(attribute_id);
849  if (!src_attribute) {
850  continue;
851  }
852 
853  eAttrDomain domain = src_attribute.domain;
855  src_attribute.varray.type());
856  GSpanAttributeWriter dst_attribute =
858  attribute_id, domain, data_type);
859  if (!dst_attribute) {
860  continue;
861  }
862 
863  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
864  using T = decltype(dummy);
865  VArraySpan<T> src{src_attribute.varray.typed<T>()};
866  MutableSpan<T> dst = dst_attribute.span.typed<T>();
867 
868  switch (domain) {
869  case ATTR_DOMAIN_CURVE:
870  threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
871  for (const int i_selection : range) {
872  const T &src_value = src[point_to_curve_map[selection[i_selection]]];
873  const IndexRange duplicate_range = range_for_offsets_index(offsets, i_selection);
874  dst.slice(duplicate_range).fill(src_value);
875  }
876  });
877  break;
878  case ATTR_DOMAIN_POINT:
879  threaded_slice_fill(offsets, selection, src, dst);
880  break;
881  default:
882  break;
883  }
884  });
885  dst_attribute.finish();
886  }
887 
888  copy_stable_id_point(offsets, src_curves.attributes(), new_curves.attributes_for_write());
889 
890  if (attribute_outputs.duplicate_index) {
893  selection,
894  attribute_outputs,
895  offsets.as_span());
896  }
897 
898  geometry_set.replace_curves(new_curves_id);
899 }
900 
903 /* -------------------------------------------------------------------- */
907 static void duplicate_points_mesh(GeometrySet &geometry_set,
908  const Field<int> &count_field,
909  const Field<bool> &selection_field,
910  const IndexAttributes &attribute_outputs)
911 {
912  const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>();
913  const Mesh &mesh = *geometry_set.get_mesh_for_read();
914  Span<MVert> src_verts(mesh.mvert, mesh.totvert);
915 
916  GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT};
917  FieldEvaluator evaluator{field_context, src_verts.size()};
918  evaluator.add(count_field);
919  evaluator.set_selection(selection_field);
920  evaluator.evaluate();
921  const VArray<int> counts = evaluator.get_evaluated<int>(0);
922  const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
923 
924  Array<int> offsets = accumulate_counts_to_offsets(selection, counts);
925 
926  Mesh *new_mesh = BKE_mesh_new_nomain(offsets.last(), 0, 0, 0, 0);
927  MutableSpan<MVert> dst_verts(new_mesh->mvert, new_mesh->totvert);
928 
929  threaded_slice_fill(offsets.as_span(), selection, src_verts, dst_verts);
930 
931  copy_attributes_without_id(geometry_set,
934  offsets,
935  selection,
937  bke::mesh_attributes_for_write(*new_mesh));
938 
941 
942  if (attribute_outputs.duplicate_index) {
945  selection,
946  attribute_outputs,
947  offsets.as_span());
948  }
949 
950  geometry_set.replace_mesh(new_mesh);
951 }
952 
955 /* -------------------------------------------------------------------- */
959 static void duplicate_points_pointcloud(GeometrySet &geometry_set,
960  const Field<int> &count_field,
961  const Field<bool> &selection_field,
962  const IndexAttributes &attribute_outputs)
963 {
964  const PointCloudComponent &src_points =
966  const int point_num = src_points.attribute_domain_size(ATTR_DOMAIN_POINT);
967 
968  GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT};
969  FieldEvaluator evaluator{field_context, point_num};
970  evaluator.add(count_field);
971  evaluator.set_selection(selection_field);
972  evaluator.evaluate();
973  const VArray<int> counts = evaluator.get_evaluated<int>(0);
974  const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
975 
976  Array<int> offsets = accumulate_counts_to_offsets(selection, counts);
977 
978  PointCloud *pointcloud = BKE_pointcloud_new_nomain(offsets.last());
979 
980  copy_attributes_without_id(geometry_set,
983  offsets,
984  selection,
985  *src_points.attributes(),
987 
989  offsets, *src_points.attributes(), bke::pointcloud_attributes_for_write(*pointcloud));
990 
991  if (attribute_outputs.duplicate_index) {
994  selection,
995  attribute_outputs,
996  offsets);
997  }
998  geometry_set.replace_pointcloud(pointcloud);
999 }
1000 
1003 /* -------------------------------------------------------------------- */
1007 static void duplicate_points(GeometrySet &geometry_set,
1008  const Field<int> &count_field,
1009  const Field<bool> &selection_field,
1010  const IndexAttributes &attribute_outputs)
1011 {
1012  Vector<GeometryComponentType> component_types = geometry_set.gather_component_types(true, true);
1013  for (const GeometryComponentType component_type : component_types) {
1014  switch (component_type) {
1016  if (geometry_set.has_pointcloud()) {
1018  geometry_set, count_field, selection_field, attribute_outputs);
1019  }
1020  break;
1022  if (geometry_set.has_mesh()) {
1023  duplicate_points_mesh(geometry_set, count_field, selection_field, attribute_outputs);
1024  }
1025  break;
1027  if (geometry_set.has_curves()) {
1028  duplicate_points_curve(geometry_set, count_field, selection_field, attribute_outputs);
1029  }
1030  break;
1031  default:
1032  break;
1033  }
1034  }
1035  component_types.append(GEO_COMPONENT_TYPE_INSTANCES);
1036  geometry_set.keep_only_during_modify(component_types);
1037 }
1038 
1041 /* -------------------------------------------------------------------- */
1045 static void duplicate_instances(GeometrySet &geometry_set,
1046  const Field<int> &count_field,
1047  const Field<bool> &selection_field,
1048  const IndexAttributes &attribute_outputs)
1049 {
1050  if (!geometry_set.has_instances()) {
1051  geometry_set.clear();
1052  return;
1053  }
1054 
1055  const InstancesComponent &src_instances =
1056  *geometry_set.get_component_for_read<InstancesComponent>();
1057 
1058  GeometryComponentFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE};
1059  FieldEvaluator evaluator{field_context, src_instances.instances_num()};
1060  evaluator.add(count_field);
1061  evaluator.set_selection(selection_field);
1062  evaluator.evaluate();
1063  IndexMask selection = evaluator.get_evaluated_selection_as_mask();
1064  const VArray<int> counts = evaluator.get_evaluated<int>(0);
1065 
1066  Array<int> offsets = accumulate_counts_to_offsets(selection, counts);
1067  if (offsets.last() == 0) {
1068  geometry_set.clear();
1069  return;
1070  }
1071 
1072  GeometrySet dst_geometry;
1073  InstancesComponent &dst_instances = dst_geometry.get_component_for_write<InstancesComponent>();
1074  dst_instances.resize(offsets.last());
1075  for (const int i_selection : selection.index_range()) {
1076  const IndexRange range = range_for_offsets_index(offsets, i_selection);
1077  if (range.size() == 0) {
1078  continue;
1079  }
1080  const int old_handle = src_instances.instance_reference_handles()[i_selection];
1081  const InstanceReference reference = src_instances.references()[old_handle];
1082  const int new_handle = dst_instances.add_reference(reference);
1083  const float4x4 transform = src_instances.instance_transforms()[i_selection];
1084  dst_instances.instance_transforms().slice(range).fill(transform);
1085  dst_instances.instance_reference_handles().slice(range).fill(new_handle);
1086  }
1087 
1088  copy_attributes_without_id(geometry_set,
1091  offsets,
1092  selection,
1093  *src_instances.attributes(),
1094  *dst_instances.attributes_for_write());
1095 
1096  if (attribute_outputs.duplicate_index) {
1099  selection,
1100  attribute_outputs,
1101  offsets);
1102  }
1103 
1104  geometry_set = std::move(dst_geometry);
1105 }
1106 
1109 /* -------------------------------------------------------------------- */
1114 {
1115  GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
1116 
1117  const NodeGeometryDuplicateElements &storage = node_storage(params.node());
1118  const eAttrDomain duplicate_domain = eAttrDomain(storage.domain);
1119 
1120  Field<int> count_field = params.extract_input<Field<int>>("Amount");
1121  Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
1122  IndexAttributes attribute_outputs;
1123  if (params.output_is_required("Duplicate Index")) {
1124  attribute_outputs.duplicate_index = StrongAnonymousAttributeID("duplicate_index");
1125  }
1126 
1127  if (duplicate_domain == ATTR_DOMAIN_INSTANCE) {
1128  duplicate_instances(geometry_set, count_field, selection_field, attribute_outputs);
1129  }
1130  else {
1131  geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
1132  switch (duplicate_domain) {
1133  case ATTR_DOMAIN_CURVE:
1134  duplicate_curves(geometry_set, count_field, selection_field, attribute_outputs);
1135  break;
1136  case ATTR_DOMAIN_FACE:
1137  duplicate_faces(geometry_set, count_field, selection_field, attribute_outputs);
1138  break;
1139  case ATTR_DOMAIN_EDGE:
1140  duplicate_edges(geometry_set, count_field, selection_field, attribute_outputs);
1141  break;
1142  case ATTR_DOMAIN_POINT:
1143  duplicate_points(geometry_set, count_field, selection_field, attribute_outputs);
1144  break;
1145  default:
1147  break;
1148  }
1149  });
1150  }
1151 
1152  if (geometry_set.is_empty()) {
1153  params.set_default_remaining_outputs();
1154  return;
1155  }
1156 
1157  if (attribute_outputs.duplicate_index) {
1158  params.set_output(
1159  "Duplicate Index",
1160  AnonymousAttributeFieldInput::Create<int>(std::move(attribute_outputs.duplicate_index),
1161  params.attribute_producer_name()));
1162  }
1163  params.set_output("Geometry", std::move(geometry_set));
1164 }
1165 
1168 } // namespace blender::nodes::node_geo_duplicate_elements_cc
1169 
1171 {
1173  static bNodeType ntype;
1175  &ntype, GEO_NODE_DUPLICATE_ELEMENTS, "Duplicate Elements", NODE_CLASS_GEOMETRY);
1176 
1177  node_type_storage(&ntype,
1178  "NodeGeometryDuplicateElements",
1181 
1186  nodeRegisterType(&ntype);
1187 }
eAttrDomain
Definition: BKE_attribute.h:25
@ ATTR_DOMAIN_CURVE
Definition: BKE_attribute.h:31
@ ATTR_DOMAIN_INSTANCE
Definition: BKE_attribute.h:32
@ 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
Low-level operations for curves.
GeometryComponentType
@ GEO_COMPONENT_TYPE_MESH
@ GEO_COMPONENT_TYPE_POINT_CLOUD
@ GEO_COMPONENT_TYPE_INSTANCES
@ GEO_COMPONENT_TYPE_CURVE
struct Mesh * BKE_mesh_new_nomain(int verts_len, int edges_len, int tessface_len, int loops_len, int polys_len)
Definition: mesh.cc:991
void node_type_init(struct bNodeType *ntype, void(*initfunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4390
#define NODE_CLASS_GEOMETRY
Definition: BKE_node.h:359
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 GEO_NODE_DUPLICATE_ELEMENTS
Definition: BKE_node.h:1495
void nodeRegisterType(struct bNodeType *ntype)
Definition: node.cc:1357
General operations for point clouds.
struct PointCloud * BKE_pointcloud_new_nomain(int totpoint)
Definition: pointcloud.cc:243
#define BLI_assert_unreachable()
Definition: BLI_assert.h:93
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define UNUSED(x)
eCustomDataType
@ CD_PROP_INT32
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to curves
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
const Curves * get_for_read() const
static void remember_deformed_curve_positions_if_necessary(GeometrySet &geometry)
int attribute_domain_size(eAttrDomain domain) const
Definition: geometry_set.cc:63
blender::Span< int > instance_reference_handles() const
std::optional< blender::bke::AttributeAccessor > attributes() const final
std::optional< blender::bke::MutableAttributeAccessor > attributes_for_write() final
int add_reference(const InstanceReference &reference)
blender::Span< InstanceReference > references() const
blender::MutableSpan< blender::float4x4 > instance_transforms()
const Mesh * get_for_read() const
std::optional< blender::bke::AttributeAccessor > attributes() const final
const T & last(const int64_t n=0) const
Definition: BLI_array.hh:284
Span< T > as_span() const
Definition: BLI_array.hh:231
MutableSpan< T > typed() const
const CPPType & type() const
VArray< T > typed() const
int64_t size() const
IndexRange index_range() const
constexpr int64_t size() const
bool remove(const Key &key)
Definition: BLI_map.hh:323
ItemIterator items() const
Definition: BLI_map.hh:859
constexpr int64_t size() const
Definition: BLI_span.hh:511
constexpr T & last(const int64_t n=0) const
Definition: BLI_span.hh:680
constexpr Span slice(int64_t start, int64_t size) const
Definition: BLI_span.hh:142
constexpr const T & last(const int64_t n=0) const
Definition: BLI_span.hh:313
constexpr int64_t size() const
Definition: BLI_span.hh:240
constexpr IndexRange index_range() const
Definition: BLI_span.hh:401
void append(const T &value)
Definition: BLI_vector.hh:433
GAttributeReader lookup(const AttributeIDRef &attribute_id) const
IndexRange curves_range() const
Definition: BKE_curves.hh:795
MutableAttributeAccessor attributes_for_write()
AttributeAccessor attributes() const
MutableSpan< int > offsets_for_write()
GSpanAttributeWriter lookup_or_add_for_write_only_span(const AttributeIDRef &attribute_id, const eAttrDomain domain, const eCustomDataType data_type)
IndexMask get_evaluated_selection_as_mask()
Definition: field.cc:797
void set_selection(Field< bool > selection)
Definition: FN_field.hh:366
int add(GField field, GVArray *varray_ptr)
Definition: field.cc:731
const GVArray & get_evaluated(const int field_index) const
Definition: FN_field.hh:431
OperationNode * node
SyclQueue void void * src
void * tree
static float verts[][3]
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int count
ccl_gpu_kernel_postfix int ccl_global int * indices
#define T
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
static struct PartialUpdateUser * wrap(PartialUpdateUserImpl *user)
void curves_copy_parameters(const Curves &src, Curves &dst)
Definition: curves.cc:391
MutableAttributeAccessor pointcloud_attributes_for_write(PointCloud &pointcloud)
AttributeAccessor mesh_attributes(const Mesh &mesh)
eCustomDataType cpp_type_to_custom_data_type(const blender::CPPType &type)
Definition: customdata.cc:5337
MutableAttributeAccessor mesh_attributes_for_write(Mesh &mesh)
Curves * curves_new_nomain(int points_num, int curves_num)
Definition: curves.cc:367
OwnedAnonymousAttributeID< true > StrongAnonymousAttributeID
static Array< int > accumulate_counts_to_offsets(const IndexMask selection, const VArray< int > &counts)
NODE_STORAGE_FUNCS(NodeGeometryDuplicateElements)
static void duplicate_edges(GeometrySet &geometry_set, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs)
static void threaded_mapped_copy(const Span< int > mapping, const Span< T > src, MutableSpan< T > dst)
static void duplicate_points(GeometrySet &geometry_set, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs)
static void node_init(bNodeTree *UNUSED(tree), bNode *node)
static void copy_stable_id_edges(const Mesh &mesh, const IndexMask selection, const Span< int > edge_offsets, const bke::AttributeAccessor src_attributes, bke::MutableAttributeAccessor dst_attributes)
static void copy_face_attributes_without_id(GeometrySet &geometry_set, const Span< int > edge_mapping, const Span< int > vert_mapping, const Span< int > loop_mapping, const Span< int > offsets, const IndexMask selection, const bke::AttributeAccessor src_attributes, bke::MutableAttributeAccessor dst_attributes)
static void threaded_slice_fill(Span< int > offsets, const IndexMask selection, Span< T > src, MutableSpan< T > dst)
static void duplicate_points_curve(GeometrySet &geometry_set, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs)
static void create_duplicate_index_attribute(bke::MutableAttributeAccessor attributes, const eAttrDomain output_domain, const IndexMask selection, const IndexAttributes &attribute_outputs, const Span< int > offsets)
static Map< AttributeIDRef, AttributeKind > gather_attributes_without_id(const GeometrySet &geometry_set, const GeometryComponentType component_type)
static void copy_stable_id_point(const Span< int > offsets, const bke::AttributeAccessor src_attributes, bke::MutableAttributeAccessor dst_attributes)
static void copy_hashed_ids(const Span< int > src, const int hash, MutableSpan< int > dst)
static IndexRange range_for_offsets_index(const Span< int > offsets, const int index)
static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
static void duplicate_faces(GeometrySet &geometry_set, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs)
static void threaded_id_offset_copy(const Span< int > offsets, const Span< int > src, MutableSpan< int > dst)
static void node_declare(NodeDeclarationBuilder &b)
static void copy_stable_id_faces(const Mesh &mesh, const IndexMask selection, const Span< int > poly_offsets, const Span< int > vert_mapping, const bke::AttributeAccessor src_attributes, bke::MutableAttributeAccessor dst_attributes)
static void copy_stable_id_curves(const bke::CurvesGeometry &src_curves, const IndexMask selection, const Span< int > curve_offsets, bke::CurvesGeometry &dst_curves)
static void duplicate_curves(GeometrySet &geometry_set, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs)
static void duplicate_points_mesh(GeometrySet &geometry_set, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs)
static void copy_edge_attributes_without_id(GeometrySet &geometry_set, const Span< int > point_mapping, const Span< int > offsets, const IndexMask selection, const bke::AttributeAccessor src_attributes, bke::MutableAttributeAccessor dst_attributes)
static void duplicate_points_pointcloud(GeometrySet &geometry_set, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs)
static void copy_curve_attributes_without_id(const GeometrySet &geometry_set, const bke::CurvesGeometry &src_curves, const IndexMask selection, const Span< int > curve_offsets, bke::CurvesGeometry &dst_curves)
static void duplicate_instances(GeometrySet &geometry_set, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs)
static void copy_attributes_without_id(GeometrySet &geometry_set, const GeometryComponentType component_type, const eAttrDomain domain, const Span< int > offsets, const IndexMask selection, const bke::AttributeAccessor src_attributes, bke::MutableAttributeAccessor dst_attributes)
static MPoly new_poly(const int loopstart, const int totloop)
void parallel_for(IndexRange range, int64_t grain_size, const Function &function)
Definition: BLI_task.hh:51
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
void register_node_type_geo_duplicate_elements()
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
#define hash
Definition: noise.c:153
#define min(a, b)
Definition: sort.c:35
CurvesGeometry geometry
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
GeometryComponent & get_component_for_write(GeometryComponentType component_type)
void keep_only_during_modify(const blender::Span< GeometryComponentType > component_types)
blender::Vector< GeometryComponentType > gather_component_types(bool include_instances, bool ignore_empty) const
const GeometryComponent * get_component_for_read(GeometryComponentType component_type) const
const Mesh * get_mesh_for_read() const
bool is_empty() const
void gather_attributes_for_propagation(blender::Span< GeometryComponentType > component_types, GeometryComponentType dst_component_type, bool include_instances, blender::Map< blender::bke::AttributeIDRef, blender::bke::AttributeKind > &r_attributes) const
bool has_instances() const
void modify_geometry_sets(ForeachSubGeometryCallback callback)
bool has_mesh() const
bool has_curves() const
void remove_geometry_during_modify()
void replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
bool has_pointcloud() const
unsigned int e
unsigned int v
struct MEdge * medge
struct MVert * mvert
int totedge
int totvert
struct MLoop * mloop
int totpoly
int totloop
struct MPoly * mpoly
Defines a node type.
Definition: BKE_node.h:226
NodeGeometryExecFunction geometry_node_execute
Definition: BKE_node.h:316
void(* draw_buttons)(struct uiLayout *, struct bContext *C, struct PointerRNA *ptr)
Definition: BKE_node.h:244
NodeDeclareFunction declare
Definition: BKE_node.h:324
float max
#define N_(msgid)
PointerRNA * ptr
Definition: wm_files.c:3480