Attributes: Use new API for C-API functions

Use the C++ API to implement more of the existing C functions.
This corrects the cases where one tries to add a builtin attribute
with the wrong domain or type on curves, though a better warning
message would be helpful in the future, and also reduces duplication
of the internal logic. Not much more is possible without changing
the interface.
This commit is contained in:
Hans Goudey 2022-07-24 12:46:08 -05:00
parent ad632a13d9
commit 31365c6b9e
Notes: blender-bot 2023-02-14 00:37:17 +01:00
Referenced by commit 25c8d72e20, Fix T100747: Cannot add "String" attribute to mesh
Referenced by issue #101660, Blender crash for mesh with string attribute.
Referenced by issue #100747, Regression: Cannot add "String" attribute to mesh
1 changed files with 52 additions and 57 deletions

View File

@ -203,6 +203,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);
@ -215,60 +216,56 @@ 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), AttributeInitDefault());
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) {
using namespace blender::bke;
char uniquename[MAX_CUSTOMDATA_LAYER_NAME];
BKE_id_attribute_calc_unique_name(id, name, uniquename);
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;
}
}
std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(*id);
if (!attributes) {
return nullptr;
}
GAttributeReader src = attributes->lookup(name);
if (!src) {
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);
const eCustomDataType type = cpp_type_to_custom_data_type(src.varray.type());
attributes->add(uniquename, src.domain, type, AttributeInitVArray(src.varray));
/* Make a copy of name in case CustomData API reallocates the layers. */
const std::string name_copy = name;
DomainInfo info[ATTR_DOMAIN_NUM];
get_domains(id, info);
CustomData *customdata = info[domain].customdata;
CustomDataLayer *new_layer = BKE_id_attribute_new(id, name_copy.c_str(), type, domain, reports);
if (new_layer == nullptr) {
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);
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)
@ -282,28 +279,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;
}
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 (BM_data_layer_free_named(em->bm, data, name)) {
return true;
}
}
return false;
}
ATTR_FALLTHROUGH;
}
default:
if (std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(
*id)) {
return attributes->remove(name);
}
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,