Blender V4.5
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
26#include "UI_interface.hh"
27#include "UI_resources.hh"
28
30
32
34{
35 b.add_input<decl::Geometry>("Geometry");
36 b.add_input<decl::Bool>("Selection").default_value(true).hide_value().field_on_all();
37 b.add_input<decl::Int>("Amount").min(0).default_value(1).field_on_all().description(
38 "The number of duplicates to create for each element");
39
40 b.add_output<decl::Geometry>("Geometry")
41 .propagate_all()
42 .description("The duplicated geometry, not including the original geometry");
43 b.add_output<decl::Int>("Duplicate Index")
44 .field_on_all()
45 .description("The indices of the duplicates for each element");
46}
47
48static void node_init(bNodeTree * /*tree*/, bNode *node)
49{
51 data->domain = int8_t(AttrDomain::Point);
52 node->storage = data;
53}
54
55static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
56{
57 layout->prop(ptr, "domain", UI_ITEM_NONE, "", ICON_NONE);
58}
59
61 std::optional<std::string> duplicate_index;
62};
63
64/* -------------------------------------------------------------------- */
67
69 const VArray<int> &counts,
70 Array<int> &r_offset_data)
71{
72 r_offset_data.reinitialize(selection.size() + 1);
73 if (const std::optional<int> count = counts.get_if_single()) {
75 }
76 else {
77 array_utils::gather(counts, selection, r_offset_data.as_mutable_span().drop_back(1), 1024);
79 }
80 return OffsetIndices<int>(r_offset_data);
81}
82
83static void copy_hashed_ids(const Span<int> src, const int hash, MutableSpan<int> dst)
84{
85 for (const int i : src.index_range()) {
86 dst[i] = noise::hash(src[i], hash);
87 }
88}
89
91 const Span<int> src,
92 MutableSpan<int> all_dst)
93{
94 BLI_assert(offsets.total_size() == all_dst.size());
95 threading::parallel_for(offsets.index_range(), 512, [&](IndexRange range) {
96 for (const int i : range) {
97 MutableSpan<int> dst = all_dst.slice(offsets[i]);
98 if (dst.is_empty()) {
99 continue;
100 }
101 dst.first() = src[i];
102 for (const int i_duplicate : dst.index_range().drop_front(1)) {
103 dst[i_duplicate] = noise::hash(src[i], i_duplicate);
104 }
105 }
106 });
107}
108
111 const AttrDomain output_domain,
112 const IndexMask &selection,
113 const IndexAttributes &attribute_outputs,
114 const OffsetIndices<int> offsets)
115{
116 SpanAttributeWriter<int> duplicate_indices = attributes.lookup_or_add_for_write_only_span<int>(
117 *attribute_outputs.duplicate_index, output_domain);
118 for (const int i : IndexRange(selection.size())) {
119 MutableSpan<int> indices = duplicate_indices.span.slice(offsets[i]);
120 for (const int i : indices.index_range()) {
121 indices[i] = i;
122 }
123 }
124 duplicate_indices.finish();
125}
126
131static void copy_stable_id_point(const OffsetIndices<int> offsets,
132 const bke::AttributeAccessor src_attributes,
133 bke::MutableAttributeAccessor dst_attributes)
134{
135 GAttributeReader src_attribute = src_attributes.lookup("id");
136 if (!src_attribute) {
137 return;
138 }
139 GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
140 "id", AttrDomain::Point, CD_PROP_INT32);
141 if (!dst_attribute) {
142 return;
143 }
144
145 VArraySpan<int> src{src_attribute.varray.typed<int>()};
146 MutableSpan<int> dst = dst_attribute.span.typed<int>();
147 threaded_id_offset_copy(offsets, src, dst);
148 dst_attribute.finish();
149}
150
152
153/* -------------------------------------------------------------------- */
156
162 const IndexMask &selection,
163 const OffsetIndices<int> curve_offsets,
164 const AttributeFilter &attribute_filter,
165 bke::CurvesGeometry &dst_curves)
166{
167 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
168 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
169
170 for (auto &attribute : bke::retrieve_attributes_for_transfer(
171 src_curves.attributes(),
172 dst_curves.attributes_for_write(),
174 bke::attribute_filter_with_skip_ref(attribute_filter, {"id"})))
175 {
176 switch (attribute.meta_data.domain) {
177 case AttrDomain::Curve:
179 curve_offsets, selection, attribute.src, attribute.dst.span);
180 break;
181 case AttrDomain::Point:
182 bke::attribute_math::convert_to_static_type(attribute.src.type(), [&](auto dummy) {
183 using T = decltype(dummy);
184 const Span<T> src = attribute.src.typed<T>();
185 MutableSpan<T> dst = attribute.dst.span.typed<T>();
186 selection.foreach_index(
187 GrainSize(512), [&](const int64_t index, const int64_t i_selection) {
188 const Span<T> curve_src = src.slice(src_points_by_curve[index]);
189 for (const int dst_curve_index : curve_offsets[i_selection]) {
190 dst.slice(dst_points_by_curve[dst_curve_index]).copy_from(curve_src);
191 }
192 });
193 });
194 break;
195 default:
197 break;
198 }
199 attribute.dst.finish();
200 }
201}
202
209static void copy_stable_id_curves(const bke::CurvesGeometry &src_curves,
210 const IndexMask &selection,
211 const OffsetIndices<int> offsets,
212 bke::CurvesGeometry &dst_curves)
213{
214 GAttributeReader src_attribute = src_curves.attributes().lookup("id");
215 if (!src_attribute) {
216 return;
217 }
218 GSpanAttributeWriter dst_attribute =
220 "id", AttrDomain::Point, CD_PROP_INT32);
221 if (!dst_attribute) {
222 return;
223 }
224
225 VArraySpan<int> src{src_attribute.varray.typed<int>()};
226 MutableSpan<int> dst = dst_attribute.span.typed<int>();
227
228 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
229 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
230
231 selection.foreach_index(
232 GrainSize(512), [&](const int64_t i_src_curve, const int64_t i_selection) {
233 const Span<int> curve_src = src.slice(src_points_by_curve[i_src_curve]);
234 const IndexRange duplicates_range = offsets[i_selection];
235 for (const int i_duplicate : IndexRange(offsets[i_selection].size()).drop_front(1)) {
236 const int i_dst_curve = duplicates_range[i_duplicate];
237 copy_hashed_ids(curve_src, i_duplicate, dst.slice(dst_points_by_curve[i_dst_curve]));
238 }
239 });
240
241 dst_attribute.finish();
242}
243
245 const FieldContext &field_context,
246 const Field<int> &count_field,
247 const Field<bool> &selection_field,
248 const IndexAttributes &attribute_outputs,
249 const AttributeFilter &attribute_filter)
250{
251 FieldEvaluator evaluator{field_context, curves.curves_num()};
252 evaluator.add(count_field);
253 evaluator.set_selection(selection_field);
254 evaluator.evaluate();
255 const VArray<int> counts = evaluator.get_evaluated<int>(0);
256 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
257
258 const OffsetIndices points_by_curve = curves.points_by_curve();
259
260 /* The offset in the result curve domain at every selected input curve. */
261 Array<int> curve_offset_data(selection.size() + 1);
262 Array<int> point_offset_data(selection.size() + 1);
263
264 int dst_curves_num = 0;
265 int dst_points_num = 0;
266
267 selection.foreach_index_optimized<int>([&](const int index, const int i_curve) {
268 const int count = counts[index];
269 curve_offset_data[i_curve] = dst_curves_num;
270 point_offset_data[i_curve] = dst_points_num;
271 dst_curves_num += count;
272 dst_points_num += count * points_by_curve[index].size();
273 });
274
275 if (dst_points_num == 0) {
276 return {};
277 }
278
279 curve_offset_data.last() = dst_curves_num;
280 point_offset_data.last() = dst_points_num;
281
282 const OffsetIndices<int> curve_offsets(curve_offset_data);
283 const OffsetIndices<int> point_offsets(point_offset_data);
284
285 bke::CurvesGeometry new_curves{dst_points_num, dst_curves_num};
286 MutableSpan<int> all_dst_offsets = new_curves.offsets_for_write();
287 selection.foreach_index(GrainSize(512),
288 [&](const int64_t i_src_curve, const int64_t i_selection) {
289 const IndexRange src_curve_range = points_by_curve[i_src_curve];
290 const IndexRange dst_curves_range = curve_offsets[i_selection];
291 MutableSpan<int> dst_offsets = all_dst_offsets.slice(dst_curves_range);
292 for (const int i_duplicate : IndexRange(dst_curves_range.size())) {
293 dst_offsets[i_duplicate] = point_offsets[i_selection].start() +
294 src_curve_range.size() * i_duplicate;
295 }
296 });
297 all_dst_offsets.last() = dst_points_num;
298
299 copy_curve_attributes_without_id(curves, selection, curve_offsets, attribute_filter, new_curves);
300 copy_stable_id_curves(curves, selection, curve_offsets, new_curves);
301
302 if (attribute_outputs.duplicate_index) {
304 AttrDomain::Curve,
305 selection,
306 attribute_outputs,
307 curve_offsets);
308 }
309
310 new_curves.update_curve_types();
311 return new_curves;
312}
313
314static void duplicate_curves(GeometrySet &geometry_set,
315 const Field<int> &count_field,
316 const Field<bool> &selection_field,
317 const IndexAttributes &attribute_outputs,
318 const AttributeFilter &attribute_filter)
319{
320 geometry_set.keep_only_during_modify(
321 {GeometryComponent::Type::Curve, GeometryComponent::Type::GreasePencil});
323 if (const Curves *curves_id = geometry_set.get_curves()) {
324 const bke::CurvesFieldContext field_context{*curves_id, AttrDomain::Curve};
325 bke::CurvesGeometry new_curves = duplicate_curves_CurveGeometry(curves_id->geometry.wrap(),
326 field_context,
327 count_field,
328 selection_field,
329 attribute_outputs,
330 attribute_filter);
331 Curves *new_curves_id = bke::curves_new_nomain(std::move(new_curves));
332 bke::curves_copy_parameters(*curves_id, *new_curves_id);
333 geometry_set.replace_curves(new_curves_id);
334 }
335 if (GreasePencil *grease_pencil = geometry_set.get_grease_pencil_for_write()) {
336 using namespace bke::greasepencil;
338 grease_pencil->layers().index_range(), 16, [&](const IndexRange layers_range) {
339 for (const int layer_i : layers_range) {
340 Layer &layer = grease_pencil->layer(layer_i);
341 Drawing *drawing = grease_pencil->get_eval_drawing(layer);
342 if (!drawing) {
343 continue;
344 }
345 bke::CurvesGeometry &curves = drawing->strokes_for_write();
346 const bke::GreasePencilLayerFieldContext field_context{
347 *grease_pencil, AttrDomain::Curve, layer_i};
348 curves = duplicate_curves_CurveGeometry(curves,
349 field_context,
350 count_field,
351 selection_field,
352 attribute_outputs,
353 attribute_filter);
354 drawing->tag_topology_changed();
355 }
356 });
357 }
358}
359
361
362/* -------------------------------------------------------------------- */
365
370static void copy_face_attributes_without_id(const Span<int> edge_mapping,
371 const Span<int> vert_mapping,
372 const Span<int> loop_mapping,
373 const OffsetIndices<int> offsets,
374 const IndexMask &selection,
375 const AttributeFilter &attribute_filter,
376 const bke::AttributeAccessor src_attributes,
377 bke::MutableAttributeAccessor dst_attributes)
378{
379 for (auto &attribute : bke::retrieve_attributes_for_transfer(
380 src_attributes,
381 dst_attributes,
384 attribute_filter, {"id", ".corner_vert", ".corner_edge", ".edge_verts"})))
385 {
386 switch (attribute.meta_data.domain) {
387 case AttrDomain::Point:
388 bke::attribute_math::gather(attribute.src, vert_mapping, attribute.dst.span);
389 break;
390 case AttrDomain::Edge:
391 bke::attribute_math::gather(attribute.src, edge_mapping, attribute.dst.span);
392 break;
393 case AttrDomain::Face:
395 offsets, selection, attribute.src, attribute.dst.span);
396 break;
397 case AttrDomain::Corner:
398 bke::attribute_math::gather(attribute.src, loop_mapping, attribute.dst.span);
399 break;
400 default:
402 break;
403 }
404 attribute.dst.finish();
405 }
406}
407
415static void copy_stable_id_faces(const Mesh &mesh,
416 const IndexMask &selection,
417 const OffsetIndices<int> face_offsets,
418 const Span<int> vert_mapping,
419 const bke::AttributeAccessor src_attributes,
420 bke::MutableAttributeAccessor dst_attributes)
421{
422 GAttributeReader src_attribute = src_attributes.lookup("id");
423 if (!src_attribute) {
424 return;
425 }
426 GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
427 "id", AttrDomain::Point, CD_PROP_INT32);
428 if (!dst_attribute) {
429 return;
430 }
431
432 VArraySpan<int> src{src_attribute.varray.typed<int>()};
433 MutableSpan<int> dst = dst_attribute.span.typed<int>();
434
435 const OffsetIndices faces = mesh.faces();
436 int loop_index = 0;
437 for (const int i_face : selection.index_range()) {
438 const IndexRange range = face_offsets[i_face];
439 if (range.is_empty()) {
440 continue;
441 }
442 const IndexRange source = faces[i_face];
443 for ([[maybe_unused]] const int i_duplicate : IndexRange(range.size())) {
444 for ([[maybe_unused]] const int i_loops : IndexRange(source.size())) {
445 if (i_duplicate == 0) {
446 dst[loop_index] = src[vert_mapping[loop_index]];
447 }
448 else {
449 dst[loop_index] = noise::hash(src[vert_mapping[loop_index]], i_duplicate);
450 }
451 loop_index++;
452 }
453 }
454 }
455
456 dst_attribute.finish();
457}
458
459static void duplicate_faces(GeometrySet &geometry_set,
460 const Field<int> &count_field,
461 const Field<bool> &selection_field,
462 const IndexAttributes &attribute_outputs,
463 const AttributeFilter &attribute_filter)
464{
465 if (!geometry_set.has_mesh()) {
466 geometry_set.remove_geometry_during_modify();
467 return;
468 }
469 geometry_set.keep_only_during_modify({GeometryComponent::Type::Mesh});
470
471 const Mesh &mesh = *geometry_set.get_mesh();
472 const OffsetIndices faces = mesh.faces();
473 const Span<int> corner_verts = mesh.corner_verts();
474 const Span<int> corner_edges = mesh.corner_edges();
475
476 const bke::MeshFieldContext field_context{mesh, AttrDomain::Face};
477 FieldEvaluator evaluator(field_context, faces.size());
478 evaluator.add(count_field);
479 evaluator.set_selection(selection_field);
480 evaluator.evaluate();
481 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
482 const VArray<int> counts = evaluator.get_evaluated<int>(0);
483
484 int total_faces = 0;
485 int total_loops = 0;
486 Array<int> offset_data(selection.size() + 1);
487 selection.foreach_index_optimized<int>([&](const int index, const int i_selection) {
488 const int count = counts[index];
489 offset_data[i_selection] = total_faces;
490 total_faces += count;
491 total_loops += count * faces[index].size();
492 });
493 offset_data[selection.size()] = total_faces;
494
495 const OffsetIndices<int> duplicates(offset_data);
496
497 Mesh *new_mesh = BKE_mesh_new_nomain(total_loops, total_loops, total_faces, total_loops);
498 MutableSpan<int2> new_edges = new_mesh->edges_for_write();
499 MutableSpan<int> new_face_offsets = new_mesh->face_offsets_for_write();
500 MutableSpan<int> new_corner_verts = new_mesh->corner_verts_for_write();
501 MutableSpan<int> new_corner_edges = new_mesh->corner_edges_for_write();
502
503 Array<int> vert_mapping(new_mesh->verts_num);
504 Array<int> edge_mapping(new_edges.size());
505 Array<int> loop_mapping(total_loops);
506
507 int face_index = 0;
508 int loop_index = 0;
509 selection.foreach_index_optimized<int>([&](const int index, const int i_selection) {
510 const IndexRange face_range = duplicates[i_selection];
511 const IndexRange source = faces[index];
512 for ([[maybe_unused]] const int i_duplicate : face_range.index_range()) {
513 new_face_offsets[face_index] = loop_index;
514 for (const int src_corner : source) {
515 loop_mapping[loop_index] = src_corner;
516 vert_mapping[loop_index] = corner_verts[src_corner];
517 edge_mapping[loop_index] = corner_edges[src_corner];
518 new_edges[loop_index][0] = loop_index;
519 if (src_corner != source.last()) {
520 new_edges[loop_index][1] = loop_index + 1;
521 }
522 else {
523 new_edges[loop_index][1] = new_face_offsets[face_index];
524 }
525 loop_index++;
526 }
527 face_index++;
528 }
529 });
530 array_utils::fill_index_range<int>(new_corner_verts);
531 array_utils::fill_index_range<int>(new_corner_edges);
532
533 new_mesh->tag_loose_verts_none();
534 new_mesh->tag_loose_edges_none();
535 new_mesh->tag_overlapping_none();
536
538 vert_mapping,
539 loop_mapping,
540 duplicates,
541 selection,
542 attribute_filter,
543 mesh.attributes(),
544 new_mesh->attributes_for_write());
545
547 selection,
548 duplicates,
549 vert_mapping,
550 mesh.attributes(),
551 new_mesh->attributes_for_write());
552
553 if (attribute_outputs.duplicate_index) {
554 create_duplicate_index_attribute(new_mesh->attributes_for_write(),
555 AttrDomain::Face,
556 selection,
557 attribute_outputs,
558 duplicates);
559 }
560
561 geometry_set.replace_mesh(new_mesh);
562}
563
565
566/* -------------------------------------------------------------------- */
569
574static void copy_edge_attributes_without_id(const Span<int> point_mapping,
575 const OffsetIndices<int> offsets,
576 const IndexMask &selection,
577 const AttributeFilter &attribute_filter,
578 const bke::AttributeAccessor src_attributes,
579 bke::MutableAttributeAccessor dst_attributes)
580{
581 for (auto &attribute : bke::retrieve_attributes_for_transfer(
582 src_attributes,
583 dst_attributes,
585 bke::attribute_filter_with_skip_ref(attribute_filter, {"id", ".edge_verts"})))
586 {
587 switch (attribute.meta_data.domain) {
588 case AttrDomain::Edge:
590 offsets, selection, attribute.src, attribute.dst.span);
591 break;
592 case AttrDomain::Point:
593 bke::attribute_math::gather(attribute.src, point_mapping, attribute.dst.span);
594 break;
595 default:
597 break;
598 }
599 attribute.dst.finish();
600 }
601}
602
607static void copy_stable_id_edges(const Mesh &mesh,
608 const IndexMask &selection,
609 const OffsetIndices<int> offsets,
610 const bke::AttributeAccessor src_attributes,
611 bke::MutableAttributeAccessor dst_attributes)
612{
613 GAttributeReader src_attribute = src_attributes.lookup("id");
614 if (!src_attribute) {
615 return;
616 }
617 GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
618 "id", AttrDomain::Point, CD_PROP_INT32);
619 if (!dst_attribute) {
620 return;
621 }
622
623 const Span<int2> edges = mesh.edges();
624
625 VArraySpan<int> src{src_attribute.varray.typed<int>()};
626 MutableSpan<int> dst = dst_attribute.span.typed<int>();
627 selection.foreach_index(GrainSize(1024), [&](const int64_t index, const int64_t i_selection) {
628 const IndexRange edge_range = offsets[i_selection];
629 if (edge_range.is_empty()) {
630 return;
631 }
632 const int2 &edge = edges[index];
633 const IndexRange vert_range = {edge_range.start() * 2, edge_range.size() * 2};
634
635 dst[vert_range[0]] = src[edge[0]];
636 dst[vert_range[1]] = src[edge[1]];
637 for (const int i_duplicate : IndexRange(1, edge_range.size() - 1)) {
638 dst[vert_range[i_duplicate * 2]] = noise::hash(src[edge[0]], i_duplicate);
639 dst[vert_range[i_duplicate * 2 + 1]] = noise::hash(src[edge[1]], i_duplicate);
640 }
641 });
642 dst_attribute.finish();
643}
644
645static void duplicate_edges(GeometrySet &geometry_set,
646 const Field<int> &count_field,
647 const Field<bool> &selection_field,
648 const IndexAttributes &attribute_outputs,
649 const AttributeFilter &attribute_filter)
650{
651 if (!geometry_set.has_mesh()) {
652 geometry_set.remove_geometry_during_modify();
653 return;
654 };
655 const Mesh &mesh = *geometry_set.get_mesh();
656 const Span<int2> edges = mesh.edges();
657
658 const bke::MeshFieldContext field_context{mesh, AttrDomain::Edge};
659 FieldEvaluator evaluator{field_context, edges.size()};
660 evaluator.add(count_field);
661 evaluator.set_selection(selection_field);
662 evaluator.evaluate();
663 const VArray<int> counts = evaluator.get_evaluated<int>(0);
664 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
665
666 Array<int> offset_data;
668 selection, counts, offset_data);
669 const int output_edges_num = duplicates.total_size();
670
671 Mesh *new_mesh = BKE_mesh_new_nomain(output_edges_num * 2, output_edges_num, 0, 0);
672 MutableSpan<int2> new_edges = new_mesh->edges_for_write();
673
674 Array<int> vert_orig_indices(output_edges_num * 2);
675 selection.foreach_index(GrainSize(1024), [&](const int64_t index, const int64_t i_selection) {
676 const int2 &edge = edges[index];
677 const IndexRange edge_range = duplicates[i_selection];
678 const IndexRange vert_range(edge_range.start() * 2, edge_range.size() * 2);
679
680 for (const int i_duplicate : IndexRange(edge_range.size())) {
681 vert_orig_indices[vert_range[i_duplicate * 2]] = edge[0];
682 vert_orig_indices[vert_range[i_duplicate * 2 + 1]] = edge[1];
683 }
684 });
685
686 threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
687 for (const int i_selection : range) {
688 const IndexRange edge_range = duplicates[i_selection];
689 const IndexRange vert_range(edge_range.start() * 2, edge_range.size() * 2);
690 for (const int i_duplicate : IndexRange(edge_range.size())) {
691 int2 &new_edge = new_edges[edge_range[i_duplicate]];
692 new_edge[0] = vert_range[i_duplicate * 2];
693 new_edge[1] = vert_range[i_duplicate * 2] + 1;
694 }
695 }
696 });
697
698 copy_edge_attributes_without_id(vert_orig_indices,
699 duplicates,
700 selection,
701 attribute_filter,
702 mesh.attributes(),
703 new_mesh->attributes_for_write());
704
705 copy_stable_id_edges(
706 mesh, selection, duplicates, mesh.attributes(), new_mesh->attributes_for_write());
707
708 if (attribute_outputs.duplicate_index) {
709 create_duplicate_index_attribute(new_mesh->attributes_for_write(),
710 AttrDomain::Edge,
711 selection,
712 attribute_outputs,
713 duplicates);
714 }
715
716 new_mesh->tag_overlapping_none();
717
718 geometry_set.replace_mesh(new_mesh);
719}
720
722
723/* -------------------------------------------------------------------- */
726
728 const bke::CurvesGeometry &src_curves,
729 const FieldContext &field_context,
730 const Field<int> &count_field,
731 const Field<bool> &selection_field,
732 const IndexAttributes &attribute_outputs,
733 const AttributeFilter &attribute_filter)
734{
735 if (src_curves.is_empty()) {
736 return {};
737 }
738
739 FieldEvaluator evaluator{field_context, src_curves.points_num()};
740 evaluator.add(count_field);
741 evaluator.set_selection(selection_field);
742 evaluator.evaluate();
743 const VArray<int> counts = evaluator.get_evaluated<int>(0);
744 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
745
746 Array<int> offset_data;
748 selection, counts, offset_data);
749 const int dst_num = duplicates.total_size();
750
751 const Array<int> point_to_curve_map = src_curves.point_to_curve_map();
752
753 bke::CurvesGeometry new_curves{dst_num, dst_num};
755
757 AttrDomain::Point,
758 AttrDomain::Point,
759 attribute_filter,
760 duplicates,
761 selection,
762 new_curves.attributes_for_write());
763
764 for (auto &attribute : bke::retrieve_attributes_for_transfer(
765 src_curves.attributes(),
766 new_curves.attributes_for_write(),
768 bke::attribute_filter_with_skip_ref(attribute_filter, {"id"})))
769 {
770 bke::attribute_math::convert_to_static_type(attribute.src.type(), [&](auto dummy) {
771 using T = decltype(dummy);
772 const Span<T> src = attribute.src.typed<T>();
773 MutableSpan<T> dst = attribute.dst.span.typed<T>();
774 selection.foreach_index(GrainSize(512), [&](const int64_t index, const int64_t i_selection) {
775 const T &src_value = src[point_to_curve_map[index]];
776 dst.slice(duplicates[i_selection]).fill(src_value);
777 });
778 });
779 attribute.dst.finish();
780 }
781
782 copy_stable_id_point(duplicates, src_curves.attributes(), new_curves.attributes_for_write());
783
784 if (attribute_outputs.duplicate_index) {
785 create_duplicate_index_attribute(new_curves.attributes_for_write(),
786 AttrDomain::Point,
787 selection,
788 attribute_outputs,
789 duplicates);
790 }
791
792 return new_curves;
793}
794
795static void duplicate_points_curve(GeometrySet &geometry_set,
796 const Field<int> &count_field,
797 const Field<bool> &selection_field,
798 const IndexAttributes &attribute_outputs,
799 const AttributeFilter &attribute_filter)
800{
801 const Curves &src_curves_id = *geometry_set.get_curves();
802 const bke::CurvesGeometry &src_curves = src_curves_id.geometry.wrap();
803
804 const bke::CurvesFieldContext field_context{src_curves_id, AttrDomain::Point};
806 field_context,
807 count_field,
808 selection_field,
809 attribute_outputs,
810 attribute_filter);
811
812 Curves *new_curves_id = bke::curves_new_nomain(std::move(new_curves));
813 bke::curves_copy_parameters(src_curves_id, *new_curves_id);
814 geometry_set.replace_curves(new_curves_id);
815}
816
818
819/* -------------------------------------------------------------------- */
822
824 const Field<int> &count_field,
825 const Field<bool> &selection_field,
826 const IndexAttributes &attribute_outputs,
827 const AttributeFilter &attribute_filter)
828{
829 using namespace bke::greasepencil;
830 GreasePencil &grease_pencil = *geometry_set.get_grease_pencil_for_write();
832 grease_pencil.layers().index_range(), 16, [&](const IndexRange layers_range) {
833 for (const int layer_i : layers_range) {
834 Layer &layer = grease_pencil.layer(layer_i);
835 Drawing *drawing = grease_pencil.get_eval_drawing(layer);
836 if (!drawing) {
837 continue;
838 }
839 bke::CurvesGeometry &curves = drawing->strokes_for_write();
840 const bke::GreasePencilLayerFieldContext field_context{
841 grease_pencil, AttrDomain::Point, layer_i};
842 curves = duplicate_points_CurvesGeometry(curves,
843 field_context,
844 count_field,
845 selection_field,
846 attribute_outputs,
847 attribute_filter);
848 drawing->tag_topology_changed();
849 }
850 });
851}
852
854
855/* -------------------------------------------------------------------- */
858
859static void duplicate_points_mesh(GeometrySet &geometry_set,
860 const Field<int> &count_field,
861 const Field<bool> &selection_field,
862 const IndexAttributes &attribute_outputs,
863 const AttributeFilter &attribute_filter)
864{
865 const Mesh &mesh = *geometry_set.get_mesh();
866
867 const bke::MeshFieldContext field_context{mesh, AttrDomain::Point};
868 FieldEvaluator evaluator{field_context, mesh.verts_num};
869 evaluator.add(count_field);
870 evaluator.set_selection(selection_field);
871 evaluator.evaluate();
872 const VArray<int> counts = evaluator.get_evaluated<int>(0);
873 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
874
875 Array<int> offset_data;
877 selection, counts, offset_data);
878
879 Mesh *new_mesh = BKE_mesh_new_nomain(duplicates.total_size(), 0, 0, 0);
880
882 AttrDomain::Point,
883 AttrDomain::Point,
884 bke::attribute_filter_with_skip_ref(attribute_filter, {"id"}),
885 duplicates,
886 selection,
887 new_mesh->attributes_for_write());
888
889 copy_stable_id_point(duplicates, mesh.attributes(), new_mesh->attributes_for_write());
890
891 if (attribute_outputs.duplicate_index) {
892 create_duplicate_index_attribute(new_mesh->attributes_for_write(),
893 AttrDomain::Point,
894 selection,
895 attribute_outputs,
896 duplicates);
897 }
898
899 new_mesh->tag_overlapping_none();
900
901 geometry_set.replace_mesh(new_mesh);
902}
903
905
906/* -------------------------------------------------------------------- */
909
910static void duplicate_points_pointcloud(GeometrySet &geometry_set,
911 const Field<int> &count_field,
912 const Field<bool> &selection_field,
913 const IndexAttributes &attribute_outputs,
914 const AttributeFilter &attribute_filter)
915{
916 const PointCloud &src_points = *geometry_set.get_pointcloud();
917
918 const bke::PointCloudFieldContext field_context{src_points};
919 FieldEvaluator evaluator{field_context, src_points.totpoint};
920 evaluator.add(count_field);
921 evaluator.set_selection(selection_field);
922 evaluator.evaluate();
923 const VArray<int> counts = evaluator.get_evaluated<int>(0);
924 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
925
926 Array<int> offset_data;
928 selection, counts, offset_data);
929
930 PointCloud *pointcloud = BKE_pointcloud_new_nomain(duplicates.total_size());
931
933 AttrDomain::Point,
934 AttrDomain::Point,
935 bke::attribute_filter_with_skip_ref(attribute_filter, {"id"}),
936 duplicates,
937 selection,
938 pointcloud->attributes_for_write());
939
940 copy_stable_id_point(duplicates, src_points.attributes(), pointcloud->attributes_for_write());
941
942 if (attribute_outputs.duplicate_index) {
943 create_duplicate_index_attribute(pointcloud->attributes_for_write(),
944 AttrDomain::Point,
945 selection,
946 attribute_outputs,
947 duplicates);
948 }
949 geometry_set.replace_pointcloud(pointcloud);
950}
951
953
954/* -------------------------------------------------------------------- */
957
958static void duplicate_points(GeometrySet &geometry_set,
959 const Field<int> &count_field,
960 const Field<bool> &selection_field,
961 const IndexAttributes &attribute_outputs,
962 const AttributeFilter &attribute_filter)
963{
964 Vector<GeometryComponent::Type> component_types = geometry_set.gather_component_types(true,
965 true);
966 for (const GeometryComponent::Type component_type : component_types) {
967 switch (component_type) {
968 case GeometryComponent::Type::PointCloud:
969 if (geometry_set.has_pointcloud()) {
971 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
972 }
973 break;
974 case GeometryComponent::Type::Mesh:
975 if (geometry_set.has_mesh()) {
977 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
978 }
979 break;
980 case GeometryComponent::Type::Curve:
981 if (geometry_set.has_curves()) {
983 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
984 }
985 break;
986 case GeometryComponent::Type::GreasePencil: {
987 if (geometry_set.has_grease_pencil()) {
989 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
990 }
991 break;
992 }
993 default:
994 break;
995 }
996 }
997 component_types.append(GeometryComponent::Type::Instance);
998 geometry_set.keep_only_during_modify(component_types);
999}
1000
1002
1003/* -------------------------------------------------------------------- */
1006
1007static void duplicate_layers(GeometrySet &geometry_set,
1008 const Field<int> &count_field,
1009 const Field<bool> &selection_field,
1010 const IndexAttributes &attribute_outputs,
1011 const AttributeFilter &attribute_filter)
1012{
1013 using namespace bke::greasepencil;
1014 if (!geometry_set.has_grease_pencil()) {
1015 geometry_set.clear();
1016 return;
1017 }
1018 geometry_set.keep_only_during_modify({GeometryComponent::Type::GreasePencil});
1020 const GreasePencil &src_grease_pencil = *geometry_set.get_grease_pencil();
1021
1022 bke::GreasePencilFieldContext field_context{src_grease_pencil};
1023 FieldEvaluator evaluator{field_context, src_grease_pencil.layers().size()};
1024 evaluator.add(count_field);
1025 evaluator.set_selection(selection_field);
1026 evaluator.evaluate();
1027 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
1028 const VArray<int> counts = evaluator.get_evaluated<int>(0);
1029
1030 Array<int> offset_data;
1032 selection, counts, offset_data);
1033 const int new_layers_num = duplicates.total_size();
1034 if (new_layers_num == 0) {
1035 geometry_set.clear();
1036 return;
1037 }
1038
1039 GreasePencil *new_grease_pencil = BKE_grease_pencil_new_nomain();
1040 BKE_grease_pencil_copy_parameters(src_grease_pencil, *new_grease_pencil);
1041
1042 new_grease_pencil->add_layers_with_empty_drawings_for_eval(new_layers_num);
1043 static bke::CurvesGeometry static_empty_curves;
1044 selection.foreach_index([&](const int src_layer_i, const int pos) {
1045 const IndexRange range = duplicates[pos];
1046 if (range.is_empty()) {
1047 return;
1048 }
1049 const Layer &src_layer = src_grease_pencil.layer(src_layer_i);
1050 const Drawing *src_drawing = src_grease_pencil.get_eval_drawing(src_layer);
1051 const bke::CurvesGeometry &src_curves = src_drawing ? src_drawing->strokes() :
1052 static_empty_curves;
1053 const StringRefNull src_layer_name = src_layer.name();
1054 for (Layer *new_layer : new_grease_pencil->layers_for_write().slice(range)) {
1055 BKE_grease_pencil_copy_layer_parameters(src_layer, *new_layer);
1056 new_layer->set_name(src_layer_name);
1057 Drawing *new_drawing = new_grease_pencil->get_eval_drawing(*new_layer);
1058 new_drawing->strokes_for_write() = src_curves;
1059 }
1060 });
1061
1062 bke::gather_attributes_to_groups(src_grease_pencil.attributes(),
1063 AttrDomain::Layer,
1064 AttrDomain::Layer,
1065 attribute_filter,
1066 duplicates,
1067 selection,
1068 new_grease_pencil->attributes_for_write());
1069
1070 if (attribute_outputs.duplicate_index) {
1071 create_duplicate_index_attribute(new_grease_pencil->attributes_for_write(),
1072 AttrDomain::Layer,
1073 selection,
1074 attribute_outputs,
1075 duplicates);
1076 }
1077
1078 geometry_set.replace_grease_pencil(new_grease_pencil);
1079}
1080
1082
1083/* -------------------------------------------------------------------- */
1086
1087static void duplicate_instances(GeometrySet &geometry_set,
1088 const Field<int> &count_field,
1089 const Field<bool> &selection_field,
1090 const IndexAttributes &attribute_outputs,
1091 const AttributeFilter &attribute_filter)
1092{
1093 if (!geometry_set.has_instances()) {
1094 geometry_set.clear();
1095 return;
1096 }
1097
1098 const bke::Instances &src_instances = *geometry_set.get_instances();
1099
1100 bke::InstancesFieldContext field_context{src_instances};
1101 FieldEvaluator evaluator{field_context, src_instances.instances_num()};
1102 evaluator.add(count_field);
1103 evaluator.set_selection(selection_field);
1104 evaluator.evaluate();
1105 IndexMask selection = evaluator.get_evaluated_selection_as_mask();
1106 const VArray<int> counts = evaluator.get_evaluated<int>(0);
1107
1108 Array<int> offset_data;
1110 selection, counts, offset_data);
1111 if (duplicates.total_size() == 0) {
1112 geometry_set.clear();
1113 return;
1114 }
1115
1116 std::unique_ptr<bke::Instances> dst_instances = std::make_unique<bke::Instances>();
1117
1118 dst_instances->resize(duplicates.total_size());
1119 selection.foreach_index([&](const int i_src, const int i_dst) {
1120 const IndexRange range = duplicates[i_dst];
1121 if (range.is_empty()) {
1122 return;
1123 }
1124 const int old_handle = src_instances.reference_handles()[i_src];
1125 const bke::InstanceReference reference = src_instances.references()[old_handle];
1126 const int new_handle = dst_instances->add_reference(reference);
1127 dst_instances->reference_handles_for_write().slice(range).fill(new_handle);
1128 });
1129
1131 src_instances.attributes(),
1132 AttrDomain::Instance,
1133 AttrDomain::Instance,
1134 bke::attribute_filter_with_skip_ref(attribute_filter, {"id", ".reference_index"}),
1135 duplicates,
1136 selection,
1137 dst_instances->attributes_for_write());
1138
1139 if (attribute_outputs.duplicate_index) {
1140 create_duplicate_index_attribute(dst_instances->attributes_for_write(),
1141 AttrDomain::Instance,
1142 selection,
1143 attribute_outputs,
1144 duplicates);
1145 }
1146
1147 geometry_set = GeometrySet::from_instances(dst_instances.release());
1148}
1149
1151
1152/* -------------------------------------------------------------------- */
1155
1157{
1158 GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
1159
1160 const NodeGeometryDuplicateElements &storage = node_storage(params.node());
1161 const AttrDomain duplicate_domain = AttrDomain(storage.domain);
1162
1163 static auto max_zero_fn = mf::build::SI1_SO<int, int>(
1164 "max_zero",
1165 [](int value) { return std::max(0, value); },
1166 mf::build::exec_presets::AllSpanOrSingle());
1167 Field<int> count_field(
1168 FieldOperation::Create(max_zero_fn, {params.extract_input<Field<int>>("Amount")}));
1169
1170 Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
1171 IndexAttributes attribute_outputs;
1172 attribute_outputs.duplicate_index = params.get_output_anonymous_attribute_id_if_needed(
1173 "Duplicate Index");
1174
1175 const NodeAttributeFilter &attribute_filter = params.get_attribute_filter("Geometry");
1176
1177 if (duplicate_domain == AttrDomain::Instance) {
1179 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
1180 }
1181 else {
1182 geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
1183 switch (duplicate_domain) {
1184 case AttrDomain::Curve:
1186 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
1187 break;
1188 case AttrDomain::Face:
1190 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
1191 break;
1192 case AttrDomain::Edge:
1194 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
1195 break;
1196 case AttrDomain::Point:
1198 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
1199 break;
1200 case AttrDomain::Layer:
1202 geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
1203 break;
1204 default:
1206 break;
1207 }
1208 });
1209 }
1210
1211 if (geometry_set.is_empty()) {
1212 params.set_default_remaining_outputs();
1213 return;
1214 }
1215
1216 params.set_output("Geometry", std::move(geometry_set));
1217}
1218
1220
1221static void node_rna(StructRNA *srna)
1222{
1223 static const EnumPropertyItem domain_items[] = {
1224 {int(AttrDomain::Point), "POINT", 0, "Point", ""},
1225 {int(AttrDomain::Edge), "EDGE", 0, "Edge", ""},
1226 {int(AttrDomain::Face), "FACE", 0, "Face", ""},
1227 {int(AttrDomain::Curve), "SPLINE", 0, "Spline", ""},
1228 {int(AttrDomain::Layer), "LAYER", 0, "Layer", ""},
1229 {int(AttrDomain::Instance), "INSTANCE", 0, "Instance", ""},
1230 {0, nullptr, 0, nullptr, nullptr},
1231 };
1232
1233 RNA_def_node_enum(srna,
1234 "domain",
1235 "Domain",
1236 "Which domain to duplicate",
1237 domain_items,
1239 int(AttrDomain::Point),
1240 nullptr,
1241 true);
1242}
1243
1244static void node_register()
1245{
1246 static blender::bke::bNodeType ntype;
1247 geo_node_type_base(&ntype, "GeometryNodeDuplicateElements", GEO_NODE_DUPLICATE_ELEMENTS);
1248 ntype.ui_name = "Duplicate Elements";
1249 ntype.ui_description = "Generate an arbitrary number copies of each selected input element";
1250 ntype.enum_name_legacy = "DUPLICATE_ELEMENTS";
1253 "NodeGeometryDuplicateElements",
1256
1257 ntype.initfunc = node_init;
1258 ntype.draw_buttons = node_layout;
1260 ntype.declare = node_declare;
1262
1263 node_rna(ntype.rna_ext.srna);
1264}
1265NOD_REGISTER_NODE(node_register)
1266
1267} // 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:1215
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:447
#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
@ CD_PROP_INT32
#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:237
const T & last(const int64_t n=0) const
Definition BLI_array.hh:285
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:398
MutableSpan< T > typed() 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:285
bke::AttributeAccessor attributes() const
Definition instances.cc:63
int instances_num() const
Definition instances.cc:398
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, eCustomDataType 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 > Create(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:2748
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:5603
Curves * curves_new_nomain(int points_num, int curves_num)
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:77
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
Definition RNA_types.hh:909
static GeometrySet from_instances(Instances *instances, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
int verts_num
void * storage
const GreasePencil * get_grease_pencil() const
void replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
void keep_only_during_modify(Span< GeometryComponent::Type > component_types)
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 modify_geometry_sets(ForeachSubGeometryCallback callback)
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:226
std::string ui_description
Definition BKE_node.hh:232
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:277
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:347
const char * enum_name_legacy
Definition BKE_node.hh:235
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:247
NodeDeclareFunction declare
Definition BKE_node.hh:355
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:4226