Blender V4.3
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
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_utf8.h"
25#include "BLI_string_utils.hh"
26
27#include "BLT_translation.hh"
28
29#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 "RNA_access.hh"
39
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 char *old_name,
212 const char *new_name,
213 ReportList *reports)
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 AttrDomain domain,
225 const eCustomDataType data_type,
226 ReportList *reports)
227{
228 if (ELEM(name,
229 "position",
230 ".edge_verts",
231 ".corner_vert",
232 ".corner_edge",
233 "sharp_edge",
234 "sharp_face",
235 "material_index"))
236 {
237 BKE_report(reports, RPT_ERROR, "Unable to create builtin attribute in edit mode");
238 return false;
239 }
240 if (name == "id") {
241 if (domain != AttrDomain::Point) {
242 BKE_report(reports, RPT_ERROR, "Domain unsupported for \"id\" attribute");
243 return false;
244 }
245 if (data_type != CD_PROP_INT32) {
246 BKE_report(reports, RPT_ERROR, "Type unsupported for \"id\" attribute");
247 return false;
248 }
249 }
250 return true;
251}
252
254 const char *old_name,
255 const char *new_name,
256 ReportList *reports)
257{
258 using namespace blender;
259 if (BKE_attribute_required(owner, old_name)) {
260 BLI_assert_msg(0, "Required attribute name is not editable");
261 return false;
262 }
263 if (STREQ(new_name, "")) {
264 BKE_report(reports, RPT_ERROR, "Attribute name cannot be empty");
265 return false;
266 }
267
268 /* NOTE: Checking if the new name matches the old name only makes sense when the name
269 * is clamped to it's maximum length, otherwise assigning an over-long name multiple times
270 * will add `.001` suffix unnecessarily. */
271 {
272 const int new_name_maxncpy = CustomData_name_maxncpy_calc(new_name);
273 /* NOTE: A function that performs a clamped comparison without copying would be handy here. */
274 char new_name_clamped[MAX_CUSTOMDATA_LAYER_NAME];
275 BLI_strncpy_utf8(new_name_clamped, new_name, new_name_maxncpy);
276 if (STREQ(old_name, new_name_clamped)) {
277 return false;
278 }
279 }
280
282 owner, old_name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL);
283 if (layer == nullptr) {
284 BKE_report(reports, RPT_ERROR, "Attribute is not part of this geometry");
285 return false;
286 }
287
288 if (owner.type() == AttributeOwnerType::Mesh) {
289 Mesh *mesh = owner.get_mesh();
290 if (mesh->runtime->edit_mesh) {
292 new_name, BKE_attribute_domain(owner, layer), eCustomDataType(layer->type), reports))
293 {
294 return false;
295 }
296 }
297 }
298
299 std::string result_name = BKE_attribute_calc_unique_name(owner, new_name);
300
301 if (layer->type == CD_PROP_FLOAT2 && owner.type() == AttributeOwnerType::Mesh) {
302 /* Rename UV sub-attributes. */
303 char buffer_src[MAX_CUSTOMDATA_LAYER_NAME];
304 char buffer_dst[MAX_CUSTOMDATA_LAYER_NAME];
305
307 owner,
308 BKE_uv_map_vert_select_name_get(layer->name, buffer_src),
309 BKE_uv_map_vert_select_name_get(result_name.c_str(), buffer_dst),
310 reports);
312 owner,
313 BKE_uv_map_edge_select_name_get(layer->name, buffer_src),
314 BKE_uv_map_edge_select_name_get(result_name.c_str(), buffer_dst),
315 reports);
317 BKE_uv_map_pin_name_get(layer->name, buffer_src),
318 BKE_uv_map_pin_name_get(result_name.c_str(), buffer_dst),
319 reports);
320 }
321
322 if (owner.type() == AttributeOwnerType::Mesh) {
323 Mesh *mesh = owner.get_mesh();
324 if (StringRef(old_name) == BKE_id_attributes_active_color_name(&mesh->id)) {
325 BKE_id_attributes_active_color_set(&mesh->id, result_name.c_str());
326 }
327 if (StringRef(old_name) == BKE_id_attributes_default_color_name(&mesh->id)) {
328 BKE_id_attributes_default_color_set(&mesh->id, result_name.c_str());
329 }
330 }
331
332 STRNCPY_UTF8(layer->name, result_name.c_str());
333
334 return true;
335}
336
337static bool attribute_name_exists(const AttributeOwner &owner, const blender::StringRef name)
338{
339 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
340
341 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
342 if (!info[domain].customdata) {
343 continue;
344 }
345
346 const CustomData *cdata = info[domain].customdata;
347 for (int i = 0; i < cdata->totlayer; i++) {
348 const CustomDataLayer *layer = cdata->layers + i;
349
350 if (layer->name == name) {
351 return true;
352 }
353 }
354 }
355
356 return false;
357}
358
360 const blender::StringRef name)
361{
362 return BLI_uniquename_cb(
363 [&](const blender::StringRef new_name) { return attribute_name_exists(owner, new_name); },
364 '.',
365 name.is_empty() ? DATA_("Attribute") : name);
366}
367
369 const char *name,
370 const eCustomDataType type,
371 const AttrDomain domain,
372 ReportList *reports)
373{
374 using namespace blender::bke;
375 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
376
377 CustomData *customdata = info[int(domain)].customdata;
378 if (customdata == nullptr) {
379 BKE_report(reports, RPT_ERROR, "Attribute domain not supported by this geometry type");
380 return nullptr;
381 }
382
383 std::string uniquename = BKE_attribute_calc_unique_name(owner, name);
384
385 if (owner.type() == AttributeOwnerType::Mesh) {
386 Mesh *mesh = owner.get_mesh();
387 if (BMEditMesh *em = mesh->runtime->edit_mesh.get()) {
388 if (!mesh_edit_mode_attribute_valid(name, domain, type, reports)) {
389 return nullptr;
390 }
391 BM_data_layer_add_named(em->bm, customdata, type, uniquename.c_str());
392 const int index = CustomData_get_named_layer_index(customdata, type, uniquename);
393 return (index == -1) ? nullptr : &(customdata->layers[index]);
394 }
395 }
396
397 std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(owner);
398 if (!attributes) {
399 return nullptr;
400 }
401
402 attributes->add(uniquename, domain, eCustomDataType(type), AttributeInitDefaultValue());
403
404 const int index = CustomData_get_named_layer_index(customdata, type, uniquename);
405 if (index == -1) {
406 BKE_reportf(reports, RPT_WARNING, "Layer '%s' could not be created", uniquename.c_str());
407 }
408
409 return (index == -1) ? nullptr : &(customdata->layers[index]);
410}
411
413 const char *srcname,
414 const char *dstname)
415{
416 using namespace blender::bke;
417
418 std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(owner);
419 if (!attributes) {
420 return;
421 }
422
423 GAttributeReader src = attributes->lookup(srcname);
424 if (!src) {
425 return;
426 }
427
428 const eCustomDataType type = cpp_type_to_custom_data_type(src.varray.type());
429 attributes->add(dstname, src.domain, type, AttributeInitVArray(src.varray));
430}
431
433 const char *name,
434 ReportList *reports)
435{
436 using namespace blender::bke;
437 std::string uniquename = BKE_attribute_calc_unique_name(owner, name);
438
439 if (owner.type() == AttributeOwnerType::Mesh) {
440 Mesh *mesh = owner.get_mesh();
441 if (mesh->runtime->edit_mesh) {
443 return nullptr;
444 }
445 }
446
447 std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(owner);
448 if (!attributes) {
449 return nullptr;
450 }
451
452 GAttributeReader src = attributes->lookup(name);
453 if (!src) {
454 BKE_report(reports, RPT_ERROR, "Attribute is not part of this geometry");
455 return nullptr;
456 }
457
458 const eCustomDataType type = cpp_type_to_custom_data_type(src.varray.type());
459 attributes->add(uniquename, src.domain, type, AttributeInitVArray(src.varray));
460
461 if (owner.type() == AttributeOwnerType::Mesh && type == CD_PROP_FLOAT2) {
462 /* Duplicate UV sub-attributes. */
463 char buffer_src[MAX_CUSTOMDATA_LAYER_NAME];
464 char buffer_dst[MAX_CUSTOMDATA_LAYER_NAME];
465
467 BKE_uv_map_vert_select_name_get(name, buffer_src),
468 BKE_uv_map_vert_select_name_get(uniquename.c_str(), buffer_dst));
470 BKE_uv_map_edge_select_name_get(name, buffer_src),
471 BKE_uv_map_edge_select_name_get(uniquename.c_str(), buffer_dst));
473 BKE_uv_map_pin_name_get(name, buffer_src),
474 BKE_uv_map_pin_name_get(uniquename.c_str(), buffer_dst));
475 }
476
478 owner, uniquename.c_str(), CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL);
479}
480
481static int color_name_to_index(AttributeOwner &owner, const char *name)
482{
486}
487
488static int color_clamp_index(AttributeOwner &owner, int index)
489{
491 return min_ii(index, length - 1);
492}
493
494static const char *color_name_from_index(AttributeOwner &owner, int index)
495{
498 return layer ? layer->name : nullptr;
499}
500
501bool BKE_attribute_remove(AttributeOwner &owner, const char *name, ReportList *reports)
502{
503 using namespace blender;
504 using namespace blender::bke;
505 if (!name || name[0] == '\0') {
506 BKE_report(reports, RPT_ERROR, "The attribute name must not be empty");
507 return false;
508 }
509 if (BKE_attribute_required(owner, name)) {
510 BKE_report(reports, RPT_ERROR, "Attribute is required and can't be removed");
511 return false;
512 }
513
514 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
515
516 if (owner.type() == AttributeOwnerType::Mesh) {
517 Mesh *mesh = owner.get_mesh();
518 if (BMEditMesh *em = mesh->runtime->edit_mesh.get()) {
519 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
520 if (CustomData *data = info[domain].customdata) {
521 const std::string name_copy = name;
522 const int layer_index = CustomData_get_named_layer_index_notype(data, name_copy);
523 if (layer_index == -1) {
524 continue;
525 }
526
527 const eCustomDataType type = eCustomDataType(data->layers[layer_index].type);
528 const bool is_active_color_attribute = name_copy.c_str() ==
529 StringRef(mesh->active_color_attribute);
530 const bool is_default_color_attribute = name_copy.c_str() ==
531 StringRef(mesh->default_color_attribute);
532 const int active_color_index = color_name_to_index(owner, mesh->active_color_attribute);
533 const int default_color_index = color_name_to_index(owner,
534 mesh->default_color_attribute);
535
536 if (!BM_data_layer_free_named(em->bm, data, name_copy.c_str())) {
538 }
539
540 if (is_active_color_attribute) {
542 &mesh->id,
543 color_name_from_index(owner, color_clamp_index(owner, active_color_index)));
544 }
545 if (is_default_color_attribute) {
547 &mesh->id,
548 color_name_from_index(owner, color_clamp_index(owner, default_color_index)));
549 }
550
551 if (type == CD_PROP_FLOAT2 && domain == int(AttrDomain::Corner)) {
552 char buffer[MAX_CUSTOMDATA_LAYER_NAME];
554 em->bm, data, BKE_uv_map_vert_select_name_get(name_copy.c_str(), buffer));
556 em->bm, data, BKE_uv_map_edge_select_name_get(name_copy.c_str(), buffer));
558 em->bm, data, BKE_uv_map_pin_name_get(name_copy.c_str(), buffer));
559 }
560 return true;
561 }
562 }
563 return false;
564 }
565 }
566
567 std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(owner);
568 if (!attributes) {
569 return false;
570 }
571
572 if (owner.type() == AttributeOwnerType::Mesh) {
573 const std::string name_copy = name;
574 std::optional<blender::bke::AttributeMetaData> metadata = attributes->lookup_meta_data(
575 name_copy);
576 if (!metadata) {
577 return false;
578 }
579 /* Update active and default color attributes. */
580 Mesh *mesh = owner.get_mesh();
581 const bool is_active_color_attribute = name_copy == StringRef(mesh->active_color_attribute);
582 const bool is_default_color_attribute = name_copy == StringRef(mesh->default_color_attribute);
583 const int active_color_index = color_name_to_index(owner, mesh->active_color_attribute);
584 const int default_color_index = color_name_to_index(owner, mesh->default_color_attribute);
585
586 if (!attributes->remove(name_copy)) {
588 }
589
590 if (is_active_color_attribute) {
592 &mesh->id, color_name_from_index(owner, color_clamp_index(owner, active_color_index)));
593 }
594 if (is_default_color_attribute) {
596 &mesh->id, color_name_from_index(owner, color_clamp_index(owner, default_color_index)));
597 }
598
599 if (metadata->data_type == CD_PROP_FLOAT2 && metadata->domain == AttrDomain::Corner) {
600 char buffer[MAX_CUSTOMDATA_LAYER_NAME];
601 attributes->remove(BKE_uv_map_vert_select_name_get(name_copy.c_str(), buffer));
602 attributes->remove(BKE_uv_map_edge_select_name_get(name_copy.c_str(), buffer));
603 attributes->remove(BKE_uv_map_pin_name_get(name_copy.c_str(), buffer));
604 }
605 return true;
606 }
607
608 return attributes->remove(name);
609}
610
612 const char *name,
613 const eCustomDataType type,
614 const AttrDomain domain)
615{
616 if (!name) {
617 return nullptr;
618 }
619 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
620
621 CustomData *customdata = info[int(domain)].customdata;
622 if (customdata == nullptr) {
623 return nullptr;
624 }
625
626 for (int i = 0; i < customdata->totlayer; i++) {
627 CustomDataLayer *layer = &customdata->layers[i];
628 if (layer->type == type && STREQ(layer->name, name)) {
629 return layer;
630 }
631 }
632
633 return nullptr;
634}
635
637 const char *name,
638 const eCustomDataMask type_mask,
639 const AttrDomainMask domain_mask)
640{
641 if (!name) {
642 return nullptr;
643 }
644 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
645
646 for (AttrDomain domain = AttrDomain::Point; int(domain) < ATTR_DOMAIN_NUM;
647 domain = AttrDomain(int(domain) + 1))
648 {
649 if (!(domain_mask & ATTR_DOMAIN_AS_MASK(domain))) {
650 continue;
651 }
652
653 CustomData *customdata = info[int(domain)].customdata;
654 if (customdata == nullptr) {
655 continue;
656 }
657
658 for (int i = 0; i < customdata->totlayer; i++) {
659 CustomDataLayer *layer = &customdata->layers[i];
660 if ((CD_TYPE_AS_MASK(layer->type) & type_mask) && STREQ(layer->name, name)) {
661 return layer;
662 }
663 }
664 }
665
666 return nullptr;
667}
668
670 const char *name,
671 const eCustomDataMask type_mask,
672 const AttrDomainMask domain_mask)
673{
674 /* Reuse the implementation of the const version.
675 * Implicit sharing for the layer's data is handled below. */
676 CustomDataLayer *layer = const_cast<CustomDataLayer *>(
677 BKE_attribute_search(owner, name, type_mask, domain_mask));
678 if (!layer) {
679 return nullptr;
680 }
681
682 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
683
684 const AttrDomain domain = BKE_attribute_domain(owner, layer);
685 CustomData_ensure_data_is_mutable(layer, info[int(domain)].length);
686
687 return layer;
688}
689
691 AttrDomainMask domain_mask,
692 eCustomDataMask mask)
693{
694 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
695
696 int length = 0;
697
698 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
699 const CustomData *customdata = info[domain].customdata;
700 if (customdata == nullptr) {
701 continue;
702 }
703
704 if ((1 << int(domain)) & domain_mask) {
705 length += CustomData_number_of_layers_typemask(customdata, mask);
706 }
707 }
708
709 return length;
710}
711
712AttrDomain BKE_attribute_domain(const AttributeOwner &owner, const CustomDataLayer *layer)
713{
714 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
715
716 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
717 const CustomData *customdata = info[domain].customdata;
718 if (customdata == nullptr) {
719 continue;
720 }
721 if (blender::Span(customdata->layers, customdata->totlayer).contains_ptr(layer)) {
722 return AttrDomain(domain);
723 }
724 }
725
726 BLI_assert_msg(0, "Custom data layer not found in geometry");
727 return AttrDomain(AttrDomain::Point);
728}
729
730int BKE_attribute_domain_size(const AttributeOwner &owner, const int domain)
731{
732 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
733 return info[domain].length;
734}
735
737{
738 /* When in mesh editmode, attributes point to bmesh customdata layers, the attribute data is
739 * empty since custom data is stored per element instead of a single array there (same es UVs
740 * etc.), see D11998. */
741 if (owner.type() == AttributeOwnerType::Mesh) {
742 Mesh *mesh = owner.get_mesh();
743 if (mesh->runtime->edit_mesh != nullptr) {
744 return 0;
745 }
746 }
747
748 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
749
750 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
751 const CustomData *customdata = info[domain].customdata;
752 if (customdata == nullptr) {
753 continue;
754 }
755 if (blender::Span(customdata->layers, customdata->totlayer).contains_ptr(layer)) {
756 return info[domain].length;
757 }
758 }
759
760 BLI_assert_msg(0, "Custom data layer not found in geometry");
761 return 0;
762}
763
764bool BKE_attribute_required(const AttributeOwner &owner, const char *name)
765{
766 switch (owner.type()) {
770 return BKE_curves_attribute_required(owner.get_curves(), name);
772 return BKE_mesh_attribute_required(name);
774 return false;
777 }
778 return false;
779}
780
782{
783 int active_index = *BKE_attributes_active_index_p(owner);
784 if (active_index == -1) {
785 return nullptr;
786 }
787 if (active_index > BKE_attributes_length(owner, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL)) {
788 active_index = 0;
789 }
790
791 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
792
793 int index = 0;
794
795 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
796 CustomData *customdata = info[domain].customdata;
797 if (customdata == nullptr) {
798 continue;
799 }
800 for (int i = 0; i < customdata->totlayer; i++) {
801 CustomDataLayer *layer = &customdata->layers[i];
802 if (CD_MASK_PROP_ALL & CD_TYPE_AS_MASK(layer->type)) {
803 if (index == active_index) {
805 return layer;
806 }
807 return nullptr;
808 }
809 index++;
810 }
811 }
812 }
813
814 return nullptr;
815}
816
817void BKE_attributes_active_set(AttributeOwner &owner, const char *name)
818{
821 BLI_assert(layer != nullptr);
822
823 const int index = BKE_attribute_to_index(owner, layer, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL);
824 *BKE_attributes_active_index_p(owner) = index;
825}
826
831
833{
834 switch (owner.type()) {
837 }
839 return &owner.get_mesh()->attributes_active_index;
840 }
843 }
846 }
849 }
850 }
851 return nullptr;
852}
853
855{
856 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
857
858 bool use_next = (layers == nullptr);
859
860 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
861 CustomData *customdata = info[domain].customdata;
862 if (customdata == nullptr) {
863 continue;
864 }
865 if (customdata->layers && customdata->totlayer) {
866 if (customdata->layers == layers) {
867 use_next = true;
868 }
869 else if (use_next) {
870 return customdata;
871 }
872 }
873 }
874
875 return nullptr;
876}
877
879 int lookup_index,
880 AttrDomainMask domain_mask,
881 eCustomDataMask layer_mask)
882{
883 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
884
885 int index = 0;
886 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
887 CustomData *customdata = info[domain].customdata;
888
889 if (!customdata || !((1 << int(domain)) & domain_mask)) {
890 continue;
891 }
892
893 for (int i = 0; i < customdata->totlayer; i++) {
894 if (!(layer_mask & CD_TYPE_AS_MASK(customdata->layers[i].type)) ||
895 (customdata->layers[i].flag & CD_FLAG_TEMPORARY))
896 {
897 continue;
898 }
899
900 if (index == lookup_index) {
901 return customdata->layers + i;
902 }
903
904 index++;
905 }
906 }
907
908 return nullptr;
909}
910
912 const CustomDataLayer *layer,
913 AttrDomainMask domain_mask,
914 eCustomDataMask layer_mask)
915{
916 if (!layer) {
917 return -1;
918 }
919
920 const std::array<DomainInfo, ATTR_DOMAIN_NUM> info = get_domains(owner);
921
922 int index = 0;
923 for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
924 const CustomData *customdata = info[domain].customdata;
925
926 if (!customdata || !((1 << int(domain)) & domain_mask)) {
927 continue;
928 }
929
930 for (int i = 0; i < customdata->totlayer; i++) {
931 const CustomDataLayer *layer_iter = customdata->layers + i;
932 if (!(layer_mask & CD_TYPE_AS_MASK(layer_iter->type)) ||
933 (layer_iter->flag & CD_FLAG_TEMPORARY))
934 {
935 continue;
936 }
937
938 if (layer == layer_iter) {
939 return index;
940 }
941
942 index++;
943 }
944 }
945
946 return -1;
947}
948
950{
951 if (GS(id->name) == ID_ME) {
952 return reinterpret_cast<const Mesh *>(id)->active_color_attribute;
953 }
954 return nullptr;
955}
956
958{
959 if (GS(id->name) == ID_ME) {
960 return reinterpret_cast<const Mesh *>(id)->default_color_attribute;
961 }
962 return nullptr;
963}
964
965void BKE_id_attributes_active_color_set(ID *id, const char *name)
966{
967 switch (GS(id->name)) {
968 case ID_ME: {
969 Mesh *mesh = reinterpret_cast<Mesh *>(id);
970 MEM_SAFE_FREE(mesh->active_color_attribute);
971 if (name) {
972 mesh->active_color_attribute = BLI_strdup(name);
973 }
974 break;
975 }
976 default:
977 break;
978 }
979}
980
982{
983 switch (GS(id->name)) {
984 case ID_ME: {
985 Mesh *mesh = reinterpret_cast<Mesh *>(id);
986 MEM_SAFE_FREE(mesh->active_color_attribute);
987 break;
988 }
989 default:
990 break;
991 }
992}
993
994void BKE_id_attributes_default_color_set(ID *id, const char *name)
995{
996 switch (GS(id->name)) {
997 case ID_ME: {
998 Mesh *mesh = reinterpret_cast<Mesh *>(id);
999 MEM_SAFE_FREE(mesh->default_color_attribute);
1000 if (name) {
1001 mesh->default_color_attribute = BLI_strdup(name);
1002 }
1003 break;
1004 }
1005 default:
1006 break;
1007 }
1008}
1009
1010const CustomDataLayer *BKE_id_attributes_color_find(const ID *id, const char *name)
1011{
1012 AttributeOwner owner = AttributeOwner::from_id(const_cast<ID *>(id));
1014}
1015
1017{
1018 std::optional<blender::bke::AttributeMetaData> meta_data = mesh.attributes().lookup_meta_data(
1019 name);
1020
1021 if (!meta_data) {
1022 return false;
1023 }
1024 if (!(ATTR_DOMAIN_AS_MASK(meta_data->domain) & ATTR_DOMAIN_MASK_COLOR) ||
1025 !(CD_TYPE_AS_MASK(meta_data->data_type) & CD_MASK_COLOR_ALL))
1026 {
1027 return false;
1028 }
1029 return true;
1030}
1031
1032const char *BKE_uv_map_vert_select_name_get(const char *uv_map_name, char *buffer)
1033{
1034 BLI_assert(strlen(UV_VERTSEL_NAME) == 2);
1035 BLI_assert(strlen(uv_map_name) < MAX_CUSTOMDATA_LAYER_NAME - 4);
1036 BLI_snprintf(buffer, MAX_CUSTOMDATA_LAYER_NAME, ".%s.%s", UV_VERTSEL_NAME, uv_map_name);
1037 return buffer;
1038}
1039
1040const char *BKE_uv_map_edge_select_name_get(const char *uv_map_name, char *buffer)
1041{
1042 BLI_assert(strlen(UV_EDGESEL_NAME) == 2);
1043 BLI_assert(strlen(uv_map_name) < MAX_CUSTOMDATA_LAYER_NAME - 4);
1044 BLI_snprintf(buffer, MAX_CUSTOMDATA_LAYER_NAME, ".%s.%s", UV_EDGESEL_NAME, uv_map_name);
1045 return buffer;
1046}
1047
1048const char *BKE_uv_map_pin_name_get(const char *uv_map_name, char *buffer)
1049{
1050 BLI_assert(strlen(UV_PINNED_NAME) == 2);
1051 BLI_assert(strlen(uv_map_name) < MAX_CUSTOMDATA_LAYER_NAME - 4);
1052 BLI_snprintf(buffer, MAX_CUSTOMDATA_LAYER_NAME, ".%s.%s", UV_PINNED_NAME, uv_map_name);
1053 return buffer;
1054}
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, const char *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 *, const char *name)
bool BKE_mesh_attribute_required(const char *name)
General operations for point clouds.
bool BKE_pointcloud_attribute_required(const PointCloud *pointcloud, const char *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:125
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
MINLINE int min_ii(int a, int b)
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
size_t BLI_snprintf(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
char * BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define STRNCPY_UTF8(dst, src)
size_t void BLI_uniquename_cb(UniquenameCheckCallback unique_check, void *arg, const char *defname, char delim, char *name, size_t name_maxncpy) ATTR_NONNULL(1
#define ELEM(...)
#define STREQ(a, b)
#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
@ CD_FLAG_TEMPORARY
#define CD_MASK_COLOR_ALL
@ CD_PROP_INT32
@ CD_PROP_FLOAT2
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
static bool bke_attribute_rename_if_exists(AttributeOwner &owner, const char *old_name, const char *new_name, ReportList *reports)
Definition attribute.cc:210
const CustomDataLayer * BKE_id_attributes_color_find(const ID *id, const char *name)
static bool mesh_edit_mode_attribute_valid(const blender::StringRef name, const AttrDomain domain, const eCustomDataType data_type, ReportList *reports)
Definition attribute.cc:223
const char * BKE_id_attributes_default_color_name(const ID *id)
Definition attribute.cc:957
CustomData * BKE_attributes_iterator_next_domain(AttributeOwner &owner, CustomDataLayer *layers)
Definition attribute.cc:854
static int color_clamp_index(AttributeOwner &owner, int index)
Definition attribute.cc:488
int BKE_attribute_to_index(const AttributeOwner &owner, const CustomDataLayer *layer, AttrDomainMask domain_mask, eCustomDataMask layer_mask)
Definition attribute.cc:911
static int color_name_to_index(AttributeOwner &owner, const char *name)
Definition attribute.cc:481
bool BKE_attribute_remove(AttributeOwner &owner, const char *name, ReportList *reports)
Definition attribute.cc:501
AttrDomain BKE_attribute_domain(const AttributeOwner &owner, const CustomDataLayer *layer)
Definition attribute.cc:712
std::string BKE_attribute_calc_unique_name(const AttributeOwner &owner, const blender::StringRef name)
Definition attribute.cc:359
static void bke_attribute_copy_if_exists(AttributeOwner &owner, const char *srcname, const char *dstname)
Definition attribute.cc:412
CustomDataLayer * BKE_attribute_from_index(AttributeOwner &owner, int lookup_index, AttrDomainMask domain_mask, eCustomDataMask layer_mask)
Definition attribute.cc:878
int * BKE_attributes_active_index_p(AttributeOwner &owner)
Definition attribute.cc:832
const char * BKE_id_attributes_active_color_name(const ID *id)
Definition attribute.cc:949
void BKE_id_attributes_active_color_set(ID *id, const char *name)
Definition attribute.cc:965
bool BKE_attribute_rename(AttributeOwner &owner, const char *old_name, const char *new_name, ReportList *reports)
Definition attribute.cc:253
CustomDataLayer * BKE_attribute_duplicate(AttributeOwner &owner, const char *name, ReportList *reports)
Definition attribute.cc:432
void BKE_id_attributes_active_color_clear(ID *id)
Definition attribute.cc:981
bool BKE_color_attribute_supported(const Mesh &mesh, const blender::StringRef name)
CustomDataLayer * BKE_attribute_search_for_write(AttributeOwner &owner, const char *name, const eCustomDataMask type_mask, const AttrDomainMask domain_mask)
Definition attribute.cc:669
CustomDataLayer * BKE_attribute_find(const AttributeOwner &owner, const char *name, const eCustomDataType type, const AttrDomain domain)
Definition attribute.cc:611
void BKE_id_attributes_default_color_set(ID *id, const char *name)
Definition attribute.cc:994
void BKE_attributes_active_set(AttributeOwner &owner, const char *name)
Definition attribute.cc:817
const char * BKE_uv_map_pin_name_get(const char *uv_map_name, char *buffer)
CustomDataLayer * BKE_attributes_active_get(AttributeOwner &owner)
Definition attribute.cc:781
const CustomDataLayer * BKE_attribute_search(const AttributeOwner &owner, const char *name, const eCustomDataMask type_mask, const AttrDomainMask domain_mask)
Definition attribute.cc:636
static std::array< DomainInfo, ATTR_DOMAIN_NUM > get_domains(const AttributeOwner &owner)
Definition attribute.cc:112
CustomDataLayer * BKE_attribute_new(AttributeOwner &owner, const char *name, const eCustomDataType type, const AttrDomain domain, ReportList *reports)
Definition attribute.cc:368
int BKE_attribute_data_length(AttributeOwner &owner, CustomDataLayer *layer)
Definition attribute.cc:736
int BKE_attributes_length(const AttributeOwner &owner, AttrDomainMask domain_mask, eCustomDataMask mask)
Definition attribute.cc:690
bool BKE_attribute_required(const AttributeOwner &owner, const char *name)
Definition attribute.cc:764
const char * BKE_uv_map_vert_select_name_get(const char *uv_map_name, char *buffer)
static bool attribute_name_exists(const AttributeOwner &owner, const blender::StringRef name)
Definition attribute.cc:337
static const char * color_name_from_index(AttributeOwner &owner, int index)
Definition attribute.cc:494
const char * BKE_uv_map_edge_select_name_get(const char *uv_map_name, char *buffer)
int BKE_attribute_domain_size(const AttributeOwner &owner, const int domain)
Definition attribute.cc:730
void BKE_attributes_active_clear(AttributeOwner &owner)
Definition attribute.cc:827
bool BM_data_layer_free_named(BMesh *bm, CustomData *data, const char *name)
void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char *name)
ATTR_WARN_UNUSED_RESULT BMesh * bm
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
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
constexpr bool contains_ptr(const T *ptr) const
Definition BLI_span.hh:292
MutableAttributeAccessor attributes_for_write()
bke::CurvesGeometry & strokes_for_write()
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
#define GS(x)
Definition iris.cc:202
bool allow_procedural_attribute_access(StringRef attribute_name)
static std::optional< blender::bke::MutableAttributeAccessor > get_attribute_accessor_for_write(AttributeOwner &owner)
Definition attribute.cc:177
int totvert
CustomData vdata
int totedge
CustomData edata
int totloop
CustomData pdata
CustomData ldata
int totface
CustomData point_data
CustomData curve_data
CurvesGeometry geometry
CustomDataLayer * layers
CustomData * customdata
Definition attribute.cc:108
Definition DNA_ID.h:413
int attributes_active_index
struct CustomData pdata