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