812 lines
22 KiB
C++
812 lines
22 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright 2006 Blender Foundation. All rights reserved. */
|
|
|
|
/** \file
|
|
* \ingroup bke
|
|
* Implementation of generic geometry attributes management. This is built
|
|
* on top of CustomData, which manages individual domains.
|
|
*/
|
|
|
|
#include <cstring>
|
|
#include <optional>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "DNA_ID.h"
|
|
#include "DNA_curves_types.h"
|
|
#include "DNA_customdata_types.h"
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_meshdata_types.h"
|
|
#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.hh"
|
|
#include "BKE_curves.hh"
|
|
#include "BKE_customdata.h"
|
|
#include "BKE_editmesh.h"
|
|
#include "BKE_pointcloud.h"
|
|
#include "BKE_report.h"
|
|
|
|
#include "RNA_access.h"
|
|
|
|
using blender::IndexRange;
|
|
|
|
struct DomainInfo {
|
|
CustomData *customdata;
|
|
int length;
|
|
};
|
|
|
|
static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM])
|
|
{
|
|
memset(info, 0, sizeof(DomainInfo) * ATTR_DOMAIN_NUM);
|
|
|
|
switch (GS(id->name)) {
|
|
case ID_PT: {
|
|
PointCloud *pointcloud = (PointCloud *)id;
|
|
info[ATTR_DOMAIN_POINT].customdata = &pointcloud->pdata;
|
|
info[ATTR_DOMAIN_POINT].length = pointcloud->totpoint;
|
|
break;
|
|
}
|
|
case ID_ME: {
|
|
Mesh *mesh = (Mesh *)id;
|
|
BMEditMesh *em = mesh->edit_mesh;
|
|
if (em != nullptr) {
|
|
BMesh *bm = em->bm;
|
|
info[ATTR_DOMAIN_POINT].customdata = &bm->vdata;
|
|
info[ATTR_DOMAIN_POINT].length = bm->totvert;
|
|
info[ATTR_DOMAIN_EDGE].customdata = &bm->edata;
|
|
info[ATTR_DOMAIN_EDGE].length = bm->totedge;
|
|
info[ATTR_DOMAIN_CORNER].customdata = &bm->ldata;
|
|
info[ATTR_DOMAIN_CORNER].length = bm->totloop;
|
|
info[ATTR_DOMAIN_FACE].customdata = &bm->pdata;
|
|
info[ATTR_DOMAIN_FACE].length = bm->totface;
|
|
}
|
|
else {
|
|
info[ATTR_DOMAIN_POINT].customdata = &mesh->vdata;
|
|
info[ATTR_DOMAIN_POINT].length = mesh->totvert;
|
|
info[ATTR_DOMAIN_EDGE].customdata = &mesh->edata;
|
|
info[ATTR_DOMAIN_EDGE].length = mesh->totedge;
|
|
info[ATTR_DOMAIN_CORNER].customdata = &mesh->ldata;
|
|
info[ATTR_DOMAIN_CORNER].length = mesh->totloop;
|
|
info[ATTR_DOMAIN_FACE].customdata = &mesh->pdata;
|
|
info[ATTR_DOMAIN_FACE].length = mesh->totpoly;
|
|
}
|
|
break;
|
|
}
|
|
case ID_CV: {
|
|
Curves *curves = (Curves *)id;
|
|
info[ATTR_DOMAIN_POINT].customdata = &curves->geometry.point_data;
|
|
info[ATTR_DOMAIN_POINT].length = curves->geometry.point_num;
|
|
info[ATTR_DOMAIN_CURVE].customdata = &curves->geometry.curve_data;
|
|
info[ATTR_DOMAIN_CURVE].length = curves->geometry.curve_num;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
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];
|
|
get_domains(id, info);
|
|
for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
|
|
if (info[domain].customdata) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BKE_attribute_allow_procedural_access(const char *attribute_name)
|
|
{
|
|
return blender::bke::allow_procedural_attribute_access(attribute_name);
|
|
}
|
|
|
|
bool BKE_id_attribute_rename(ID *id,
|
|
const char *old_name,
|
|
const char *new_name,
|
|
ReportList *reports)
|
|
{
|
|
if (BKE_id_attribute_required(id, old_name)) {
|
|
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);
|
|
if (layer == nullptr) {
|
|
BKE_report(reports, RPT_ERROR, "Attribute is not part of this geometry");
|
|
return false;
|
|
}
|
|
|
|
char result_name[MAX_CUSTOMDATA_LAYER_NAME];
|
|
BKE_id_attribute_calc_unique_name(id, new_name, result_name);
|
|
BLI_strncpy_utf8(layer->name, result_name, sizeof(layer->name));
|
|
|
|
return true;
|
|
}
|
|
|
|
struct AttrUniqueData {
|
|
ID *id;
|
|
};
|
|
|
|
static bool unique_name_cb(void *arg, const char *name)
|
|
{
|
|
AttrUniqueData *data = (AttrUniqueData *)arg;
|
|
|
|
DomainInfo info[ATTR_DOMAIN_NUM];
|
|
get_domains(data->id, info);
|
|
|
|
for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
|
|
if (!info[domain].customdata) {
|
|
continue;
|
|
}
|
|
|
|
const CustomData *cdata = info[domain].customdata;
|
|
for (int i = 0; i < cdata->totlayer; i++) {
|
|
const CustomDataLayer *layer = cdata->layers + i;
|
|
|
|
if (STREQ(layer->name, name)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool BKE_id_attribute_calc_unique_name(ID *id, const char *name, char *outname)
|
|
{
|
|
AttrUniqueData data{id};
|
|
|
|
/* 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);
|
|
}
|
|
|
|
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);
|
|
|
|
CustomData *customdata = info[domain].customdata;
|
|
if (customdata == nullptr) {
|
|
BKE_report(reports, RPT_ERROR, "Attribute domain not supported by this geometry type");
|
|
return nullptr;
|
|
}
|
|
|
|
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) {
|
|
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)
|
|
{
|
|
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 = cpp_type_to_custom_data_type(src.varray.type());
|
|
attributes->add(uniquename, src.domain, type, AttributeInitVArray(src.varray));
|
|
|
|
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;
|
|
}
|
|
|
|
DomainInfo info[ATTR_DOMAIN_NUM];
|
|
get_domains(id, info);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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,
|
|
const char *name,
|
|
const int type,
|
|
const eAttrDomain domain)
|
|
{
|
|
DomainInfo info[ATTR_DOMAIN_NUM];
|
|
get_domains(id, info);
|
|
|
|
CustomData *customdata = info[domain].customdata;
|
|
if (customdata == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
for (int i = 0; i < customdata->totlayer; i++) {
|
|
CustomDataLayer *layer = &customdata->layers[i];
|
|
if (layer->type == type && STREQ(layer->name, name)) {
|
|
return layer;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
CustomDataLayer *BKE_id_attribute_search(ID *id,
|
|
const char *name,
|
|
const eCustomDataMask type_mask,
|
|
const eAttrDomainMask domain_mask)
|
|
{
|
|
DomainInfo info[ATTR_DOMAIN_NUM];
|
|
get_domains(id, info);
|
|
|
|
for (eAttrDomain domain = ATTR_DOMAIN_POINT; domain < ATTR_DOMAIN_NUM;
|
|
domain = static_cast<eAttrDomain>(int(domain) + 1)) {
|
|
if (!(domain_mask & ATTR_DOMAIN_AS_MASK(domain))) {
|
|
continue;
|
|
}
|
|
|
|
CustomData *customdata = info[domain].customdata;
|
|
if (customdata == nullptr) {
|
|
continue;
|
|
}
|
|
|
|
for (int i = 0; i < customdata->totlayer; i++) {
|
|
CustomDataLayer *layer = &customdata->layers[i];
|
|
if ((CD_TYPE_AS_MASK(layer->type) & type_mask) && STREQ(layer->name, name)) {
|
|
return layer;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
int BKE_id_attributes_length(const ID *id, eAttrDomainMask domain_mask, eCustomDataMask mask)
|
|
{
|
|
DomainInfo info[ATTR_DOMAIN_NUM];
|
|
get_domains(id, info);
|
|
|
|
int length = 0;
|
|
|
|
for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
|
|
const CustomData *customdata = info[domain].customdata;
|
|
if (customdata == nullptr) {
|
|
continue;
|
|
}
|
|
|
|
if ((1 << int(domain)) & domain_mask) {
|
|
length += CustomData_number_of_layers_typemask(customdata, mask);
|
|
}
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
eAttrDomain BKE_id_attribute_domain(const ID *id, const CustomDataLayer *layer)
|
|
{
|
|
DomainInfo info[ATTR_DOMAIN_NUM];
|
|
get_domains(id, info);
|
|
|
|
for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
BLI_assert_msg(0, "Custom data layer not found in geometry");
|
|
return static_cast<eAttrDomain>(ATTR_DOMAIN_POINT);
|
|
}
|
|
|
|
int BKE_id_attribute_data_length(ID *id, CustomDataLayer *layer)
|
|
{
|
|
/* When in mesh editmode, attributes point to bmesh customdata layers, the attribute data is
|
|
* empty since custom data is stored per element instead of a single array there (same es UVs
|
|
* etc.), see D11998. */
|
|
switch (GS(id->name)) {
|
|
case ID_ME: {
|
|
Mesh *mesh = (Mesh *)id;
|
|
if (mesh->edit_mesh != nullptr) {
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
DomainInfo info[ATTR_DOMAIN_NUM];
|
|
get_domains(id, info);
|
|
|
|
for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
|
|
const CustomData *customdata = info[domain].customdata;
|
|
if (customdata == nullptr) {
|
|
continue;
|
|
}
|
|
if (ARRAY_HAS_ITEM((CustomDataLayer *)layer, customdata->layers, customdata->totlayer)) {
|
|
return info[domain].length;
|
|
}
|
|
}
|
|
|
|
BLI_assert_msg(0, "Custom data layer not found in geometry");
|
|
return 0;
|
|
}
|
|
|
|
bool BKE_id_attribute_required(const ID *id, const char *name)
|
|
{
|
|
switch (GS(id->name)) {
|
|
case ID_PT:
|
|
return BKE_pointcloud_attribute_required((const PointCloud *)id, name);
|
|
case ID_CV:
|
|
return BKE_curves_attribute_required((const Curves *)id, name);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
CustomDataLayer *BKE_id_attributes_active_get(ID *id)
|
|
{
|
|
int active_index = *BKE_id_attributes_active_index_p(id);
|
|
if (active_index > BKE_id_attributes_length(id, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL)) {
|
|
active_index = 0;
|
|
}
|
|
|
|
DomainInfo info[ATTR_DOMAIN_NUM];
|
|
get_domains(id, info);
|
|
|
|
int index = 0;
|
|
|
|
for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
|
|
CustomData *customdata = info[domain].customdata;
|
|
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;
|
|
}
|
|
return nullptr;
|
|
}
|
|
index++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void BKE_id_attributes_active_set(ID *id, CustomDataLayer *active_layer)
|
|
{
|
|
DomainInfo info[ATTR_DOMAIN_NUM];
|
|
get_domains(id, info);
|
|
|
|
int index = 0;
|
|
|
|
for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
|
|
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++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int *BKE_id_attributes_active_index_p(ID *id)
|
|
{
|
|
switch (GS(id->name)) {
|
|
case ID_PT: {
|
|
return &((PointCloud *)id)->attributes_active_index;
|
|
}
|
|
case ID_ME: {
|
|
return &((Mesh *)id)->attributes_active_index;
|
|
}
|
|
case ID_CV: {
|
|
return &((Curves *)id)->attributes_active_index;
|
|
}
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
CustomData *BKE_id_attributes_iterator_next_domain(ID *id, CustomDataLayer *layers)
|
|
{
|
|
DomainInfo info[ATTR_DOMAIN_NUM];
|
|
get_domains(id, info);
|
|
|
|
bool use_next = (layers == nullptr);
|
|
|
|
for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
|
|
CustomData *customdata = info[domain].customdata;
|
|
if (customdata == nullptr) {
|
|
continue;
|
|
}
|
|
if (customdata->layers && customdata->totlayer) {
|
|
if (customdata->layers == layers) {
|
|
use_next = true;
|
|
}
|
|
else if (use_next) {
|
|
return customdata;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
CustomDataLayer *BKE_id_attribute_from_index(ID *id,
|
|
int lookup_index,
|
|
eAttrDomainMask domain_mask,
|
|
eCustomDataMask layer_mask)
|
|
{
|
|
DomainInfo info[ATTR_DOMAIN_NUM];
|
|
get_domains(id, info);
|
|
|
|
int index = 0;
|
|
for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
|
|
CustomData *customdata = info[domain].customdata;
|
|
|
|
if (!customdata || !((1 << int(domain)) & domain_mask)) {
|
|
continue;
|
|
}
|
|
|
|
for (int i = 0; i < customdata->totlayer; i++) {
|
|
if (!(layer_mask & CD_TYPE_AS_MASK(customdata->layers[i].type)) ||
|
|
(customdata->layers[i].flag & CD_FLAG_TEMPORARY)) {
|
|
continue;
|
|
}
|
|
|
|
if (index == lookup_index) {
|
|
return customdata->layers + i;
|
|
}
|
|
|
|
index++;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/** Get list of domain types but with ATTR_DOMAIN_FACE and
|
|
* ATTR_DOMAIN_CORNER swapped.
|
|
*/
|
|
static void get_domains_types(eAttrDomain domains[ATTR_DOMAIN_NUM])
|
|
{
|
|
for (const int i : IndexRange(ATTR_DOMAIN_NUM)) {
|
|
domains[i] = static_cast<eAttrDomain>(i);
|
|
}
|
|
|
|
/* Swap corner and face. */
|
|
SWAP(eAttrDomain, domains[ATTR_DOMAIN_FACE], domains[ATTR_DOMAIN_CORNER]);
|
|
}
|
|
|
|
int BKE_id_attribute_to_index(const ID *id,
|
|
const CustomDataLayer *layer,
|
|
eAttrDomainMask domain_mask,
|
|
eCustomDataMask layer_mask)
|
|
{
|
|
if (!layer) {
|
|
return -1;
|
|
}
|
|
|
|
DomainInfo info[ATTR_DOMAIN_NUM];
|
|
eAttrDomain domains[ATTR_DOMAIN_NUM];
|
|
get_domains_types(domains);
|
|
get_domains(id, info);
|
|
|
|
int index = 0;
|
|
for (int i = 0; i < ATTR_DOMAIN_NUM; i++) {
|
|
if (!(domain_mask & (1 << domains[i])) || !info[domains[i]].customdata) {
|
|
continue;
|
|
}
|
|
|
|
const CustomData *cdata = info[domains[i]].customdata;
|
|
for (int j = 0; j < cdata->totlayer; j++) {
|
|
const CustomDataLayer *layer_iter = cdata->layers + j;
|
|
|
|
if (!(CD_TYPE_AS_MASK(layer_iter->type) & layer_mask) ||
|
|
(layer_iter->flag & CD_FLAG_TEMPORARY)) {
|
|
continue;
|
|
}
|
|
|
|
if (layer == layer_iter) {
|
|
return index;
|
|
}
|
|
|
|
index++;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
CustomDataLayer *BKE_id_attribute_subset_active_get(const ID *id,
|
|
int active_flag,
|
|
eAttrDomainMask domain_mask,
|
|
eCustomDataMask mask)
|
|
{
|
|
DomainInfo info[ATTR_DOMAIN_NUM];
|
|
eAttrDomain domains[ATTR_DOMAIN_NUM];
|
|
|
|
get_domains_types(domains);
|
|
get_domains(id, info);
|
|
|
|
CustomDataLayer *candidate = nullptr;
|
|
for (int i = 0; i < ARRAY_SIZE(domains); i++) {
|
|
if (!((1 << domains[i]) & domain_mask) || !info[domains[i]].customdata) {
|
|
continue;
|
|
}
|
|
|
|
CustomData *cdata = info[domains[i]].customdata;
|
|
|
|
for (int j = 0; j < cdata->totlayer; j++) {
|
|
CustomDataLayer *layer = cdata->layers + j;
|
|
|
|
if (!(CD_TYPE_AS_MASK(layer->type) & mask) || (layer->flag & CD_FLAG_TEMPORARY)) {
|
|
continue;
|
|
}
|
|
|
|
if (layer->flag & active_flag) {
|
|
return layer;
|
|
}
|
|
|
|
candidate = layer;
|
|
}
|
|
}
|
|
|
|
return candidate;
|
|
}
|
|
|
|
void BKE_id_attribute_subset_active_set(ID *id,
|
|
CustomDataLayer *layer,
|
|
int active_flag,
|
|
eAttrDomainMask domain_mask,
|
|
eCustomDataMask mask)
|
|
{
|
|
DomainInfo info[ATTR_DOMAIN_NUM];
|
|
eAttrDomain domains[ATTR_DOMAIN_NUM];
|
|
|
|
get_domains_types(domains);
|
|
get_domains(id, info);
|
|
|
|
for (int i = 0; i < ATTR_DOMAIN_NUM; i++) {
|
|
eAttrDomainMask domain_mask2 = (eAttrDomainMask)(1 << domains[i]);
|
|
|
|
if (!(domain_mask2 & domain_mask) || !info[domains[i]].customdata) {
|
|
continue;
|
|
}
|
|
|
|
CustomData *cdata = info[domains[i]].customdata;
|
|
|
|
for (int j = 0; j < cdata->totlayer; j++) {
|
|
CustomDataLayer *layer_iter = cdata->layers + j;
|
|
|
|
if (!(CD_TYPE_AS_MASK(layer_iter->type) & mask) || (layer_iter->flag & CD_FLAG_TEMPORARY)) {
|
|
continue;
|
|
}
|
|
|
|
layer_iter->flag &= ~active_flag;
|
|
}
|
|
}
|
|
|
|
layer->flag |= active_flag;
|
|
}
|
|
|
|
CustomDataLayer *BKE_id_attributes_active_color_get(const ID *id)
|
|
{
|
|
return BKE_id_attribute_subset_active_get(
|
|
id, CD_FLAG_COLOR_ACTIVE, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL);
|
|
}
|
|
|
|
void BKE_id_attributes_active_color_set(ID *id, CustomDataLayer *active_layer)
|
|
{
|
|
BKE_id_attribute_subset_active_set(
|
|
id, active_layer, CD_FLAG_COLOR_ACTIVE, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL);
|
|
}
|
|
|
|
CustomDataLayer *BKE_id_attributes_render_color_get(const ID *id)
|
|
{
|
|
return BKE_id_attribute_subset_active_get(
|
|
id, CD_FLAG_COLOR_RENDER, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL);
|
|
}
|
|
|
|
void BKE_id_attributes_render_color_set(ID *id, CustomDataLayer *active_layer)
|
|
{
|
|
BKE_id_attribute_subset_active_set(
|
|
id, active_layer, CD_FLAG_COLOR_RENDER, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL);
|
|
}
|
|
|
|
CustomDataLayer *BKE_id_attributes_color_find(const ID *id, const char *name)
|
|
{
|
|
CustomDataLayer *layer = BKE_id_attribute_find(id, name, CD_PROP_COLOR, ATTR_DOMAIN_POINT);
|
|
if (layer == nullptr) {
|
|
layer = BKE_id_attribute_find(id, name, CD_PROP_COLOR, ATTR_DOMAIN_CORNER);
|
|
}
|
|
if (layer == nullptr) {
|
|
layer = BKE_id_attribute_find(id, name, CD_PROP_BYTE_COLOR, ATTR_DOMAIN_POINT);
|
|
}
|
|
if (layer == nullptr) {
|
|
layer = BKE_id_attribute_find(id, name, CD_PROP_BYTE_COLOR, ATTR_DOMAIN_CORNER);
|
|
}
|
|
|
|
return layer;
|
|
}
|
|
|
|
void BKE_id_attribute_copy_domains_temp(short id_type,
|
|
const CustomData *vdata,
|
|
const CustomData *edata,
|
|
const CustomData *ldata,
|
|
const CustomData *pdata,
|
|
const CustomData *cdata,
|
|
ID *r_id)
|
|
{
|
|
CustomData reset;
|
|
|
|
CustomData_reset(&reset);
|
|
|
|
switch (id_type) {
|
|
case ID_ME: {
|
|
Mesh *me = (Mesh *)r_id;
|
|
memset((void *)me, 0, sizeof(*me));
|
|
|
|
me->edit_mesh = nullptr;
|
|
|
|
me->vdata = vdata ? *vdata : reset;
|
|
me->edata = edata ? *edata : reset;
|
|
me->ldata = ldata ? *ldata : reset;
|
|
me->pdata = pdata ? *pdata : reset;
|
|
|
|
break;
|
|
}
|
|
case ID_PT: {
|
|
PointCloud *pointcloud = (PointCloud *)r_id;
|
|
|
|
memset((void *)pointcloud, 0, sizeof(*pointcloud));
|
|
|
|
pointcloud->pdata = vdata ? *vdata : reset;
|
|
break;
|
|
}
|
|
case ID_CV: {
|
|
Curves *curves = (Curves *)r_id;
|
|
|
|
memset((void *)curves, 0, sizeof(*curves));
|
|
|
|
curves->geometry.point_data = vdata ? *vdata : reset;
|
|
curves->geometry.curve_data = cdata ? *cdata : reset;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
*((short *)r_id->name) = id_type;
|
|
}
|