Blender V4.3
attribute_access.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 <utility>
6
8#include "BKE_curves.hh"
9#include "BKE_customdata.hh"
10#include "BKE_deform.hh"
11#include "BKE_geometry_set.hh"
13
14#include "DNA_ID.h"
16#include "DNA_mesh_types.h"
17#include "DNA_meshdata_types.h"
19
20#include "BLI_array_utils.hh"
21#include "BLI_color.hh"
23#include "BLI_span.hh"
24
25#include "BLT_translation.hh"
26
27#include "FN_field.hh"
28
29#include "CLG_log.h"
30
32
33namespace blender::bke {
34
36{
37 switch (type) {
38 case CD_PROP_FLOAT:
39 return &CPPType::get<float>();
40 case CD_PROP_FLOAT2:
41 return &CPPType::get<float2>();
42 case CD_PROP_FLOAT3:
43 return &CPPType::get<float3>();
44 case CD_PROP_INT32:
45 return &CPPType::get<int>();
47 return &CPPType::get<int2>();
48 case CD_PROP_COLOR:
50 case CD_PROP_BOOL:
51 return &CPPType::get<bool>();
52 case CD_PROP_INT8:
53 return &CPPType::get<int8_t>();
59 return &CPPType::get<float4x4>();
60 case CD_PROP_STRING:
62 default:
63 return nullptr;
64 }
65}
66
68{
69 if (type.is<float>()) {
70 return CD_PROP_FLOAT;
71 }
72 if (type.is<float2>()) {
73 return CD_PROP_FLOAT2;
74 }
75 if (type.is<float3>()) {
76 return CD_PROP_FLOAT3;
77 }
78 if (type.is<int>()) {
79 return CD_PROP_INT32;
80 }
81 if (type.is<int2>()) {
82 return CD_PROP_INT32_2D;
83 }
84 if (type.is<ColorGeometry4f>()) {
85 return CD_PROP_COLOR;
86 }
87 if (type.is<bool>()) {
88 return CD_PROP_BOOL;
89 }
90 if (type.is<int8_t>()) {
91 return CD_PROP_INT8;
92 }
93 if (type.is<ColorGeometry4b>()) {
94 return CD_PROP_BYTE_COLOR;
95 }
96 if (type.is<math::Quaternion>()) {
97 return CD_PROP_QUATERNION;
98 }
99 if (type.is<float4x4>()) {
100 return CD_PROP_FLOAT4X4;
101 }
102 if (type.is<MStringProperty>()) {
103 return CD_PROP_STRING;
104 }
105 return static_cast<eCustomDataType>(-1);
106}
107
109 "This attribute cannot be accessed in a procedural context");
110
112{
113 if (attribute_name.startswith(".corner")) {
114 return false;
115 }
116 if (attribute_name.startswith(".edge")) {
117 return false;
118 }
119 if (attribute_name.startswith(".select")) {
120 return false;
121 }
122 if (attribute_name.startswith(".sculpt")) {
123 return false;
124 }
125 if (attribute_name.startswith(".hide")) {
126 return false;
127 }
128 if (attribute_name.startswith(".uv")) {
129 return false;
130 }
131 if (attribute_name == ".reference_index") {
132 return false;
133 }
134 if (attribute_name.startswith("." UV_VERTSEL_NAME ".")) {
135 return false;
136 }
137 if (attribute_name.startswith("." UV_EDGESEL_NAME ".")) {
138 return false;
139 }
140 if (attribute_name.startswith("." UV_PINNED_NAME ".")) {
141 return false;
142 }
143 return true;
144}
145
147{
148 switch (data_type) {
149 case CD_PROP_BOOL:
150 return 0;
151 case CD_PROP_INT8:
152 return 1;
153 case CD_PROP_INT32:
154 return 2;
155 case CD_PROP_FLOAT:
156 return 3;
157 case CD_PROP_INT32_2D:
158 return 4;
159 case CD_PROP_FLOAT2:
160 return 5;
161 case CD_PROP_FLOAT3:
162 return 6;
164 return 7;
166 return 8;
167 case CD_PROP_COLOR:
168 return 9;
169 case CD_PROP_FLOAT4X4:
170 return 10;
171#if 0 /* These attribute types are not supported yet. */
172 case CD_PROP_STRING:
173 return 10;
174#endif
175 default:
176 /* Only accept "generic" custom data types used by the attribute system. */
178 return 0;
179 }
180}
181
183{
184 int highest_complexity = INT_MIN;
185 eCustomDataType most_complex_type = CD_PROP_COLOR;
186
187 for (const eCustomDataType data_type : data_types) {
188 const int complexity = attribute_data_type_complexity(data_type);
189 if (complexity > highest_complexity) {
190 highest_complexity = complexity;
191 most_complex_type = data_type;
192 }
193 }
194
195 return most_complex_type;
196}
197
202static int attribute_domain_priority(const AttrDomain domain)
203{
204 switch (domain) {
206 return 0;
208 return 1;
210 return 2;
211 case AttrDomain::Face:
212 return 3;
213 case AttrDomain::Edge:
214 return 4;
216 return 5;
218 return 6;
219 default:
220 /* Domain not supported in nodes yet. */
222 return 0;
223 }
224}
225
227{
228 int highest_priority = INT_MIN;
229 AttrDomain highest_priority_domain = AttrDomain::Corner;
230
231 for (const AttrDomain domain : domains) {
232 const int priority = attribute_domain_priority(domain);
233 if (priority > highest_priority) {
234 highest_priority = priority;
235 highest_priority_domain = domain;
236 }
237 }
238
239 return highest_priority_domain;
240}
241
243 const eCustomDataType data_type,
244 const eCDAllocType alloctype,
245 const int domain_size,
246 const StringRef attribute_id)
247{
248 return CustomData_add_layer_named(&custom_data, data_type, alloctype, domain_size, attribute_id);
249}
250
252 CustomData &custom_data,
253 const eCustomDataType data_type,
254 const StringRef attribute_id,
255 const int domain_size,
256 void *layer_data,
257 const ImplicitSharingInfo *sharing_info)
258{
260 &custom_data, data_type, layer_data, domain_size, attribute_id, sharing_info);
261}
262
264 CustomData &custom_data,
265 const eCustomDataType data_type,
266 const int domain_num,
267 const AttributeInit &initializer,
268 const GPointer custom_default_value_ptr)
269{
270 const int old_layer_num = custom_data.totlayer;
271 switch (initializer.type) {
274 custom_data, data_type, CD_CONSTRUCT, domain_num, attribute_id);
275 break;
276 }
278 if (const void *default_value = custom_default_value_ptr.get()) {
279 const CPPType &type = *custom_default_value_ptr.type();
281 custom_data, data_type, CD_CONSTRUCT, domain_num, attribute_id);
282 type.fill_assign_n(default_value, data, domain_num);
283 }
284 else {
286 custom_data, data_type, CD_SET_DEFAULT, domain_num, attribute_id);
287 }
288 break;
289 }
292 custom_data, data_type, CD_CONSTRUCT, domain_num, attribute_id);
293 if (data != nullptr) {
294 const GVArray &varray = static_cast<const AttributeInitVArray &>(initializer).varray;
296 }
297 break;
298 }
300 void *data = static_cast<const AttributeInitMoveArray &>(initializer).data;
302 custom_data, data_type, attribute_id, domain_num, data, nullptr);
303 break;
304 }
306 const AttributeInitShared &init = static_cast<const AttributeInitShared &>(initializer);
308 data_type,
309 attribute_id,
311 const_cast<void *>(init.data),
312 init.sharing_info);
313 break;
314 }
315 }
316 return old_layer_num < custom_data.totlayer;
317}
318
320 const StringRef attribute_id)
321{
322 return layer.name == attribute_id;
323}
324
325bool BuiltinCustomDataLayerProvider::layer_exists(const CustomData &custom_data) const
326{
327 return CustomData_get_named_layer_index(&custom_data, data_type_, name_) != -1;
328}
329
331{
332 const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner);
333 if (custom_data == nullptr) {
334 return {};
335 }
336
337 /* When the number of elements is zero, layers might have null data but still exist. */
339 const int element_num = custom_data_access_.get_element_num(owner);
340 if (element_num == 0) {
341 if (this->layer_exists(*custom_data)) {
342 return {GVArray::ForSpan({type, nullptr, 0}), domain_, nullptr};
343 }
344 return {};
345 }
346
347 const int index = CustomData_get_named_layer_index(custom_data, data_type_, name_);
348 if (index == -1) {
349 return {};
350 }
351 const CustomDataLayer &layer = custom_data->layers[index];
352 return {GVArray::ForSpan({type, layer.data, element_num}), domain_, layer.sharing_info};
353}
354
356{
357 CustomData *custom_data = custom_data_access_.get_custom_data(owner);
358 if (custom_data == nullptr) {
359 return {};
360 }
361
362 std::function<void()> tag_modified_fn;
363 if (update_on_change_ != nullptr) {
364 tag_modified_fn = [owner, update = update_on_change_]() { update(owner); };
365 }
366
367 /* When the number of elements is zero, layers might have null data but still exist. */
369 const int element_num = custom_data_access_.get_element_num(owner);
370 if (element_num == 0) {
371 if (this->layer_exists(*custom_data)) {
372 return {GVMutableArray::ForSpan({type, nullptr, 0}), domain_, std::move(tag_modified_fn)};
373 }
374 return {};
375 }
376
377 void *data = CustomData_get_layer_named_for_write(custom_data, data_type_, name_, element_num);
378 if (data == nullptr) {
379 return {};
380 }
381 return {GVMutableArray::ForSpan({type, data, element_num}), domain_, std::move(tag_modified_fn)};
382}
383
385{
386 if (deletable_ != Deletable) {
387 return false;
388 }
389 CustomData *custom_data = custom_data_access_.get_custom_data(owner);
390 if (custom_data == nullptr) {
391 return {};
392 }
393
394 const int element_num = custom_data_access_.get_element_num(owner);
395 if (CustomData_free_layer_named(custom_data, name_, element_num)) {
396 if (update_on_change_ != nullptr) {
397 update_on_change_(owner);
398 }
399 return true;
400 }
401 return false;
402}
403
405 const AttributeInit &initializer) const
406{
407 CustomData *custom_data = custom_data_access_.get_custom_data(owner);
408 if (custom_data == nullptr) {
409 return false;
410 }
411
412 const int element_num = custom_data_access_.get_element_num(owner);
413 if (CustomData_has_layer_named(custom_data, data_type_, name_)) {
414 /* Exists already. */
415 return false;
416 }
418 name_, *custom_data, data_type_, element_num, initializer, default_value_))
419 {
420 if (initializer.type != AttributeInit::Type::Construct) {
421 /* Avoid calling update function when values are not initialized. In that case
422 * values must be set elsewhere anyway, which will cause a separate update tag. */
423 if (update_on_change_ != nullptr) {
424 update_on_change_(owner);
425 }
426 }
427 return true;
428 }
429 return false;
430}
431
432bool BuiltinCustomDataLayerProvider::exists(const void *owner) const
433{
434 const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner);
435 if (custom_data == nullptr) {
436 return false;
437 }
438 return CustomData_has_layer_named(custom_data, data_type_, name_);
439}
440
442 const StringRef attribute_id) const
443{
444 const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner);
445 if (custom_data == nullptr) {
446 return {};
447 }
448 const int element_num = custom_data_access_.get_element_num(owner);
449 for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
450 if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) {
451 continue;
452 }
453 const CPPType *type = custom_data_type_to_cpp_type((eCustomDataType)layer.type);
454 if (type == nullptr) {
455 continue;
456 }
457 GSpan data{*type, layer.data, element_num};
458 return {GVArray::ForSpan(data), domain_, layer.sharing_info};
459 }
460 return {};
461}
462
464 const StringRef attribute_id) const
465{
466 CustomData *custom_data = custom_data_access_.get_custom_data(owner);
467 if (custom_data == nullptr) {
468 return {};
469 }
470 const int element_num = custom_data_access_.get_element_num(owner);
471 for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
472 if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) {
473 continue;
474 }
476 custom_data, eCustomDataType(layer.type), layer.name, element_num);
477
478 const CPPType *type = custom_data_type_to_cpp_type(eCustomDataType(layer.type));
479 if (type == nullptr) {
480 continue;
481 }
482 GMutableSpan data{*type, layer.data, element_num};
483 return {GVMutableArray::ForSpan(data), domain_};
484 }
485 return {};
486}
487
488bool CustomDataAttributeProvider::try_delete(void *owner, const StringRef attribute_id) const
489{
490 CustomData *custom_data = custom_data_access_.get_custom_data(owner);
491 if (custom_data == nullptr) {
492 return false;
493 }
494 const int element_num = custom_data_access_.get_element_num(owner);
495 for (const int i : IndexRange(custom_data->totlayer)) {
496 const CustomDataLayer &layer = custom_data->layers[i];
497 if (this->type_is_supported((eCustomDataType)layer.type) &&
498 custom_data_layer_matches_attribute_id(layer, attribute_id))
499 {
500 CustomData_free_layer(custom_data, eCustomDataType(layer.type), element_num, i);
501 return true;
502 }
503 }
504 return false;
505}
506
508 const StringRef attribute_id,
509 const AttrDomain domain,
510 const eCustomDataType data_type,
511 const AttributeInit &initializer) const
512{
513 if (domain_ != domain) {
514 return false;
515 }
516 if (!this->type_is_supported(data_type)) {
517 return false;
518 }
519 CustomData *custom_data = custom_data_access_.get_custom_data(owner);
520 if (custom_data == nullptr) {
521 return false;
522 }
523 for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
524 if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
525 return false;
526 }
527 }
528 const int element_num = custom_data_access_.get_element_num(owner);
530 attribute_id, *custom_data, data_type, element_num, initializer, {});
531 return true;
532}
533
535 const void *owner, const FunctionRef<void(const AttributeIter &)> fn) const
536{
537 const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner);
538 if (custom_data == nullptr) {
539 return true;
540 }
541 for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
542 const eCustomDataType data_type = (eCustomDataType)layer.type;
543 if (this->type_is_supported(data_type)) {
544 const auto get_fn = [&]() {
545 const CPPType *type = custom_data_type_to_cpp_type(data_type);
546 BLI_assert(type);
547 GSpan data{*type, layer.data, custom_data_access_.get_element_num(owner)};
548 return GAttributeReader{GVArray::ForSpan(data), domain_, layer.sharing_info};
549 };
550
551 AttributeIter iter{layer.name, domain_, data_type, get_fn};
552 fn(iter);
553 if (iter.is_stopped()) {
554 return false;
555 }
556 }
557 }
558 return true;
559}
560
561/* -------------------------------------------------------------------- */
565static GVArray try_adapt_data_type(GVArray varray, const CPPType &to_type)
566{
568 return conversions.try_convert(std::move(varray), to_type);
569}
570
571std::optional<AttributeAccessor> AttributeAccessor::from_id(const ID &id)
572{
573 switch (GS(id.name)) {
574 case ID_ME:
575 return reinterpret_cast<const Mesh &>(id).attributes();
576 case ID_PT:
577 return reinterpret_cast<const PointCloud &>(id).attributes();
578 case ID_CV:
579 return reinterpret_cast<const Curves &>(id).geometry.wrap().attributes();
580 case ID_GP:
581 return reinterpret_cast<const GreasePencil &>(id).attributes();
582 default:
583 return {};
584 }
585 return {};
586}
587
589 GAttributeReader attribute,
590 const std::optional<AttrDomain> domain,
591 const std::optional<eCustomDataType> data_type,
592 const AttributeAccessor &accessor)
593{
594 if (!attribute) {
595 return {};
596 }
597 if (domain.has_value()) {
598 if (attribute.domain != domain) {
599 attribute.varray = accessor.adapt_domain(attribute.varray, attribute.domain, *domain);
600 attribute.domain = *domain;
601 attribute.sharing_info = nullptr;
602 if (!attribute.varray) {
603 return {};
604 }
605 }
606 }
607 if (data_type.has_value()) {
608 const CPPType &type = *custom_data_type_to_cpp_type(*data_type);
609 if (attribute.varray.type() != type) {
610 attribute.varray = try_adapt_data_type(std::move(attribute.varray), type);
611 attribute.sharing_info = nullptr;
612 if (!attribute.varray) {
613 return {};
614 }
615 }
616 }
617 return attribute;
618}
619
621 const std::optional<AttrDomain> domain,
622 const std::optional<eCustomDataType> data_type) const
623{
624 return adapt_domain_and_type_if_necessary(this->lookup(attribute_id), domain, data_type, *this);
625}
626
627GAttributeReader AttributeIter::get(std::optional<AttrDomain> domain,
628 std::optional<eCustomDataType> data_type) const
629{
630 return adapt_domain_and_type_if_necessary(this->get(), domain, data_type, *accessor);
631}
632
634 const AttrDomain domain,
635 const eCustomDataType data_type,
636 const void *default_value) const
637{
638 GAttributeReader attribute = this->lookup(attribute_id, domain, data_type);
639 if (attribute) {
640 return attribute;
641 }
642 const CPPType &type = *custom_data_type_to_cpp_type(data_type);
643 const int64_t domain_size = this->domain_size(domain);
644 if (default_value == nullptr) {
645 return {GVArray::ForSingleRef(type, domain_size, type.default_value()), domain, nullptr};
646 }
647 return {GVArray::ForSingle(type, domain_size, default_value), domain, nullptr};
648}
649
650bool AttributeAccessor::contains(const StringRef attribute_id) const
651{
652 bool found = false;
653 this->foreach_attribute([&](const AttributeIter &iter) {
654 if (attribute_id == iter.name) {
655 found = true;
656 iter.stop();
657 }
658 });
659 return found;
660}
661
662std::optional<AttributeMetaData> AttributeAccessor::lookup_meta_data(
663 const StringRef attribute_id) const
664{
665 std::optional<AttributeMetaData> meta_data;
666 this->foreach_attribute([&](const AttributeIter &iter) {
667 if (attribute_id == iter.name) {
668 meta_data = AttributeMetaData{iter.domain, iter.data_type};
669 iter.stop();
670 }
671 });
672 return meta_data;
673}
674
675Set<StringRefNull> AttributeAccessor::all_ids() const
676{
678 this->foreach_attribute([&](const AttributeIter &iter) { ids.add(iter.name); });
679 return ids;
680}
681
682void MutableAttributeAccessor::remove_anonymous()
683{
684 Vector<std::string> anonymous_ids;
685 for (const StringRef id : this->all_ids()) {
687 anonymous_ids.append(id);
688 }
689 }
690
691 while (!anonymous_ids.is_empty()) {
692 this->remove(anonymous_ids.pop_last());
693 }
694}
695
699#ifndef NDEBUG
701 std::string name;
702 bool finish_called = false;
703 std::function<void()> real_finish_fn;
704
706 {
707 if (!this->finish_called) {
708 std::cerr << "Forgot to call `finish()` for '" << this->name << "'.\n";
709 }
710 }
711};
712#endif
713
714GAttributeWriter MutableAttributeAccessor::lookup_for_write(const StringRef attribute_id)
715{
716 GAttributeWriter attribute = fn_->lookup_for_write(owner_, attribute_id);
717 /* Check that the #finish method is called in debug builds. */
718#ifndef NDEBUG
719 if (attribute) {
720 auto checker = std::make_shared<FinishCallChecker>();
721 checker->name = attribute_id;
722 checker->real_finish_fn = attribute.tag_modified_fn;
723 attribute.tag_modified_fn = [checker]() {
724 if (checker->real_finish_fn) {
725 checker->real_finish_fn();
726 }
727 checker->finish_called = true;
728 };
729 }
730#endif
731 return attribute;
732}
733
734GSpanAttributeWriter MutableAttributeAccessor::lookup_for_write_span(const StringRef attribute_id)
735{
736 GAttributeWriter attribute = this->lookup_for_write(attribute_id);
737 if (attribute) {
738 return GSpanAttributeWriter{std::move(attribute), true};
739 }
740 return {};
741}
742
743GAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write(
744 const StringRef attribute_id,
745 const AttrDomain domain,
746 const eCustomDataType data_type,
747 const AttributeInit &initializer)
748{
749 std::optional<AttributeMetaData> meta_data = this->lookup_meta_data(attribute_id);
750 if (meta_data.has_value()) {
751 if (meta_data->domain == domain && meta_data->data_type == data_type) {
752 return this->lookup_for_write(attribute_id);
753 }
754 return {};
755 }
756 if (this->add(attribute_id, domain, data_type, initializer)) {
757 return this->lookup_for_write(attribute_id);
758 }
759 return {};
760}
761
762GSpanAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write_span(
763 const StringRef attribute_id,
764 const AttrDomain domain,
765 const eCustomDataType data_type,
766 const AttributeInit &initializer)
767{
768 GAttributeWriter attribute = this->lookup_or_add_for_write(
769 attribute_id, domain, data_type, initializer);
770 if (attribute) {
771 return GSpanAttributeWriter{std::move(attribute), true};
772 }
773 return {};
774}
775
776GSpanAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write_only_span(
777 const StringRef attribute_id, const AttrDomain domain, const eCustomDataType data_type)
778{
779 GAttributeWriter attribute = this->lookup_or_add_for_write(
780 attribute_id, domain, data_type, AttributeInitConstruct());
781 if (attribute) {
782 return GSpanAttributeWriter{std::move(attribute), false};
783 }
784 return {};
785}
786
787bool MutableAttributeAccessor::rename(const StringRef old_attribute_id,
788 const StringRef new_attribute_id)
789{
790 if (old_attribute_id == new_attribute_id) {
791 return true;
792 }
793 if (this->contains(new_attribute_id)) {
794 return false;
795 }
796 const GAttributeReader old_attribute = this->lookup(old_attribute_id);
797 if (!old_attribute) {
798 return false;
799 }
800 const eCustomDataType type = cpp_type_to_custom_data_type(old_attribute.varray.type());
801 if (old_attribute.sharing_info != nullptr && old_attribute.varray.is_span()) {
802 if (!this->add(new_attribute_id,
803 old_attribute.domain,
804 type,
805 AttributeInitShared{old_attribute.varray.get_internal_span().data(),
806 *old_attribute.sharing_info}))
807 {
808 return false;
809 }
810 }
811 else {
812 if (!this->add(new_attribute_id,
813 old_attribute.domain,
814 type,
815 AttributeInitVArray{old_attribute.varray}))
816 {
817 return false;
818 }
819 }
820 this->remove(old_attribute_id);
821 return true;
822}
823
824fn::GField AttributeValidator::validate_field_if_necessary(const fn::GField &field) const
825{
826 if (function) {
827 auto validate_op = fn::FieldOperation::Create(*function, {field});
828 return fn::GField(validate_op);
829 }
830 return field;
831}
832
834 const AttributeAccessor src_attributes,
835 MutableAttributeAccessor dst_attributes,
836 const AttrDomainMask domain_mask,
837 const bke::AttributeFilter &attribute_filter)
838{
840 src_attributes.foreach_attribute([&](const AttributeIter &iter) {
841 if (!(ATTR_DOMAIN_AS_MASK(iter.domain) & domain_mask)) {
842 return;
843 }
844 if (attribute_filter.allow_skip(iter.name)) {
845 return;
846 }
847 GVArray src = *iter.get();
849 iter.name, iter.domain, iter.data_type);
850 attributes.append({std::move(src), {iter.domain, iter.data_type}, std::move(dst)});
851 });
852 return attributes;
853}
854
857void gather_attributes(const AttributeAccessor src_attributes,
858 const AttrDomain src_domain,
859 const AttrDomain dst_domain,
860 const AttributeFilter &attribute_filter,
861 const IndexMask &selection,
862 MutableAttributeAccessor dst_attributes)
863{
864 const int src_size = src_attributes.domain_size(src_domain);
865 src_attributes.foreach_attribute([&](const AttributeIter &iter) {
866 if (iter.domain != src_domain) {
867 return;
868 }
869 if (iter.data_type == CD_PROP_STRING) {
870 return;
871 }
872 if (attribute_filter.allow_skip(iter.name)) {
873 return;
874 }
875 const GAttributeReader src = iter.get(src_domain);
876 if (selection.size() == src_size && src.sharing_info && src.varray.is_span()) {
877 const AttributeInitShared init(src.varray.get_internal_span().data(), *src.sharing_info);
878 if (dst_attributes.add(iter.name, dst_domain, iter.data_type, init)) {
879 return;
880 }
881 }
883 iter.name, dst_domain, iter.data_type);
884 if (!dst) {
885 return;
886 }
887 array_utils::gather(src.varray, selection, dst.span);
888 dst.finish();
889 });
890}
891
892void gather_attributes(const AttributeAccessor src_attributes,
893 const AttrDomain src_domain,
894 const AttrDomain dst_domain,
895 const AttributeFilter &attribute_filter,
896 const Span<int> indices,
897 MutableAttributeAccessor dst_attributes)
898{
899 if (array_utils::indices_are_range(indices, IndexRange(src_attributes.domain_size(src_domain))))
900 {
901 copy_attributes(src_attributes, src_domain, dst_domain, attribute_filter, dst_attributes);
902 }
903 else {
904 src_attributes.foreach_attribute([&](const AttributeIter &iter) {
905 if (iter.domain != src_domain) {
906 return;
907 }
908 if (iter.data_type == CD_PROP_STRING) {
909 return;
910 }
911 if (attribute_filter.allow_skip(iter.name)) {
912 return;
913 }
914 const GAttributeReader src = iter.get(src_domain);
916 iter.name, dst_domain, iter.data_type);
917 if (!dst) {
918 return;
919 }
920 attribute_math::gather(src.varray, indices, dst.span);
921 dst.finish();
922 });
923 }
924}
925
927 const AttrDomain src_domain,
928 const AttrDomain dst_domain,
929 const AttributeFilter &attribute_filter,
930 const OffsetIndices<int> src_offsets,
931 const OffsetIndices<int> dst_offsets,
932 const IndexMask &selection,
933 MutableAttributeAccessor dst_attributes)
934{
935 src_attributes.foreach_attribute([&](const AttributeIter &iter) {
936 if (iter.domain != src_domain) {
937 return;
938 }
939 if (iter.data_type == CD_PROP_STRING) {
940 return;
941 }
942 if (attribute_filter.allow_skip(iter.name)) {
943 return;
944 }
945 const GVArraySpan src = *iter.get(src_domain);
947 iter.name, dst_domain, iter.data_type);
948 if (!dst) {
949 return;
950 }
951 attribute_math::gather_group_to_group(src_offsets, dst_offsets, selection, src, dst.span);
952 dst.finish();
953 });
954}
955
957 const AttrDomain src_domain,
958 const AttrDomain dst_domain,
959 const AttributeFilter &attribute_filter,
960 const OffsetIndices<int> dst_offsets,
961 const IndexMask &src_selection,
962 MutableAttributeAccessor dst_attributes)
963{
964 src_attributes.foreach_attribute([&](const AttributeIter &iter) {
965 if (iter.domain != src_domain) {
966 return;
967 }
968 if (iter.data_type == CD_PROP_STRING) {
969 return;
970 }
971 if (attribute_filter.allow_skip(iter.name)) {
972 return;
973 }
974 const GVArraySpan src = *iter.get(src_domain);
976 iter.name, dst_domain, iter.data_type);
977 if (!dst) {
978 return;
979 }
980 attribute_math::gather_to_groups(dst_offsets, src_selection, src, dst.span);
981 dst.finish();
982 });
983}
984
985void copy_attributes(const AttributeAccessor src_attributes,
986 const AttrDomain src_domain,
987 const AttrDomain dst_domain,
988 const AttributeFilter &attribute_filter,
989 MutableAttributeAccessor dst_attributes)
990{
991 BLI_assert(src_attributes.domain_size(src_domain) == dst_attributes.domain_size(dst_domain));
992 return gather_attributes(src_attributes,
993 src_domain,
994 dst_domain,
995 attribute_filter,
996 IndexMask(src_attributes.domain_size(src_domain)),
997 dst_attributes);
998}
999
1001 const AttrDomain src_domain,
1002 const AttrDomain dst_domain,
1003 const AttributeFilter &attribute_filter,
1004 const OffsetIndices<int> src_offsets,
1005 const OffsetIndices<int> dst_offsets,
1006 const IndexMask &selection,
1007 MutableAttributeAccessor dst_attributes)
1008{
1009 if (selection.is_empty()) {
1010 return;
1011 }
1012 src_attributes.foreach_attribute([&](const AttributeIter &iter) {
1013 if (iter.domain != src_domain) {
1014 return;
1015 }
1016 if (iter.data_type == CD_PROP_STRING) {
1017 return;
1018 }
1019 if (attribute_filter.allow_skip(iter.name)) {
1020 return;
1021 }
1022 const GVArraySpan src = *iter.get(src_domain);
1024 iter.name, dst_domain, iter.data_type);
1025 if (!dst) {
1026 return;
1027 }
1028 array_utils::copy_group_to_group(src_offsets, dst_offsets, selection, src, dst.span);
1029 dst.finish();
1030 });
1031}
1032
1034 const AttrDomain domain,
1035 const AttributeFilter &attribute_filter,
1036 const IndexRange range)
1037{
1038 attributes.foreach_attribute([&](const AttributeIter &iter) {
1039 if (iter.domain != domain) {
1040 return;
1041 }
1042 if (attribute_filter.allow_skip(iter.name)) {
1043 return;
1044 }
1045 if (iter.data_type == CD_PROP_STRING) {
1046 return;
1047 }
1048 GSpanAttributeWriter attribute = attributes.lookup_for_write_span(iter.name);
1049 const CPPType &type = attribute.span.type();
1050 GMutableSpan data = attribute.span.slice(range);
1051 type.fill_assign_n(type.default_value(), data.data(), data.size());
1052 attribute.finish();
1053 });
1054}
1055
1056} // namespace blender::bke
AttrDomainMask
#define ATTR_DOMAIN_AS_MASK(domain)
Low-level operations for curves.
CustomData interface, see also DNA_customdata_types.h.
void * CustomData_get_layer_named_for_write(CustomData *data, eCustomDataType type, blender::StringRef name, int totelem)
bool CustomData_free_layer_named(CustomData *data, blender::StringRef name, const int totelem)
eCDAllocType
@ CD_SET_DEFAULT
@ CD_CONSTRUCT
void * CustomData_add_layer_named(CustomData *data, eCustomDataType type, eCDAllocType alloctype, int totelem, blender::StringRef name)
#define UV_VERTSEL_NAME
int CustomData_get_named_layer_index(const CustomData *data, eCustomDataType type, blender::StringRef name)
bool CustomData_has_layer_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
bool CustomData_free_layer(CustomData *data, eCustomDataType type, int totelem, int index)
#define UV_PINNED_NAME
const void * CustomData_add_layer_named_with_data(CustomData *data, eCustomDataType type, void *layer_data, int totelem, blender::StringRef name, const blender::ImplicitSharingInfo *sharing_info)
#define UV_EDGESEL_NAME
support for deformation groups and hooks.
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
ID and Library types, which are fundamental for SDNA.
@ ID_CV
@ ID_ME
@ ID_GP
@ ID_PT
@ CD_PROP_BYTE_COLOR
@ CD_PROP_FLOAT
@ CD_PROP_FLOAT3
@ CD_PROP_INT32_2D
@ CD_PROP_COLOR
@ CD_PROP_QUATERNION
@ CD_PROP_INT32
@ CD_PROP_FLOAT2
@ CD_PROP_STRING
@ CD_PROP_FLOAT4X4
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
static void copy_attributes(PointCloud *pointcloud, const ::PointCloud &b_pointcloud, const bool need_motion, const float motion_scale)
void init()
static const CPPType & get()
GMutableSpan slice(const int64_t start, int64_t size) const
const CPPType * type() const
const void * get() const
const void * data() const
void materialize_to_uninitialized(void *dst) const
static GVArray ForSingleRef(const CPPType &type, int64_t size, const void *value)
static GVArray ForSpan(GSpan span)
static GVArray ForSingle(const CPPType &type, int64_t size, const void *value)
static GVMutableArray ForSpan(GMutableSpan span)
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr bool startswith(StringRef prefix) const
void append(const T &value)
bool is_empty() const
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
static std::optional< AttributeAccessor > from_id(const ID &id)
GVArray adapt_domain(const GVArray &varray, const AttrDomain from_domain, const AttrDomain to_domain) const
GAttributeReader lookup(const StringRef attribute_id) const
std::optional< AttributeMetaData > lookup_meta_data(const StringRef attribute_id) const
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
int domain_size(const AttrDomain domain) const
bool contains(const StringRef attribute_id) const
GAttributeReader get() const
const AttributeAccessor * accessor
GAttributeWriter try_get_for_write(void *owner) const final
bool try_create(void *owner, const AttributeInit &initializer) const final
bool exists(const void *owner) const final
GAttributeReader try_get_for_read(const void *owner) const final
GAttributeWriter try_get_for_write(void *owner, StringRef attribute_id) const final
bool try_delete(void *owner, StringRef attribute_id) const final
GAttributeReader try_get_for_read(const void *owner, StringRef attribute_id) const final
bool foreach_attribute(const void *owner, FunctionRef< void(const AttributeIter &)> fn) const final
bool try_create(void *owner, StringRef attribute_id, AttrDomain domain, const eCustomDataType data_type, const AttributeInit &initializer) const final
GVArray try_convert(GVArray varray, const CPPType &to_type) const
bool add(const StringRef attribute_id, const AttrDomain domain, const eCustomDataType data_type, const AttributeInit &initializer)
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type)
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:244
#define GS(x)
Definition iris.cc:202
static void add(blender::Map< std::string, std::string > &messages, Message &msg)
Definition msgfmt.cc:227
bool indices_are_range(Span< int > indices, IndexRange range)
void copy_group_to_group(OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, GSpan src, GMutableSpan dst)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
bool attribute_name_is_anonymous(const StringRef name)
Vector< AttributeTransferData > retrieve_attributes_for_transfer(const AttributeAccessor src_attributes, MutableAttributeAccessor dst_attributes, AttrDomainMask domain_mask, const AttributeFilter &attribute_filter={})
static GAttributeReader adapt_domain_and_type_if_necessary(GAttributeReader attribute, const std::optional< AttrDomain > domain, const std::optional< eCustomDataType > data_type, const AttributeAccessor &accessor)
void fill_attribute_range_default(MutableAttributeAccessor dst_attributes, AttrDomain domain, const AttributeFilter &attribute_filter, IndexRange range)
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)
static GVArray try_adapt_data_type(GVArray varray, const CPPType &to_type)
const DataTypeConversions & get_implicit_type_conversions()
static int attribute_domain_priority(const AttrDomain domain)
bool allow_procedural_attribute_access(StringRef attribute_name)
eCustomDataType cpp_type_to_custom_data_type(const CPPType &type)
static bool add_custom_data_layer_from_attribute_init(const StringRef attribute_id, CustomData &custom_data, const eCustomDataType data_type, const int domain_num, const AttributeInit &initializer, const GPointer custom_default_value_ptr)
const char * no_procedural_access_message
static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer, const StringRef attribute_id)
void gather_attributes(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
eCustomDataType attribute_data_type_highest_complexity(Span< eCustomDataType > data_types)
AttrDomain attribute_domain_highest_priority(Span< AttrDomain > domains)
static void * add_generic_custom_data_layer(CustomData &custom_data, const eCustomDataType data_type, const eCDAllocType alloctype, const int domain_size, const StringRef attribute_id)
const CPPType * custom_data_type_to_cpp_type(eCustomDataType type)
void gather_attributes_group_to_group(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
static int domain_num(const CurvesGeometry &curves, const AttrDomain domain)
void copy_attributes_group_to_group(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
static const void * add_generic_custom_data_layer_with_existing_data(CustomData &custom_data, const eCustomDataType data_type, const StringRef attribute_id, const int domain_size, void *layer_data, const ImplicitSharingInfo *sharing_info)
static int attribute_data_type_complexity(const eCustomDataType data_type)
static void update(bNodeTree *ntree)
__int64 int64_t
Definition stdint.h:89
signed char int8_t
Definition stdint.h:75
CustomDataLayer * layers
Definition DNA_ID.h:413
bool allow_skip(const StringRef name) const
std::function< void()> real_finish_fn
const ImplicitSharingInfo * sharing_info
#define N_(msgid)