diff options
Diffstat (limited to 'source/blender/blenkernel/intern/attribute.cc')
-rw-r--r-- | source/blender/blenkernel/intern/attribute.cc | 233 |
1 files changed, 147 insertions, 86 deletions
diff --git a/source/blender/blenkernel/intern/attribute.cc b/source/blender/blenkernel/intern/attribute.cc index 7c09b4a4ce3..f66a1f9ee93 100644 --- a/source/blender/blenkernel/intern/attribute.cc +++ b/source/blender/blenkernel/intern/attribute.cc @@ -8,6 +8,7 @@ */ #include <cstring> +#include <optional> #include "MEM_guardedalloc.h" @@ -19,12 +20,15 @@ #include "DNA_pointcloud_types.h" #include "BLI_index_range.hh" +#include "BLI_string.h" #include "BLI_string_utf8.h" #include "BLI_string_utils.h" +#include "BLT_translation.h" + #include "BKE_attribute.h" -#include "BKE_attribute_access.hh" -#include "BKE_curves.h" +#include "BKE_attribute.hh" +#include "BKE_curves.hh" #include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_pointcloud.h" @@ -89,6 +93,36 @@ static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM]) } } +namespace blender::bke { + +static std::optional<blender::bke::MutableAttributeAccessor> get_attribute_accessor_for_write( + ID &id) +{ + switch (GS(id.name)) { + case ID_ME: { + Mesh &mesh = reinterpret_cast<Mesh &>(id); + /* The attribute API isn't implemented for BMesh, so edit mode meshes are not supported. */ + BLI_assert(mesh.edit_mesh == nullptr); + return mesh.attributes_for_write(); + } + case ID_PT: { + PointCloud &pointcloud = reinterpret_cast<PointCloud &>(id); + return pointcloud.attributes_for_write(); + } + case ID_CV: { + Curves &curves_id = reinterpret_cast<Curves &>(id); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + return curves.attributes_for_write(); + } + default: { + BLI_assert_unreachable(); + return {}; + } + } +} + +} // namespace blender::bke + bool BKE_id_attributes_supported(const ID *id) { DomainInfo info[ATTR_DOMAIN_NUM]; @@ -115,6 +149,13 @@ bool BKE_id_attribute_rename(ID *id, BLI_assert_msg(0, "Required attribute name is not editable"); return false; } + if (STREQ(new_name, "")) { + BKE_report(reports, RPT_ERROR, "Attribute name can not be empty"); + return false; + } + if (STREQ(old_name, new_name)) { + return false; + } CustomDataLayer *layer = BKE_id_attribute_search( id, old_name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL); @@ -146,9 +187,9 @@ static bool unique_name_cb(void *arg, const char *name) continue; } - CustomData *cdata = info[domain].customdata; + const CustomData *cdata = info[domain].customdata; for (int i = 0; i < cdata->totlayer; i++) { - CustomDataLayer *layer = cdata->layers + i; + const CustomDataLayer *layer = cdata->layers + i; if (STREQ(layer->name, name)) { return true; @@ -163,7 +204,14 @@ bool BKE_id_attribute_calc_unique_name(ID *id, const char *name, char *outname) { AttrUniqueData data{id}; - BLI_strncpy_utf8(outname, name, MAX_CUSTOMDATA_LAYER_NAME); + /* Set default name if none specified. + * NOTE: We only call IFACE_() if needed to avoid locale lookup overhead. */ + if (!name || name[0] == '\0') { + BLI_strncpy(outname, IFACE_("Attribute"), MAX_CUSTOMDATA_LAYER_NAME); + } + else { + BLI_strncpy_utf8(outname, name, MAX_CUSTOMDATA_LAYER_NAME); + } return BLI_uniquename_cb( unique_name_cb, &data, nullptr, '.', outname, MAX_CUSTOMDATA_LAYER_NAME); @@ -172,6 +220,7 @@ bool BKE_id_attribute_calc_unique_name(ID *id, const char *name, char *outname) CustomDataLayer *BKE_id_attribute_new( ID *id, const char *name, const int type, const eAttrDomain domain, ReportList *reports) { + using namespace blender::bke; DomainInfo info[ATTR_DOMAIN_NUM]; get_domains(id, info); @@ -184,64 +233,65 @@ CustomDataLayer *BKE_id_attribute_new( char uniquename[MAX_CUSTOMDATA_LAYER_NAME]; BKE_id_attribute_calc_unique_name(id, name, uniquename); - switch (GS(id->name)) { - case ID_ME: { - Mesh *me = (Mesh *)id; - BMEditMesh *em = me->edit_mesh; - if (em != nullptr) { - BM_data_layer_add_named(em->bm, customdata, type, uniquename); - } - else { - CustomData_add_layer_named( - customdata, type, CD_DEFAULT, nullptr, info[domain].length, uniquename); - } - break; - } - default: { - CustomData_add_layer_named( - customdata, type, CD_DEFAULT, nullptr, info[domain].length, uniquename); - break; + if (GS(id->name) == ID_ME) { + Mesh *mesh = reinterpret_cast<Mesh *>(id); + if (BMEditMesh *em = mesh->edit_mesh) { + BM_data_layer_add_named(em->bm, customdata, type, uniquename); + const int index = CustomData_get_named_layer_index(customdata, type, uniquename); + return (index == -1) ? nullptr : &(customdata->layers[index]); } } + std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(*id); + if (!attributes) { + return nullptr; + } + + attributes->add(uniquename, domain, eCustomDataType(type), AttributeInitDefaultValue()); + const int index = CustomData_get_named_layer_index(customdata, type, uniquename); return (index == -1) ? nullptr : &(customdata->layers[index]); } CustomDataLayer *BKE_id_attribute_duplicate(ID *id, const char *name, ReportList *reports) { - const CustomDataLayer *src_layer = BKE_id_attribute_search( - id, name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL); - if (src_layer == nullptr) { - BKE_report(reports, RPT_ERROR, "Attribute is not part of this geometry"); - return nullptr; - } - - const eCustomDataType type = (eCustomDataType)src_layer->type; - const eAttrDomain domain = BKE_id_attribute_domain(id, src_layer); + using namespace blender::bke; + char uniquename[MAX_CUSTOMDATA_LAYER_NAME]; + BKE_id_attribute_calc_unique_name(id, name, uniquename); - /* Make a copy of name in case CustomData API reallocates the layers. */ - const std::string name_copy = name; + if (GS(id->name) == ID_ME) { + Mesh *mesh = reinterpret_cast<Mesh *>(id); + if (BMEditMesh *em = mesh->edit_mesh) { + BLI_assert_unreachable(); + UNUSED_VARS(em); + return nullptr; + } + } - DomainInfo info[ATTR_DOMAIN_NUM]; - get_domains(id, info); - CustomData *customdata = info[domain].customdata; + std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(*id); + if (!attributes) { + return nullptr; + } - CustomDataLayer *new_layer = BKE_id_attribute_new(id, name_copy.c_str(), type, domain, reports); - if (new_layer == nullptr) { + GAttributeReader src = attributes->lookup(name); + if (!src) { + BKE_report(reports, RPT_ERROR, "Attribute is not part of this geometry"); return nullptr; } - const int from_index = CustomData_get_named_layer_index(customdata, type, name_copy.c_str()); - const int to_index = CustomData_get_named_layer_index(customdata, type, new_layer->name); - CustomData_copy_data_layer( - customdata, customdata, from_index, to_index, 0, 0, info[domain].length); + const eCustomDataType type = cpp_type_to_custom_data_type(src.varray.type()); + attributes->add(uniquename, src.domain, type, AttributeInitVArray(src.varray)); - return new_layer; + return BKE_id_attribute_search(id, uniquename, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL); } bool BKE_id_attribute_remove(ID *id, const char *name, ReportList *reports) { + using namespace blender::bke; + if (!name || name[0] == '\0') { + BKE_report(reports, RPT_ERROR, "The attribute name must not be empty"); + return false; + } if (BKE_id_attribute_required(id, name)) { BKE_report(reports, RPT_ERROR, "Attribute is required and can't be removed"); return false; @@ -250,31 +300,26 @@ bool BKE_id_attribute_remove(ID *id, const char *name, ReportList *reports) DomainInfo info[ATTR_DOMAIN_NUM]; get_domains(id, info); - switch (GS(id->name)) { - case ID_ME: { - Mesh *mesh = reinterpret_cast<Mesh *>(id); - if (BMEditMesh *em = mesh->edit_mesh) { - for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { - if (CustomData *data = info[domain].customdata) { - if (BM_data_layer_free_named(em->bm, data, name)) { - return true; - } - } - } - return false; - } - ATTR_FALLTHROUGH; - } - default: + if (GS(id->name) == ID_ME) { + Mesh *mesh = reinterpret_cast<Mesh *>(id); + if (BMEditMesh *em = mesh->edit_mesh) { for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { if (CustomData *data = info[domain].customdata) { - if (CustomData_free_layer_named(data, name, info[domain].length)) { + if (BM_data_layer_free_named(em->bm, data, name)) { return true; } } } return false; + } + } + + std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(*id); + if (!attributes) { + return false; } + + return attributes->remove(name); } CustomDataLayer *BKE_id_attribute_find(const ID *id, @@ -338,9 +383,12 @@ int BKE_id_attributes_length(const ID *id, eAttrDomainMask domain_mask, eCustomD int length = 0; for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { - CustomData *customdata = info[domain].customdata; + const CustomData *customdata = info[domain].customdata; + if (customdata == nullptr) { + continue; + } - if (customdata && ((1 << (int)domain) & domain_mask)) { + if ((1 << (int)domain) & domain_mask) { length += CustomData_number_of_layers_typemask(customdata, mask); } } @@ -354,9 +402,11 @@ eAttrDomain BKE_id_attribute_domain(const ID *id, const CustomDataLayer *layer) get_domains(id, info); for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { - CustomData *customdata = info[domain].customdata; - if (customdata && - ARRAY_HAS_ITEM((CustomDataLayer *)layer, customdata->layers, customdata->totlayer)) { + const CustomData *customdata = info[domain].customdata; + if (customdata == nullptr) { + continue; + } + if (ARRAY_HAS_ITEM((CustomDataLayer *)layer, customdata->layers, customdata->totlayer)) { return static_cast<eAttrDomain>(domain); } } @@ -376,6 +426,7 @@ int BKE_id_attribute_data_length(ID *id, CustomDataLayer *layer) if (mesh->edit_mesh != nullptr) { return 0; } + break; } default: break; @@ -385,9 +436,11 @@ int BKE_id_attribute_data_length(ID *id, CustomDataLayer *layer) get_domains(id, info); for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { - CustomData *customdata = info[domain].customdata; - if (customdata && - ARRAY_HAS_ITEM((CustomDataLayer *)layer, customdata->layers, customdata->totlayer)) { + const CustomData *customdata = info[domain].customdata; + if (customdata == nullptr) { + continue; + } + if (ARRAY_HAS_ITEM((CustomDataLayer *)layer, customdata->layers, customdata->totlayer)) { return info[domain].length; } } @@ -424,15 +477,19 @@ CustomDataLayer *BKE_id_attributes_active_get(ID *id) for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { CustomData *customdata = info[domain].customdata; - if (customdata) { - for (int i = 0; i < customdata->totlayer; i++) { - CustomDataLayer *layer = &customdata->layers[i]; - if (CD_MASK_PROP_ALL & CD_TYPE_AS_MASK(layer->type)) { - if (index == active_index) { + if (customdata == nullptr) { + continue; + } + for (int i = 0; i < customdata->totlayer; i++) { + CustomDataLayer *layer = &customdata->layers[i]; + if (CD_MASK_PROP_ALL & CD_TYPE_AS_MASK(layer->type)) { + if (index == active_index) { + if (BKE_attribute_allow_procedural_access(layer->name)) { return layer; } - index++; + return nullptr; } + index++; } } } @@ -448,17 +505,18 @@ void BKE_id_attributes_active_set(ID *id, CustomDataLayer *active_layer) int index = 0; for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { - CustomData *customdata = info[domain].customdata; - if (customdata) { - for (int i = 0; i < customdata->totlayer; i++) { - CustomDataLayer *layer = &customdata->layers[i]; - if (layer == active_layer) { - *BKE_id_attributes_active_index_p(id) = index; - return; - } - if (CD_MASK_PROP_ALL & CD_TYPE_AS_MASK(layer->type)) { - index++; - } + const CustomData *customdata = info[domain].customdata; + if (customdata == nullptr) { + continue; + } + for (int i = 0; i < customdata->totlayer; i++) { + const CustomDataLayer *layer = &customdata->layers[i]; + if (layer == active_layer) { + *BKE_id_attributes_active_index_p(id) = index; + return; + } + if (CD_MASK_PROP_ALL & CD_TYPE_AS_MASK(layer->type)) { + index++; } } } @@ -490,7 +548,10 @@ CustomData *BKE_id_attributes_iterator_next_domain(ID *id, CustomDataLayer *laye for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) { CustomData *customdata = info[domain].customdata; - if (customdata && customdata->layers && customdata->totlayer) { + if (customdata == nullptr) { + continue; + } + if (customdata->layers && customdata->totlayer) { if (customdata->layers == layers) { use_next = true; } |