Blender V5.0
attribute.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2006 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include <cstring>
12#include <optional>
13
14#include "MEM_guardedalloc.h"
15
16#include "DNA_ID.h"
17#include "DNA_curves_types.h"
19#include "DNA_mesh_types.h"
21
22#include "BLI_index_range.hh"
23#include "BLI_string.h"
24#include "BLI_string_utils.hh"
25
26#include "BLT_translation.hh"
27
28#include "BKE_attribute.hh"
30#include "BKE_curves.hh"
31#include "BKE_customdata.hh"
32#include "BKE_editmesh.hh"
33#include "BKE_grease_pencil.hh"
34#include "BKE_mesh.hh"
35#include "BKE_pointcloud.hh"
36#include "BKE_report.hh"
37
38#include <fmt/format.h>
39
43
45{
46 if (id == nullptr) {
47 return {};
48 }
49 switch (GS(id->name)) {
50 case ID_ME:
52 case ID_PT:
54 case ID_CV:
56 case ID_GP:
58 default:
59 return {};
60 }
61}
62
64{
65 return type_;
66}
67
69{
70 return ptr_ != nullptr;
71}
72
74{
75 BLI_assert(this->is_valid());
77 return reinterpret_cast<Mesh *>(ptr_);
78}
79
81{
82 BLI_assert(this->is_valid());
84 return reinterpret_cast<PointCloud *>(ptr_);
85}
86
88{
89 BLI_assert(this->is_valid());
91 return reinterpret_cast<Curves *>(ptr_);
92}
93
95{
96 BLI_assert(this->is_valid());
98 return reinterpret_cast<GreasePencil *>(ptr_);
99}
100
107
109{
110 switch (type_) {
112 return &this->get_mesh()->attribute_storage.wrap();
114 return &this->get_pointcloud()->attribute_storage.wrap();
116 return &this->get_curves()->geometry.attribute_storage.wrap();
118 return &this->get_grease_pencil()->attribute_storage.wrap();
121 }
122 BLI_assert(false);
123 return nullptr;
124}
125
126std::optional<blender::bke::MutableAttributeAccessor> AttributeOwner::get_accessor() const
127{
128 switch (type_) {
130 /* The attribute API isn't implemented for BMesh, so edit mode meshes are not supported. */
131 BLI_assert(this->get_mesh()->runtime->edit_mesh == nullptr);
132 return this->get_mesh()->attributes_for_write();
134 return this->get_pointcloud()->attributes_for_write();
136 return this->get_curves()->geometry.wrap().attributes_for_write();
138 return this->get_grease_pencil()->attributes_for_write();
140 return this->get_grease_pencil_drawing()->geometry.wrap().attributes_for_write();
141 }
142 BLI_assert(false);
143 return std::nullopt;
144}
145
148 int length = 0;
149};
150
151static std::array<DomainInfo, ATTR_DOMAIN_NUM> get_domains(const AttributeOwner &owner)
152{
153 std::array<DomainInfo, ATTR_DOMAIN_NUM> info;
154
155 switch (owner.type()) {
160 /* This should be implemented with #AttributeStorage instead. */
162 break;
163 }
165 Mesh *mesh = owner.get_mesh();
166 if (BMEditMesh *em = mesh->runtime->edit_mesh.get()) {
167 BMesh *bm = em->bm;
168 info[int(AttrDomain::Point)].customdata = &bm->vdata;
169 info[int(AttrDomain::Point)].length = bm->totvert;
170 info[int(AttrDomain::Edge)].customdata = &bm->edata;
171 info[int(AttrDomain::Edge)].length = bm->totedge;
172 info[int(AttrDomain::Corner)].customdata = &bm->ldata;
173 info[int(AttrDomain::Corner)].length = bm->totloop;
174 info[int(AttrDomain::Face)].customdata = &bm->pdata;
175 info[int(AttrDomain::Face)].length = bm->totface;
176 }
177 else {
178 info[int(AttrDomain::Point)].customdata = &mesh->vert_data;
179 info[int(AttrDomain::Point)].length = mesh->verts_num;
180 info[int(AttrDomain::Edge)].customdata = &mesh->edge_data;
181 info[int(AttrDomain::Edge)].length = mesh->edges_num;
182 info[int(AttrDomain::Corner)].customdata = &mesh->corner_data;
183 info[int(AttrDomain::Corner)].length = mesh->corners_num;
184 info[int(AttrDomain::Face)].customdata = &mesh->face_data;
185 info[int(AttrDomain::Face)].length = mesh->faces_num;
186 }
187 break;
188 }
189 }
190
191 return info;
192}
193
195 const StringRef old_name,
196 const StringRef new_name,
197 ReportList *reports)
198{
200 owner, old_name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL);
201 if (layer == nullptr) {
202 return false;
203 }
204 return BKE_attribute_rename(owner, old_name, new_name, reports);
205}
206
208 const blender::bke::AttributeAccessor attributes,
209 const StringRef name,
210 const AttrDomain domain,
211 const blender::bke::AttrType data_type,
212 ReportList *reports)
213{
214 if (const std::optional metadata = attributes.get_builtin_domain_and_type(name)) {
215 if (domain != metadata->domain) {
216 BKE_reportf(reports,
217 RPT_ERROR,
218 "Domain unsupported for \"%s\" attribute",
219 std::string(name).c_str());
220 return false;
221 }
222 if (data_type != metadata->data_type) {
224 reports, RPT_ERROR, "Type unsupported for \"%s\" attribute", std::string(name).c_str());
225 return false;
226 }
227 }
228 return true;
229}
230
231static bool mesh_attribute_valid(const Mesh &mesh,
232 const StringRef name,
233 const AttrDomain domain,
234 const blender::bke::AttrType data_type,
235 ReportList *reports)
236{
237 using namespace blender;
238 if (mesh.runtime->edit_mesh) {
240 BKE_report(reports, RPT_ERROR, "Unable to create attribute in edit mode");
241 return false;
242 }
243 }
244 if (!name_valid_for_builtin_domain_and_type(mesh.attributes(), name, domain, data_type, reports))
245 {
246 return false;
247 }
248 return true;
249}
250
252 const StringRef old_name,
253 const StringRef new_name,
254 ReportList *reports)
255{
256 using namespace blender;
257 if (BKE_attribute_required(owner, old_name)) {
258 BLI_assert_msg(0, "Required attribute name is not editable");
259 return false;
260 }
261 if (new_name.is_empty()) {
262 BKE_report(reports, RPT_ERROR, "Attribute name cannot be empty");
263 return false;
264 }
265
266 if (owner.type() != AttributeOwnerType::Mesh) {
267 bke::AttributeStorage &attributes = *owner.get_storage();
268 if (!attributes.lookup(old_name)) {
269 BKE_report(reports, RPT_ERROR, "Attribute is not part of this geometry");
270 return false;
271 }
272 attributes.rename(old_name, new_name);
273 return true;
274 }
275
276 /* NOTE: Checking if the new name matches the old name only makes sense when the name
277 * is clamped to it's maximum length, otherwise assigning an over-long name multiple times
278 * will add `.001` suffix unnecessarily. */
279 {
280 const int new_name_maxncpy = CustomData_name_maxncpy_calc(new_name);
281 /* NOTE: A function that performs a clamped comparison without copying would be handy here. */
282 char new_name_clamped[MAX_CUSTOMDATA_LAYER_NAME];
283 new_name.copy_utf8_truncated(new_name_clamped, new_name_maxncpy);
284 if (old_name == new_name_clamped) {
285 return false;
286 }
287 }
288
290 owner, old_name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL);
291 if (layer == nullptr) {
292 BKE_report(reports, RPT_ERROR, "Attribute is not part of this geometry");
293 return false;
294 }
295
296 if (owner.type() == AttributeOwnerType::Mesh) {
297 Mesh *mesh = owner.get_mesh();
298 if (!mesh_attribute_valid(*mesh,
299 new_name,
300 BKE_attribute_domain(owner, layer),
302 reports))
303 {
304 return false;
305 }
306 }
307 else if (owner.type() == AttributeOwnerType::Curves) {
308 Curves *curves = owner.get_curves();
310 curves->geometry.wrap().attributes(),
311 new_name,
312 BKE_attribute_domain(owner, layer),
314 reports))
315 {
316 return false;
317 }
318 }
319
320 std::string result_name = BKE_attribute_calc_unique_name(owner, new_name);
321
322 if (layer->type == CD_PROP_FLOAT2 && owner.type() == AttributeOwnerType::Mesh) {
323 /* Rename UV sub-attributes. */
324 char buffer_src[MAX_CUSTOMDATA_LAYER_NAME];
325 char buffer_dst[MAX_CUSTOMDATA_LAYER_NAME];
326
328 BKE_uv_map_pin_name_get(layer->name, buffer_src),
329 BKE_uv_map_pin_name_get(result_name, buffer_dst),
330 reports);
331 }
332
333 if (owner.type() == AttributeOwnerType::Mesh) {
334 Mesh *mesh = owner.get_mesh();
335 if (old_name == BKE_id_attributes_active_color_name(&mesh->id)) {
336 BKE_id_attributes_active_color_set(&mesh->id, result_name);
337 }
338 if (old_name == BKE_id_attributes_default_color_name(&mesh->id)) {
339 BKE_id_attributes_default_color_set(&mesh->id, result_name);
340 }
341 }
342
343 StringRef(result_name).copy_utf8_truncated(layer->name);
344
345 return true;
346}
347
348static bool attribute_name_exists(const AttributeOwner &owner, const StringRef name)
349{
350 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
351
352 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
353 if (!info[domain].customdata) {
354 continue;
355 }
356
357 const CustomData *cdata = info[domain].customdata;
358 for (int i = 0; i < cdata->totlayer; i++) {
359 const CustomDataLayer *layer = cdata->layers + i;
360
361 if (layer->name == name) {
362 return true;
363 }
364 }
365 }
366
367 return false;
368}
369
371{
372 if (owner.type() != AttributeOwnerType::Mesh) {
374 return storage.unique_name_calc(name);
375 }
376 return BLI_uniquename_cb(
377 [&](const StringRef new_name) { return attribute_name_exists(owner, new_name); },
378 '.',
379 name.is_empty() ? DATA_("Attribute") : name);
380}
381
383 const StringRef name,
384 const eCustomDataType type,
385 const AttrDomain domain,
386 ReportList *reports)
387{
388 using namespace blender::bke;
389 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
390
391 CustomData *customdata = info[int(domain)].customdata;
392 if (customdata == nullptr) {
393 BKE_report(reports, RPT_ERROR, "Attribute domain not supported by this geometry type");
394 return nullptr;
395 }
396
397 std::string uniquename = BKE_attribute_calc_unique_name(owner, name);
398
399 if (owner.type() == AttributeOwnerType::Mesh) {
400 Mesh *mesh = owner.get_mesh();
401 if (BMEditMesh *em = mesh->runtime->edit_mesh.get()) {
403 *mesh, name, domain, *custom_data_type_to_attr_type(type), reports))
404 {
405 return nullptr;
406 }
407 BM_data_layer_add_named(em->bm, customdata, type, uniquename.c_str());
408 const int index = CustomData_get_named_layer_index(customdata, type, uniquename);
409 return (index == -1) ? nullptr : &(customdata->layers[index]);
410 }
411 }
412
413 std::optional<MutableAttributeAccessor> attributes = owner.get_accessor();
414 if (!attributes) {
415 return nullptr;
416 }
417
418 attributes->add(
419 uniquename, domain, *custom_data_type_to_attr_type(type), AttributeInitDefaultValue());
420
421 const int index = CustomData_get_named_layer_index(customdata, type, uniquename);
422 if (index == -1) {
423 BKE_reportf(reports, RPT_WARNING, "Layer '%s' could not be created", uniquename.c_str());
424 }
425
426 return (index == -1) ? nullptr : &(customdata->layers[index]);
427}
428
430 const StringRef srcname,
431 const StringRef dstname)
432{
433 using namespace blender::bke;
434
435 std::optional<MutableAttributeAccessor> attributes = owner.get_accessor();
436 if (!attributes) {
437 return;
438 }
439
440 GAttributeReader src = attributes->lookup(srcname);
441 if (!src) {
442 return;
443 }
444
445 attributes->add(dstname,
446 src.domain,
449}
450
452 const StringRef name,
453 ReportList *reports)
454{
455 using namespace blender::bke;
456 std::string uniquename = BKE_attribute_calc_unique_name(owner, name);
457
458 if (owner.type() == AttributeOwnerType::Mesh) {
459 Mesh *mesh = owner.get_mesh();
460 if (mesh->runtime->edit_mesh) {
462 return nullptr;
463 }
464 }
465
466 std::optional<MutableAttributeAccessor> attributes = owner.get_accessor();
467 if (!attributes) {
468 return nullptr;
469 }
470
471 GAttributeReader src = attributes->lookup(name);
472 if (!src) {
473 BKE_report(reports, RPT_ERROR, "Attribute is not part of this geometry");
474 return nullptr;
475 }
476
478 attributes->add(uniquename, src.domain, type, AttributeInitVArray(src.varray));
479
480 if (owner.type() == AttributeOwnerType::Mesh && type == AttrType::Float2) {
481 /* Duplicate UV sub-attributes. */
482 char buffer_src[MAX_CUSTOMDATA_LAYER_NAME];
483 char buffer_dst[MAX_CUSTOMDATA_LAYER_NAME];
485 BKE_uv_map_pin_name_get(name, buffer_src),
486 BKE_uv_map_pin_name_get(uniquename, buffer_dst));
487 }
488
490}
491
498
499static int color_clamp_index(AttributeOwner &owner, int index)
500{
502 return min_ii(index, length - 1);
503}
504
506{
509 return layer ? layer->name : "";
510}
511
513{
514 using namespace blender;
515 using namespace blender::bke;
516 if (name.is_empty()) {
517 BKE_report(reports, RPT_ERROR, "The attribute name must not be empty");
518 return false;
519 }
520 if (BKE_attribute_required(owner, name)) {
521 BKE_report(reports, RPT_ERROR, "Attribute is required and cannot be removed");
522 return false;
523 }
524
525 if (owner.type() == AttributeOwnerType::Mesh) {
526 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
527 Mesh *mesh = owner.get_mesh();
528 if (BMEditMesh *em = mesh->runtime->edit_mesh.get()) {
529 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
530 if (CustomData *data = info[domain].customdata) {
531 const std::string name_copy = name;
532 const int layer_index = CustomData_get_named_layer_index_notype(data, name_copy);
533 if (layer_index == -1) {
534 continue;
535 }
536
537 const eCustomDataType type = eCustomDataType(data->layers[layer_index].type);
538 const bool is_active_color_attribute = name_copy.c_str() ==
539 StringRef(mesh->active_color_attribute);
540 const bool is_default_color_attribute = name_copy.c_str() ==
541 StringRef(mesh->default_color_attribute);
542 const int active_color_index = color_name_to_index(owner, mesh->active_color_attribute);
543 const int default_color_index = color_name_to_index(owner,
544 mesh->default_color_attribute);
545
546 if (!BM_data_layer_free_named(em->bm, data, name_copy.c_str())) {
548 }
549
550 if (is_active_color_attribute) {
552 &mesh->id,
553 color_name_from_index(owner, color_clamp_index(owner, active_color_index)));
554 }
555 if (is_default_color_attribute) {
557 &mesh->id,
558 color_name_from_index(owner, color_clamp_index(owner, default_color_index)));
559 }
560
561 if (type == CD_PROP_FLOAT2 && domain == int(AttrDomain::Corner)) {
562 char buffer[MAX_CUSTOMDATA_LAYER_NAME];
563 BM_data_layer_free_named(em->bm, data, BKE_uv_map_pin_name_get(name_copy, buffer));
564 }
565 return true;
566 }
567 }
568 return false;
569 }
570 }
571
572 std::optional<MutableAttributeAccessor> attributes = owner.get_accessor();
573 if (!attributes) {
574 return false;
575 }
576
577 if (owner.type() == AttributeOwnerType::Mesh) {
578 const std::string name_copy = name;
579 std::optional<blender::bke::AttributeMetaData> metadata = attributes->lookup_meta_data(
580 name_copy);
581 if (!metadata) {
582 return false;
583 }
584 /* Update active and default color attributes. */
585 Mesh *mesh = owner.get_mesh();
586 const bool is_active_color_attribute = name_copy == StringRef(mesh->active_color_attribute);
587 const bool is_default_color_attribute = name_copy == StringRef(mesh->default_color_attribute);
588 const int active_color_index = color_name_to_index(owner, mesh->active_color_attribute);
589 const int default_color_index = color_name_to_index(owner, mesh->default_color_attribute);
590
591 if (!attributes->remove(name_copy)) {
593 }
594
595 if (is_active_color_attribute) {
597 &mesh->id, color_name_from_index(owner, color_clamp_index(owner, active_color_index)));
598 }
599 if (is_default_color_attribute) {
601 &mesh->id, color_name_from_index(owner, color_clamp_index(owner, default_color_index)));
602 }
603
604 if (metadata->data_type == AttrType::Float2 && metadata->domain == AttrDomain::Corner) {
605 char buffer[MAX_CUSTOMDATA_LAYER_NAME];
606 attributes->remove(BKE_uv_map_pin_name_get(name_copy, buffer));
607 }
608 return true;
609 }
610
611 return attributes->remove(name);
612}
613
615 const StringRef name,
616 const eCustomDataType type,
617 const AttrDomain domain)
618{
619 if (name.is_empty()) {
620 return nullptr;
621 }
622 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
623
624 CustomData *customdata = info[int(domain)].customdata;
625 if (customdata == nullptr) {
626 return nullptr;
627 }
628
629 for (int i = 0; i < customdata->totlayer; i++) {
630 CustomDataLayer *layer = &customdata->layers[i];
631 if (layer->type == type && layer->name == name) {
632 return layer;
633 }
634 }
635
636 return nullptr;
637}
638
640 const StringRef name,
641 const eCustomDataMask type_mask,
642 const AttrDomainMask domain_mask)
643{
644 if (name.is_empty()) {
645 return nullptr;
646 }
647 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
648
649 for (AttrDomain domain = AttrDomain::Point; int(domain) < ATTR_DOMAIN_NUM;
650 domain = AttrDomain(int(domain) + 1))
651 {
652 if (!(domain_mask & ATTR_DOMAIN_AS_MASK(domain))) {
653 continue;
654 }
655
656 CustomData *customdata = info[int(domain)].customdata;
657 if (customdata == nullptr) {
658 continue;
659 }
660
661 for (int i = 0; i < customdata->totlayer; i++) {
662 CustomDataLayer *layer = &customdata->layers[i];
663 if ((CD_TYPE_AS_MASK(eCustomDataType(layer->type)) & type_mask) && layer->name == name) {
664 return layer;
665 }
666 }
667 }
668
669 return nullptr;
670}
671
673 const StringRef name,
674 const eCustomDataMask type_mask,
675 const AttrDomainMask domain_mask)
676{
677 /* Reuse the implementation of the const version.
678 * Implicit sharing for the layer's data is handled below. */
679 CustomDataLayer *layer = const_cast<CustomDataLayer *>(
680 BKE_attribute_search(owner, name, type_mask, domain_mask));
681 if (!layer) {
682 return nullptr;
683 }
684
685 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
686
687 const AttrDomain domain = BKE_attribute_domain(owner, layer);
688 CustomData_ensure_data_is_mutable(layer, info[int(domain)].length);
689
690 return layer;
691}
692
694 AttrDomainMask domain_mask,
695 eCustomDataMask mask)
696{
697 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
698
699 int length = 0;
700
701 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
702 const CustomData *customdata = info[domain].customdata;
703 if (customdata == nullptr) {
704 continue;
705 }
706
707 if ((1 << int(domain)) & domain_mask) {
709 }
710 }
711
712 return length;
713}
714
716{
717 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
718
719 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
720 const CustomData *customdata = info[domain].customdata;
721 if (customdata == nullptr) {
722 continue;
723 }
724 if (blender::Span(customdata->layers, customdata->totlayer).contains_ptr(layer)) {
725 return AttrDomain(domain);
726 }
727 }
728
729 BLI_assert_msg(0, "Custom data layer not found in geometry");
731}
732
733int BKE_attribute_domain_size(const AttributeOwner &owner, const int domain)
734{
735 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
736 return info[domain].length;
737}
738
740{
741 /* When in mesh editmode, attributes point to bmesh customdata layers, the attribute data is
742 * empty since custom data is stored per element instead of a single array there (same es UVs
743 * etc.), see D11998. */
744 if (owner.type() == AttributeOwnerType::Mesh) {
745 Mesh *mesh = owner.get_mesh();
746 if (mesh->runtime->edit_mesh != nullptr) {
747 return 0;
748 }
749 }
750
751 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
752
753 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
754 const CustomData *customdata = info[domain].customdata;
755 if (customdata == nullptr) {
756 continue;
757 }
758 if (blender::Span(customdata->layers, customdata->totlayer).contains_ptr(layer)) {
759 return info[domain].length;
760 }
761 }
762
763 BLI_assert_msg(0, "Custom data layer not found in geometry");
764 return 0;
765}
766
783
784std::optional<blender::StringRefNull> BKE_attributes_active_name_get(AttributeOwner &owner)
785{
786 using namespace blender;
787 using namespace blender::bke;
788 int active_index = *BKE_attributes_active_index_p(owner);
789 if (active_index == -1) {
790 return std::nullopt;
791 }
792 if (owner.type() != AttributeOwnerType::Mesh) {
793 bke::AttributeStorage &storage = *owner.get_storage();
794 if (!IndexRange(storage.count()).contains(active_index)) {
795 return std::nullopt;
796 }
797 return storage.at_index(active_index).name();
798 }
799
800 if (active_index > BKE_attributes_length(owner, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL)) {
801 active_index = 0;
802 }
803
804 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
805
806 int index = 0;
807
808 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
809 CustomData *customdata = info[domain].customdata;
810 if (customdata == nullptr) {
811 continue;
812 }
813 for (int i = 0; i < customdata->totlayer; i++) {
814 CustomDataLayer *layer = &customdata->layers[i];
816 if (index == active_index) {
818 return layer->name;
819 }
820 return std::nullopt;
821 }
822 index++;
823 }
824 }
825 }
826
827 return std::nullopt;
828}
829
831{
832 using namespace blender;
833 if (owner.type() != AttributeOwnerType::Mesh) {
834 bke::AttributeStorage &attributes = *owner.get_storage();
835 *BKE_attributes_active_index_p(owner) = attributes.index_of(name);
836 return;
837 }
838
841 BLI_assert(layer != nullptr);
842
843 const int index = BKE_attribute_to_index(owner, layer, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL);
844 *BKE_attributes_active_index_p(owner) = index;
845}
846
851
853{
854 switch (owner.type()) {
857 }
859 return &owner.get_mesh()->attributes_active_index;
860 }
863 }
866 }
869 }
870 }
871 return nullptr;
872}
873
875{
876 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
877
878 bool use_next = (layers == nullptr);
879
880 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
881 CustomData *customdata = info[domain].customdata;
882 if (customdata == nullptr) {
883 continue;
884 }
885 if (customdata->layers && customdata->totlayer) {
886 if (customdata->layers == layers) {
887 use_next = true;
888 }
889 else if (use_next) {
890 return customdata;
891 }
892 }
893 }
894
895 return nullptr;
896}
897
899 int lookup_index,
900 AttrDomainMask domain_mask,
901 eCustomDataMask layer_mask)
902{
903 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
904
905 int index = 0;
906 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
907 CustomData *customdata = info[domain].customdata;
908
909 if (!customdata || !((1 << int(domain)) & domain_mask)) {
910 continue;
911 }
912
913 for (int i = 0; i < customdata->totlayer; i++) {
914 if (!(layer_mask & CD_TYPE_AS_MASK(eCustomDataType(customdata->layers[i].type))) ||
915 (customdata->layers[i].flag & CD_FLAG_TEMPORARY))
916 {
917 continue;
918 }
919
920 if (index == lookup_index) {
921 return customdata->layers + i;
922 }
923
924 index++;
925 }
926 }
927
928 return nullptr;
929}
930
932 const CustomDataLayer *layer,
933 AttrDomainMask domain_mask,
934 eCustomDataMask layer_mask)
935{
936 if (!layer) {
937 return -1;
938 }
939
940 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
941
942 int index = 0;
943 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
944 const CustomData *customdata = info[domain].customdata;
945
946 if (!customdata || !((1 << int(domain)) & domain_mask)) {
947 continue;
948 }
949
950 for (int i = 0; i < customdata->totlayer; i++) {
951 const CustomDataLayer *layer_iter = customdata->layers + i;
952 if (!(layer_mask & CD_TYPE_AS_MASK(eCustomDataType(layer_iter->type))) ||
953 (layer_iter->flag & CD_FLAG_TEMPORARY))
954 {
955 continue;
956 }
957
958 if (layer == layer_iter) {
959 return index;
960 }
961
962 index++;
963 }
964 }
965
966 return -1;
967}
968
969std::optional<StringRef> BKE_id_attributes_active_color_name(const ID *id)
970{
971 if (GS(id->name) == ID_ME) {
972 return reinterpret_cast<const Mesh *>(id)->active_color_attribute;
973 }
974 return std::nullopt;
975}
976
977std::optional<StringRef> BKE_id_attributes_default_color_name(const ID *id)
978{
979 if (GS(id->name) == ID_ME) {
980 return reinterpret_cast<const Mesh *>(id)->default_color_attribute;
981 }
982 return std::nullopt;
983}
984
985void BKE_id_attributes_active_color_set(ID *id, const std::optional<StringRef> name)
986{
987 switch (GS(id->name)) {
988 case ID_ME: {
989 Mesh *mesh = reinterpret_cast<Mesh *>(id);
990 MEM_SAFE_FREE(mesh->active_color_attribute);
991 if (name) {
992 mesh->active_color_attribute = BLI_strdupn(name->data(), name->size());
993 }
994 break;
995 }
996 default:
997 break;
998 }
999}
1000
1002{
1003 switch (GS(id->name)) {
1004 case ID_ME: {
1005 Mesh *mesh = reinterpret_cast<Mesh *>(id);
1006 MEM_SAFE_FREE(mesh->active_color_attribute);
1007 break;
1008 }
1009 default:
1010 break;
1011 }
1012}
1013
1014void BKE_id_attributes_default_color_set(ID *id, const std::optional<StringRef> name)
1015{
1016 switch (GS(id->name)) {
1017 case ID_ME: {
1018 Mesh *mesh = reinterpret_cast<Mesh *>(id);
1019 MEM_SAFE_FREE(mesh->default_color_attribute);
1020 if (name) {
1021 mesh->default_color_attribute = BLI_strdupn(name->data(), name->size());
1022 }
1023 break;
1024 }
1025 default:
1026 break;
1027 }
1028}
1029
1035
1037{
1038 std::optional<blender::bke::AttributeMetaData> meta_data = mesh.attributes().lookup_meta_data(
1039 name);
1040
1041 if (!meta_data) {
1042 return false;
1043 }
1044 if (!(ATTR_DOMAIN_AS_MASK(meta_data->domain) & ATTR_DOMAIN_MASK_COLOR) ||
1046 {
1047 return false;
1048 }
1049 return true;
1050}
1051
1052StringRef BKE_uv_map_pin_name_get(const StringRef uv_map_name, char *buffer)
1053{
1054 BLI_assert(strlen(UV_PINNED_NAME) == 2);
1055 BLI_assert(uv_map_name.size() < MAX_CUSTOMDATA_LAYER_NAME - 4);
1056 const auto result = fmt::format_to_n(
1057 buffer, MAX_CUSTOMDATA_LAYER_NAME, ".{}.{}", UV_PINNED_NAME, uv_map_name);
1058 return StringRef(buffer, result.size);
1059}
blender::StringRef BKE_uv_map_pin_name_get(blender::StringRef uv_map_name, char *buffer)
blender::bke::AttrDomain BKE_attribute_domain(const AttributeOwner &owner, const struct CustomDataLayer *layer)
AttrDomainMask
@ ATTR_DOMAIN_MASK_ALL
AttributeOwnerType
#define ATTR_DOMAIN_MASK_COLOR
#define ATTR_DOMAIN_AS_MASK(domain)
#define ATTR_DOMAIN_NUM
bool BKE_curves_attribute_required(const struct Curves *curves, blender::StringRef name)
Low-level operations for curves.
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_named_layer_index_notype(const CustomData *data, blender::StringRef name)
eCustomDataMask CD_TYPE_AS_MASK(eCustomDataType type)
int CustomData_number_of_layers_typemask(const CustomData *data, eCustomDataMask mask)
int CustomData_get_named_layer_index(const CustomData *data, eCustomDataType type, blender::StringRef name)
int CustomData_name_maxncpy_calc(blender::StringRef name)
#define UV_PINNED_NAME
void CustomData_ensure_data_is_mutable(CustomDataLayer *layer, int totelem)
Low-level operations for grease pencil.
bool BKE_grease_pencil_drawing_attribute_required(const GreasePencilDrawing *, blender::StringRef name)
bool BKE_mesh_attribute_required(blender::StringRef name)
General operations for point clouds.
bool BKE_pointcloud_attribute_required(const PointCloud *pointcloud, blender::StringRef name)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
MINLINE int min_ii(int a, int b)
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.cc:30
size_t void BLI_uniquename_cb(blender::FunctionRef< bool(blender::StringRefNull)> unique_check, const char *defname, char delim, char *name, size_t name_maxncpy) ATTR_NONNULL(2
#define DATA_(msgid)
ID and Library types, which are fundamental for SDNA.
@ ID_CV
@ ID_ME
@ ID_GP
@ ID_PT
#define MAX_CUSTOMDATA_LAYER_NAME
#define CD_MASK_PROP_ALL
#define CD_MASK_COLOR_ALL
@ CD_PROP_FLOAT2
@ CD_FLAG_TEMPORARY
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
bool BKE_color_attribute_supported(const Mesh &mesh, const StringRef name)
static int color_name_to_index(AttributeOwner &owner, const StringRef name)
Definition attribute.cc:492
std::optional< StringRef > BKE_id_attributes_active_color_name(const ID *id)
Definition attribute.cc:969
bool BKE_attribute_rename(AttributeOwner &owner, const StringRef old_name, const StringRef new_name, ReportList *reports)
Definition attribute.cc:251
std::optional< blender::StringRefNull > BKE_attributes_active_name_get(AttributeOwner &owner)
Definition attribute.cc:784
CustomData * BKE_attributes_iterator_next_domain(AttributeOwner &owner, CustomDataLayer *layers)
Definition attribute.cc:874
static int color_clamp_index(AttributeOwner &owner, int index)
Definition attribute.cc:499
int BKE_attribute_to_index(const AttributeOwner &owner, const CustomDataLayer *layer, AttrDomainMask domain_mask, eCustomDataMask layer_mask)
Definition attribute.cc:931
CustomDataLayer * BKE_attribute_new(AttributeOwner &owner, const StringRef name, const eCustomDataType type, const AttrDomain domain, ReportList *reports)
Definition attribute.cc:382
AttrDomain BKE_attribute_domain(const AttributeOwner &owner, const CustomDataLayer *layer)
Definition attribute.cc:715
StringRef BKE_uv_map_pin_name_get(const StringRef uv_map_name, char *buffer)
CustomDataLayer * BKE_attribute_search_for_write(AttributeOwner &owner, const StringRef name, const eCustomDataMask type_mask, const AttrDomainMask domain_mask)
Definition attribute.cc:672
static void bke_attribute_copy_if_exists(AttributeOwner &owner, const StringRef srcname, const StringRef dstname)
Definition attribute.cc:429
const CustomDataLayer * BKE_attribute_search(const AttributeOwner &owner, const StringRef name, const eCustomDataMask type_mask, const AttrDomainMask domain_mask)
Definition attribute.cc:639
void BKE_id_attributes_default_color_set(ID *id, const std::optional< StringRef > name)
CustomDataLayer * BKE_attribute_from_index(AttributeOwner &owner, int lookup_index, AttrDomainMask domain_mask, eCustomDataMask layer_mask)
Definition attribute.cc:898
void BKE_attributes_active_set(AttributeOwner &owner, const StringRef name)
Definition attribute.cc:830
static bool attribute_name_exists(const AttributeOwner &owner, const StringRef name)
Definition attribute.cc:348
int * BKE_attributes_active_index_p(AttributeOwner &owner)
Definition attribute.cc:852
bool BKE_attribute_remove(AttributeOwner &owner, const StringRef name, ReportList *reports)
Definition attribute.cc:512
std::string BKE_attribute_calc_unique_name(const AttributeOwner &owner, const StringRef name)
Definition attribute.cc:370
CustomDataLayer * BKE_attribute_duplicate(AttributeOwner &owner, const StringRef name, ReportList *reports)
Definition attribute.cc:451
void BKE_id_attributes_active_color_set(ID *id, const std::optional< StringRef > name)
Definition attribute.cc:985
const CustomDataLayer * BKE_id_attributes_color_find(const ID *id, const StringRef name)
void BKE_id_attributes_active_color_clear(ID *id)
static bool mesh_attribute_valid(const Mesh &mesh, const StringRef name, const AttrDomain domain, const blender::bke::AttrType data_type, ReportList *reports)
Definition attribute.cc:231
static StringRef color_name_from_index(AttributeOwner &owner, int index)
Definition attribute.cc:505
static bool bke_attribute_rename_if_exists(AttributeOwner &owner, const StringRef old_name, const StringRef new_name, ReportList *reports)
Definition attribute.cc:194
CustomDataLayer * BKE_attribute_find(const AttributeOwner &owner, const StringRef name, const eCustomDataType type, const AttrDomain domain)
Definition attribute.cc:614
std::optional< StringRef > BKE_id_attributes_default_color_name(const ID *id)
Definition attribute.cc:977
static std::array< DomainInfo, ATTR_DOMAIN_NUM > get_domains(const AttributeOwner &owner)
Definition attribute.cc:151
bool BKE_attribute_required(const AttributeOwner &owner, const StringRef name)
Definition attribute.cc:767
int BKE_attribute_data_length(AttributeOwner &owner, CustomDataLayer *layer)
Definition attribute.cc:739
int BKE_attributes_length(const AttributeOwner &owner, AttrDomainMask domain_mask, eCustomDataMask mask)
Definition attribute.cc:693
static bool name_valid_for_builtin_domain_and_type(const blender::bke::AttributeAccessor attributes, const StringRef name, const AttrDomain domain, const blender::bke::AttrType data_type, ReportList *reports)
Definition attribute.cc:207
int BKE_attribute_domain_size(const AttributeOwner &owner, const int domain)
Definition attribute.cc:733
void BKE_attributes_active_clear(AttributeOwner &owner)
Definition attribute.cc:847
bool BM_data_layer_free_named(BMesh *bm, CustomData *data, StringRef name)
void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const StringRef name)
BMesh const char void * data
BMesh * bm
bool BM_attribute_stored_in_bmesh_builtin(const StringRef name)
AttributeOwnerType type() const
Definition attribute.cc:63
std::optional< blender::bke::MutableAttributeAccessor > get_accessor() const
Definition attribute.cc:126
static AttributeOwner from_id(ID *id)
Definition attribute.cc:44
bool is_valid() const
Definition attribute.cc:68
Mesh * get_mesh() const
Definition attribute.cc:73
GreasePencil * get_grease_pencil() const
Definition attribute.cc:94
PointCloud * get_pointcloud() const
Definition attribute.cc:80
Curves * get_curves() const
Definition attribute.cc:87
GreasePencilDrawing * get_grease_pencil_drawing() const
Definition attribute.cc:101
blender::bke::AttributeStorage * get_storage() const
Definition attribute.cc:108
AttributeSet attributes
constexpr bool contains(int64_t value) const
constexpr bool contains_ptr(const T *ptr) const
Definition BLI_span.hh:291
constexpr bool is_empty() const
constexpr int64_t size() const
void copy_utf8_truncated(char *dst, int64_t dst_size) const
Definition string_ref.cc:28
std::optional< AttributeDomainAndType > get_builtin_domain_and_type(const StringRef name) const
void rename(StringRef old_name, std::string new_name)
Attribute * lookup(StringRef name)
std::string unique_name_calc(StringRef name) const
int index_of(StringRef name) const
#define GS(x)
float length(VecOp< float, D >) RET
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
std::optional< eCustomDataType > attr_type_to_custom_data_type(AttrType attr_type)
bool allow_procedural_attribute_access(StringRef attribute_name)
std::optional< AttrType > custom_data_type_to_attr_type(eCustomDataType data_type)
AttrType cpp_type_to_attribute_type(const CPPType &type)
const char * name
struct AttributeStorage attribute_storage
CurvesGeometry geometry
CustomDataLayer * layers
CustomData * customdata
Definition attribute.cc:147
struct AttributeStorage attribute_storage
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
int corners_num
CustomData edge_data
struct AttributeStorage attribute_storage
int edges_num
MeshRuntimeHandle * runtime
CustomData corner_data
CustomData face_data
CustomData vert_data
int attributes_active_index
int faces_num
int verts_num
struct AttributeStorage attribute_storage
i
Definition text_draw.cc:230