This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/blenkernel/intern/attribute_access.cc
Hans Goudey 12becbf0df Mesh: Move selection flags to generic attributes
Using the attribute name semantics from T97452, this patch moves the
selection status of mesh elements from the `SELECT` of vertices, and
edges, and the `ME_FACE_SEL` of faces to generic boolean attribute
Storing this data as generic attributes can significantly simplify and
improve code, as described in T95965.

The attributes are called `.select_vert`, `.select_edge`, and
`.select_poly`. The `.` prefix means they are "UI attributes",so they
still contain original data edited by users, but they aren't meant to
be accessed procedurally by the user in arbitrary situations. They are
also be hidden in the spreadsheet and the attribute list.

Until 4.0, the attributes are still written to and read from the mesh
in the old way, so neither forward nor backward compatibility are
affected. This means memory requirements will be increased by one byte
per element when selection is used. When the flags are removed
completely, requirements will decrease.

Further notes:
* The `MVert` flag is empty at runtime now, so it can be ignored.
* `BMesh` is unchanged, otherwise the change would be much larger.
* Many tests have slightly different results, since the selection
  attribute uses more generic propagation. Previously you couldn't
  really rely on edit mode selections being propagated procedurally.
  Now it mostly works as expected.

Similar to 2480b55f21
Ref T95965

Differential Revision: https://developer.blender.org/D15795
2022-09-23 10:45:07 -05:00

