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