Blender V5.0
node_geo_duplicate_elements.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_array_utils.hh"
6#include "BLI_noise.hh"
8#include "BLI_span.hh"
9#include "BLI_task.hh"
10
12
13#include "BKE_attribute_math.hh"
14#include "BKE_curves.hh"
15#include "BKE_grease_pencil.hh"
16#include "BKE_instances.hh"
17#include "BKE_mesh.hh"
18#include "BKE_pointcloud.hh"
19
20#include "node_geometry_util.hh"
21
22#include "NOD_rna_define.hh"
23
25
27
29#include "UI_resources.hh"
30
32
34
36{
37 b.add_input<decl::Geometry>("Geometry").description("Geometry to duplicate elements of");
38 b.add_input<decl::Bool>("Selection").default_value(true).hide_value().field_on_all();
39 b.add_input<decl::Int>("Amount")
40 .min(0)
42 .field_on_all()
43 .description("The number of duplicates to create for each element")
44 .translation_context(BLT_I18NCONTEXT_COUNTABLE);
45
46 b.add_output<decl::Geometry>("Geometry")
47 .propagate_all()
48 .description("The duplicated geometry, not including the original geometry");
49 b.add_output<decl::Int>("Duplicate Index")
50 .field_on_all()
51 .description("The indices of the duplicates for each element");
52}
53
54static void node_init(bNodeTree * /*tree*/, bNode *node)
55{
57 data->domain = int8_t(AttrDomain::Point);
58 node->storage = data;
59}
60
61static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
62{
63 layout->prop(ptr, "domain", UI_ITEM_NONE, "", ICON_NONE);
64}
65
67 std::optional<std::string> duplicate_index;
68};
69
70/* -------------------------------------------------------------------- */
73
75 const VArray<int> &counts,
76 Array<int> &r_offset_data)
77{
78 r_offset_data.reinitialize(selection.size() + 1);
79 if (const std::optional<int> count = counts.get_if_single()) {
81 }
82 else {
83 array_utils::gather(counts, selection, r_offset_data.as_mutable_span().drop_back(1), 1024);
85 }
86 return OffsetIndices<int>(r_offset_data);
87}
88
89static void copy_hashed_ids(const Span<int> src, const int hash, MutableSpan<int> dst)
90{
91 for (const int i : src.index_range()) {
92 dst[i] = noise::hash(src[i], hash);
93 }
94}
95
97 const Span<int> src,
98 MutableSpan<int> all_dst)
99{
100 BLI_assert(offsets.total_size() == all_dst.size());
101 threading::parallel_for(offsets.index_range(), 512, [&](IndexRange range) {
102 for (const int i : range) {
103 MutableSpan<int> dst = all_dst.slice(offsets[i]);
104 if (dst.is_empty()) {
105 continue;
106 }
107 dst.first() = src[i];
108 for (const int i_duplicate : dst.index_range().drop_front(1)) {
109 dst[i_duplicate] = noise::hash(src[i], i_duplicate);
110 }
111 }
112 });
113}
114
117 const AttrDomain output_domain,
118 const IndexMask &selection,
119 const IndexAttributes &attribute_outputs,
120 const OffsetIndices<int> offsets)
121{
122 SpanAttributeWriter<int> duplicate_indices = attributes.lookup_or_add_for_write_only_span<int>(
123 *attribute_outputs.duplicate_index, output_domain);
124 for (const int i : IndexRange(selection.size())) {
125 MutableSpan<int> indices = duplicate_indices.span.slice(offsets[i]);
126 for (const int i : indices.index_range()) {
127 indices[i] = i;
128 }
129 }
130 duplicate_indices.finish();
131}
132
137static void copy_stable_id_point(const OffsetIndices<int> offsets,
138 const bke::AttributeAccessor src_attributes,
139 bke::MutableAttributeAccessor dst_attributes)
140{
141 GAttributeReader src_attribute = src_attributes.lookup("id");
142 if (!src_attribute) {
143 return;
144 }
145 if (!ELEM(src_attribute.domain, AttrDomain::Point, AttrDomain::Instance)) {
146 return;
147 }
148 if (!src_attribute.varray.type().is<int>()) {
149 return;
150 }
151 SpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span<int>(
152 "id", AttrDomain::Point);
153 if (!dst_attribute) {
154 return;
155 }
156
157 VArraySpan<int> src{src_attribute.varray.typed<int>()};
158 MutableSpan<int> dst = dst_attribute.span;
159 threaded_id_offset_copy(offsets, src, dst);
160 dst_attribute.finish();
161}
162
164
165/* -------------------------------------------------------------------- */
168
174 const IndexMask &selection,
175 const OffsetIndices<int> curve_offsets,
176 const AttributeFilter &attribute_filter,
177 bke::CurvesGeometry &dst_curves)
178{
179 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
180 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
181
182 for (auto &attribute : bke::retrieve_attributes_for_transfer(
183 src_curves.attributes(),
184 dst_curves.attributes_for_write(),
186 bke::attribute_filter_with_skip_ref(attribute_filter, {"id"})))
187 {
188 switch (attribute.meta_data.domain) {
189 case AttrDomain::Curve:
191 curve_offsets, selection, attribute.src, attribute.dst.span);
192 break;
193 case AttrDomain::Point:
194 bke::attribute_math::convert_to_static_type(attribute.src.type(), [&](auto dummy) {
195 using T = decltype(dummy);
196 const Span<T> src = attribute.src.typed<T>();
197 MutableSpan<T> dst = attribute.dst.span.typed<T>();
198 selection.foreach_index(
199 GrainSize(512), [&](const int64_t index, const int64_t i_selection) {
200 const Span<T> curve_src = src.slice(src_points_by_curve[index]);
201 for (const int dst_curve_index : curve_offsets[i_selection]) {
202 dst.slice(dst_points_by_curve[dst_curve_index]).copy_from(curve_src);
203 }
204 });
205 });
206 break;
207 default:
209 break;
210 }
211 attribute.dst.finish();
212 }
213}
214
221static void copy_stable_id_curves(const bke::CurvesGeometry &src_curves,
222 const IndexMask &selection,
223 const OffsetIndices<int> offsets,
224 bke::CurvesGeometry &dst_curves)
225{
226 GAttributeReader src_attribute = src_curves.attributes().lookup("id");
227 if (!src_attribute) {
228 return;
229 }
230 if (src_attribute.domain != AttrDomain::Point) {
231 return;
232 }
233 if (!src_attribute.varray.type().is<int>()) {
234 return;
235 }
236
237 SpanAttributeWriter dst_attribute =
239 AttrDomain::Point);
240 if (!dst_attribute) {
241 return;
242 }
243
244 VArraySpan<int> src{src_attribute.varray.typed<int>()};
245 MutableSpan<int> dst = dst_attribute.span;
246
247 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
248 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
249
250 selection.foreach_index(
251 GrainSize(512), [&](const int64_t i_src_curve, const int64_t i_selection) {
252 const Span<int> curve_src = src.slice(src_points_by_curve[i_src_curve]);
253 const IndexRange duplicates_range = offsets[i_selection];
254 for (const int i_duplicate : IndexRange(offsets[i_selection].size()).drop_front(1)) {
255 const int i_dst_curve = duplicates_range[i_duplicate];
256 copy_hashed_ids(curve_src, i_duplicate, dst.slice(dst_points_by_curve[i_dst_curve]));
257 }
258 });
259
260 dst_attribute.finish();
261}
262
264 const FieldContext &field_context,
265 const Field<int> &count_field,
266 const Field<bool> &selection_field,
267 const IndexAttributes &attribute_outputs,
268 const AttributeFilter &attribute_filter)
269{
270 FieldEvaluator evaluator{field_context, curves.curves_num()};
271 evaluator.add(count_field);
272 evaluator.set_selection(selection_field);
273 evaluator.evaluate();
274 const VArray<int> counts = evaluator.get_evaluated<int>(0);
275 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
276
277 const OffsetIndices points_by_curve = curves.points_by_curve();
278
279 /* The offset in the result curve domain at every selected input curve. */
280 Array<int> curve_offset_data(selection.size() + 1);
281 Array<int> point_offset_data(selection.size() + 1);
282
283 int dst_curves_num = 0;
284 int dst_points_num = 0;
285
286 selection.foreach_index_optimized<int>([&](const int index, const int i_curve) {
287 const int count = counts[index];
288 curve_offset_data[i_curve] = dst_curves_num;
289 point_offset_data[i_curve] = dst_points_num;
290 dst_curves_num += count;
291 dst_points_num += count * points_by_curve[index].size();
292 });
293
294 if (dst_points_num == 0) {
295 return {};
296 }
297
298 curve_offset_data.last() = dst_curves_num;
299 point_offset_data.last() = dst_points_num;
300
301 const OffsetIndices<int> curve_offsets(curve_offset_data);
302 const OffsetIndices<int> point_offsets(point_offset_data);
303
304 bke::CurvesGeometry new_curves{dst_points_num, dst_curves_num};
305 MutableSpan<int> all_dst_offsets = new_curves.offsets_for_write();
306 selection.foreach_index(GrainSize(512),
307 [&](const int64_t i_src_curve, const int64_t i_selection) {
308 const IndexRange src_curve_range = points_by_curve[i_src_curve];
309 const IndexRange dst_curves_range = curve_offsets[i_selection];
310 MutableSpan<int> dst_offsets = all_dst_offsets.slice(dst_curves_range);
311 for (const int i_duplicate : IndexRange(dst_curves_range.size())) {
312 dst_offsets[i_duplicate] = point_offsets[i_selection].start() +
313 src_curve_range.size() * i_duplicate;
314 }
315 });
316 all_dst_offsets.last() = dst_points_num;
317
318 copy_curve_attributes_without_id(curves, selection, curve_offsets, attribute_filter, new_curves);
319 copy_stable_id_curves(curves, selection, curve_offsets, new_curves);
320
321 if (attribute_outputs.duplicate_index) {
323 AttrDomain::Curve,
324 selection,
325 attribute_outputs,
326 curve_offsets);
327 }
328
329 new_curves.update_curve_types();
330 return new_curves;
331}
332
333static void duplicate_curves(GeometrySet &geometry_set,
334 const Field<int> &count_field,
335 const Field<bool> &selection_field,
336 const IndexAttributes &attribute_outputs,
337 const AttributeFilter &attribute_filter)
338{
339 geometry_set.keep_only({GeometryComponent::Type::Curve,
340 GeometryComponent::Type::GreasePencil,
341 GeometryComponent::Type::Edit});
343 if (const Curves *curves_id = geometry_set.get_curves()) {
344 const bke::CurvesFieldContext field_context{*curves_id, AttrDomain::Curve};
345 bke::CurvesGeometry new_curves = duplicate_curves_CurveGeometry(curves_id->geometry.wrap(),
346 field_context,
347 count_field,
348 selection_field,
349 attribute_outputs,
350 attribute_filter);
351 Curves *new_curves_id = bke::curves_new_nomain(std::move(new_curves));
352 bke::curves_copy_parameters(*curves_id, *new_curves_id);
353 geometry_set.replace_curves(new_curves_id);
354 }
355 if (GreasePencil *grease_pencil = geometry_set.get_grease_pencil_for_write()) {
356 using namespace bke::greasepencil;
358 grease_pencil->layers().index_range(), 16, [&](const IndexRange layers_range) {
359 for (const int layer_i : layers_range) {
360 Layer &layer = grease_pencil->layer(layer_i);
361 Drawing *drawing = grease_pencil->get_eval_drawing(layer);
362 if (!drawing) {
363 continue;
364 }
365 bke::CurvesGeometry &curves = drawing->strokes_for_write();
366 const bke::GreasePencilLayerFieldContext field_context{
367 *grease_pencil, AttrDomain::Curve, layer_i};
368 curves = duplicate_curves_CurveGeometry(curves,
369 field_context,
370 count_field,
371 selection_field,
372 attribute_outputs,
373 attribute_filter);
374 drawing->tag_topology_changed();
375 }
376 });
377 }
378}
379
381
382/* -------------------------------------------------------------------- */
385
390static void copy_face_attributes_without_id(const Span<int> edge_mapping,
391 const Span<int> vert_mapping,
392 const Span<int> loop_mapping,
393 const OffsetIndices<int> offsets,
394 const IndexMask &selection,
395 const AttributeFilter &attribute_filter,
396 const bke::AttributeAccessor src_attributes,
397 bke::MutableAttributeAccessor dst_attributes)
398{
399 for (auto &attribute : bke::retrieve_attributes_for_transfer(
400 src_attributes,
401 dst_attributes,
404 attribute_filter, {"id", ".corner_vert", ".corner_edge", ".edge_verts"})))
405 {
406 switch (attribute.meta_data.domain) {
407 case AttrDomain::Point:
408 bke::attribute_math::gather(attribute.src, vert_mapping, attribute.dst.span);
409 break;
410 case AttrDomain::Edge:
411 bke::attribute_math::gather(attribute.src, edge_mapping, attribute.dst.span);
412 break;
413 case AttrDomain::Face:
415 offsets, selection, attribute.src, attribute.dst.span);
416 break;
417 case AttrDomain::Corner:
418 bke::attribute_math::gather(attribute.src, loop_mapping, attribute.dst.span);
419 break;
420 default:
422 break;
423 }
424 attribute.dst.finish();
425 }
426}
427
435static void copy_stable_id_faces(const Mesh &mesh,
436 const IndexMask &selection,
437 const OffsetIndices<int> face_offsets,
438 const Span<int> vert_mapping,
439 const bke::AttributeAccessor src_attributes,
440 bke::MutableAttributeAccessor dst_attributes)
441{
442 GAttributeReader src_attribute = src_attributes.lookup("id");
443 if (!src_attribute) {
444 return;
445 }
446 if (src_attribute.domain != AttrDomain::Point) {
447 return;
448 }
449 if (!src_attribute.varray.type().is<int>()) {
450 return;
451 }
452 SpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span<int>(
453 "id", AttrDomain::Point);
454 if (!dst_attribute) {
455 return;
456 }
457
458 VArraySpan<int> src{src_attribute.varray.typed<int>()};
459 MutableSpan<int> dst = dst_attribute.span;
460
461 const OffsetIndices faces = mesh.faces();
462 int loop_index = 0;
463 for (const int i_face : selection.index_range()) {
464 const IndexRange range = face_offsets[i_face];
465 if (range.is_empty()) {
466 continue;
467 }
468 const IndexRange source = faces[i_face];
469 for ([[maybe_unused]] const int i_duplicate : IndexRange(range.size())) {
470 for ([[maybe_unused]] const int i_loops : IndexRange(source.size())) {
471 if (i_duplicate == 0) {
472 dst[loop_index] = src[vert_mapping[loop_index]];
473 }
474 else {
475 dst[loop_index] = noise::hash(src[vert_mapping[loop_index]], i_duplicate);
476 }
477 loop_index++;
478 }
479 }
480 }
481
482 dst_attribute.finish();
483}
484
485static void duplicate_faces(GeometrySet &geometry_set,
486 const Field<int> &count_field,
487 const Field<bool> &selection_field,
488 const IndexAttributes &attribute_outputs,
489 const AttributeFilter &attribute_filter)
490{
491 if (!geometry_set.has_mesh()) {
492 geometry_set.clear();
493 return;
494 }
495 geometry_set.keep_only({GeometryComponent::Type::Mesh, GeometryComponent::Type::Edit});
496
497 const Mesh &mesh = *geometry_set.get_mesh();
498 const OffsetIndices faces = mesh.faces();
499 const Span<int> corner_verts = mesh.corner_verts();
500 const Span<int> corner_edges = mesh.corner_edges();
501
502 const bke::MeshFieldContext field_context{mesh, AttrDomain::Face};
503 FieldEvaluator evaluator(field_context, faces.size());
504 evaluator.add(count_field);
505 evaluator.set_selection(selection_field);
506 evaluator.evaluate();
507 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
508 const VArray<int> counts = evaluator.get_evaluated<int>(0);
509
510 int total_faces = 0;
511 int total_loops = 0;
512 Array<int> offset_data(selection.size() + 1);
513 selection.foreach_index_optimized<int>([&](const int index, const int i_selection) {
514 const int count = counts[index];
515 offset_data[i_selection] = total_faces;
516 total_faces += count;
517 total_loops += count * faces[index].size();
518 });
519 offset_data[selection.size()] = total_faces;
520
521 const OffsetIndices<int> duplicates(offset_data);
522
523 Mesh *new_mesh = BKE_mesh_new_nomain(total_loops, total_loops, total_faces, total_loops);
524 MutableSpan<int2> new_edges = new_mesh->edges_for_write();
525 MutableSpan<int> new_face_offsets = new_mesh->face_offsets_for_write();
526 MutableSpan<int> new_corner_verts = new_mesh->corner_verts_for_write();
527 MutableSpan<int> new_corner_edges = new_mesh->corner_edges_for_write();
528
529 Array<int> vert_mapping(new_mesh->verts_num);
530 Array<int> edge_mapping(new_edges.size());
531 Array<int> loop_mapping(total_loops);
532
533 int face_index = 0;
534 int loop_index = 0;
535 selection.foreach_index_optimized<int>([&](const int index, const int i_selection) {
536 const IndexRange face_range = duplicates[i_selection];
537 const IndexRange source = faces[index];
538 for ([[maybe_unused]] const int i_duplicate : face_range.index_range()) {
539 new_face_offsets[face_index] = loop_index;
540 for (const int src_corner : source) {
541 loop_mapping[loop_index] = src_corner;
542 vert_mapping[loop_index] = corner_verts[src_corner];
543 edge_mapping[loop_index] = corner_edges[src_corner];
544 new_edges[loop_index][0] = loop_index;
545 if (src_corner != source.last()) {
546 new_edges[loop_index][1] = loop_index + 1;
547 }
548 else {
549 new_edges[loop_index][1] = new_face_offsets[face_index];
550 }
551 loop_index++;
552 }
553 face_index++;
554 }
555 });
556 array_utils::fill_index_range<int>(new_corner_verts);
557 array_utils::fill_index_range<int>(new_corner_edges);
558
559 new_mesh->tag_loose_verts_none();
560 new_mesh->tag_loose_edges_none();
561 new_mesh->tag_overlapping_none();
562
564 vert_mapping,
565 loop_mapping,
566 duplicates,
567 selection,
568 attribute_filter,
569 mesh.attributes(),
570 new_mesh->attributes_for_write());
571
573 selection,
574 duplicates,
575 vert_mapping,
576 mesh.attributes(),
577 new_mesh->attributes_for_write());
578
579 if (attribute_outputs.duplicate_index) {
580 create_duplicate_index_attribute(new_mesh->attributes_for_write(),
581 AttrDomain::Face,
582 selection,
583 attribute_outputs,
584 duplicates);
585 }
586
587 geometry_set.replace_mesh(new_mesh);
588}
589
591
592/* -------------------------------------------------------------------- */
595
600static void copy_edge_attributes_without_id(const Span<int> point_mapping,
601 const OffsetIndices<int> offsets,
602 const IndexMask &selection,
603 const AttributeFilter &attribute_filter,
604 const bke::AttributeAccessor src_attributes,
605 bke::MutableAttributeAccessor dst_attributes)
606{
607 for (auto &attribute : bke::retrieve_attributes_for_transfer(
608 src_attributes,
609 dst_attributes,
611 bke::attribute_filter_with_skip_ref(attribute_filter, {"id", ".edge_verts"})))
612 {
613 switch (attribute.meta_data.domain) {
614 case AttrDomain::Edge:
616 offsets, selection, attribute.src, attribute.dst.span);
617 break;
618 case AttrDomain::Point:
619 bke::attribute_math::gather(attribute.src, point_mapping, attribute.dst.span);
620 break;
621 default:
623 break;
624 }
625 attribute.dst.finish();
626 }
627}
628
633static void copy_stable_id_edges(const Mesh &mesh,
634 const IndexMask &selection,
635 const OffsetIndices<int> offsets,
636 const bke::AttributeAccessor src_attributes,
637 bke::MutableAttributeAccessor dst_attributes)
638{
639 GAttributeReader src_attribute = src_attributes.lookup("id");
640 if (!src_attribute) {
641 return;
642 }
643 if (src_attribute.domain != AttrDomain::Point) {
644 return;
645 }
646 if (!src_attribute.varray.type().is<int>()) {
647 return;
648 }
649 SpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span<int>(
650 "id", AttrDomain::Point);
651 if (!dst_attribute) {
652 return;
653 }
654
655 const Span<int2> edges = mesh.edges();
656
657 VArraySpan<int> src{src_attribute.varray.typed<int>()};
658 MutableSpan<int> dst = dst_attribute.span;
659 selection.foreach_index(GrainSize(1024), [&](const int64_t index, const int64_t i_selection) {
660 const IndexRange edge_range = offsets[i_selection];
661 if (edge_range.is_empty()) {
662 return;
663 }
664 const int2 &edge = edges[index];
665 const IndexRange vert_range = {edge_range.start() * 2, edge_range.size() * 2};
666
667 dst[vert_range[0]] = src[edge[0]];
668 dst[vert_range[1]] = src[edge[1]];
669 for (const int i_duplicate : IndexRange(1, edge_range.size() - 1)) {
670 dst[vert_range[i_duplicate * 2]] = noise::hash(src[edge[0]], i_duplicate);
671 dst[vert_range[i_duplicate * 2 + 1]] = noise::hash(src[edge[1]], i_duplicate);
672 }
673 });
674 dst_attribute.finish();
675}
676
677static void duplicate_edges(GeometrySet &geometry_set,
678 const Field<int> &count_field,
679 const Field<bool> &selection_field,
680 const IndexAttributes &attribute_outputs,
681 const AttributeFilter &attribute_filter)
682{
683 if (!geometry_set.has_mesh()) {
684 geometry_set.clear();
685 return;
686 };
687 const Mesh &mesh = *geometry_set.get_mesh();
688 const Span<int2> edges = mesh.edges();
689
690 const bke::MeshFieldContext field_context{mesh, AttrDomain::Edge};
691 FieldEvaluator evaluator{field_context, edges.size()};
692 evaluator.add(count_field);
693 evaluator.set_selection(selection_field);
694 evaluator.evaluate();
695 const VArray<int> counts = evaluator.get_evaluated<int>(0);
696 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
697
698 Array<int> offset_data;
700 selection, counts, offset_data);
701 const int output_edges_num = duplicates.total_size();
702
703 Mesh *new_mesh = BKE_mesh_new_nomain(output_edges_num * 2, output_edges_num, 0, 0);
704 MutableSpan<int2> new_edges = new_mesh->edges_for_write();
705
706 Array<int> vert_orig_indices(output_edges_num * 2);
707 selection.foreach_index(GrainSize(1024), [&](const int64_t index, const int64_t i_selection) {
708 const int2 &edge = edges[index];
709 const IndexRange edge_range = duplicates[i_selection];
710 const IndexRange vert_range(edge_range.start() * 2, edge_range.size() * 2);
711
712 for (const int i_duplicate : IndexRange(edge_range.size())) {
713 vert_orig_indices[vert_range[i_duplicate * 2]] = edge[0];
714 vert_orig_indices[vert_range[i_duplicate * 2 + 1]] = edge[1];
715 }
716 });
717
718 threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
719 for (const int i_selection : range) {
720 const IndexRange edge_range = duplicates[i_selection];
721 const IndexRange vert_range(edge_range.start() * 2, edge_range.size() * 2);
722 for (const int i_duplicate : IndexRange(edge_range.size())) {
723 int2 &new_edge = new_edges[edge_range[i_duplicate]];
724 new_edge[0] = vert_range[i_duplicate * 2];
725 new_edge[1] = vert_range[i_duplicate * 2] + 1;
726 }
727 }
728 });
729
730 copy_edge_attributes_without_id(vert_orig_indices,
731 duplicates,
732 selection,
733 attribute_filter,
734 mesh.attributes(),
735 new_mesh->attributes_for_write());
736
737 copy_stable_id_edges(
738 mesh, selection, duplicates, mesh.attributes(), new_mesh->attributes_for_write());
739
740 if (attribute_outputs.duplicate_index) {
741 create_duplicate_index_attribute(new_mesh->attributes_for_write(),
742 AttrDomain::Edge,
743 selection,
744 attribute_outputs,
745 duplicates);
746 }
747
748 new_mesh->tag_overlapping_none();
749
750 geometry_set.replace_mesh(new_mesh);
751}
752
754
755/* -------------------------------------------------------------------- */
758
760 const bke::CurvesGeometry &src_curves,
761 const FieldContext &field_context,
762 const Field<int> &count_field,
763 const Field<bool> &selection_field,
764 const IndexAttributes &attribute_outputs,
765 const AttributeFilter &attribute_filter)
766{
767 if (src_curves.is_empty()) {
768 return {};
769 }
770
771 FieldEvaluator evaluator{field_context, src_curves.points_num()};
772 evaluator.add(count_field);
773 evaluator.set_selection(selection_field);
774 evaluator.evaluate();
775 const VArray<int> counts = evaluator.get_evaluated<int>(0);
776 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
777
778 Array<int> offset_data;
780 selection, counts, offset_data);
781 const int dst_num = duplicates.total_size();
782
783 const Array<int> point_to_curve_map = src_curves.point_to_curve_map();
784
785 bke::CurvesGeometry new_curves{dst_num, dst_num};
787
789 AttrDomain::Point,
790 AttrDomain::Point,
791 attribute_filter,
792 duplicates,
793 selection,
794 new_curves.attributes_for_write());
795
796 for (auto &attribute : bke::retrieve_attributes_for_transfer(
797 src_curves.attributes(),
798 new_curves.attributes_for_write(),
800 bke::attribute_filter_with_skip_ref(attribute_filter, {"id"})))
801 {
802 bke::attribute_math::convert_to_static_type(attribute.src.type(), [&](auto dummy) {
803 using T = decltype(dummy);
804 const Span<T> src = attribute.src.typed<T>();
805 MutableSpan<T> dst = attribute.dst.span.typed<T>();
806 selection.foreach_index(GrainSize(512), [&](const int64_t index, const int64_t i_selection) {
807 const T &src_value = src[point_to_curve_map[index]];
808 dst.slice(duplicates[i_selection]).fill(src_value);
809 });
810 });
811 attribute.dst.finish();
812 }
813
814 copy_stable_id_point(duplicates, src_curves.attributes(), new_curves.attributes_for_write());
815
816 if (attribute_outputs.duplicate_index) {
817 create_duplicate_index_attribute(new_curves.attributes_for_write(),
818 AttrDomain::Point,
819 selection,
820 attribute_outputs,
821 duplicates);
822 }
823
824 return new_curves;
825}
826
827static void duplicate_points_curve(GeometrySet &geometry_set,
828 const Field<int> &count_field,
829 const Field<bool> &selection_field,
830 const IndexAttributes &attribute_outputs,
831 const AttributeFilter &attribute_filter)
832{
833 const Curves &src_curves_id = *geometry_set.get_curves();
834 const bke::CurvesGeometry &src_curves = src_curves_id.geometry.wrap();
835
836 const bke::CurvesFieldContext field_context{src_curves_id, AttrDomain::Point};
838 field_context,
839 count_field,
840 selection_field,
841 attribute_outputs,
842 attribute_filter);
843
844 Curves *new_curves_id = bke::curves_new_nomain(std::move(new_curves));
845 bke::curves_copy_parameters(src_curves_id, *new_curves_id);
846 geometry_set.replace_curves(new_curves_id);
847}
848
850
851/* -------------------------------------------------------------------- */
854
856 const Field<int> &count_field,
857 const Field<bool> &selection_field,
858 const IndexAttributes &attribute_outputs,
859 const AttributeFilter &attribute_filter)
860{
861 using namespace bke::greasepencil;
862 GreasePencil &grease_pencil = *geometry_set.get_grease_pencil_for_write();
864 grease_pencil.layers().index_range(), 16, [&](const IndexRange layers_range) {
865 for (const int layer_i : layers_range) {
866 Layer &layer = grease_pencil.layer(layer_i);
867 Drawing *drawing = grease_pencil.get_eval_drawing(layer);
868 if (!drawing) {
869 continue;
870 }
871 bke::CurvesGeometry &curves = drawing->strokes_for_write();
872 const bke::GreasePencilLayerFieldContext field_context{
873 grease_pencil, AttrDomain::Point, layer_i};
874 curves = duplicate_points_CurvesGeometry(curves,
875 field_context,
876 count_field,
877 selection_field,
878 attribute_outputs,
879 attribute_filter);
880 drawing->tag_topology_changed();
881 }
882 });
883}
884
886
887/* -------------------------------------------------------------------- */
890
891static void duplicate_points_mesh(GeometrySet &geometry_set,
892 const Field<int> &count_field,
893 const Field<bool> &selection_field,
894 const IndexAttributes &attribute_outputs,
895 const AttributeFilter &attribute_filter)
896{
897 const Mesh &mesh = *geometry_set.get_mesh();
898
899 const bke::MeshFieldContext field_context{mesh, AttrDomain::Point};
900 FieldEvaluator evaluator{field_context, mesh.verts_num};
901 evaluator.add(count_field);
902 evaluator.set_selection(selection_field);
903 evaluator.evaluate();
904 const VArray<int> counts = evaluator.get_evaluated<int>(0);
905 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
906
907 Array<int> offset_data;
909 selection, counts, offset_data);
910
911 Mesh *new_mesh = BKE_mesh_new_nomain(duplicates.total_size(), 0, 0, 0);
912
914 AttrDomain::Point,
915 AttrDomain::Point,
916 bke::attribute_filter_with_skip_ref(attribute_filter, {"id"}),
917 duplicates,
918 selection,
919 new_mesh->attributes_for_write());
920
921 copy_stable_id_point(duplicates, mesh.attributes(), new_mesh->attributes_for_write());
922
923 if (attribute_outputs.duplicate_index) {
924 create_duplicate_index_attribute(new_mesh->attributes_for_write(),
925 AttrDomain::Point,
926 selection,
927 attribute_outputs,
928 duplicates);
929 }
930
931 new_mesh->tag_overlapping_none();
932
933 geometry_set.replace_mesh(new_mesh);
934}
935
937
938/* -------------------------------------------------------------------- */
941
942static void duplicate_points_pointcloud(GeometrySet &geometry_set,
943 const Field<int> &count_field,
944 const Field<bool> &selection_field,
945 const IndexAttributes &attribute_outputs,
946 const AttributeFilter &attribute_filter)
947{
948 const PointCloud &src_points = *geometry_set.get_pointcloud();
949
950 const bke::PointCloudFieldContext field_context{src_points};
951 FieldEvaluator evaluator{field_context, src_points.totpoint};
952 evaluator.add(count_field);
953 evaluator.set_selection(selection_field);
954 evaluator.evaluate();
955 const VArray<int> counts = evaluator.get_evaluated<int>(0);
956 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
957
958 Array<int> offset_data;
960 selection, counts, offset_data);
961
962 PointCloud *pointcloud = BKE_pointcloud_new_nomain(duplicates.total_size());
963
965 AttrDomain::Point,
966 AttrDomain::Point,
967 bke::attribute_filter_with_skip_ref(attribute_filter, {"id"}),
968 duplicates,
969 selection,
970 pointcloud->attributes_for_write());
971
972 copy_stable_id_point(duplicates, src_points.attributes(), pointcloud->attributes_for_write());
973
974 if (attribute_outputs.duplicate_index) {
975 create_duplicate_index_attribute(pointcloud->attributes_for_write(),
976 AttrDomain::Point,
977 selection,
978 attribute_outputs,
979 duplicates);
980 }
981 geometry_set.replace_pointcloud(pointcloud);
982}
983
985
986/* -------------------------------------------------------------------- */
989
990static void duplicate_points(GeometrySet &geometry_set,
991 const Field<int> &count_field,
992 const Field<bool> &selection_field,
993 const IndexAttributes &attribute_outputs,
994 const AttributeFilter &attribute_filter)
995{
996 Vector<GeometryComponent::Type> component_types = geometry_set.gather_component_types(true,
997 true);
998 for (const GeometryComponent::Type component_type : component_types) {
999 switch (component_type) {
1000 case GeometryComponent::Type::PointCloud:
1001 if (geometry_set.has_pointcloud()) {
1003 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
1004 }
1005 break;
1006 case GeometryComponent::Type::Mesh:
1007 if (geometry_set.has_mesh()) {
1009 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
1010 }
1011 break;
1012 case GeometryComponent::Type::Curve:
1013 if (geometry_set.has_curves()) {
1015 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
1016 }
1017 break;
1018 case GeometryComponent::Type::GreasePencil: {
1019 if (geometry_set.has_grease_pencil()) {
1021 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
1022 }
1023 break;
1024 }
1025 default:
1026 break;
1027 }
1028 }
1029 component_types.append(GeometryComponent::Type::Edit);
1030 geometry_set.keep_only(component_types);
1031}
1032
1034
1035/* -------------------------------------------------------------------- */
1038
1039static void duplicate_layers(GeometrySet &geometry_set,
1040 const Field<int> &count_field,
1041 const Field<bool> &selection_field,
1042 const IndexAttributes &attribute_outputs,
1043 const AttributeFilter &attribute_filter)
1044{
1045 using namespace bke::greasepencil;
1046 if (!geometry_set.has_grease_pencil()) {
1047 geometry_set.clear();
1048 return;
1049 }
1050 geometry_set.keep_only({GeometryComponent::Type::GreasePencil, GeometryComponent::Type::Edit});
1052 const GreasePencil &src_grease_pencil = *geometry_set.get_grease_pencil();
1053
1054 bke::GreasePencilFieldContext field_context{src_grease_pencil};
1055 FieldEvaluator evaluator{field_context, src_grease_pencil.layers().size()};
1056 evaluator.add(count_field);
1057 evaluator.set_selection(selection_field);
1058 evaluator.evaluate();
1059 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
1060 const VArray<int> counts = evaluator.get_evaluated<int>(0);
1061
1062 Array<int> offset_data;
1064 selection, counts, offset_data);
1065 const int new_layers_num = duplicates.total_size();
1066 if (new_layers_num == 0) {
1067 geometry_set.clear();
1068 return;
1069 }
1070
1071 GreasePencil *new_grease_pencil = BKE_grease_pencil_new_nomain();
1072 BKE_grease_pencil_copy_parameters(src_grease_pencil, *new_grease_pencil);
1073
1074 new_grease_pencil->add_layers_with_empty_drawings_for_eval(new_layers_num);
1075 static bke::CurvesGeometry static_empty_curves;
1076 selection.foreach_index([&](const int src_layer_i, const int pos) {
1077 const IndexRange range = duplicates[pos];
1078 if (range.is_empty()) {
1079 return;
1080 }
1081 const Layer &src_layer = src_grease_pencil.layer(src_layer_i);
1082 const Drawing *src_drawing = src_grease_pencil.get_eval_drawing(src_layer);
1083 const bke::CurvesGeometry &src_curves = src_drawing ? src_drawing->strokes() :
1084 static_empty_curves;
1085 const StringRefNull src_layer_name = src_layer.name();
1086 for (Layer *new_layer : new_grease_pencil->layers_for_write().slice(range)) {
1087 BKE_grease_pencil_copy_layer_parameters(src_layer, *new_layer);
1088 new_layer->set_name(src_layer_name);
1089 Drawing *new_drawing = new_grease_pencil->get_eval_drawing(*new_layer);
1090 new_drawing->strokes_for_write() = src_curves;
1091 }
1092 });
1093
1094 bke::gather_attributes_to_groups(src_grease_pencil.attributes(),
1095 AttrDomain::Layer,
1096 AttrDomain::Layer,
1097 attribute_filter,
1098 duplicates,
1099 selection,
1100 new_grease_pencil->attributes_for_write());
1101
1102 if (attribute_outputs.duplicate_index) {
1103 create_duplicate_index_attribute(new_grease_pencil->attributes_for_write(),
1104 AttrDomain::Layer,
1105 selection,
1106 attribute_outputs,
1107 duplicates);
1108 }
1109
1110 geometry_set.replace_grease_pencil(new_grease_pencil);
1111}
1112
1114
1115/* -------------------------------------------------------------------- */
1118
1119static void duplicate_instances(GeometrySet &geometry_set,
1120 const Field<int> &count_field,
1121 const Field<bool> &selection_field,
1122 const IndexAttributes &attribute_outputs,
1123 const AttributeFilter &attribute_filter)
1124{
1125 if (!geometry_set.has_instances()) {
1126 geometry_set.clear();
1127 return;
1128 }
1129
1130 const bke::Instances &src_instances = *geometry_set.get_instances();
1131
1132 bke::InstancesFieldContext field_context{src_instances};
1133 FieldEvaluator evaluator{field_context, src_instances.instances_num()};
1134 evaluator.add(count_field);
1135 evaluator.set_selection(selection_field);
1136 evaluator.evaluate();
1137 IndexMask selection = evaluator.get_evaluated_selection_as_mask();
1138 const VArray<int> counts = evaluator.get_evaluated<int>(0);
1139
1140 Array<int> offset_data;
1142 selection, counts, offset_data);
1143 if (duplicates.total_size() == 0) {
1144 geometry_set.clear();
1145 return;
1146 }
1147
1148 std::unique_ptr<bke::Instances> dst_instances = std::make_unique<bke::Instances>();
1149
1150 dst_instances->resize(duplicates.total_size());
1151 selection.foreach_index([&](const int i_src, const int i_dst) {
1152 const IndexRange range = duplicates[i_dst];
1153 if (range.is_empty()) {
1154 return;
1155 }
1156 const int old_handle = src_instances.reference_handles()[i_src];
1157 const bke::InstanceReference reference = src_instances.references()[old_handle];
1158 const int new_handle = dst_instances->add_reference(reference);
1159 dst_instances->reference_handles_for_write().slice(range).fill(new_handle);
1160 });
1161
1163 src_instances.attributes(),
1164 AttrDomain::Instance,
1165 AttrDomain::Instance,
1166 bke::attribute_filter_with_skip_ref(attribute_filter, {"id", ".reference_index"}),
1167 duplicates,
1168 selection,
1169 dst_instances->attributes_for_write());
1170
1171 if (attribute_outputs.duplicate_index) {
1172 create_duplicate_index_attribute(dst_instances->attributes_for_write(),
1173 AttrDomain::Instance,
1174 selection,
1175 attribute_outputs,
1176 duplicates);
1177 }
1178
1179 geometry_set = GeometrySet::from_instances(dst_instances.release());
1180}
1181
1183
1184/* -------------------------------------------------------------------- */
1187
1189{
1190 GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
1191
1192 const NodeGeometryDuplicateElements &storage = node_storage(params.node());
1193 const AttrDomain duplicate_domain = AttrDomain(storage.domain);
1194
1195 static auto max_zero_fn = mf::build::SI1_SO<int, int>(
1196 "max_zero",
1197 [](int value) { return std::max(0, value); },
1198 mf::build::exec_presets::AllSpanOrSingle());
1199 Field<int> count_field(
1200 FieldOperation::from(max_zero_fn, {params.extract_input<Field<int>>("Amount")}));
1201
1202 Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
1203 IndexAttributes attribute_outputs;
1204 attribute_outputs.duplicate_index = params.get_output_anonymous_attribute_id_if_needed(
1205 "Duplicate Index");
1206
1207 const NodeAttributeFilter &attribute_filter = params.get_attribute_filter("Geometry");
1208
1209 if (duplicate_domain == AttrDomain::Instance) {
1211 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
1212 }
1213 else {
1214 geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
1215 switch (duplicate_domain) {
1216 case AttrDomain::Curve:
1218 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
1219 break;
1220 case AttrDomain::Face:
1222 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
1223 break;
1224 case AttrDomain::Edge:
1226 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
1227 break;
1228 case AttrDomain::Point:
1230 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
1231 break;
1232 case AttrDomain::Layer:
1234 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
1235 break;
1236 default:
1238 break;
1239 }
1240 });
1241 }
1242
1243 if (geometry_set.is_empty()) {
1244 params.set_default_remaining_outputs();
1245 return;
1246 }
1247
1248 params.set_output("Geometry", std::move(geometry_set));
1249}
1250
1252
1253static void node_rna(StructRNA *srna)
1254{
1255 static const EnumPropertyItem domain_items[] = {
1256 {int(AttrDomain::Point), "POINT", 0, "Point", ""},
1257 {int(AttrDomain::Edge), "EDGE", 0, "Edge", ""},
1258 {int(AttrDomain::Face), "FACE", 0, "Face", ""},
1259 {int(AttrDomain::Curve), "SPLINE", 0, "Spline", ""},
1260 {int(AttrDomain::Layer), "LAYER", 0, "Layer", ""},
1261 {int(AttrDomain::Instance), "INSTANCE", 0, "Instance", ""},
1262 {0, nullptr, 0, nullptr, nullptr},
1263 };
1264
1265 RNA_def_node_enum(srna,
1266 "domain",
1267 "Domain",
1268 "Which domain to duplicate",
1269 domain_items,
1271 int(AttrDomain::Point),
1272 nullptr,
1273 true);
1274}
1275
1276static void node_register()
1277{
1278 static blender::bke::bNodeType ntype;
1279 geo_node_type_base(&ntype, "GeometryNodeDuplicateElements", GEO_NODE_DUPLICATE_ELEMENTS);
1280 ntype.ui_name = "Duplicate Elements";
1281 ntype.ui_description = "Generate an arbitrary number copies of each selected input element";
1282 ntype.enum_name_legacy = "DUPLICATE_ELEMENTS";
1285 "NodeGeometryDuplicateElements",
1288
1289 ntype.initfunc = node_init;
1290 ntype.draw_buttons = node_layout;
1292 ntype.declare = node_declare;
1294
1295 node_rna(ntype.rna_ext.srna);
1296}
1297NOD_REGISTER_NODE(node_register)
1298
1299} // namespace blender::nodes::node_geo_duplicate_elements_cc
@ ATTR_DOMAIN_MASK_POINT
@ ATTR_DOMAIN_MASK_EDGE
@ ATTR_DOMAIN_MASK_ALL
@ ATTR_DOMAIN_MASK_CURVE
Low-level operations for curves.
Low-level operations for grease pencil.
void BKE_grease_pencil_copy_parameters(const GreasePencil &src, GreasePencil &dst)
GreasePencil * BKE_grease_pencil_new_nomain()
void BKE_grease_pencil_copy_layer_parameters(const blender::bke::greasepencil::Layer &src, blender::bke::greasepencil::Layer &dst)
Mesh * BKE_mesh_new_nomain(int verts_num, int edges_num, int faces_num, int corners_num)
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1240
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:461
#define GEO_NODE_DUPLICATE_ELEMENTS
General operations for point clouds.
PointCloud * BKE_pointcloud_new_nomain(int totpoint)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define ELEM(...)
#define BLT_I18NCONTEXT_COUNTABLE
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_storage_enum_accessors(member)
#define UI_ITEM_NONE
AttrDomain
BMesh const char void * data
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
AttributeSet attributes
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:248
const T & last(const int64_t n=0) const
Definition BLI_array.hh:296
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
bool is() const
constexpr int64_t size() const
constexpr bool is_empty() const
constexpr int64_t start() const
constexpr IndexRange index_range() const
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:689
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
std::optional< T > get_if_single() const
void append(const T &value)
GAttributeReader lookup(const StringRef attribute_id) const
Array< int > point_to_curve_map() const
OffsetIndices< int > points_by_curve() const
MutableAttributeAccessor attributes_for_write()
AttributeAccessor attributes() const
MutableSpan< int > offsets_for_write()
Span< int > reference_handles() const
Definition instances.cc:215
Span< InstanceReference > references() const
Definition instances.cc:275
bke::AttributeAccessor attributes() const
Definition instances.cc:64
int instances_num() const
Definition instances.cc:393
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, AttrType data_type)
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
void set_selection(Field< bool > selection)
Definition FN_field.hh:383
int add(GField field, GVArray *varray_ptr)
Definition field.cc:751
IndexMask get_evaluated_selection_as_mask() const
Definition field.cc:817
const GVArray & get_evaluated(const int field_index) const
Definition FN_field.hh:448
void foreach_index_optimized(Fn &&fn) const
void foreach_index(Fn &&fn) const
static std::shared_ptr< FieldOperation > from(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:242
static void remember_deformed_positions_if_necessary(GeometrySet &geometry)
static ushort indices[]
uint pos
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int count
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
static char faces[256]
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
void fill_index_range(MutableSpan< T > span, const T start=0)
void gather(GSpan src, Span< int > map, GMutableSpan dst)
void gather_to_groups(OffsetIndices< int > dst_offsets, const IndexMask &src_selection, GSpan src, GMutableSpan dst)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
void curves_copy_parameters(const Curves &src, Curves &dst)
Vector< AttributeTransferData > retrieve_attributes_for_transfer(const AttributeAccessor src_attributes, MutableAttributeAccessor dst_attributes, AttrDomainMask domain_mask, const AttributeFilter &attribute_filter={})
void gather_attributes_to_groups(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, OffsetIndices< int > dst_offsets, const IndexMask &src_selection, MutableAttributeAccessor dst_attributes)
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
auto attribute_filter_with_skip_ref(AttributeFilter filter, const Span< StringRef > skip)
void node_type_storage(bNodeType &ntype, std::optional< StringRefNull > storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:5414
Curves * curves_new_nomain(int points_num, int curves_num)
void foreach_real_geometry(bke::GeometrySet &geometry, FunctionRef< void(bke::GeometrySet &geometry_set)> fn)
static void copy_stable_id_curves(const bke::CurvesGeometry &src_curves, const IndexMask &selection, const OffsetIndices< int > offsets, bke::CurvesGeometry &dst_curves)
static void duplicate_points_grease_pencil(GeometrySet &geometry_set, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs, const AttributeFilter &attribute_filter)
static void duplicate_points_pointcloud(GeometrySet &geometry_set, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs, const AttributeFilter &attribute_filter)
static void duplicate_faces(GeometrySet &geometry_set, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs, const AttributeFilter &attribute_filter)
static OffsetIndices< int > accumulate_counts_to_offsets(const IndexMask &selection, const VArray< int > &counts, Array< int > &r_offset_data)
static void copy_edge_attributes_without_id(const Span< int > point_mapping, const OffsetIndices< int > offsets, const IndexMask &selection, const AttributeFilter &attribute_filter, const bke::AttributeAccessor src_attributes, bke::MutableAttributeAccessor dst_attributes)
static void duplicate_instances(GeometrySet &geometry_set, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs, const AttributeFilter &attribute_filter)
static bke::CurvesGeometry duplicate_curves_CurveGeometry(const bke::CurvesGeometry &curves, const FieldContext &field_context, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs, const AttributeFilter &attribute_filter)
static void copy_stable_id_faces(const Mesh &mesh, const IndexMask &selection, const OffsetIndices< int > face_offsets, const Span< int > vert_mapping, const bke::AttributeAccessor src_attributes, bke::MutableAttributeAccessor dst_attributes)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void copy_hashed_ids(const Span< int > src, const int hash, MutableSpan< int > dst)
static void duplicate_layers(GeometrySet &geometry_set, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs, const AttributeFilter &attribute_filter)
static void duplicate_curves(GeometrySet &geometry_set, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs, const AttributeFilter &attribute_filter)
static void copy_stable_id_edges(const Mesh &mesh, const IndexMask &selection, const OffsetIndices< int > offsets, const bke::AttributeAccessor src_attributes, bke::MutableAttributeAccessor dst_attributes)
static void copy_face_attributes_without_id(const Span< int > edge_mapping, const Span< int > vert_mapping, const Span< int > loop_mapping, const OffsetIndices< int > offsets, const IndexMask &selection, const AttributeFilter &attribute_filter, const bke::AttributeAccessor src_attributes, bke::MutableAttributeAccessor dst_attributes)
static void node_declare(NodeDeclarationBuilder &b)
static void duplicate_points_curve(GeometrySet &geometry_set, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs, const AttributeFilter &attribute_filter)
static bke::CurvesGeometry duplicate_points_CurvesGeometry(const bke::CurvesGeometry &src_curves, const FieldContext &field_context, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs, const AttributeFilter &attribute_filter)
static void duplicate_points_mesh(GeometrySet &geometry_set, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs, const AttributeFilter &attribute_filter)
static void copy_curve_attributes_without_id(const bke::CurvesGeometry &src_curves, const IndexMask &selection, const OffsetIndices< int > curve_offsets, const AttributeFilter &attribute_filter, bke::CurvesGeometry &dst_curves)
static void create_duplicate_index_attribute(bke::MutableAttributeAccessor attributes, const AttrDomain output_domain, const IndexMask &selection, const IndexAttributes &attribute_outputs, const OffsetIndices< int > offsets)
static void duplicate_edges(GeometrySet &geometry_set, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs, const AttributeFilter &attribute_filter)
static void threaded_id_offset_copy(const OffsetIndices< int > offsets, const Span< int > src, MutableSpan< int > all_dst)
static void copy_stable_id_point(const OffsetIndices< int > offsets, const bke::AttributeAccessor src_attributes, bke::MutableAttributeAccessor dst_attributes)
static void duplicate_points(GeometrySet &geometry_set, const Field< int > &count_field, const Field< bool > &selection_field, const IndexAttributes &attribute_outputs, const AttributeFilter &attribute_filter)
PropertyRNA * RNA_def_node_enum(StructRNA *srna, const char *identifier, const char *ui_name, const char *ui_description, const EnumPropertyItem *static_items, const EnumRNAAccessors accessors, std::optional< int > default_value, const EnumPropertyItemFunc item_func, const bool allow_animation)
uint32_t hash(uint32_t kx)
Definition noise.cc:90
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
void fill_constant_group_size(int size, int start_offset, MutableSpan< int > offsets)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
VecBase< int32_t, 2 > int2
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:42
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:54
#define hash
Definition noise_c.cc:154
#define min(a, b)
Definition sort.cc:36
CurvesGeometry geometry
StructRNA * srna
static GeometrySet from_instances(Instances *instances, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
int verts_num
void * storage
void keep_only(Span< GeometryComponent::Type > component_types)
const GreasePencil * get_grease_pencil() const
void replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
const Curves * get_curves() const
const Instances * get_instances() const
void replace_curves(Curves *curves, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
const PointCloud * get_pointcloud() const
const Mesh * get_mesh() const
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
GreasePencil * get_grease_pencil_for_write()
Vector< GeometryComponent::Type > gather_component_types(bool include_instances, bool ignore_empty) const
void replace_grease_pencil(GreasePencil *grease_pencil, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:289
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:354
const char * enum_name_legacy
Definition BKE_node.hh:247
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:259
NodeDeclareFunction declare
Definition BKE_node.hh:362
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4238