Blender V4.5
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"
29#include "BKE_curves.hh"
30#include "BKE_customdata.hh"
31#include "BKE_editmesh.hh"
32#include "BKE_grease_pencil.hh"
33#include "BKE_mesh.hh"
34#include "BKE_pointcloud.hh"
35#include "BKE_report.hh"
36
37#include <fmt/format.h>
38
42
44{
45 if (id == nullptr) {
46 return {};
47 }
48 switch (GS(id->name)) {
49 case ID_ME:
51 case ID_PT:
53 case ID_CV:
55 case ID_GP:
57 default:
58 return {};
59 }
60}
61
63{
64 return type_;
65}
66
68{
69 return ptr_ != nullptr;
70}
71
73{
74 BLI_assert(this->is_valid());
76 return reinterpret_cast<Mesh *>(ptr_);
77}
78
80{
81 BLI_assert(this->is_valid());
83 return reinterpret_cast<PointCloud *>(ptr_);
84}
85
87{
88 BLI_assert(this->is_valid());
90 return reinterpret_cast<Curves *>(ptr_);
91}
92
94{
95 BLI_assert(this->is_valid());
97 return reinterpret_cast<GreasePencil *>(ptr_);
98}
99
106
109 int length = 0;
110};
111
112static std::array<DomainInfo, ATTR_DOMAIN_NUM> get_domains(const AttributeOwner &owner)
113{
114 std::array<DomainInfo, ATTR_DOMAIN_NUM> info;
115
116 switch (owner.type()) {
118 PointCloud *pointcloud = owner.get_pointcloud();
119 info[int(AttrDomain::Point)].customdata = &pointcloud->pdata;
120 info[int(AttrDomain::Point)].length = pointcloud->totpoint;
121 break;
122 }
124 Mesh *mesh = owner.get_mesh();
125 if (BMEditMesh *em = mesh->runtime->edit_mesh.get()) {
126 BMesh *bm = em->bm;
127 info[int(AttrDomain::Point)].customdata = &bm->vdata;
128 info[int(AttrDomain::Point)].length = bm->totvert;
129 info[int(AttrDomain::Edge)].customdata = &bm->edata;
130 info[int(AttrDomain::Edge)].length = bm->totedge;
131 info[int(AttrDomain::Corner)].customdata = &bm->ldata;
132 info[int(AttrDomain::Corner)].length = bm->totloop;
133 info[int(AttrDomain::Face)].customdata = &bm->pdata;
134 info[int(AttrDomain::Face)].length = bm->totface;
135 }
136 else {
137 info[int(AttrDomain::Point)].customdata = &mesh->vert_data;
138 info[int(AttrDomain::Point)].length = mesh->verts_num;
139 info[int(AttrDomain::Edge)].customdata = &mesh->edge_data;
140 info[int(AttrDomain::Edge)].length = mesh->edges_num;
141 info[int(AttrDomain::Corner)].customdata = &mesh->corner_data;
142 info[int(AttrDomain::Corner)].length = mesh->corners_num;
143 info[int(AttrDomain::Face)].customdata = &mesh->face_data;
144 info[int(AttrDomain::Face)].length = mesh->faces_num;
145 }
146 break;
147 }
149 Curves *curves = owner.get_curves();
150 info[int(AttrDomain::Point)].customdata = &curves->geometry.point_data;
151 info[int(AttrDomain::Point)].length = curves->geometry.point_num;
152 info[int(AttrDomain::Curve)].customdata = &curves->geometry.curve_data;
153 info[int(AttrDomain::Curve)].length = curves->geometry.curve_num;
154 break;
155 }
157 GreasePencil *grease_pencil = owner.get_grease_pencil();
158 info[int(AttrDomain::Layer)].customdata = &grease_pencil->layers_data;
159 info[int(AttrDomain::Layer)].length = grease_pencil->layers().size();
160 break;
161 }
164 info[int(AttrDomain::Point)].customdata = &drawing.geometry.point_data;
165 info[int(AttrDomain::Point)].length = drawing.geometry.point_num;
166 info[int(AttrDomain::Curve)].customdata = &drawing.geometry.curve_data;
167 info[int(AttrDomain::Curve)].length = drawing.geometry.curve_num;
168 break;
169 }
170 }
171
172 return info;
173}
174
175namespace blender::bke {
176
177static std::optional<blender::bke::MutableAttributeAccessor> get_attribute_accessor_for_write(
178 AttributeOwner &owner)
179{
180 switch (owner.type()) {
182 Mesh &mesh = *owner.get_mesh();
183 /* The attribute API isn't implemented for BMesh, so edit mode meshes are not supported. */
184 BLI_assert(mesh.runtime->edit_mesh == nullptr);
185 return mesh.attributes_for_write();
186 }
188 PointCloud &pointcloud = *owner.get_pointcloud();
189 return pointcloud.attributes_for_write();
190 }
192 Curves &curves_id = *owner.get_curves();
193 CurvesGeometry &curves = curves_id.geometry.wrap();
194 return curves.attributes_for_write();
195 }
197 GreasePencil &grease_pencil = *owner.get_grease_pencil();
198 return grease_pencil.attributes_for_write();
199 }
202 return drawing.strokes_for_write().attributes_for_write();
203 }
204 }
205 return {};
206}
207
208} // namespace blender::bke
209
211 const StringRef old_name,
212 const StringRef new_name,
214{
216 owner, old_name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL);
217 if (layer == nullptr) {
218 return false;
219 }
220 return BKE_attribute_rename(owner, old_name, new_name, reports);
221}
222
224 const blender::bke::AttributeAccessor attributes,
225 const StringRef name,
226 const AttrDomain domain,
227 const eCustomDataType data_type,
229{
230 if (const std::optional metadata = attributes.get_builtin_domain_and_type(name)) {
231 if (domain != metadata->domain) {
233 RPT_ERROR,
234 "Domain unsupported for \"%s\" attribute",
235 std::string(name).c_str());
236 return false;
237 }
238 if (data_type != metadata->data_type) {
240 reports, RPT_ERROR, "Type unsupported for \"%s\" attribute", std::string(name).c_str());
241 return false;
242 }
243 }
244 return true;
245}
246
247static bool mesh_attribute_valid(const Mesh &mesh,
248 const StringRef name,
249 const AttrDomain domain,
250 const eCustomDataType data_type,
252{
253 using namespace blender;
254 if (mesh.runtime->edit_mesh) {
256 BKE_report(reports, RPT_ERROR, "Unable to create attribute in edit mode");
257 return false;
258 }
259 }
260 if (!name_valid_for_builtin_domain_and_type(mesh.attributes(), name, domain, data_type, reports))
261 {
262 return false;
263 }
264 return true;
265}
266
268 const StringRef old_name,
269 const StringRef new_name,
271{
272 using namespace blender;
273 if (BKE_attribute_required(owner, old_name)) {
274 BLI_assert_msg(0, "Required attribute name is not editable");
275 return false;
276 }
277 if (new_name.is_empty()) {
278 BKE_report(reports, RPT_ERROR, "Attribute name cannot be empty");
279 return false;
280 }
281
282 /* NOTE: Checking if the new name matches the old name only makes sense when the name
283 * is clamped to it's maximum length, otherwise assigning an over-long name multiple times
284 * will add `.001` suffix unnecessarily. */
285 {
286 const int new_name_maxncpy = CustomData_name_maxncpy_calc(new_name);
287 /* NOTE: A function that performs a clamped comparison without copying would be handy here. */
288 char new_name_clamped[MAX_CUSTOMDATA_LAYER_NAME];
289 new_name.copy_utf8_truncated(new_name_clamped, new_name_maxncpy);
290 if (old_name == new_name_clamped) {
291 return false;
292 }
293 }
294
296 owner, old_name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL);
297 if (layer == nullptr) {
298 BKE_report(reports, RPT_ERROR, "Attribute is not part of this geometry");
299 return false;
300 }
301
302 if (owner.type() == AttributeOwnerType::Mesh) {
303 Mesh *mesh = owner.get_mesh();
304 if (!mesh_attribute_valid(*mesh,
305 new_name,
306 BKE_attribute_domain(owner, layer),
307 eCustomDataType(layer->type),
308 reports))
309 {
310 return false;
311 }
312 }
313 else if (owner.type() == AttributeOwnerType::Curves) {
314 Curves *curves = owner.get_curves();
315 if (!name_valid_for_builtin_domain_and_type(curves->geometry.wrap().attributes(),
316 new_name,
317 BKE_attribute_domain(owner, layer),
318 eCustomDataType(layer->type),
319 reports))
320 {
321 return false;
322 }
323 }
324
325 std::string result_name = BKE_attribute_calc_unique_name(owner, new_name);
326
327 if (layer->type == CD_PROP_FLOAT2 && owner.type() == AttributeOwnerType::Mesh) {
328 /* Rename UV sub-attributes. */
329 char buffer_src[MAX_CUSTOMDATA_LAYER_NAME];
330 char buffer_dst[MAX_CUSTOMDATA_LAYER_NAME];
331
333 BKE_uv_map_vert_select_name_get(layer->name, buffer_src),
334 BKE_uv_map_vert_select_name_get(result_name, buffer_dst),
335 reports);
337 BKE_uv_map_edge_select_name_get(layer->name, buffer_src),
338 BKE_uv_map_edge_select_name_get(result_name, buffer_dst),
339 reports);
341 BKE_uv_map_pin_name_get(layer->name, buffer_src),
342 BKE_uv_map_pin_name_get(result_name, buffer_dst),
343 reports);
344 }
345
346 if (owner.type() == AttributeOwnerType::Mesh) {
347 Mesh *mesh = owner.get_mesh();
348 if (old_name == BKE_id_attributes_active_color_name(&mesh->id)) {
349 BKE_id_attributes_active_color_set(&mesh->id, result_name);
350 }
351 if (old_name == BKE_id_attributes_default_color_name(&mesh->id)) {
352 BKE_id_attributes_default_color_set(&mesh->id, result_name);
353 }
354 }
355
356 StringRef(result_name).copy_utf8_truncated(layer->name);
357
358 return true;
359}
360
361static bool attribute_name_exists(const AttributeOwner &owner, const StringRef name)
362{
363 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
364
365 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
366 if (!info[domain].customdata) {
367 continue;
368 }
369
370 const CustomData *cdata = info[domain].customdata;
371 for (int i = 0; i < cdata->totlayer; i++) {
372 const CustomDataLayer *layer = cdata->layers + i;
373
374 if (layer->name == name) {
375 return true;
376 }
377 }
378 }
379
380 return false;
381}
382
383std::string BKE_attribute_calc_unique_name(const AttributeOwner &owner, const StringRef name)
384{
385 return BLI_uniquename_cb(
386 [&](const StringRef new_name) { return attribute_name_exists(owner, new_name); },
387 '.',
388 name.is_empty() ? DATA_("Attribute") : name);
389}
390
392 const StringRef name,
393 const eCustomDataType type,
394 const AttrDomain domain,
396{
397 using namespace blender::bke;
398 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
399
400 CustomData *customdata = info[int(domain)].customdata;
401 if (customdata == nullptr) {
402 BKE_report(reports, RPT_ERROR, "Attribute domain not supported by this geometry type");
403 return nullptr;
404 }
405
406 std::string uniquename = BKE_attribute_calc_unique_name(owner, name);
407
408 if (owner.type() == AttributeOwnerType::Mesh) {
409 Mesh *mesh = owner.get_mesh();
410 if (BMEditMesh *em = mesh->runtime->edit_mesh.get()) {
411 if (!mesh_attribute_valid(*mesh, name, domain, type, reports)) {
412 return nullptr;
413 }
414 BM_data_layer_add_named(em->bm, customdata, type, uniquename.c_str());
415 const int index = CustomData_get_named_layer_index(customdata, type, uniquename);
416 return (index == -1) ? nullptr : &(customdata->layers[index]);
417 }
418 }
419
420 std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(owner);
421 if (!attributes) {
422 return nullptr;
423 }
424
425 attributes->add(uniquename, domain, eCustomDataType(type), AttributeInitDefaultValue());
426
427 const int index = CustomData_get_named_layer_index(customdata, type, uniquename);
428 if (index == -1) {
429 BKE_reportf(reports, RPT_WARNING, "Layer '%s' could not be created", uniquename.c_str());
430 }
431
432 return (index == -1) ? nullptr : &(customdata->layers[index]);
433}
434
436 const StringRef srcname,
437 const StringRef dstname)
438{
439 using namespace blender::bke;
440
441 std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(owner);
442 if (!attributes) {
443 return;
444 }
445
446 GAttributeReader src = attributes->lookup(srcname);
447 if (!src) {
448 return;
449 }
450
452 attributes->add(dstname, src.domain, type, AttributeInitVArray(src.varray));
453}
454
456 const StringRef name,
458{
459 using namespace blender::bke;
460 std::string uniquename = BKE_attribute_calc_unique_name(owner, name);
461
462 if (owner.type() == AttributeOwnerType::Mesh) {
463 Mesh *mesh = owner.get_mesh();
464 if (mesh->runtime->edit_mesh) {
466 return nullptr;
467 }
468 }
469
470 std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(owner);
471 if (!attributes) {
472 return nullptr;
473 }
474
475 GAttributeReader src = attributes->lookup(name);
476 if (!src) {
477 BKE_report(reports, RPT_ERROR, "Attribute is not part of this geometry");
478 return nullptr;
479 }
480
482 attributes->add(uniquename, src.domain, type, AttributeInitVArray(src.varray));
483
484 if (owner.type() == AttributeOwnerType::Mesh && type == CD_PROP_FLOAT2) {
485 /* Duplicate UV sub-attributes. */
486 char buffer_src[MAX_CUSTOMDATA_LAYER_NAME];
487 char buffer_dst[MAX_CUSTOMDATA_LAYER_NAME];
488
490 BKE_uv_map_vert_select_name_get(name, buffer_src),
491 BKE_uv_map_vert_select_name_get(uniquename, buffer_dst));
493 BKE_uv_map_edge_select_name_get(name, buffer_src),
494 BKE_uv_map_edge_select_name_get(uniquename, buffer_dst));
496 BKE_uv_map_pin_name_get(name, buffer_src),
497 BKE_uv_map_pin_name_get(uniquename, buffer_dst));
498 }
499
501}
502
509
510static int color_clamp_index(AttributeOwner &owner, int index)
511{
513 return min_ii(index, length - 1);
514}
515
517{
520 return layer ? layer->name : "";
521}
522
524{
525 using namespace blender;
526 using namespace blender::bke;
527 if (name.is_empty()) {
528 BKE_report(reports, RPT_ERROR, "The attribute name must not be empty");
529 return false;
530 }
531 if (BKE_attribute_required(owner, name)) {
532 BKE_report(reports, RPT_ERROR, "Attribute is required and can't be removed");
533 return false;
534 }
535
536 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
537
538 if (owner.type() == AttributeOwnerType::Mesh) {
539 Mesh *mesh = owner.get_mesh();
540 if (BMEditMesh *em = mesh->runtime->edit_mesh.get()) {
541 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
542 if (CustomData *data = info[domain].customdata) {
543 const std::string name_copy = name;
544 const int layer_index = CustomData_get_named_layer_index_notype(data, name_copy);
545 if (layer_index == -1) {
546 continue;
547 }
548
549 const eCustomDataType type = eCustomDataType(data->layers[layer_index].type);
550 const bool is_active_color_attribute = name_copy.c_str() ==
551 StringRef(mesh->active_color_attribute);
552 const bool is_default_color_attribute = name_copy.c_str() ==
553 StringRef(mesh->default_color_attribute);
554 const int active_color_index = color_name_to_index(owner, mesh->active_color_attribute);
555 const int default_color_index = color_name_to_index(owner,
556 mesh->default_color_attribute);
557
558 if (!BM_data_layer_free_named(em->bm, data, name_copy.c_str())) {
560 }
561
562 if (is_active_color_attribute) {
564 &mesh->id,
565 color_name_from_index(owner, color_clamp_index(owner, active_color_index)));
566 }
567 if (is_default_color_attribute) {
569 &mesh->id,
570 color_name_from_index(owner, color_clamp_index(owner, default_color_index)));
571 }
572
573 if (type == CD_PROP_FLOAT2 && domain == int(AttrDomain::Corner)) {
574 char buffer[MAX_CUSTOMDATA_LAYER_NAME];
576 em->bm, data, BKE_uv_map_vert_select_name_get(name_copy, buffer));
578 em->bm, data, BKE_uv_map_edge_select_name_get(name_copy, buffer));
579 BM_data_layer_free_named(em->bm, data, BKE_uv_map_pin_name_get(name_copy, buffer));
580 }
581 return true;
582 }
583 }
584 return false;
585 }
586 }
587
588 std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(owner);
589 if (!attributes) {
590 return false;
591 }
592
593 if (owner.type() == AttributeOwnerType::Mesh) {
594 const std::string name_copy = name;
595 std::optional<blender::bke::AttributeMetaData> metadata = attributes->lookup_meta_data(
596 name_copy);
597 if (!metadata) {
598 return false;
599 }
600 /* Update active and default color attributes. */
601 Mesh *mesh = owner.get_mesh();
602 const bool is_active_color_attribute = name_copy == StringRef(mesh->active_color_attribute);
603 const bool is_default_color_attribute = name_copy == StringRef(mesh->default_color_attribute);
604 const int active_color_index = color_name_to_index(owner, mesh->active_color_attribute);
605 const int default_color_index = color_name_to_index(owner, mesh->default_color_attribute);
606
607 if (!attributes->remove(name_copy)) {
609 }
610
611 if (is_active_color_attribute) {
613 &mesh->id, color_name_from_index(owner, color_clamp_index(owner, active_color_index)));
614 }
615 if (is_default_color_attribute) {
617 &mesh->id, color_name_from_index(owner, color_clamp_index(owner, default_color_index)));
618 }
619
620 if (metadata->data_type == CD_PROP_FLOAT2 && metadata->domain == AttrDomain::Corner) {
621 char buffer[MAX_CUSTOMDATA_LAYER_NAME];
622 attributes->remove(BKE_uv_map_vert_select_name_get(name_copy, buffer));
623 attributes->remove(BKE_uv_map_edge_select_name_get(name_copy, buffer));
624 attributes->remove(BKE_uv_map_pin_name_get(name_copy, buffer));
625 }
626 return true;
627 }
628
629 return attributes->remove(name);
630}
631
633 const StringRef name,
634 const eCustomDataType type,
635 const AttrDomain domain)
636{
637 if (name.is_empty()) {
638 return nullptr;
639 }
640 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
641
642 CustomData *customdata = info[int(domain)].customdata;
643 if (customdata == nullptr) {
644 return nullptr;
645 }
646
647 for (int i = 0; i < customdata->totlayer; i++) {
648 CustomDataLayer *layer = &customdata->layers[i];
649 if (layer->type == type && layer->name == name) {
650 return layer;
651 }
652 }
653
654 return nullptr;
655}
656
658 const StringRef name,
659 const eCustomDataMask type_mask,
660 const AttrDomainMask domain_mask)
661{
662 if (name.is_empty()) {
663 return nullptr;
664 }
665 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
666
667 for (AttrDomain domain = AttrDomain::Point; int(domain) < ATTR_DOMAIN_NUM;
668 domain = AttrDomain(int(domain) + 1))
669 {
670 if (!(domain_mask & ATTR_DOMAIN_AS_MASK(domain))) {
671 continue;
672 }
673
674 CustomData *customdata = info[int(domain)].customdata;
675 if (customdata == nullptr) {
676 continue;
677 }
678
679 for (int i = 0; i < customdata->totlayer; i++) {
680 CustomDataLayer *layer = &customdata->layers[i];
681 if ((CD_TYPE_AS_MASK(layer->type) & type_mask) && layer->name == name) {
682 return layer;
683 }
684 }
685 }
686
687 return nullptr;
688}
689
691 const StringRef name,
692 const eCustomDataMask type_mask,
693 const AttrDomainMask domain_mask)
694{
695 /* Reuse the implementation of the const version.
696 * Implicit sharing for the layer's data is handled below. */
697 CustomDataLayer *layer = const_cast<CustomDataLayer *>(
698 BKE_attribute_search(owner, name, type_mask, domain_mask));
699 if (!layer) {
700 return nullptr;
701 }
702
703 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
704
705 const AttrDomain domain = BKE_attribute_domain(owner, layer);
706 CustomData_ensure_data_is_mutable(layer, info[int(domain)].length);
707
708 return layer;
709}
710
712 AttrDomainMask domain_mask,
713 eCustomDataMask mask)
714{
715 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
716
717 int length = 0;
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
725 if ((1 << int(domain)) & domain_mask) {
727 }
728 }
729
730 return length;
731}
732
734{
735 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
736
737 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
738 const CustomData *customdata = info[domain].customdata;
739 if (customdata == nullptr) {
740 continue;
741 }
742 if (blender::Span(customdata->layers, customdata->totlayer).contains_ptr(layer)) {
743 return AttrDomain(domain);
744 }
745 }
746
747 BLI_assert_msg(0, "Custom data layer not found in geometry");
749}
750
751int BKE_attribute_domain_size(const AttributeOwner &owner, const int domain)
752{
753 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
754 return info[domain].length;
755}
756
758{
759 /* When in mesh editmode, attributes point to bmesh customdata layers, the attribute data is
760 * empty since custom data is stored per element instead of a single array there (same es UVs
761 * etc.), see D11998. */
762 if (owner.type() == AttributeOwnerType::Mesh) {
763 Mesh *mesh = owner.get_mesh();
764 if (mesh->runtime->edit_mesh != nullptr) {
765 return 0;
766 }
767 }
768
769 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
770
771 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
772 const CustomData *customdata = info[domain].customdata;
773 if (customdata == nullptr) {
774 continue;
775 }
776 if (blender::Span(customdata->layers, customdata->totlayer).contains_ptr(layer)) {
777 return info[domain].length;
778 }
779 }
780
781 BLI_assert_msg(0, "Custom data layer not found in geometry");
782 return 0;
783}
784
785bool BKE_attribute_required(const AttributeOwner &owner, const StringRef name)
786{
787 switch (owner.type()) {
791 return BKE_curves_attribute_required(owner.get_curves(), name);
793 return BKE_mesh_attribute_required(name);
795 return false;
798 }
799 return false;
800}
801
802std::optional<blender::StringRefNull> BKE_attributes_active_name_get(AttributeOwner &owner)
803{
804 int active_index = *BKE_attributes_active_index_p(owner);
805 if (active_index == -1) {
806 return std::nullopt;
807 }
808 if (active_index > BKE_attributes_length(owner, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL)) {
809 active_index = 0;
810 }
811
812 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
813
814 int index = 0;
815
816 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
817 CustomData *customdata = info[domain].customdata;
818 if (customdata == nullptr) {
819 continue;
820 }
821 for (int i = 0; i < customdata->totlayer; i++) {
822 CustomDataLayer *layer = &customdata->layers[i];
823 if (CD_MASK_PROP_ALL & CD_TYPE_AS_MASK(layer->type)) {
824 if (index == active_index) {
826 return layer->name;
827 }
828 return std::nullopt;
829 }
830 index++;
831 }
832 }
833 }
834
835 return std::nullopt;
836}
837
839{
842 BLI_assert(layer != nullptr);
843
844 const int index = BKE_attribute_to_index(owner, layer, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL);
845 *BKE_attributes_active_index_p(owner) = index;
846}
847
852
854{
855 switch (owner.type()) {
858 }
860 return &owner.get_mesh()->attributes_active_index;
861 }
864 }
867 }
870 }
871 }
872 return nullptr;
873}
874
876{
877 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
878
879 bool use_next = (layers == nullptr);
880
881 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
882 CustomData *customdata = info[domain].customdata;
883 if (customdata == nullptr) {
884 continue;
885 }
886 if (customdata->layers && customdata->totlayer) {
887 if (customdata->layers == layers) {
888 use_next = true;
889 }
890 else if (use_next) {
891 return customdata;
892 }
893 }
894 }
895
896 return nullptr;
897}
898
900 int lookup_index,
901 AttrDomainMask domain_mask,
902 eCustomDataMask layer_mask)
903{
904 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
905
906 int index = 0;
907 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
908 CustomData *customdata = info[domain].customdata;
909
910 if (!customdata || !((1 << int(domain)) & domain_mask)) {
911 continue;
912 }
913
914 for (int i = 0; i < customdata->totlayer; i++) {
915 if (!(layer_mask & CD_TYPE_AS_MASK(customdata->layers[i].type)) ||
916 (customdata->layers[i].flag & CD_FLAG_TEMPORARY))
917 {
918 continue;
919 }
920
921 if (index == lookup_index) {
922 return customdata->layers + i;
923 }
924
925 index++;
926 }
927 }
928
929 return nullptr;
930}
931
933 const CustomDataLayer *layer,
934 AttrDomainMask domain_mask,
935 eCustomDataMask layer_mask)
936{
937 if (!layer) {
938 return -1;
939 }
940
941 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
942
943 int index = 0;
944 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
945 const CustomData *customdata = info[domain].customdata;
946
947 if (!customdata || !((1 << int(domain)) & domain_mask)) {
948 continue;
949 }
950
951 for (int i = 0; i < customdata->totlayer; i++) {
952 const CustomDataLayer *layer_iter = customdata->layers + i;
953 if (!(layer_mask & CD_TYPE_AS_MASK(layer_iter->type)) ||
954 (layer_iter->flag & CD_FLAG_TEMPORARY))
955 {
956 continue;
957 }
958
959 if (layer == layer_iter) {
960 return index;
961 }
962
963 index++;
964 }
965 }
966
967 return -1;
968}
969
970std::optional<StringRef> BKE_id_attributes_active_color_name(const ID *id)
971{
972 if (GS(id->name) == ID_ME) {
973 return reinterpret_cast<const Mesh *>(id)->active_color_attribute;
974 }
975 return std::nullopt;
976}
977
978std::optional<StringRef> BKE_id_attributes_default_color_name(const ID *id)
979{
980 if (GS(id->name) == ID_ME) {
981 return reinterpret_cast<const Mesh *>(id)->default_color_attribute;
982 }
983 return std::nullopt;
984}
985
986void BKE_id_attributes_active_color_set(ID *id, const std::optional<StringRef> name)
987{
988 switch (GS(id->name)) {
989 case ID_ME: {
990 Mesh *mesh = reinterpret_cast<Mesh *>(id);
991 MEM_SAFE_FREE(mesh->active_color_attribute);
992 if (name) {
993 mesh->active_color_attribute = BLI_strdupn(name->data(), name->size());
994 }
995 break;
996 }
997 default:
998 break;
999 }
1000}
1001
1003{
1004 switch (GS(id->name)) {
1005 case ID_ME: {
1006 Mesh *mesh = reinterpret_cast<Mesh *>(id);
1007 MEM_SAFE_FREE(mesh->active_color_attribute);
1008 break;
1009 }
1010 default:
1011 break;
1012 }
1013}
1014
1015void BKE_id_attributes_default_color_set(ID *id, const std::optional<StringRef> name)
1016{
1017 switch (GS(id->name)) {
1018 case ID_ME: {
1019 Mesh *mesh = reinterpret_cast<Mesh *>(id);
1020 MEM_SAFE_FREE(mesh->default_color_attribute);
1021 if (name) {
1022 mesh->default_color_attribute = BLI_strdupn(name->data(), name->size());
1023 }
1024 break;
1025 }
1026 default:
1027 break;
1028 }
1029}
1030
1032{
1033 AttributeOwner owner = AttributeOwner::from_id(const_cast<ID *>(id));
1035}
1036
1038{
1039 std::optional<blender::bke::AttributeMetaData> meta_data = mesh.attributes().lookup_meta_data(
1040 name);
1041
1042 if (!meta_data) {
1043 return false;
1044 }
1045 if (!(ATTR_DOMAIN_AS_MASK(meta_data->domain) & ATTR_DOMAIN_MASK_COLOR) ||
1046 !(CD_TYPE_AS_MASK(meta_data->data_type) & CD_MASK_COLOR_ALL))
1047 {
1048 return false;
1049 }
1050 return true;
1051}
1052
1054{
1055 BLI_assert(strlen(UV_VERTSEL_NAME) == 2);
1056 BLI_assert(uv_map_name.size() < MAX_CUSTOMDATA_LAYER_NAME - 4);
1057 const auto result = fmt::format_to_n(
1058 buffer, MAX_CUSTOMDATA_LAYER_NAME, ".{}.{}", UV_VERTSEL_NAME, uv_map_name);
1059 return StringRef(buffer, result.size);
1060}
1061
1063{
1064 BLI_assert(strlen(UV_EDGESEL_NAME) == 2);
1065 BLI_assert(uv_map_name.size() < MAX_CUSTOMDATA_LAYER_NAME - 4);
1066 const auto result = fmt::format_to_n(
1067 buffer, MAX_CUSTOMDATA_LAYER_NAME, ".{}.{}", UV_EDGESEL_NAME, uv_map_name);
1068 return StringRef(buffer, result.size);
1069}
1070
1071StringRef BKE_uv_map_pin_name_get(const StringRef uv_map_name, char *buffer)
1072{
1073 BLI_assert(strlen(UV_PINNED_NAME) == 2);
1074 BLI_assert(uv_map_name.size() < MAX_CUSTOMDATA_LAYER_NAME - 4);
1075 const auto result = fmt::format_to_n(
1076 buffer, MAX_CUSTOMDATA_LAYER_NAME, ".{}.{}", UV_PINNED_NAME, uv_map_name);
1077 return StringRef(buffer, result.size);
1078}
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
blender::StringRef BKE_uv_map_edge_select_name_get(blender::StringRef uv_map_name, char *buffer)
#define ATTR_DOMAIN_MASK_COLOR
#define ATTR_DOMAIN_AS_MASK(domain)
blender::StringRef BKE_uv_map_vert_select_name_get(blender::StringRef uv_map_name, char *buffer)
#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)
#define UV_VERTSEL_NAME
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)
#define CD_TYPE_AS_MASK(_type)
#define UV_EDGESEL_NAME
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
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
#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
@ CD_FLAG_TEMPORARY
@ CD_PROP_FLOAT2
Read Guarded memory(de)allocation.
ReportList * reports
Definition WM_types.hh:1025
static bool mesh_attribute_valid(const Mesh &mesh, const StringRef name, const AttrDomain domain, const eCustomDataType data_type, ReportList *reports)
Definition attribute.cc:247
bool BKE_color_attribute_supported(const Mesh &mesh, const StringRef name)
StringRef BKE_uv_map_edge_select_name_get(const StringRef uv_map_name, char *buffer)
static int color_name_to_index(AttributeOwner &owner, const StringRef name)
Definition attribute.cc:503
std::optional< StringRef > BKE_id_attributes_active_color_name(const ID *id)
Definition attribute.cc:970
bool BKE_attribute_rename(AttributeOwner &owner, const StringRef old_name, const StringRef new_name, ReportList *reports)
Definition attribute.cc:267
std::optional< blender::StringRefNull > BKE_attributes_active_name_get(AttributeOwner &owner)
Definition attribute.cc:802
CustomData * BKE_attributes_iterator_next_domain(AttributeOwner &owner, CustomDataLayer *layers)
Definition attribute.cc:875
static int color_clamp_index(AttributeOwner &owner, int index)
Definition attribute.cc:510
int BKE_attribute_to_index(const AttributeOwner &owner, const CustomDataLayer *layer, AttrDomainMask domain_mask, eCustomDataMask layer_mask)
Definition attribute.cc:932
CustomDataLayer * BKE_attribute_new(AttributeOwner &owner, const StringRef name, const eCustomDataType type, const AttrDomain domain, ReportList *reports)
Definition attribute.cc:391
AttrDomain BKE_attribute_domain(const AttributeOwner &owner, const CustomDataLayer *layer)
Definition attribute.cc:733
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:690
static void bke_attribute_copy_if_exists(AttributeOwner &owner, const StringRef srcname, const StringRef dstname)
Definition attribute.cc:435
const CustomDataLayer * BKE_attribute_search(const AttributeOwner &owner, const StringRef name, const eCustomDataMask type_mask, const AttrDomainMask domain_mask)
Definition attribute.cc:657
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:899
void BKE_attributes_active_set(AttributeOwner &owner, const StringRef name)
Definition attribute.cc:838
static bool attribute_name_exists(const AttributeOwner &owner, const StringRef name)
Definition attribute.cc:361
int * BKE_attributes_active_index_p(AttributeOwner &owner)
Definition attribute.cc:853
bool BKE_attribute_remove(AttributeOwner &owner, const StringRef name, ReportList *reports)
Definition attribute.cc:523
std::string BKE_attribute_calc_unique_name(const AttributeOwner &owner, const StringRef name)
Definition attribute.cc:383
CustomDataLayer * BKE_attribute_duplicate(AttributeOwner &owner, const StringRef name, ReportList *reports)
Definition attribute.cc:455
void BKE_id_attributes_active_color_set(ID *id, const std::optional< StringRef > name)
Definition attribute.cc:986
const CustomDataLayer * BKE_id_attributes_color_find(const ID *id, const StringRef name)
void BKE_id_attributes_active_color_clear(ID *id)
StringRef BKE_uv_map_vert_select_name_get(const StringRef uv_map_name, char *buffer)
static StringRef color_name_from_index(AttributeOwner &owner, int index)
Definition attribute.cc:516
static bool bke_attribute_rename_if_exists(AttributeOwner &owner, const StringRef old_name, const StringRef new_name, ReportList *reports)
Definition attribute.cc:210
CustomDataLayer * BKE_attribute_find(const AttributeOwner &owner, const StringRef name, const eCustomDataType type, const AttrDomain domain)
Definition attribute.cc:632
std::optional< StringRef > BKE_id_attributes_default_color_name(const ID *id)
Definition attribute.cc:978
static std::array< DomainInfo, ATTR_DOMAIN_NUM > get_domains(const AttributeOwner &owner)
Definition attribute.cc:112
bool BKE_attribute_required(const AttributeOwner &owner, const StringRef name)
Definition attribute.cc:785
int BKE_attribute_data_length(AttributeOwner &owner, CustomDataLayer *layer)
Definition attribute.cc:757
int BKE_attributes_length(const AttributeOwner &owner, AttrDomainMask domain_mask, eCustomDataMask mask)
Definition attribute.cc:711
static bool name_valid_for_builtin_domain_and_type(const blender::bke::AttributeAccessor attributes, const StringRef name, const AttrDomain domain, const eCustomDataType data_type, ReportList *reports)
Definition attribute.cc:223
int BKE_attribute_domain_size(const AttributeOwner &owner, const int domain)
Definition attribute.cc:751
void BKE_attributes_active_clear(AttributeOwner &owner)
Definition attribute.cc:848
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:62
static AttributeOwner from_id(ID *id)
Definition attribute.cc:43
bool is_valid() const
Definition attribute.cc:67
Mesh * get_mesh() const
Definition attribute.cc:72
GreasePencil * get_grease_pencil() const
Definition attribute.cc:93
PointCloud * get_pointcloud() const
Definition attribute.cc:79
Curves * get_curves() const
Definition attribute.cc:86
GreasePencilDrawing * get_grease_pencil_drawing() const
Definition attribute.cc:100
AttributeSet attributes
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
MutableAttributeAccessor attributes_for_write()
bke::CurvesGeometry & strokes_for_write()
float length(VecOp< float, D >) RET
#define MAX_CUSTOMDATA_LAYER_NAME
#define MEM_SAFE_FREE(v)
#define CD_MASK_PROP_ALL
#define CD_MASK_COLOR_ALL
#define GS(a)
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
bool allow_procedural_attribute_access(StringRef attribute_name)
eCustomDataType cpp_type_to_custom_data_type(const CPPType &type)
static std::optional< blender::bke::MutableAttributeAccessor > get_attribute_accessor_for_write(AttributeOwner &owner)
Definition attribute.cc:177
CustomData point_data
CustomData curve_data
CurvesGeometry geometry
CustomDataLayer * layers
CustomData * customdata
Definition attribute.cc:108
Definition DNA_ID.h:404
char name[66]
Definition DNA_ID.h:415
int corners_num
CustomData edge_data
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 CustomData pdata
i
Definition text_draw.cc:230