Blender V4.5
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
9#include "BKE_curves.hh"
10#include "BKE_customdata.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
30
31#ifndef NDEBUG
32# include <iostream>
33#endif
34
35namespace blender::bke {
36
70
72{
73 if (type.is<float>()) {
74 return AttrType::Float;
75 }
76 if (type.is<float2>()) {
77 return AttrType::Float2;
78 }
79 if (type.is<float3>()) {
80 return AttrType::Float3;
81 }
82 if (type.is<int>()) {
83 return AttrType::Int32;
84 }
85 if (type.is<int2>()) {
86 return AttrType::Int32_2D;
87 }
88 if (type.is<ColorGeometry4f>()) {
90 }
91 if (type.is<bool>()) {
92 return AttrType::Bool;
93 }
94 if (type.is<int8_t>()) {
95 return AttrType::Int8;
96 }
97 if (type.is<ColorGeometry4b>()) {
99 }
100 if (type.is<math::Quaternion>()) {
102 }
103 if (type.is<float4x4>()) {
104 return AttrType::Float4x4;
105 }
106 if (type.is<short2>()) {
107 return AttrType::Int16_2D;
108 }
109 if (type.is<MStringProperty>()) {
110 return AttrType::String;
111 }
113 return AttrType::Bool;
114}
115
117{
118 switch (type) {
119 case CD_PROP_FLOAT:
120 return &CPPType::get<float>();
121 case CD_PROP_FLOAT2:
122 return &CPPType::get<float2>();
123 case CD_PROP_FLOAT3:
124 return &CPPType::get<float3>();
125 case CD_PROP_INT32:
126 return &CPPType::get<int>();
127 case CD_PROP_INT32_2D:
128 return &CPPType::get<int2>();
129 case CD_PROP_COLOR:
131 case CD_PROP_BOOL:
132 return &CPPType::get<bool>();
133 case CD_PROP_INT8:
134 return &CPPType::get<int8_t>();
139 case CD_PROP_FLOAT4X4:
140 return &CPPType::get<float4x4>();
141 case CD_PROP_INT16_2D:
142 return &CPPType::get<short2>();
143 case CD_PROP_STRING:
145 default:
146 return nullptr;
147 }
148}
149
151{
152 if (type.is<float>()) {
153 return CD_PROP_FLOAT;
154 }
155 if (type.is<float2>()) {
156 return CD_PROP_FLOAT2;
157 }
158 if (type.is<float3>()) {
159 return CD_PROP_FLOAT3;
160 }
161 if (type.is<int>()) {
162 return CD_PROP_INT32;
163 }
164 if (type.is<int2>()) {
165 return CD_PROP_INT32_2D;
166 }
167 if (type.is<ColorGeometry4f>()) {
168 return CD_PROP_COLOR;
169 }
170 if (type.is<bool>()) {
171 return CD_PROP_BOOL;
172 }
173 if (type.is<int8_t>()) {
174 return CD_PROP_INT8;
175 }
176 if (type.is<ColorGeometry4b>()) {
177 return CD_PROP_BYTE_COLOR;
178 }
179 if (type.is<math::Quaternion>()) {
180 return CD_PROP_QUATERNION;
181 }
182 if (type.is<float4x4>()) {
183 return CD_PROP_FLOAT4X4;
184 }
185 if (type.is<short2>()) {
186 return CD_PROP_INT16_2D;
187 }
188 if (type.is<MStringProperty>()) {
189 return CD_PROP_STRING;
190 }
191 return eCustomDataType(-1);
192}
193
195 "This attribute cannot be accessed in a procedural context");
196
198{
199 if (attribute_name.startswith(".corner")) {
200 return false;
201 }
202 if (attribute_name.startswith(".edge")) {
203 return false;
204 }
205 if (attribute_name.startswith(".select")) {
206 return false;
207 }
208 if (attribute_name.startswith(".sculpt")) {
209 return false;
210 }
211 if (attribute_name.startswith(".hide")) {
212 return false;
213 }
214 if (attribute_name.startswith(".uv")) {
215 return false;
216 }
217 if (attribute_name == ".reference_index") {
218 return false;
219 }
220 if (attribute_name.startswith("." UV_VERTSEL_NAME ".")) {
221 return false;
222 }
223 if (attribute_name.startswith("." UV_EDGESEL_NAME ".")) {
224 return false;
225 }
226 if (attribute_name.startswith("." UV_PINNED_NAME ".")) {
227 return false;
228 }
229 return true;
230}
231
233{
234 switch (data_type) {
235 case CD_PROP_BOOL:
236 return 0;
237 case CD_PROP_INT8:
238 return 1;
239 case CD_PROP_INT32:
240 return 2;
241 case CD_PROP_FLOAT:
242 return 3;
243 case CD_PROP_INT16_2D:
244 return 4;
245 case CD_PROP_INT32_2D:
246 return 5;
247 case CD_PROP_FLOAT2:
248 return 6;
249 case CD_PROP_FLOAT3:
250 return 7;
252 return 8;
254 return 9;
255 case CD_PROP_COLOR:
256 return 10;
257 case CD_PROP_FLOAT4X4:
258 return 11;
259#if 0 /* These attribute types are not supported yet. */
260 case CD_PROP_STRING:
261 return 12;
262#endif
263 default:
264 /* Only accept "generic" custom data types used by the attribute system. */
266 return 0;
267 }
268}
269
271{
272 int highest_complexity = INT_MIN;
273 eCustomDataType most_complex_type = CD_PROP_COLOR;
274
275 for (const eCustomDataType data_type : data_types) {
276 const int complexity = attribute_data_type_complexity(data_type);
277 if (complexity > highest_complexity) {
278 highest_complexity = complexity;
279 most_complex_type = data_type;
280 }
281 }
282
283 return most_complex_type;
284}
285
290static int attribute_domain_priority(const AttrDomain domain)
291{
292 switch (domain) {
294 return 0;
296 return 1;
298 return 2;
299 case AttrDomain::Face:
300 return 3;
301 case AttrDomain::Edge:
302 return 4;
304 return 5;
306 return 6;
307 default:
308 /* Domain not supported in nodes yet. */
310 return 0;
311 }
312}
313
315{
316 int highest_priority = INT_MIN;
317 AttrDomain highest_priority_domain = AttrDomain::Corner;
318
319 for (const AttrDomain domain : domains) {
320 const int priority = attribute_domain_priority(domain);
321 if (priority > highest_priority) {
322 highest_priority = priority;
323 highest_priority_domain = domain;
324 }
325 }
326
327 return highest_priority_domain;
328}
329
331 const eCustomDataType data_type,
332 const eCDAllocType alloctype,
333 const int domain_size,
334 const StringRef attribute_id)
335{
336 return CustomData_add_layer_named(&custom_data, data_type, alloctype, domain_size, attribute_id);
337}
338
340 CustomData &custom_data,
341 const eCustomDataType data_type,
342 const StringRef attribute_id,
343 const int domain_size,
344 void *layer_data,
345 const ImplicitSharingInfo *sharing_info)
346{
348 &custom_data, data_type, layer_data, domain_size, attribute_id, sharing_info);
349}
350
352 CustomData &custom_data,
353 const eCustomDataType data_type,
354 const int domain_num,
355 const AttributeInit &initializer,
356 const GPointer custom_default_value_ptr)
357{
358 const int old_layer_num = custom_data.totlayer;
359 switch (initializer.type) {
362 custom_data, data_type, CD_CONSTRUCT, domain_num, attribute_id);
363 break;
364 }
366 if (const void *default_value = custom_default_value_ptr.get()) {
367 const CPPType &type = *custom_default_value_ptr.type();
369 custom_data, data_type, CD_CONSTRUCT, domain_num, attribute_id);
370 type.fill_assign_n(default_value, data, domain_num);
371 }
372 else {
374 custom_data, data_type, CD_SET_DEFAULT, domain_num, attribute_id);
375 }
376 break;
377 }
380 custom_data, data_type, CD_CONSTRUCT, domain_num, attribute_id);
381 if (data != nullptr) {
382 const GVArray &varray = static_cast<const AttributeInitVArray &>(initializer).varray;
384 }
385 break;
386 }
388 void *data = static_cast<const AttributeInitMoveArray &>(initializer).data;
390 custom_data, data_type, attribute_id, domain_num, data, nullptr);
391 break;
392 }
394 const AttributeInitShared &init = static_cast<const AttributeInitShared &>(initializer);
396 data_type,
397 attribute_id,
399 const_cast<void *>(init.data),
400 init.sharing_info);
401 break;
402 }
403 }
404 return old_layer_num < custom_data.totlayer;
405}
406
407bool BuiltinCustomDataLayerProvider::layer_exists(const CustomData &custom_data) const
408{
409 return CustomData_get_named_layer_index(&custom_data, data_type_, name_) != -1;
410}
411
413{
414 const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner);
415 if (custom_data == nullptr) {
416 return {};
417 }
418
419 /* When the number of elements is zero, layers might have null data but still exist. */
421 const int element_num = custom_data_access_.get_element_num(owner);
422 if (element_num == 0) {
423 if (this->layer_exists(*custom_data)) {
424 return {GVArray::ForSpan({type, nullptr, 0}), domain_, nullptr};
425 }
426 return {};
427 }
428
429 const int index = CustomData_get_named_layer_index(custom_data, data_type_, name_);
430 if (index == -1) {
431 return {};
432 }
433 const CustomDataLayer &layer = custom_data->layers[index];
434 return {GVArray::ForSpan({type, layer.data, element_num}), domain_, layer.sharing_info};
435}
436
438{
439 CustomData *custom_data = custom_data_access_.get_custom_data(owner);
440 if (custom_data == nullptr) {
441 return {};
442 }
443
444 std::function<void()> tag_modified_fn;
445 if (update_on_change_ != nullptr) {
446 tag_modified_fn = [owner, update = update_on_change_]() { update(owner); };
447 }
448
449 /* When the number of elements is zero, layers might have null data but still exist. */
451 const int element_num = custom_data_access_.get_element_num(owner);
452 if (element_num == 0) {
453 if (this->layer_exists(*custom_data)) {
454 return {GVMutableArray::ForSpan({type, nullptr, 0}), domain_, std::move(tag_modified_fn)};
455 }
456 return {};
457 }
458
459 void *data = CustomData_get_layer_named_for_write(custom_data, data_type_, name_, element_num);
460 if (data == nullptr) {
461 return {};
462 }
463 return {GVMutableArray::ForSpan({type, data, element_num}), domain_, std::move(tag_modified_fn)};
464}
465
467{
468 if (deletable_ != Deletable) {
469 return false;
470 }
471 CustomData *custom_data = custom_data_access_.get_custom_data(owner);
472 if (custom_data == nullptr) {
473 return {};
474 }
475
476 if (CustomData_free_layer_named(custom_data, name_)) {
477 if (update_on_change_ != nullptr) {
478 update_on_change_(owner);
479 }
480 return true;
481 }
482 return false;
483}
484
486 const AttributeInit &initializer) const
487{
488 CustomData *custom_data = custom_data_access_.get_custom_data(owner);
489 if (custom_data == nullptr) {
490 return false;
491 }
492
493 const int element_num = custom_data_access_.get_element_num(owner);
494 if (CustomData_has_layer_named(custom_data, data_type_, name_)) {
495 /* Exists already. */
496 return false;
497 }
499 name_, *custom_data, data_type_, element_num, initializer, default_value_))
500 {
501 if (initializer.type != AttributeInit::Type::Construct) {
502 /* Avoid calling update function when values are not default-initialized. Without default
503 * initialization or otherwise meaningful initial values, they should be set elsewhere
504 * anyway, which will cause a separate update tag. */
505 if (update_on_change_ != nullptr) {
506 update_on_change_(owner);
507 }
508 }
509 return true;
510 }
511 return false;
512}
513
514bool BuiltinCustomDataLayerProvider::exists(const void *owner) const
515{
516 const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner);
517 if (custom_data == nullptr) {
518 return false;
519 }
520 return CustomData_has_layer_named(custom_data, data_type_, name_);
521}
522
524 const StringRef attribute_id) const
525{
526 const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner);
527 if (custom_data == nullptr) {
528 return {};
529 }
530 const int element_num = custom_data_access_.get_element_num(owner);
531 for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
532 if (layer.name != attribute_id) {
533 continue;
534 }
535 const CPPType *type = custom_data_type_to_cpp_type(eCustomDataType(layer.type));
536 if (type == nullptr) {
537 continue;
538 }
539 GSpan data{*type, layer.data, element_num};
540 return {GVArray::ForSpan(data), domain_, layer.sharing_info};
541 }
542 return {};
543}
544
546 const StringRef attribute_id) const
547{
548 CustomData *custom_data = custom_data_access_.get_custom_data(owner);
549 if (custom_data == nullptr) {
550 return {};
551 }
552 const int element_num = custom_data_access_.get_element_num(owner);
553 for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
554 if (layer.name != attribute_id) {
555 continue;
556 }
558 custom_data, eCustomDataType(layer.type), layer.name, element_num);
559
560 const CPPType *type = custom_data_type_to_cpp_type(eCustomDataType(layer.type));
561 if (type == nullptr) {
562 continue;
563 }
564 std::function<void()> tag_modified_fn;
565 if (custom_data_access_.get_tag_modified_function != nullptr) {
566 tag_modified_fn = custom_data_access_.get_tag_modified_function(owner, attribute_id);
567 }
568 GMutableSpan data{*type, layer.data, element_num};
569 return {GVMutableArray::ForSpan(data), domain_, tag_modified_fn};
570 }
571 return {};
572}
573
574bool CustomDataAttributeProvider::try_delete(void *owner, const StringRef attribute_id) const
575{
576 CustomData *custom_data = custom_data_access_.get_custom_data(owner);
577 if (custom_data == nullptr) {
578 return false;
579 }
580 for (const int i : IndexRange(custom_data->totlayer)) {
581 const CustomDataLayer &layer = custom_data->layers[i];
582 if (this->type_is_supported(eCustomDataType(layer.type)) && layer.name == attribute_id) {
583 CustomData_free_layer(custom_data, eCustomDataType(layer.type), i);
584 if (custom_data_access_.get_tag_modified_function != nullptr) {
585 if (const std::function<void()> fn = custom_data_access_.get_tag_modified_function(
586 owner, attribute_id))
587 {
588 fn();
589 }
590 }
591 return true;
592 }
593 }
594 return false;
595}
596
598 const StringRef attribute_id,
599 const AttrDomain domain,
600 const eCustomDataType data_type,
601 const AttributeInit &initializer) const
602{
603 if (domain_ != domain) {
604 return false;
605 }
606 if (!this->type_is_supported(data_type)) {
607 return false;
608 }
609 CustomData *custom_data = custom_data_access_.get_custom_data(owner);
610 if (custom_data == nullptr) {
611 return false;
612 }
613 for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
614 if (layer.name == attribute_id) {
615 return false;
616 }
617 }
618 const int element_num = custom_data_access_.get_element_num(owner);
620 attribute_id, *custom_data, data_type, element_num, initializer, {});
621 if (initializer.type != AttributeInit::Type::Construct) {
622 /* Avoid calling update function when values are not default-initialized. Without default
623 * initialization or otherwise meaningful initial values, they should be set elsewhere
624 * anyway, which will cause a separate update tag. */
625 if (custom_data_access_.get_tag_modified_function != nullptr) {
626 if (const std::function<void()> fn = custom_data_access_.get_tag_modified_function(
627 owner, attribute_id))
628 {
629 fn();
630 }
631 }
632 }
633 return true;
634}
635
637 const void *owner, const FunctionRef<void(const AttributeIter &)> fn) const
638{
639 const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner);
640 if (custom_data == nullptr) {
641 return true;
642 }
643 for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
644 const eCustomDataType data_type = eCustomDataType(layer.type);
645 if (this->type_is_supported(data_type)) {
646 const auto get_fn = [&]() {
647 const CPPType *type = custom_data_type_to_cpp_type(data_type);
648 BLI_assert(type);
649 GSpan data{*type, layer.data, custom_data_access_.get_element_num(owner)};
650 return GAttributeReader{GVArray::ForSpan(data), domain_, layer.sharing_info};
651 };
652
653 AttributeIter iter{layer.name, domain_, data_type, get_fn};
654 fn(iter);
655 if (iter.is_stopped()) {
656 return false;
657 }
658 }
659 }
660 return true;
661}
662
663/* -------------------------------------------------------------------- */
666
667static GVArray try_adapt_data_type(GVArray varray, const CPPType &to_type)
668{
670 return conversions.try_convert(std::move(varray), to_type);
671}
672
673std::optional<AttributeAccessor> AttributeAccessor::from_id(const ID &id)
674{
675 switch (GS(id.name)) {
676 case ID_ME:
677 return reinterpret_cast<const Mesh &>(id).attributes();
678 case ID_PT:
679 return reinterpret_cast<const PointCloud &>(id).attributes();
680 case ID_CV:
681 return reinterpret_cast<const Curves &>(id).geometry.wrap().attributes();
682 case ID_GP:
683 return reinterpret_cast<const GreasePencil &>(id).attributes();
684 default:
685 return {};
686 }
687 return {};
688}
689
691 GAttributeReader attribute,
692 const std::optional<AttrDomain> domain,
693 const std::optional<eCustomDataType> data_type,
694 const AttributeAccessor &accessor)
695{
696 if (!attribute) {
697 return {};
698 }
699 if (domain.has_value()) {
700 if (attribute.domain != domain) {
701 attribute.varray = accessor.adapt_domain(attribute.varray, attribute.domain, *domain);
702 attribute.domain = *domain;
703 attribute.sharing_info = nullptr;
704 if (!attribute.varray) {
705 return {};
706 }
707 }
708 }
709 if (data_type.has_value()) {
710 const CPPType &type = *custom_data_type_to_cpp_type(*data_type);
711 if (attribute.varray.type() != type) {
712 attribute.varray = try_adapt_data_type(std::move(attribute.varray), type);
713 attribute.sharing_info = nullptr;
714 if (!attribute.varray) {
715 return {};
716 }
717 }
718 }
719 return attribute;
720}
721
723 const std::optional<AttrDomain> domain,
724 const std::optional<eCustomDataType> data_type) const
725{
726 return adapt_domain_and_type_if_necessary(this->lookup(attribute_id), domain, data_type, *this);
727}
728
730 std::optional<eCustomDataType> data_type) const
731{
732 return adapt_domain_and_type_if_necessary(this->get(), domain, data_type, *accessor);
733}
734
736 const AttrDomain domain,
737 const eCustomDataType data_type,
738 const void *default_value) const
739{
740 GAttributeReader attribute = this->lookup(attribute_id, domain, data_type);
741 if (attribute) {
742 return attribute;
743 }
744 const CPPType &type = *custom_data_type_to_cpp_type(data_type);
745 const int64_t domain_size = this->domain_size(domain);
746 if (default_value == nullptr) {
747 return {GVArray::ForSingleRef(type, domain_size, type.default_value()), domain, nullptr};
748 }
749 return {GVArray::ForSingle(type, domain_size, default_value), domain, nullptr};
750}
751
752bool AttributeAccessor::contains(const StringRef attribute_id) const
753{
754 bool found = false;
755 this->foreach_attribute([&](const AttributeIter &iter) {
756 if (attribute_id == iter.name) {
757 found = true;
758 iter.stop();
759 }
760 });
761 return found;
762}
763
764std::optional<AttributeMetaData> AttributeAccessor::lookup_meta_data(
765 const StringRef attribute_id) const
766{
767 std::optional<AttributeMetaData> meta_data;
768 this->foreach_attribute([&](const AttributeIter &iter) {
769 if (attribute_id == iter.name) {
770 meta_data = AttributeMetaData{iter.domain, iter.data_type};
771 iter.stop();
772 }
773 });
774 return meta_data;
775}
776
778{
780 this->foreach_attribute([&](const AttributeIter &iter) { ids.add(iter.name); });
781 return ids;
782}
783
785{
786 Vector<std::string> anonymous_ids;
787 for (const StringRef id : this->all_ids()) {
789 anonymous_ids.append(id);
790 }
791 }
792
793 while (!anonymous_ids.is_empty()) {
794 this->remove(anonymous_ids.pop_last());
795 }
796}
797
801#ifndef NDEBUG
803 std::string name;
804 bool finish_called = false;
805 std::function<void()> real_finish_fn;
806
808 {
809 if (!this->finish_called) {
810 std::cerr << "Forgot to call `finish()` for '" << this->name << "'.\n";
811 }
812 }
813};
814#endif
815
817{
818 GAttributeWriter attribute = fn_->lookup_for_write(owner_, attribute_id);
819 /* Check that the #finish method is called in debug builds. */
820#ifndef NDEBUG
821 if (attribute) {
822 auto checker = std::make_shared<FinishCallChecker>();
823 checker->name = attribute_id;
824 checker->real_finish_fn = attribute.tag_modified_fn;
825 attribute.tag_modified_fn = [checker]() {
826 if (checker->real_finish_fn) {
827 checker->real_finish_fn();
828 }
829 checker->finish_called = true;
830 };
831 }
832#endif
833 return attribute;
834}
835
837{
838 GAttributeWriter attribute = this->lookup_for_write(attribute_id);
839 if (attribute) {
840 return GSpanAttributeWriter{std::move(attribute), true};
841 }
842 return {};
843}
844
846 const StringRef attribute_id,
847 const AttrDomain domain,
848 const eCustomDataType data_type,
849 const AttributeInit &initializer)
850{
851 std::optional<AttributeMetaData> meta_data = this->lookup_meta_data(attribute_id);
852 if (meta_data.has_value()) {
853 if (meta_data->domain == domain && meta_data->data_type == data_type) {
854 return this->lookup_for_write(attribute_id);
855 }
856 return {};
857 }
858 if (this->add(attribute_id, domain, data_type, initializer)) {
859 return this->lookup_for_write(attribute_id);
860 }
861 return {};
862}
863
865 const StringRef attribute_id,
866 const AttrDomain domain,
867 const eCustomDataType data_type,
868 const AttributeInit &initializer)
869{
871 attribute_id, domain, data_type, initializer);
872 if (attribute) {
873 return GSpanAttributeWriter{std::move(attribute), true};
874 }
875 return {};
876}
877
879 const StringRef attribute_id, const AttrDomain domain, const eCustomDataType data_type)
880{
882 attribute_id, domain, data_type, AttributeInitConstruct());
883 if (attribute) {
884 return GSpanAttributeWriter{std::move(attribute), false};
885 }
886 return {};
887}
888
889bool MutableAttributeAccessor::rename(const StringRef old_attribute_id,
890 const StringRef new_attribute_id)
891{
892 if (old_attribute_id == new_attribute_id) {
893 return true;
894 }
895 if (this->contains(new_attribute_id)) {
896 return false;
897 }
898 const GAttributeReader old_attribute = this->lookup(old_attribute_id);
899 if (!old_attribute) {
900 return false;
901 }
902 const eCustomDataType type = cpp_type_to_custom_data_type(old_attribute.varray.type());
903 if (old_attribute.sharing_info != nullptr && old_attribute.varray.is_span()) {
904 if (!this->add(new_attribute_id,
905 old_attribute.domain,
906 type,
907 AttributeInitShared{old_attribute.varray.get_internal_span().data(),
908 *old_attribute.sharing_info}))
909 {
910 return false;
911 }
912 }
913 else {
914 if (!this->add(new_attribute_id,
915 old_attribute.domain,
916 type,
917 AttributeInitVArray{old_attribute.varray}))
918 {
919 return false;
920 }
921 }
922 this->remove(old_attribute_id);
923 return true;
924}
925
927{
928 if (function) {
929 auto validate_op = fn::FieldOperation::Create(*function, {field});
930 return fn::GField(validate_op);
931 }
932 return field;
933}
934
936 const AttributeAccessor src_attributes,
937 MutableAttributeAccessor dst_attributes,
938 const AttrDomainMask domain_mask,
939 const bke::AttributeFilter &attribute_filter)
940{
942 src_attributes.foreach_attribute([&](const AttributeIter &iter) {
943 if (!(ATTR_DOMAIN_AS_MASK(iter.domain) & domain_mask)) {
944 return;
945 }
946 if (iter.data_type == CD_PROP_STRING) {
947 return;
948 }
949 if (attribute_filter.allow_skip(iter.name)) {
950 return;
951 }
952 GVArray src = *iter.get();
954 iter.name, iter.domain, iter.data_type);
955 attributes.append({std::move(src), iter.name, {iter.domain, iter.data_type}, std::move(dst)});
956 });
957 return attributes;
958}
959
961
962void gather_attributes(const AttributeAccessor src_attributes,
963 const AttrDomain src_domain,
964 const AttrDomain dst_domain,
965 const AttributeFilter &attribute_filter,
966 const IndexMask &selection,
967 MutableAttributeAccessor dst_attributes)
968{
969 const int src_size = src_attributes.domain_size(src_domain);
970 src_attributes.foreach_attribute([&](const AttributeIter &iter) {
971 if (iter.domain != src_domain) {
972 return;
973 }
974 if (iter.data_type == CD_PROP_STRING) {
975 return;
976 }
977 if (attribute_filter.allow_skip(iter.name)) {
978 return;
979 }
980 const GAttributeReader src = iter.get(src_domain);
981 if (selection.size() == src_size && src.sharing_info && src.varray.is_span()) {
983 if (dst_attributes.add(iter.name, dst_domain, iter.data_type, init)) {
984 return;
985 }
986 }
988 iter.name, dst_domain, iter.data_type);
989 if (!dst) {
990 return;
991 }
992 array_utils::gather(src.varray, selection, dst.span);
993 dst.finish();
994 });
995}
996
997void gather_attributes(const AttributeAccessor src_attributes,
998 const AttrDomain src_domain,
999 const AttrDomain dst_domain,
1000 const AttributeFilter &attribute_filter,
1001 const Span<int> indices,
1002 MutableAttributeAccessor dst_attributes)
1003{
1004 if (array_utils::indices_are_range(indices, IndexRange(src_attributes.domain_size(src_domain))))
1005 {
1006 copy_attributes(src_attributes, src_domain, dst_domain, attribute_filter, dst_attributes);
1007 }
1008 else {
1009 src_attributes.foreach_attribute([&](const AttributeIter &iter) {
1010 if (iter.domain != src_domain) {
1011 return;
1012 }
1013 if (iter.data_type == CD_PROP_STRING) {
1014 return;
1015 }
1016 if (attribute_filter.allow_skip(iter.name)) {
1017 return;
1018 }
1019 const GAttributeReader src = iter.get(src_domain);
1021 iter.name, dst_domain, iter.data_type);
1022 if (!dst) {
1023 return;
1024 }
1026 dst.finish();
1027 });
1028 }
1029}
1030
1032 const AttrDomain src_domain,
1033 const AttrDomain dst_domain,
1034 const AttributeFilter &attribute_filter,
1035 const OffsetIndices<int> src_offsets,
1036 const OffsetIndices<int> dst_offsets,
1037 const IndexMask &selection,
1038 MutableAttributeAccessor dst_attributes)
1039{
1040 if (selection.size() == src_offsets.size()) {
1041 if (src_attributes.domain_size(src_domain) == dst_attributes.domain_size(src_domain)) {
1042 /* When all groups are selected and the domains are the same size, all values are copied,
1043 * because corresponding groups are required to be the same size. */
1044 copy_attributes(src_attributes, src_domain, dst_domain, attribute_filter, dst_attributes);
1045 return;
1046 }
1047 }
1048 src_attributes.foreach_attribute([&](const AttributeIter &iter) {
1049 if (iter.domain != src_domain) {
1050 return;
1051 }
1052 if (iter.data_type == CD_PROP_STRING) {
1053 return;
1054 }
1055 if (attribute_filter.allow_skip(iter.name)) {
1056 return;
1057 }
1058 const GVArraySpan src = *iter.get(src_domain);
1060 iter.name, dst_domain, iter.data_type);
1061 if (!dst) {
1062 return;
1063 }
1064 attribute_math::gather_group_to_group(src_offsets, dst_offsets, selection, src, dst.span);
1065 dst.finish();
1066 });
1067}
1068
1070 const AttrDomain src_domain,
1071 const AttrDomain dst_domain,
1072 const AttributeFilter &attribute_filter,
1073 const OffsetIndices<int> dst_offsets,
1074 const IndexMask &src_selection,
1075 MutableAttributeAccessor dst_attributes)
1076{
1077 src_attributes.foreach_attribute([&](const AttributeIter &iter) {
1078 if (iter.domain != src_domain) {
1079 return;
1080 }
1081 if (iter.data_type == CD_PROP_STRING) {
1082 return;
1083 }
1084 if (attribute_filter.allow_skip(iter.name)) {
1085 return;
1086 }
1087 const GVArraySpan src = *iter.get(src_domain);
1089 iter.name, dst_domain, iter.data_type);
1090 if (!dst) {
1091 return;
1092 }
1093 attribute_math::gather_to_groups(dst_offsets, src_selection, src, dst.span);
1094 dst.finish();
1095 });
1096}
1097
1098void copy_attributes(const AttributeAccessor src_attributes,
1099 const AttrDomain src_domain,
1100 const AttrDomain dst_domain,
1101 const AttributeFilter &attribute_filter,
1102 MutableAttributeAccessor dst_attributes)
1103{
1104 BLI_assert(src_attributes.domain_size(src_domain) == dst_attributes.domain_size(dst_domain));
1105 gather_attributes(src_attributes,
1106 src_domain,
1107 dst_domain,
1108 attribute_filter,
1109 IndexMask(src_attributes.domain_size(src_domain)),
1110 dst_attributes);
1111}
1112
1114 const AttrDomain src_domain,
1115 const AttrDomain dst_domain,
1116 const AttributeFilter &attribute_filter,
1117 const OffsetIndices<int> src_offsets,
1118 const OffsetIndices<int> dst_offsets,
1119 const IndexMask &selection,
1120 MutableAttributeAccessor dst_attributes)
1121{
1122 if (selection.is_empty()) {
1123 return;
1124 }
1125 src_attributes.foreach_attribute([&](const AttributeIter &iter) {
1126 if (iter.domain != src_domain) {
1127 return;
1128 }
1129 if (iter.data_type == CD_PROP_STRING) {
1130 return;
1131 }
1132 if (attribute_filter.allow_skip(iter.name)) {
1133 return;
1134 }
1135 const GVArraySpan src = *iter.get(src_domain);
1137 iter.name, dst_domain, iter.data_type);
1138 if (!dst) {
1139 return;
1140 }
1141 array_utils::copy_group_to_group(src_offsets, dst_offsets, selection, src, dst.span);
1142 dst.finish();
1143 });
1144}
1145
1147 const AttrDomain domain,
1148 const AttributeFilter &attribute_filter,
1149 const IndexRange range)
1150{
1151 attributes.foreach_attribute([&](const AttributeIter &iter) {
1152 if (iter.domain != domain) {
1153 return;
1154 }
1155 if (attribute_filter.allow_skip(iter.name)) {
1156 return;
1157 }
1158 if (iter.data_type == CD_PROP_STRING) {
1159 return;
1160 }
1161 GSpanAttributeWriter attribute = attributes.lookup_for_write_span(iter.name);
1162 const CPPType &type = attribute.span.type();
1163 GMutableSpan data = attribute.span.slice(range);
1164 type.fill_assign_n(type.default_value(), data.data(), data.size());
1165 attribute.finish();
1166 });
1167}
1168
1170 MutableAttributeAccessor &attributes)
1171{
1172 const GAttributeReader normals = attributes.lookup("custom_normal");
1173 if (!normals) {
1174 return;
1175 }
1176 if (!normals.varray.type().is<float3>()) {
1177 return;
1178 }
1179 if (normals.sharing_info->is_mutable()) {
1181 "custom_normal");
1183 normals.finish();
1184 }
1185 else {
1186 /* It's a bit faster to combine transforming and copying the attribute if it's shared. */
1187 float3 *new_data = MEM_malloc_arrayN<float3>(size_t(normals.varray.size()), __func__);
1190 {new_data, normals.varray.size()});
1191 const AttrDomain domain = normals.domain;
1192 attributes.remove("custom_normal");
1193 attributes.add<float3>("custom_normal", domain, AttributeInitMoveArray(new_data));
1194 }
1195}
1196
1197} // 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)
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
bool CustomData_free_layer(CustomData *data, eCustomDataType type, int index)
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)
#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)
bool CustomData_free_layer_named(CustomData *data, blender::StringRef name)
#define UV_EDGESEL_NAME
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
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_INT16_2D
@ CD_PROP_STRING
@ CD_PROP_FLOAT4X4
BMesh const char void * data
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
void init()
long long int int64_t
static const CPPType & get()
void fill_assign_n(const void *value, void *dst, int64_t n) const
bool is() const
const void * default_value() const
GMutableSpan slice(const int64_t start, int64_t size) const
const CPPType & type() 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
bool contains(StringRef attribute_id) 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
Set< StringRefNull > all_ids() 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
std::optional< AttributeMetaData > lookup_meta_data(StringRef attribute_id) const
const AttributeAccessorFunctions * fn_
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
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
GAttributeWriter lookup_or_add_for_write(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
GAttributeWriter lookup_for_write(StringRef attribute_id)
bool remove(const StringRef attribute_id)
bool add(const StringRef attribute_id, const AttrDomain domain, const eCustomDataType data_type, const AttributeInit &initializer)
bool rename(StringRef old_attribute_id, StringRef new_attribute_id)
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type)
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:242
static ushort indices[]
static float normals[][3]
#define GS(a)
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
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)
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 gather_group_to_group(OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, GSpan src, GMutableSpan dst)
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)
void copy_attributes(const AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, MutableAttributeAccessor dst_attributes)
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
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)
void transform_custom_normal_attribute(const float4x4 &transform, MutableAttributeAccessor &attributes)
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)
AttrType cpp_type_to_attribute_type(const CPPType &type)
const CPPType & attribute_type_to_cpp_type(AttrType type)
static int attribute_data_type_complexity(const eCustomDataType data_type)
QuaternionBase< float > Quaternion
void transform_normals(const float3x3 &transform, MutableSpan< float3 > normals)
MatBase< float, 4, 4 > float4x4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
MatBase< float, 3, 3 > float3x3
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:342
VecBase< float, 3 > float3
blender::VecBase< int16_t, 2 > short2
ColorSceneLinearByteEncoded4b< eAlpha::Premultiplied > ColorGeometry4b
Definition BLI_color.hh:343
static void update(bNodeTree *ntree)
static void init(bNodeTree *, bNode *node)
const ImplicitSharingInfoHandle * sharing_info
CustomDataLayer * layers
Definition DNA_ID.h:404
bool allow_skip(const StringRef name) const
const fn::multi_function::MultiFunction * function
fn::GField validate_field_if_necessary(const fn::GField &field) const
std::function< void()> real_finish_fn
const ImplicitSharingInfo * sharing_info
std::function< void()> tag_modified_fn
i
Definition text_draw.cc:230
#define N_(msgid)