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/* -------------------------------------------------------------------- */
66
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
151
152/* -------------------------------------------------------------------- */
155
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
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:
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);
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) {
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});
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;
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
360
361/* -------------------------------------------------------------------- */
364
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{
379 src_attributes,
380 dst_attributes,
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:
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();
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
564
565/* -------------------------------------------------------------------- */
568
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{
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:
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);
664
665 Array<int> offset_data;
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
721
722/* -------------------------------------------------------------------- */
725
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);
744
745 Array<int> offset_data;
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};
754
756 AttrDomain::Point,
757 AttrDomain::Point,
758 attribute_filter,
759 duplicates,
760 selection,
761 new_curves.attributes_for_write());
762
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
817
818/* -------------------------------------------------------------------- */
821
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();
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
853
854/* -------------------------------------------------------------------- */
857
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);
873
874 Array<int> offset_data;
876 selection, counts, offset_data);
877
878 Mesh *new_mesh = BKE_mesh_new_nomain(duplicates.total_size(), 0, 0, 0);
879
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
904
905/* -------------------------------------------------------------------- */
908
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);
924
925 Array<int> offset_data;
927 selection, counts, offset_data);
928
929 PointCloud *pointcloud = BKE_pointcloud_new_nomain(duplicates.total_size());
930
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
952
953/* -------------------------------------------------------------------- */
956
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
1001
1002/* -------------------------------------------------------------------- */
1005
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});
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();
1027 const VArray<int> counts = evaluator.get_evaluated<int>(0);
1028
1029 Array<int> offset_data;
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
1081
1082/* -------------------------------------------------------------------- */
1085
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();
1105 const VArray<int> counts = evaluator.get_evaluated<int>(0);
1106
1107 Array<int> offset_data;
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
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
1150
1151/* -------------------------------------------------------------------- */
1154
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:
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:
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
1219
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
#define GEO_NODE_DUPLICATE_ELEMENTS
Definition BKE_node.hh:1283
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)
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
#define UI_ITEM_NONE
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
AttrDomain
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:388
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: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 int64_t size() const
Definition BLI_span.hh:253
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()
MutableSpan< int > offsets_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
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:244
static void remember_deformed_positions_if_necessary(GeometrySet &geometry)
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
static ushort indices[]
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int count
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)
auto attribute_filter_with_skip_ref(AttributeFilter filter, const Span< StringRef > skip)
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
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: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
VecBase< int32_t, 2 > int2
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
GPU_SHADER_INTERFACE_INFO(overlay_edit_curve_handle_iface, "vert").flat(Type pos vertex_in(1, Type::UINT, "data") .vertex_out(overlay_edit_curve_handle_iface) .geometry_layout(PrimitiveIn Frequency::GEOMETRY storage_buf(1, Qualifier::READ, "uint", "data[]", Frequency::GEOMETRY) .push_constant(Type Frequency::GEOMETRY selection[]
#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
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: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