1024 lines
32 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <utility>
#include "BKE_attribute_math.hh"
#include "BKE_customdata.h"
#include "BKE_deform.h"
#include "BKE_geometry_set.hh"
#include "BKE_mesh.h"
#include "BKE_pointcloud.h"
#include "BKE_type_conversions.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_pointcloud_types.h"
#include "BLI_array_utils.hh"
#include "BLI_color.hh"
#include "BLI_math_vec_types.hh"
#include "BLI_span.hh"
#include "FN_field.hh"
#include "BLT_translation.h"
#include "CLG_log.h"
#include "attribute_access_intern.hh"
using blender::float3;
using blender::GMutableSpan;
using blender::GSpan;
using blender::GVArrayImpl_For_GSpan;
using blender::Set;
using blender::StringRef;
using blender::StringRefNull;
using blender::bke::AttributeIDRef;
namespace blender::bke {
std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id)
{
if (attribute_id.is_named()) {
stream << attribute_id.name();
}
else if (attribute_id.is_anonymous()) {
const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id();
stream << "<" << BKE_anonymous_attribute_id_debug_name(&anonymous_id) << ">";
}
else {
stream << "<none>";
}
return stream;
}
const char *no_procedural_access_message =
"This attribute can not be accessed in a procedural context";
bool allow_procedural_attribute_access(StringRef attribute_name)
{
return !attribute_name.startswith(".sculpt") && !attribute_name.startswith(".select") &&
!attribute_name.startswith(".hide");
}
static int attribute_data_type_complexity(const eCustomDataType data_type)
{
switch (data_type) {
case CD_PROP_BOOL:
return 0;
case CD_PROP_INT8:
return 1;
case CD_PROP_INT32:
return 2;
case CD_PROP_FLOAT:
return 3;
case CD_PROP_FLOAT2:
return 4;
case CD_PROP_FLOAT3:
return 5;
case CD_PROP_BYTE_COLOR:
return 6;
case CD_PROP_COLOR:
return 7;
#if 0 /* These attribute types are not supported yet. */
case CD_PROP_STRING:
return 6;
#endif
default:
/* Only accept "generic" custom data types used by the attribute system. */
BLI_assert_unreachable();
return 0;
}
}
eCustomDataType attribute_data_type_highest_complexity(Span<eCustomDataType> data_types)
{
int highest_complexity = INT_MIN;
eCustomDataType most_complex_type = CD_PROP_COLOR;
for (const eCustomDataType data_type : data_types) {
const int complexity = attribute_data_type_complexity(data_type);
if (complexity > highest_complexity) {
highest_complexity = complexity;
most_complex_type = data_type;
}
}
return most_complex_type;
}
/**
* \note Generally the order should mirror the order of the domains
* established in each component's ComponentAttributeProviders.
*/
static int attribute_domain_priority(const eAttrDomain domain)
{
switch (domain) {
case ATTR_DOMAIN_INSTANCE:
return 0;
case ATTR_DOMAIN_CURVE:
return 1;
case ATTR_DOMAIN_FACE:
return 2;
case ATTR_DOMAIN_EDGE:
return 3;
case ATTR_DOMAIN_POINT:
return 4;
case ATTR_DOMAIN_CORNER:
return 5;
default:
/* Domain not supported in nodes yet. */
BLI_assert_unreachable();
return 0;
}
}
eAttrDomain attribute_domain_highest_priority(Span<eAttrDomain> domains)
{
int highest_priority = INT_MIN;
eAttrDomain highest_priority_domain = ATTR_DOMAIN_CORNER;
for (const eAttrDomain domain : domains) {
const int priority = attribute_domain_priority(domain);
if (priority > highest_priority) {
highest_priority = priority;
highest_priority_domain = domain;
}
}
return highest_priority_domain;
}
static AttributeIDRef attribute_id_from_custom_data_layer(const CustomDataLayer &layer)
{
if (layer.anonymous_id != nullptr) {
return layer.anonymous_id;
}
return layer.name;
}
static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data,
const eCustomDataType data_type,
const int domain_num,
const AttributeInit &initializer)
{
switch (initializer.type) {
case AttributeInit::Type::Construct: {
void *data = CustomData_add_layer(
&custom_data, data_type, CD_CONSTRUCT, nullptr, domain_num);
return data != nullptr;
}
case AttributeInit::Type::DefaultValue: {
void *data = CustomData_add_layer(
&custom_data, data_type, CD_SET_DEFAULT, nullptr, domain_num);
return data != nullptr;
}
case AttributeInit::Type::VArray: {
void *data = CustomData_add_layer(
&custom_data, data_type, CD_CONSTRUCT, nullptr, domain_num);
if (data == nullptr) {
return false;
}
const GVArray &varray = static_cast<const AttributeInitVArray &>(initializer).varray;
varray.materialize_to_uninitialized(varray.index_range(), data);
return true;
}
case AttributeInit::Type::MoveArray: {
void *source_data = static_cast<const AttributeInitMoveArray &>(initializer).data;
void *data = CustomData_add_layer(
&custom_data, data_type, CD_ASSIGN, source_data, domain_num);
if (data == nullptr) {
MEM_freeN(source_data);
return false;
}
return true;
}
}
BLI_assert_unreachable();
return false;
}
static void *add_generic_custom_data_layer(CustomData &custom_data,
const eCustomDataType data_type,
const eCDAllocType alloctype,
void *layer_data,
const int domain_num,
const AttributeIDRef &attribute_id)
{
if (attribute_id.is_named()) {
char attribute_name_c[MAX_NAME];
attribute_id.name().copy(attribute_name_c);
return CustomData_add_layer_named(
&custom_data, data_type, alloctype, layer_data, domain_num, attribute_name_c);
}
const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id();
return CustomData_add_layer_anonymous(
&custom_data, data_type, alloctype, layer_data, domain_num, &anonymous_id);
}
static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attribute_id,
CustomData &custom_data,
const eCustomDataType data_type,
const int domain_num,
const AttributeInit &initializer)
{
const int old_layer_num = custom_data.totlayer;
switch (initializer.type) {
case AttributeInit::Type::Construct: {
add_generic_custom_data_layer(
custom_data, data_type, CD_CONSTRUCT, nullptr, domain_num, attribute_id);
break;
}
case AttributeInit::Type::DefaultValue: {
add_generic_custom_data_layer(
custom_data, data_type, CD_SET_DEFAULT, nullptr, domain_num, attribute_id);
break;
}
case AttributeInit::Type::VArray: {
void *data = add_generic_custom_data_layer(
custom_data, data_type, CD_CONSTRUCT, nullptr, domain_num, attribute_id);
if (data != nullptr) {
const GVArray &varray = static_cast<const AttributeInitVArray &>(initializer).varray;
varray.materialize_to_uninitialized(varray.index_range(), data);
}
break;
}
case AttributeInit::Type::MoveArray: {
void *source_data = static_cast<const AttributeInitMoveArray &>(initializer).data;
void *data = add_generic_custom_data_layer(
custom_data, data_type, CD_ASSIGN, source_data, domain_num, attribute_id);
if (source_data != nullptr && data == nullptr) {
MEM_freeN(source_data);
}
break;
}
}
return old_layer_num < custom_data.totlayer;
}
static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer,
const AttributeIDRef &attribute_id)
{
if (!attribute_id) {
return false;
}
if (attribute_id.is_anonymous()) {
return layer.anonymous_id == &attribute_id.anonymous_id();
}
return layer.name == attribute_id.name();
}
bool BuiltinCustomDataLayerProvider::layer_exists(const CustomData &custom_data) const
{
if (stored_as_named_attribute_) {
return CustomData_get_named_layer_index(&custom_data, stored_type_, name_.c_str()) != -1;
}
return CustomData_has_layer(&custom_data, stored_type_);
}
GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const void *owner) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner);
if (custom_data == nullptr) {
return {};
}
/* When the number of elements is zero, layers might have null data but still exist. */
const int element_num = custom_data_access_.get_element_num(owner);
if (element_num == 0) {
if (this->layer_exists(*custom_data)) {
return as_read_attribute_(nullptr, 0);
}
return {};
}
const void *data = nullptr;
if (stored_as_named_attribute_) {
data = CustomData_get_layer_named(custom_data, stored_type_, name_.c_str());
}
else {
data = CustomData_get_layer(custom_data, stored_type_);
}
if (data == nullptr) {
return {};
}
return as_read_attribute_(data, element_num);
}
GAttributeWriter BuiltinCustomDataLayerProvider::try_get_for_write(void *owner) const
{
if (writable_ != Writable) {
return {};
}
CustomData *custom_data = custom_data_access_.get_custom_data(owner);
if (custom_data == nullptr) {
return {};
}
std::function<void()> tag_modified_fn;
if (update_on_change_ != nullptr) {
tag_modified_fn = [owner, update = update_on_change_]() { update(owner); };
}
/* When the number of elements is zero, layers might have null data but still exist. */
const int element_num = custom_data_access_.get_element_num(owner);
if (element_num == 0) {
if (this->layer_exists(*custom_data)) {
return {as_write_attribute_(nullptr, 0), domain_, std::move(tag_modified_fn)};
}
return {};
}
void *data = nullptr;
if (stored_as_named_attribute_) {
data = CustomData_duplicate_referenced_layer_named(
custom_data, stored_type_, name_.c_str(), element_num);
}
else {
data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, element_num);
}
if (data == nullptr) {
return {};
}
return {as_write_attribute_(data, element_num), domain_, std::move(tag_modified_fn)};
}
bool BuiltinCustomDataLayerProvider::try_delete(void *owner) const
{
if (deletable_ != Deletable) {
return false;
}
CustomData *custom_data = custom_data_access_.get_custom_data(owner);
if (custom_data == nullptr) {
return {};
}
auto update = [&]() {
if (update_on_change_ != nullptr) {
update_on_change_(owner);
}
};
const int element_num = custom_data_access_.get_element_num(owner);
if (stored_as_named_attribute_) {
if (CustomData_free_layer_named(custom_data, name_.c_str(), element_num)) {
update();
return true;
}
return false;
}
const int layer_index = CustomData_get_layer_index(custom_data, stored_type_);
if (CustomData_free_layer(custom_data, stored_type_, element_num, layer_index)) {
update();
return true;
}
return false;
}
bool BuiltinCustomDataLayerProvider::try_create(void *owner,
const AttributeInit &initializer) const
{
if (createable_ != Creatable) {
return false;
}
CustomData *custom_data = custom_data_access_.get_custom_data(owner);
if (custom_data == nullptr) {
return false;
}
const int element_num = custom_data_access_.get_element_num(owner);
if (stored_as_named_attribute_) {
if (CustomData_get_layer_named(custom_data, data_type_, name_.c_str())) {
/* Exists already. */
return false;
}
return add_custom_data_layer_from_attribute_init(
name_, *custom_data, stored_type_, element_num, initializer);
}
if (CustomData_get_layer(custom_data, stored_type_) != nullptr) {
/* Exists already. */
return false;
}
return add_builtin_type_custom_data_layer_from_init(
*custom_data, stored_type_, element_num, initializer);
}
bool BuiltinCustomDataLayerProvider::exists(const void *owner) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner);
if (custom_data == nullptr) {
return false;
}
if (stored_as_named_attribute_) {
return CustomData_get_layer_named(custom_data, stored_type_, name_.c_str()) != nullptr;
}
return CustomData_get_layer(custom_data, stored_type_) != nullptr;
}
GAttributeReader CustomDataAttributeProvider::try_get_for_read(
const void *owner, const AttributeIDRef &attribute_id) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner);
if (custom_data == nullptr) {
return {};
}
const int element_num = custom_data_access_.get_element_num(owner);
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) {
continue;
}
const CPPType *type = custom_data_type_to_cpp_type((eCustomDataType)layer.type);
if (type == nullptr) {
continue;
}
GSpan data{*type, layer.data, element_num};
return {GVArray::ForSpan(data), domain_};
}
return {};
}
GAttributeWriter CustomDataAttributeProvider::try_get_for_write(
void *owner, const AttributeIDRef &attribute_id) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(owner);
if (custom_data == nullptr) {
return {};
}
const int element_num = custom_data_access_.get_element_num(owner);
for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) {
continue;
}
if (attribute_id.is_named()) {
CustomData_duplicate_referenced_layer_named(
custom_data, layer.type, layer.name, element_num);
}
else {
CustomData_duplicate_referenced_layer_anonymous(
custom_data, layer.type, &attribute_id.anonymous_id(), element_num);
}
const CPPType *type = custom_data_type_to_cpp_type((eCustomDataType)layer.type);
if (type == nullptr) {
continue;
}
GMutableSpan data{*type, layer.data, element_num};
return {GVMutableArray::ForSpan(data), domain_};
}
return {};
}
bool CustomDataAttributeProvider::try_delete(void *owner, const AttributeIDRef &attribute_id) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(owner);
if (custom_data == nullptr) {
return false;
}
const int element_num = custom_data_access_.get_element_num(owner);
;
for (const int i : IndexRange(custom_data->totlayer)) {
const CustomDataLayer &layer = custom_data->layers[i];
if (this->type_is_supported((eCustomDataType)layer.type) &&
custom_data_layer_matches_attribute_id(layer, attribute_id)) {
CustomData_free_layer(custom_data, layer.type, element_num, i);
return true;
}
}
return false;
}
bool CustomDataAttributeProvider::try_create(void *owner,
const AttributeIDRef &attribute_id,
const eAttrDomain domain,
const eCustomDataType data_type,
const AttributeInit &initializer) const
{
if (domain_ != domain) {
return false;
}
if (!this->type_is_supported(data_type)) {
return false;
}
CustomData *custom_data = custom_data_access_.get_custom_data(owner);
if (custom_data == nullptr) {
return false;
}
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
return false;
}
}
const int element_num = custom_data_access_.get_element_num(owner);
add_custom_data_layer_from_attribute_init(
attribute_id, *custom_data, data_type, element_num, initializer);
return true;
}
bool CustomDataAttributeProvider::foreach_attribute(const void *owner,
const AttributeForeachCallback callback) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner);
if (custom_data == nullptr) {
return true;
}
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
const eCustomDataType data_type = (eCustomDataType)layer.type;
if (this->type_is_supported(data_type)) {
AttributeMetaData meta_data{domain_, data_type};
const AttributeIDRef attribute_id = attribute_id_from_custom_data_layer(layer);
if (!callback(attribute_id, meta_data)) {
return false;
}
}
}
return true;
}
GAttributeReader NamedLegacyCustomDataProvider::try_get_for_read(
const void *owner, const AttributeIDRef &attribute_id) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner);
if (custom_data == nullptr) {
return {};
}
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
if (layer.type == stored_type_) {
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
const int domain_num = custom_data_access_.get_element_num(owner);
return {as_read_attribute_(layer.data, domain_num), domain_};
}
}
}
return {};
}
GAttributeWriter NamedLegacyCustomDataProvider::try_get_for_write(
void *owner, const AttributeIDRef &attribute_id) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(owner);
if (custom_data == nullptr) {
return {};
}
for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
if (layer.type == stored_type_) {
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
const int element_num = custom_data_access_.get_element_num(owner);
void *data = CustomData_duplicate_referenced_layer_named(
custom_data, stored_type_, layer.name, element_num);
return {as_write_attribute_(data, element_num), domain_};
}
}
}
return {};
}
bool NamedLegacyCustomDataProvider::try_delete(void *owner,
const AttributeIDRef &attribute_id) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(owner);
if (custom_data == nullptr) {
return false;
}
for (const int i : IndexRange(custom_data->totlayer)) {
const CustomDataLayer &layer = custom_data->layers[i];
if (layer.type == stored_type_) {
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
const int element_num = custom_data_access_.get_element_num(owner);
CustomData_free_layer(custom_data, stored_type_, element_num, i);
return true;
}
}
}
return false;
}
bool NamedLegacyCustomDataProvider::foreach_attribute(
const void *owner, const AttributeForeachCallback callback) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner);
if (custom_data == nullptr) {
return true;
}
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
if (layer.type == stored_type_) {
AttributeMetaData meta_data{domain_, attribute_type_};
if (!callback(layer.name, meta_data)) {
return false;
}
}
}
return true;
}
void NamedLegacyCustomDataProvider::foreach_domain(
const FunctionRef<void(eAttrDomain)> callback) const
{
callback(domain_);
}
CustomDataAttributes::CustomDataAttributes()
{
CustomData_reset(&data);
size_ = 0;
}
CustomDataAttributes::~CustomDataAttributes()
{
CustomData_free(&data, size_);
}
CustomDataAttributes::CustomDataAttributes(const CustomDataAttributes &other)
{
size_ = other.size_;
CustomData_copy(&other.data, &data, CD_MASK_ALL, CD_DUPLICATE, size_);
}
CustomDataAttributes::CustomDataAttributes(CustomDataAttributes &&other)
{
size_ = other.size_;
data = other.data;
CustomData_reset(&other.data);
}
CustomDataAttributes &CustomDataAttributes::operator=(const CustomDataAttributes &other)
{
if (this != &other) {
CustomData_copy(&other.data, &data, CD_MASK_ALL, CD_DUPLICATE, other.size_);
size_ = other.size_;
}
return *this;
}
std::optional<GSpan> CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id) const
{
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
const CPPType *cpp_type = custom_data_type_to_cpp_type((eCustomDataType)layer.type);
BLI_assert(cpp_type != nullptr);
return GSpan(*cpp_type, layer.data, size_);
}
}
return {};
}
GVArray CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id,
const eCustomDataType data_type,
const void *default_value) const
{
const CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
std::optional<GSpan> attribute = this->get_for_read(attribute_id);
if (!attribute) {
const int domain_num = this->size_;
return GVArray::ForSingle(
*type, domain_num, (default_value == nullptr) ? type->default_value() : default_value);
}
if (attribute->type() == *type) {
return GVArray::ForSpan(*attribute);
}
const blender::bke::DataTypeConversions &conversions =
blender::bke::get_implicit_type_conversions();
return conversions.try_convert(GVArray::ForSpan(*attribute), *type);
}
std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const AttributeIDRef &attribute_id)
{
for (CustomDataLayer &layer : MutableSpan(data.layers, data.totlayer)) {
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
const CPPType *cpp_type = custom_data_type_to_cpp_type((eCustomDataType)layer.type);
BLI_assert(cpp_type != nullptr);
return GMutableSpan(*cpp_type, layer.data, size_);
}
}
return {};
}
bool CustomDataAttributes::create(const AttributeIDRef &attribute_id,
const eCustomDataType data_type)
{
void *result = add_generic_custom_data_layer(
data, data_type, CD_SET_DEFAULT, nullptr, size_, attribute_id);
return result != nullptr;
}
bool CustomDataAttributes::create_by_move(const AttributeIDRef &attribute_id,
const eCustomDataType data_type,
void *buffer)
{
void *result = add_generic_custom_data_layer(
data, data_type, CD_ASSIGN, buffer, size_, attribute_id);
return result != nullptr;
}
bool CustomDataAttributes::remove(const AttributeIDRef &attribute_id)
{
for (const int i : IndexRange(data.totlayer)) {
const CustomDataLayer &layer = data.layers[i];
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
CustomData_free_layer(&data, layer.type, size_, i);
return true;
}
}
return false;
}
void CustomDataAttributes::reallocate(const int size)
{
const int old_size = size_;
size_ = size;
CustomData_realloc(&data, old_size, size_);
if (size_ > old_size) {
/* Fill default new values. */
const int new_elements_num = size_ - old_size;
this->foreach_attribute(
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData /*meta_data*/) {
GMutableSpan new_data = this->get_for_write(id)->take_back(new_elements_num);
const CPPType &type = new_data.type();
type.fill_assign_n(type.default_value(), new_data.data(), new_data.size());
return true;
},
/* Dummy. */
ATTR_DOMAIN_POINT);
}
}
void CustomDataAttributes::clear()
{
CustomData_free(&data, size_);
size_ = 0;
}
bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback callback,
const eAttrDomain domain) const
{
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
AttributeMetaData meta_data{domain, (eCustomDataType)layer.type};
const AttributeIDRef attribute_id = attribute_id_from_custom_data_layer(layer);
if (!callback(attribute_id, meta_data)) {
return false;
}
}
return true;
}
/* -------------------------------------------------------------------- */
/** \name Attribute API
* \{ */
static blender::GVArray try_adapt_data_type(blender::GVArray varray,
const blender::CPPType &to_type)
{
const blender::bke::DataTypeConversions &conversions =
blender::bke::get_implicit_type_conversions();
return conversions.try_convert(std::move(varray), to_type);
}
GVArray AttributeAccessor::lookup(const AttributeIDRef &attribute_id,
const std::optional<eAttrDomain> domain,
const std::optional<eCustomDataType> data_type) const
{
GAttributeReader attribute = this->lookup(attribute_id);
if (!attribute) {
return {};
}
GVArray varray = std::move(attribute.varray);
if (domain.has_value()) {
if (attribute.domain != domain) {
varray = this->adapt_domain(varray, attribute.domain, *domain);
if (!varray) {
return {};
}
}
}
if (data_type.has_value()) {
const CPPType &type = *custom_data_type_to_cpp_type(*data_type);
if (varray.type() != type) {
varray = try_adapt_data_type(std::move(varray), type);
if (!varray) {
return {};
}
}
}
return varray;
}
GVArray AttributeAccessor::lookup_or_default(const AttributeIDRef &attribute_id,
const eAttrDomain domain,
const eCustomDataType data_type,
const void *default_value) const
{
GVArray varray = this->lookup(attribute_id, domain, data_type);
if (varray) {
return varray;
}
const CPPType &type = *custom_data_type_to_cpp_type(data_type);
const int64_t domain_size = this->domain_size(domain);
if (default_value == nullptr) {
return GVArray::ForSingleRef(type, domain_size, type.default_value());
}
return GVArray::ForSingle(type, domain_size, default_value);
}
Set<AttributeIDRef> AttributeAccessor::all_ids() const
{
Set<AttributeIDRef> ids;
this->for_all(
[&](const AttributeIDRef &attribute_id, const AttributeMetaData & /* meta_data */) {
ids.add(attribute_id);
return true;
});
return ids;
}
void MutableAttributeAccessor::remove_anonymous()
{
Vector<const AnonymousAttributeID *> anonymous_ids;
for (const AttributeIDRef &id : this->all_ids()) {
if (id.is_anonymous()) {
anonymous_ids.append(&id.anonymous_id());
}
}
while (!anonymous_ids.is_empty()) {
this->remove(anonymous_ids.pop_last());
}
}
/**
* Debug utility that checks whether the #finish function of an #AttributeWriter has been called.
*/
#ifdef DEBUG
struct FinishCallChecker {
std::string name;
bool finish_called = false;
std::function<void()> real_finish_fn;
~FinishCallChecker()
{
if (!this->finish_called) {
std::cerr << "Forgot to call `finish()` for '" << this->name << "'.\n";
}
}
};
#endif
GAttributeWriter MutableAttributeAccessor::lookup_for_write(const AttributeIDRef &attribute_id)
{
GAttributeWriter attribute = fn_->lookup_for_write(owner_, attribute_id);
/* Check that the #finish method is called in debug builds. */
#ifdef DEBUG
if (attribute) {
auto checker = std::make_shared<FinishCallChecker>();
if (attribute_id.is_named()) {
checker->name = attribute_id.name();
}
else {
checker->name = BKE_anonymous_attribute_id_debug_name(&attribute_id.anonymous_id());
}
checker->real_finish_fn = attribute.tag_modified_fn;
attribute.tag_modified_fn = [checker]() {
if (checker->real_finish_fn) {
checker->real_finish_fn();
}
checker->finish_called = true;
};
}
#endif
return attribute;
}
GSpanAttributeWriter MutableAttributeAccessor::lookup_for_write_span(
const AttributeIDRef &attribute_id)
{
GAttributeWriter attribute = this->lookup_for_write(attribute_id);
if (attribute) {
return GSpanAttributeWriter{std::move(attribute), true};
}
return {};
}
GAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write(
const AttributeIDRef &attribute_id,
const eAttrDomain domain,
const eCustomDataType data_type,
const AttributeInit &initializer)
{
std::optional<AttributeMetaData> meta_data = this->lookup_meta_data(attribute_id);
if (meta_data.has_value()) {
if (meta_data->domain == domain && meta_data->data_type == data_type) {
return this->lookup_for_write(attribute_id);
}
return {};
}
if (this->add(attribute_id, domain, data_type, initializer)) {
return this->lookup_for_write(attribute_id);
}
return {};
}
GSpanAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write_span(
const AttributeIDRef &attribute_id,
const eAttrDomain domain,
const eCustomDataType data_type,
const AttributeInit &initializer)
{
GAttributeWriter attribute = this->lookup_or_add_for_write(
attribute_id, domain, data_type, initializer);
if (attribute) {
return GSpanAttributeWriter{std::move(attribute), true};
}
return {};
}
GSpanAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write_only_span(
const AttributeIDRef &attribute_id, const eAttrDomain domain, const eCustomDataType data_type)
{
GAttributeWriter attribute = this->lookup_or_add_for_write(
attribute_id, domain, data_type, AttributeInitConstruct());
if (attribute) {
return GSpanAttributeWriter{std::move(attribute), false};
}
return {};
}
fn::GField AttributeValidator::validate_field_if_necessary(const fn::GField &field) const
{
if (function) {
auto validate_op = fn::FieldOperation::Create(*function, {field});
return fn::GField(validate_op);
}
return field;
}
Vector<AttributeTransferData> retrieve_attributes_for_transfer(
const bke::AttributeAccessor src_attributes,
bke::MutableAttributeAccessor dst_attributes,
const eAttrDomainMask domain_mask,
const Set<std::string> &skip)
{
Vector<AttributeTransferData> attributes;
src_attributes.for_all(
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) {
if (!(ATTR_DOMAIN_AS_MASK(meta_data.domain) & domain_mask)) {
return true;
}
if (id.is_named() && skip.contains(id.name())) {
return true;
}
if (!id.should_be_kept()) {
return true;
}
GVArray src = src_attributes.lookup(id, meta_data.domain);
BLI_assert(src);
bke::GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
id, meta_data.domain, meta_data.data_type);
BLI_assert(dst);
attributes.append({std::move(src), meta_data, std::move(dst)});
return true;
});
return attributes;
}
void copy_attribute_domain(const AttributeAccessor src_attributes,
MutableAttributeAccessor dst_attributes,
const IndexMask selection,
const eAttrDomain domain,
const Set<std::string> &skip)
{
src_attributes.for_all(
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData &meta_data) {
if (meta_data.domain != domain) {
return true;
}
if (id.is_named() && skip.contains(id.name())) {
return true;
}
if (!id.should_be_kept()) {
return true;
}
const GVArray src = src_attributes.lookup(id, meta_data.domain);
BLI_assert(src);
/* Copy attribute. */
GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
id, domain, meta_data.data_type);
array_utils::copy(src, selection, dst.span);
dst.finish();
return true;
});
}
} // namespace blender::bke
/** \} */