Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2021-02-09 13:24:28 +0300
committerJacques Lucke <jacques@blender.org>2021-02-09 13:24:28 +0300
commit4601e3a5916c7746ad29d20add91651929c6f621 (patch)
tree5ef438331c1370dc917690f2fc1a712c7da4f582 /source
parent494913f17abc12921717f15a8a76849517eca117 (diff)
Geometry Nodes: refactor internal attribute access architecture
Goals: * Clarify the distinction between builtin and other attributes at the code level. * Reduce number of places that need to be modified to add more builtin attributes. * Reduce number of virtual methods that need to be implemented by e.g. `MeshComponent`. To achieve these goals, this patch implements the concept of "attribute providers". An attribute provider knows how to give access to attributes on a geometry component. Each geometry component can have multiple attribute providers, whereby each provider manages an different set of attributes. The separation of builtin and other attributes is now done at the attribute provider level. There are two types of attribute providers. One for builtin attributes and one for all others. This refactor also helps with T84297. Differential Revision: https://developer.blender.org/D10341
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh66
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc1382
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc11
3 files changed, 935 insertions, 524 deletions
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index 2bed1a36ed7..9a871574f6f 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -68,6 +68,10 @@ template<> struct DefaultHash<GeometryComponentType> {
};
} // namespace blender
+namespace blender::bke {
+struct ComponentAttributeProviders;
+}
+
class GeometryComponent;
/**
@@ -152,23 +156,18 @@ class GeometryComponent {
bool attribute_exists(const blender::StringRef attribute_name) const;
/* Returns true when the geometry component supports this attribute domain. */
- virtual bool attribute_domain_supported(const AttributeDomain domain) const;
- /* Returns true when the given data type is supported in the given domain. */
- virtual bool attribute_domain_with_type_supported(const AttributeDomain domain,
- const CustomDataType data_type) const;
+ bool attribute_domain_supported(const AttributeDomain domain) const;
/* Can only be used with supported domain types. */
virtual int attribute_domain_size(const AttributeDomain domain) const;
- /* Attributes with these names cannot be created or removed via this api. */
- virtual bool attribute_is_builtin(const blender::StringRef attribute_name) const;
/* Get read-only access to the highest priority attribute with the given name.
* Returns null if the attribute does not exist. */
- virtual blender::bke::ReadAttributePtr attribute_try_get_for_read(
+ blender::bke::ReadAttributePtr attribute_try_get_for_read(
const blender::StringRef attribute_name) const;
/* Get read and write access to the highest priority attribute with the given name.
* Returns null if the attribute does not exist. */
- virtual blender::bke::WriteAttributePtr attribute_try_get_for_write(
+ blender::bke::WriteAttributePtr attribute_try_get_for_write(
const blender::StringRef attribute_name);
/* Get a read-only attribute for the domain based on the given attribute. This can be used to
@@ -178,14 +177,14 @@ class GeometryComponent {
blender::bke::ReadAttributePtr attribute, const AttributeDomain domain) const;
/* Returns true when the attribute has been deleted. */
- virtual bool attribute_try_delete(const blender::StringRef attribute_name);
+ bool attribute_try_delete(const blender::StringRef attribute_name);
/* Returns true when the attribute has been created. */
- virtual bool attribute_try_create(const blender::StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type);
+ bool attribute_try_create(const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type);
- virtual blender::Set<std::string> attribute_names() const;
+ blender::Set<std::string> attribute_names() const;
virtual bool is_empty() const;
/* Get a read-only attribute for the given domain and data type.
@@ -257,6 +256,9 @@ class GeometryComponent {
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value = nullptr);
+
+ private:
+ virtual const blender::bke::ComponentAttributeProviders *get_attribute_providers() const;
};
template<typename T>
@@ -359,30 +361,20 @@ class MeshComponent : public GeometryComponent {
Mesh *release();
void copy_vertex_group_names_from_object(const struct Object &object);
+ const blender::Map<std::string, int> &vertex_group_names() const;
+ blender::Map<std::string, int> &vertex_group_names();
const Mesh *get_for_read() const;
Mesh *get_for_write();
- bool attribute_domain_supported(const AttributeDomain domain) const final;
- bool attribute_domain_with_type_supported(const AttributeDomain domain,
- const CustomDataType data_type) const final;
int attribute_domain_size(const AttributeDomain domain) const final;
- bool attribute_is_builtin(const blender::StringRef attribute_name) const final;
-
- blender::bke::ReadAttributePtr attribute_try_get_for_read(
- const blender::StringRef attribute_name) const final;
- blender::bke::WriteAttributePtr attribute_try_get_for_write(
- const blender::StringRef attribute_name) final;
- bool attribute_try_delete(const blender::StringRef attribute_name) final;
- bool attribute_try_create(const blender::StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type) final;
-
- blender::Set<std::string> attribute_names() const final;
bool is_empty() const final;
static constexpr inline GeometryComponentType static_type = GeometryComponentType::Mesh;
+
+ private:
+ const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
};
/** A geometry component that stores a point cloud. */
@@ -405,26 +397,14 @@ class PointCloudComponent : public GeometryComponent {
const PointCloud *get_for_read() const;
PointCloud *get_for_write();
- bool attribute_domain_supported(const AttributeDomain domain) const final;
- bool attribute_domain_with_type_supported(const AttributeDomain domain,
- const CustomDataType data_type) const final;
int attribute_domain_size(const AttributeDomain domain) const final;
- bool attribute_is_builtin(const blender::StringRef attribute_name) const final;
-
- blender::bke::ReadAttributePtr attribute_try_get_for_read(
- const blender::StringRef attribute_name) const final;
- blender::bke::WriteAttributePtr attribute_try_get_for_write(
- const blender::StringRef attribute_name) final;
- bool attribute_try_delete(const blender::StringRef attribute_name) final;
- bool attribute_try_create(const blender::StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type) final;
-
- blender::Set<std::string> attribute_names() const final;
bool is_empty() const final;
static constexpr inline GeometryComponentType static_type = GeometryComponentType::PointCloud;
+
+ private:
+ const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
};
/** A geometry component that stores instances. */
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index 85dabe4490c..772309349ff 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -323,29 +323,24 @@ template<typename T> class ArrayReadAttribute final : public ReadAttribute {
}
};
-template<typename StructT, typename ElemT, typename GetFuncT, typename SetFuncT>
+template<typename StructT,
+ typename ElemT,
+ ElemT (*GetFunc)(const StructT &),
+ void (*SetFunc)(StructT &, const ElemT &)>
class DerivedArrayWriteAttribute final : public WriteAttribute {
private:
MutableSpan<StructT> data_;
- GetFuncT get_function_;
- SetFuncT set_function_;
public:
- DerivedArrayWriteAttribute(AttributeDomain domain,
- MutableSpan<StructT> data,
- GetFuncT get_function,
- SetFuncT set_function)
- : WriteAttribute(domain, CPPType::get<ElemT>(), data.size()),
- data_(data),
- get_function_(std::move(get_function)),
- set_function_(std::move(set_function))
+ DerivedArrayWriteAttribute(AttributeDomain domain, MutableSpan<StructT> data)
+ : WriteAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
{
}
void get_internal(const int64_t index, void *r_value) const override
{
const StructT &struct_value = data_[index];
- const ElemT value = get_function_(struct_value);
+ const ElemT value = GetFunc(struct_value);
new (r_value) ElemT(value);
}
@@ -353,28 +348,25 @@ class DerivedArrayWriteAttribute final : public WriteAttribute {
{
StructT &struct_value = data_[index];
const ElemT &typed_value = *reinterpret_cast<const ElemT *>(value);
- set_function_(struct_value, typed_value);
+ SetFunc(struct_value, typed_value);
}
};
-template<typename StructT, typename ElemT, typename GetFuncT>
+template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
class DerivedArrayReadAttribute final : public ReadAttribute {
private:
Span<StructT> data_;
- GetFuncT get_function_;
public:
- DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data, GetFuncT get_function)
- : ReadAttribute(domain, CPPType::get<ElemT>(), data.size()),
- data_(data),
- get_function_(std::move(get_function))
+ DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data)
+ : ReadAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
{
}
void get_internal(const int64_t index, void *r_value) const override
{
const StructT &struct_value = data_[index];
- const ElemT value = get_function_(struct_value);
+ const ElemT value = GetFunc(struct_value);
new (r_value) ElemT(value);
}
};
@@ -492,144 +484,835 @@ CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type)
return static_cast<CustomDataType>(-1);
}
-} // namespace blender::bke
+/**
+ * A #BuiltinAttributeProvider is responsible for exactly one attribute on a geometry component.
+ * The attribute is identified by its name and has a fixed domain and type. Builtin attributes do
+ * not follow the same loose rules as other attributes, because they are mapped to internal
+ * "legacy" data structures. For example, some builtin attributes cannot be deleted. */
+class BuiltinAttributeProvider {
+ public:
+ /* Some utility enums to avoid hard to read bools in function calls. */
+ enum CreatableEnum {
+ Creatable,
+ NonCreatable,
+ };
+ enum WritableEnum {
+ Writable,
+ Readonly,
+ };
+ enum DeletableEnum {
+ Deletable,
+ NonDeletable,
+ };
-/* -------------------------------------------------------------------- */
-/** \name Utilities for Accessing Attributes
- * \{ */
+ protected:
+ const std::string name_;
+ const AttributeDomain domain_;
+ const CustomDataType data_type_;
+ const CreatableEnum createable_;
+ const WritableEnum writable_;
+ const DeletableEnum deletable_;
-static ReadAttributePtr read_attribute_from_custom_data(const CustomData &custom_data,
- const int size,
- const StringRef attribute_name,
- const AttributeDomain domain)
-{
- using namespace blender;
- using namespace blender::bke;
- for (const CustomDataLayer &layer : Span(custom_data.layers, custom_data.totlayer)) {
- if (layer.name != nullptr && layer.name == attribute_name) {
- switch (layer.type) {
+ public:
+ BuiltinAttributeProvider(std::string name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const CreatableEnum createable,
+ const WritableEnum writable,
+ const DeletableEnum deletable)
+ : name_(std::move(name)),
+ domain_(domain),
+ data_type_(data_type),
+ createable_(createable),
+ writable_(writable),
+ deletable_(deletable)
+ {
+ }
+
+ virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component) const = 0;
+ virtual WriteAttributePtr try_get_for_write(GeometryComponent &component) const = 0;
+ virtual bool try_delete(GeometryComponent &component) const = 0;
+ virtual bool try_create(GeometryComponent &UNUSED(component)) const = 0;
+ virtual bool exists(const GeometryComponent &component) const = 0;
+
+ StringRefNull name() const
+ {
+ return name_;
+ }
+
+ AttributeDomain domain() const
+ {
+ return domain_;
+ }
+
+ CustomDataType data_type() const
+ {
+ return data_type_;
+ }
+};
+
+/**
+ * A #DynamicAttributesProvider manages a set of named attributes on a geometry component. Each
+ * attribute has a name, domain and type.
+ */
+class DynamicAttributesProvider {
+ public:
+ virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const = 0;
+ virtual WriteAttributePtr try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const = 0;
+ virtual bool try_delete(GeometryComponent &component, const StringRef attribute_name) const = 0;
+ virtual bool try_create(GeometryComponent &UNUSED(component),
+ const StringRef UNUSED(attribute_name),
+ const AttributeDomain UNUSED(domain),
+ const CustomDataType UNUSED(data_type)) const
+ {
+ /* Some providers should not create new attributes. */
+ return false;
+ };
+
+ virtual void list(const GeometryComponent &component, Set<std::string> &r_names) const = 0;
+ virtual void supported_domains(Vector<AttributeDomain> &r_domains) const = 0;
+};
+
+/**
+ * Utility to group together multiple functions that are used to access custom data on geometry
+ * components in a generic way.
+ */
+struct CustomDataAccessInfo {
+ using CustomDataGetter = CustomData *(*)(GeometryComponent &component);
+ using ConstCustomDataGetter = const CustomData *(*)(const GeometryComponent &component);
+ using UpdateCustomDataPointers = void (*)(GeometryComponent &component);
+
+ CustomDataGetter get_custom_data;
+ ConstCustomDataGetter get_const_custom_data;
+ UpdateCustomDataPointers update_custom_data_pointers;
+};
+
+/**
+ * This provider is used to provide access to builtin attributes. It supports making internal types
+ * available as different types. For example, the vertex position attribute is stored as part of
+ * the #MVert struct, but is exposed as float3 attribute.
+ */
+class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
+ using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
+ using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
+ const CustomDataType stored_type_;
+ const CustomDataAccessInfo custom_data_access_;
+ const AsReadAttribute as_read_attribute_;
+ const AsWriteAttribute as_write_attribute_;
+
+ public:
+ BuiltinCustomDataLayerProvider(std::string attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType attribute_type,
+ const CustomDataType stored_type,
+ const CreatableEnum creatable,
+ const WritableEnum writable,
+ const DeletableEnum deletable,
+ const CustomDataAccessInfo custom_data_access,
+ const AsReadAttribute as_read_attribute,
+ const AsWriteAttribute as_write_attribute)
+ : BuiltinAttributeProvider(
+ std::move(attribute_name), domain, attribute_type, creatable, writable, deletable),
+ stored_type_(stored_type),
+ custom_data_access_(custom_data_access),
+ as_read_attribute_(as_read_attribute),
+ as_write_attribute_(as_write_attribute)
+ {
+ }
+
+ ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final
+ {
+ const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
+ if (custom_data == nullptr) {
+ return {};
+ }
+ const int domain_size = component.attribute_domain_size(domain_);
+ const void *data = CustomData_get_layer(custom_data, stored_type_);
+ if (data == nullptr) {
+ return {};
+ }
+ return as_read_attribute_(data, domain_size);
+ }
+
+ WriteAttributePtr try_get_for_write(GeometryComponent &component) const final
+ {
+ if (writable_ != Writable) {
+ return {};
+ }
+ CustomData *custom_data = custom_data_access_.get_custom_data(component);
+ if (custom_data == nullptr) {
+ return {};
+ }
+ const int domain_size = component.attribute_domain_size(domain_);
+ void *data = CustomData_get_layer(custom_data, stored_type_);
+ if (data == nullptr) {
+ return {};
+ }
+ void *new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_size);
+ if (data != new_data) {
+ custom_data_access_.update_custom_data_pointers(component);
+ data = new_data;
+ }
+ return as_write_attribute_(data, domain_size);
+ }
+
+ bool try_delete(GeometryComponent &component) const final
+ {
+ if (deletable_ != Deletable) {
+ return false;
+ }
+ CustomData *custom_data = custom_data_access_.get_custom_data(component);
+ if (custom_data == nullptr) {
+ return {};
+ }
+
+ const int domain_size = component.attribute_domain_size(domain_);
+ const int layer_index = CustomData_get_layer_index(custom_data, stored_type_);
+ const bool delete_success = CustomData_free_layer(
+ custom_data, stored_type_, domain_size, layer_index);
+ if (delete_success) {
+ custom_data_access_.update_custom_data_pointers(component);
+ }
+ return delete_success;
+ }
+
+ bool try_create(GeometryComponent &component) const final
+ {
+ if (createable_ != Creatable) {
+ return false;
+ }
+ CustomData *custom_data = custom_data_access_.get_custom_data(component);
+ if (custom_data == nullptr) {
+ return false;
+ }
+ if (CustomData_get_layer(custom_data, stored_type_) != nullptr) {
+ /* Exists already. */
+ return false;
+ }
+ const int domain_size = component.attribute_domain_size(domain_);
+ const void *data = CustomData_add_layer(
+ custom_data, stored_type_, CD_DEFAULT, nullptr, domain_size);
+ const bool success = data != nullptr;
+ if (success) {
+ custom_data_access_.update_custom_data_pointers(component);
+ }
+ return success;
+ }
+
+ bool exists(const GeometryComponent &component) const final
+ {
+ const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
+ if (custom_data == nullptr) {
+ return false;
+ }
+ const void *data = CustomData_get_layer(custom_data, data_type_);
+ return data != nullptr;
+ }
+};
+
+/**
+ * This is the attribute provider for most user generated attributes.
+ */
+class CustomDataAttributeProvider final : public DynamicAttributesProvider {
+ private:
+ static constexpr uint64_t supported_types_mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 |
+ CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 |
+ CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL;
+ const AttributeDomain domain_;
+ const CustomDataAccessInfo custom_data_access_;
+
+ public:
+ CustomDataAttributeProvider(const AttributeDomain domain,
+ const CustomDataAccessInfo custom_data_access)
+ : domain_(domain), custom_data_access_(custom_data_access)
+ {
+ }
+
+ ReadAttributePtr try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final
+ {
+ const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
+ if (custom_data == nullptr) {
+ return {};
+ }
+ const int domain_size = component.attribute_domain_size(domain_);
+ for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
+ if (layer.name != attribute_name) {
+ continue;
+ }
+ const CustomDataType data_type = (CustomDataType)layer.type;
+ switch (data_type) {
case CD_PROP_FLOAT:
- return std::make_unique<ArrayReadAttribute<float>>(
- domain, Span(static_cast<float *>(layer.data), size));
+ return this->layer_to_read_attribute<float>(layer, domain_size);
case CD_PROP_FLOAT2:
- return std::make_unique<ArrayReadAttribute<float2>>(
- domain, Span(static_cast<float2 *>(layer.data), size));
+ return this->layer_to_read_attribute<float2>(layer, domain_size);
case CD_PROP_FLOAT3:
- return std::make_unique<ArrayReadAttribute<float3>>(
- domain, Span(static_cast<float3 *>(layer.data), size));
+ return this->layer_to_read_attribute<float3>(layer, domain_size);
case CD_PROP_INT32:
- return std::make_unique<ArrayReadAttribute<int>>(
- domain, Span(static_cast<int *>(layer.data), size));
+ return this->layer_to_read_attribute<int>(layer, domain_size);
case CD_PROP_COLOR:
- return std::make_unique<ArrayReadAttribute<Color4f>>(
- domain, Span(static_cast<Color4f *>(layer.data), size));
+ return this->layer_to_read_attribute<Color4f>(layer, domain_size);
case CD_PROP_BOOL:
- return std::make_unique<ArrayReadAttribute<bool>>(
- domain, Span(static_cast<bool *>(layer.data), size));
- case CD_MLOOPUV:
- auto get_uv = [](const MLoopUV &uv) { return float2(uv.uv); };
- return std::make_unique<DerivedArrayReadAttribute<MLoopUV, float2, decltype(get_uv)>>(
- domain, Span(static_cast<MLoopUV *>(layer.data), size), get_uv);
+ return this->layer_to_read_attribute<bool>(layer, domain_size);
+ default:
+ break;
}
}
+ return {};
}
- return {};
-}
-
-static WriteAttributePtr write_attribute_from_custom_data(
- CustomData &custom_data,
- const int size,
- const StringRef attribute_name,
- const AttributeDomain domain,
- const std::function<void()> &update_customdata_pointers)
-{
- using namespace blender;
- using namespace blender::bke;
- for (const CustomDataLayer &layer : Span(custom_data.layers, custom_data.totlayer)) {
- if (layer.name != nullptr && layer.name == attribute_name) {
- const void *data_before = layer.data;
- /* The data layer might be shared with someone else. Since the caller wants to modify it, we
- * copy it first. */
- CustomData_duplicate_referenced_layer_named(&custom_data, layer.type, layer.name, size);
- if (data_before != layer.data) {
- update_customdata_pointers();
+ WriteAttributePtr try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final
+ {
+ CustomData *custom_data = custom_data_access_.get_custom_data(component);
+ if (custom_data == nullptr) {
+ return {};
+ }
+ const int domain_size = component.attribute_domain_size(domain_);
+ for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
+ if (layer.name != attribute_name) {
+ continue;
}
- switch (layer.type) {
+ CustomData_duplicate_referenced_layer_named(
+ custom_data, layer.type, layer.name, domain_size);
+ const CustomDataType data_type = (CustomDataType)layer.type;
+ switch (data_type) {
case CD_PROP_FLOAT:
- return std::make_unique<ArrayWriteAttribute<float>>(
- domain, MutableSpan(static_cast<float *>(layer.data), size));
+ return this->layer_to_write_attribute<float>(layer, domain_size);
case CD_PROP_FLOAT2:
- return std::make_unique<ArrayWriteAttribute<float2>>(
- domain, MutableSpan(static_cast<float2 *>(layer.data), size));
+ return this->layer_to_write_attribute<float2>(layer, domain_size);
case CD_PROP_FLOAT3:
- return std::make_unique<ArrayWriteAttribute<float3>>(
- domain, MutableSpan(static_cast<float3 *>(layer.data), size));
+ return this->layer_to_write_attribute<float3>(layer, domain_size);
case CD_PROP_INT32:
- return std::make_unique<ArrayWriteAttribute<int>>(
- domain, MutableSpan(static_cast<int *>(layer.data), size));
+ return this->layer_to_write_attribute<int>(layer, domain_size);
case CD_PROP_COLOR:
- return std::make_unique<ArrayWriteAttribute<Color4f>>(
- domain, MutableSpan(static_cast<Color4f *>(layer.data), size));
+ return this->layer_to_write_attribute<Color4f>(layer, domain_size);
case CD_PROP_BOOL:
- return std::make_unique<ArrayWriteAttribute<bool>>(
- domain, MutableSpan(static_cast<bool *>(layer.data), size));
- case CD_MLOOPUV:
- auto get_uv = [](const MLoopUV &uv) { return float2(uv.uv); };
- auto set_uv = [](MLoopUV &uv, const float2 value) { copy_v2_v2(uv.uv, value); };
- return std::make_unique<
- DerivedArrayWriteAttribute<MLoopUV, float2, decltype(get_uv), decltype(set_uv)>>(
- domain, MutableSpan(static_cast<MLoopUV *>(layer.data), size), get_uv, set_uv);
+ return this->layer_to_write_attribute<bool>(layer, domain_size);
+ default:
+ break;
}
}
+ return {};
}
- return {};
+
+ bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
+ {
+ CustomData *custom_data = custom_data_access_.get_custom_data(component);
+ if (custom_data == nullptr) {
+ return false;
+ }
+ const int domain_size = component.attribute_domain_size(domain_);
+ for (const int i : IndexRange(custom_data->totlayer)) {
+ const CustomDataLayer &layer = custom_data->layers[i];
+ if (this->type_is_supported((CustomDataType)layer.type) && layer.name == attribute_name) {
+ CustomData_free_layer(custom_data, layer.type, domain_size, i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool try_create(GeometryComponent &component,
+ const StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type) const final
+ {
+ if (domain_ != domain) {
+ return false;
+ }
+ if (!this->type_is_supported(data_type)) {
+ return false;
+ }
+ CustomData *custom_data = custom_data_access_.get_custom_data(component);
+ if (custom_data == nullptr) {
+ return false;
+ }
+ for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
+ if (layer.name == attribute_name) {
+ return false;
+ }
+ }
+ const int domain_size = component.attribute_domain_size(domain_);
+ char attribute_name_c[MAX_NAME];
+ attribute_name.copy(attribute_name_c);
+ CustomData_add_layer_named(
+ custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
+ return true;
+ }
+
+ void list(const GeometryComponent &component, Set<std::string> &r_names) const final
+ {
+ const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
+ if (custom_data == nullptr) {
+ return;
+ }
+ for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
+ if (this->type_is_supported((CustomDataType)layer.type)) {
+ r_names.add(layer.name);
+ }
+ }
+ }
+
+ void supported_domains(Vector<AttributeDomain> &r_domains) const final
+ {
+ r_domains.append_non_duplicates(domain_);
+ }
+
+ private:
+ template<typename T>
+ ReadAttributePtr layer_to_read_attribute(const CustomDataLayer &layer,
+ const int domain_size) const
+ {
+ return std::make_unique<ArrayReadAttribute<T>>(
+ domain_, Span(static_cast<const T *>(layer.data), domain_size));
+ }
+
+ template<typename T>
+ WriteAttributePtr layer_to_write_attribute(CustomDataLayer &layer, const int domain_size) const
+ {
+ return std::make_unique<ArrayWriteAttribute<T>>(
+ domain_, MutableSpan(static_cast<T *>(layer.data), domain_size));
+ }
+
+ bool type_is_supported(CustomDataType data_type) const
+ {
+ return ((1ULL << data_type) & supported_types_mask) != 0;
+ }
+};
+
+static Mesh *get_mesh_from_component_for_write(GeometryComponent &component)
+{
+ BLI_assert(component.type() == GeometryComponentType::Mesh);
+ MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
+ return mesh_component.get_for_write();
}
-/* Returns true when the layer was found and is deleted. */
-static bool delete_named_custom_data_layer(CustomData &custom_data,
- const StringRef attribute_name,
- const int size)
+static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &component)
{
- for (const int index : blender::IndexRange(custom_data.totlayer)) {
- const CustomDataLayer &layer = custom_data.layers[index];
- if (layer.name == attribute_name) {
- CustomData_free_layer(&custom_data, layer.type, size, index);
+ BLI_assert(component.type() == GeometryComponentType::Mesh);
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ return mesh_component.get_for_read();
+}
+
+/**
+ * This attribute provider makes uv maps available as float2 attributes.
+ */
+class MeshUVsAttributeProvider final : public DynamicAttributesProvider {
+ public:
+ ReadAttributePtr try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final
+ {
+ const Mesh *mesh = get_mesh_from_component_for_read(component);
+ if (mesh == nullptr) {
+ return {};
+ }
+ for (const CustomDataLayer &layer : Span(mesh->ldata.layers, mesh->ldata.totlayer)) {
+ if (layer.type == CD_MLOOPUV) {
+ if (layer.name == attribute_name) {
+ return std::make_unique<DerivedArrayReadAttribute<MLoopUV, float2, get_loop_uv>>(
+ ATTR_DOMAIN_CORNER, Span(static_cast<const MLoopUV *>(layer.data), mesh->totloop));
+ }
+ }
+ }
+ return {};
+ }
+
+ WriteAttributePtr try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final
+ {
+ Mesh *mesh = get_mesh_from_component_for_write(component);
+ if (mesh == nullptr) {
+ return {};
+ }
+ for (CustomDataLayer &layer : MutableSpan(mesh->ldata.layers, mesh->ldata.totlayer)) {
+ if (layer.type == CD_MLOOPUV) {
+ if (layer.name == attribute_name) {
+ void *data_old = layer.data;
+ void *data_new = CustomData_duplicate_referenced_layer_named(
+ &mesh->ldata, CD_MLOOPUV, layer.name, mesh->totloop);
+ if (data_old != data_new) {
+ BKE_mesh_update_customdata_pointers(mesh, false);
+ }
+ return std::make_unique<
+ DerivedArrayWriteAttribute<MLoopUV, float2, get_loop_uv, set_loop_uv>>(
+ ATTR_DOMAIN_CORNER, MutableSpan(static_cast<MLoopUV *>(layer.data), mesh->totloop));
+ }
+ }
+ }
+ return {};
+ }
+
+ bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
+ {
+ Mesh *mesh = get_mesh_from_component_for_write(component);
+ if (mesh == nullptr) {
+ return false;
+ }
+ for (const int i : IndexRange(mesh->ldata.totlayer)) {
+ const CustomDataLayer &layer = mesh->ldata.layers[i];
+ if (layer.type == CD_MLOOPUV && layer.name == attribute_name) {
+ CustomData_free_layer(&mesh->ldata, CD_MLOOPUV, mesh->totloop, i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void list(const GeometryComponent &component, Set<std::string> &r_names) const final
+ {
+ const Mesh *mesh = get_mesh_from_component_for_read(component);
+ if (mesh == nullptr) {
+ return;
+ }
+ for (const CustomDataLayer &layer : Span(mesh->ldata.layers, mesh->ldata.totlayer)) {
+ if (layer.type == CD_MLOOPUV) {
+ r_names.add(layer.name);
+ }
+ }
+ }
+
+ void supported_domains(Vector<AttributeDomain> &r_domains) const final
+ {
+ r_domains.append_non_duplicates(ATTR_DOMAIN_CORNER);
+ }
+
+ private:
+ static float2 get_loop_uv(const MLoopUV &uv)
+ {
+ return float2(uv.uv);
+ }
+
+ static void set_loop_uv(MLoopUV &uv, const float2 &co)
+ {
+ copy_v2_v2(uv.uv, co);
+ }
+};
+
+/**
+ * This provider makes vertex groups available as float attributes.
+ */
+class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
+ public:
+ ReadAttributePtr try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final
+ {
+ BLI_assert(component.type() == GeometryComponentType::Mesh);
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ const Mesh *mesh = mesh_component.get_for_read();
+ const int vertex_group_index = mesh_component.vertex_group_names().lookup_default_as(
+ attribute_name, -1);
+ if (vertex_group_index < 0) {
+ return {};
+ }
+ if (mesh == nullptr || mesh->dvert == nullptr) {
+ static const float default_value = 0.0f;
+ return std::make_unique<ConstantReadAttribute>(
+ ATTR_DOMAIN_POINT, mesh->totvert, CPPType::get<float>(), &default_value);
+ }
+ return std::make_unique<VertexWeightReadAttribute>(
+ mesh->dvert, mesh->totvert, vertex_group_index);
+ }
+
+ WriteAttributePtr try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final
+ {
+ BLI_assert(component.type() == GeometryComponentType::Mesh);
+ MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
+ Mesh *mesh = mesh_component.get_for_write();
+ if (mesh == nullptr) {
+ return {};
+ }
+ const int vertex_group_index = mesh_component.vertex_group_names().lookup_default_as(
+ attribute_name, -1);
+ if (vertex_group_index < 0) {
+ return {};
+ }
+ if (mesh->dvert == nullptr) {
+ BKE_object_defgroup_data_create(&mesh->id);
+ }
+ else {
+ /* Copy the data layer if it is shared with some other mesh. */
+ mesh->dvert = (MDeformVert *)CustomData_duplicate_referenced_layer(
+ &mesh->vdata, CD_MDEFORMVERT, mesh->totvert);
+ }
+ return std::make_unique<blender::bke::VertexWeightWriteAttribute>(
+ mesh->dvert, mesh->totvert, vertex_group_index);
+ }
+
+ bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
+ {
+ BLI_assert(component.type() == GeometryComponentType::Mesh);
+ MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
+
+ const int vertex_group_index = mesh_component.vertex_group_names().pop_default_as(
+ attribute_name, -1);
+ if (vertex_group_index < 0) {
+ return false;
+ }
+ Mesh *mesh = mesh_component.get_for_write();
+ if (mesh == nullptr) {
return true;
}
+ if (mesh->dvert == nullptr) {
+ return true;
+ }
+ for (MDeformVert &dvert : MutableSpan(mesh->dvert, mesh->totvert)) {
+ MDeformWeight *weight = BKE_defvert_find_index(&dvert, vertex_group_index);
+ BKE_defvert_remove_group(&dvert, weight);
+ }
+ return true;
}
- return false;
+
+ void list(const GeometryComponent &component, Set<std::string> &r_names) const final
+ {
+ BLI_assert(component.type() == GeometryComponentType::Mesh);
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ mesh_component.vertex_group_names().foreach_item(
+ [&](StringRef name, const int vertex_group_index) {
+ if (vertex_group_index >= 0) {
+ r_names.add(name);
+ }
+ });
+ }
+
+ void supported_domains(Vector<AttributeDomain> &r_domains) const final
+ {
+ r_domains.append_non_duplicates(ATTR_DOMAIN_POINT);
+ }
+};
+
+/**
+ * This is a container for multiple attribute providers that are used by one geometry component
+ * type (e.g. there is a set of attribute providers for mesh components).
+ */
+class ComponentAttributeProviders {
+ private:
+ /**
+ * Builtin attribute providers are identified by their name. Attribute names that are in this
+ * map will only be accessed using builtin attribute providers. Therefore, these providers have
+ * higher priority when an attribute name is looked up. Usually, that means that builtin
+ * providers are checked before dynamic ones.
+ */
+ Map<std::string, const BuiltinAttributeProvider *> builtin_attribute_providers_;
+ /**
+ * An ordered list of dynamic attribute providers. The order is important because that is order
+ * in which they are checked when an attribute is looked up.
+ */
+ Vector<const DynamicAttributesProvider *> dynamic_attribute_providers_;
+ /**
+ * All the domains that are supported by at least one of the providers above.
+ */
+ Vector<AttributeDomain> supported_domains_;
+
+ public:
+ ComponentAttributeProviders(Span<const BuiltinAttributeProvider *> builtin_attribute_providers,
+ Span<const DynamicAttributesProvider *> dynamic_attribute_providers)
+ : dynamic_attribute_providers_(dynamic_attribute_providers)
+ {
+ Set<AttributeDomain> domains;
+ for (const BuiltinAttributeProvider *provider : builtin_attribute_providers) {
+ /* Use #add_new to make sure that no two builtin attributes have the same name. */
+ builtin_attribute_providers_.add_new(provider->name(), provider);
+ supported_domains_.append_non_duplicates(provider->domain());
+ }
+ for (const DynamicAttributesProvider *provider : dynamic_attribute_providers) {
+ provider->supported_domains(supported_domains_);
+ }
+ }
+
+ const Map<std::string, const BuiltinAttributeProvider *> &builtin_attribute_providers() const
+ {
+ return builtin_attribute_providers_;
+ }
+
+ Span<const DynamicAttributesProvider *> dynamic_attribute_providers() const
+ {
+ return dynamic_attribute_providers_;
+ }
+
+ Span<AttributeDomain> supported_domains() const
+ {
+ return supported_domains_;
+ }
+};
+
+static float3 get_vertex_position(const MVert &vert)
+{
+ return float3(vert.co);
+}
+
+static void set_vertex_position(MVert &vert, const float3 &position)
+{
+ copy_v3_v3(vert.co, position);
+}
+
+static ReadAttributePtr make_vertex_position_read_attribute(const void *data,
+ const int domain_size)
+{
+ return std::make_unique<DerivedArrayReadAttribute<MVert, float3, get_vertex_position>>(
+ ATTR_DOMAIN_POINT, Span<MVert>((const MVert *)data, domain_size));
+}
+
+static WriteAttributePtr make_vertex_position_write_attribute(void *data, const int domain_size)
+{
+ return std::make_unique<
+ DerivedArrayWriteAttribute<MVert, float3, get_vertex_position, set_vertex_position>>(
+ ATTR_DOMAIN_POINT, MutableSpan<MVert>((MVert *)data, domain_size));
}
-static void get_custom_data_layer_attribute_names(const CustomData &custom_data,
- const GeometryComponent &component,
- const AttributeDomain domain,
- Set<std::string> &r_names)
+template<typename T, AttributeDomain Domain>
+static ReadAttributePtr make_array_read_attribute(const void *data, const int domain_size)
{
- for (const CustomDataLayer &layer : blender::Span(custom_data.layers, custom_data.totlayer)) {
- const CustomDataType data_type = static_cast<CustomDataType>(layer.type);
- if (component.attribute_domain_with_type_supported(domain, data_type) ||
- ELEM(data_type, CD_MLOOPUV)) {
- r_names.add(layer.name);
+ return std::make_unique<ArrayReadAttribute<T>>(Domain, Span<T>((const T *)data, domain_size));
+}
+
+template<typename T, AttributeDomain Domain>
+static WriteAttributePtr make_array_write_attribute(void *data, const int domain_size)
+{
+ return std::make_unique<ArrayWriteAttribute<T>>(Domain, MutableSpan<T>((T *)data, domain_size));
+}
+
+/**
+ * In this function all the attribute providers for a mesh component are created. Most data in this
+ * function is statically allocated, because it does not change over time.
+ */
+static ComponentAttributeProviders create_attribute_providers_for_mesh()
+{
+ static auto update_custom_data_pointers = [](GeometryComponent &component) {
+ Mesh *mesh = get_mesh_from_component_for_write(component);
+ if (mesh != nullptr) {
+ BKE_mesh_update_customdata_pointers(mesh, false);
}
- }
+ };
+
+#define MAKE_MUTABLE_CUSTOM_DATA_GETTER(NAME) \
+ [](GeometryComponent &component) -> CustomData * { \
+ Mesh *mesh = get_mesh_from_component_for_write(component); \
+ return mesh ? &mesh->NAME : nullptr; \
+ }
+#define MAKE_CONST_CUSTOM_DATA_GETTER(NAME) \
+ [](const GeometryComponent &component) -> const CustomData * { \
+ const Mesh *mesh = get_mesh_from_component_for_read(component); \
+ return mesh ? &mesh->NAME : nullptr; \
+ }
+
+ static CustomDataAccessInfo corner_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(ldata),
+ MAKE_CONST_CUSTOM_DATA_GETTER(ldata),
+ update_custom_data_pointers};
+ static CustomDataAccessInfo point_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(vdata),
+ MAKE_CONST_CUSTOM_DATA_GETTER(vdata),
+ update_custom_data_pointers};
+ static CustomDataAccessInfo edge_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(edata),
+ MAKE_CONST_CUSTOM_DATA_GETTER(edata),
+ update_custom_data_pointers};
+ static CustomDataAccessInfo polygon_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(pdata),
+ MAKE_CONST_CUSTOM_DATA_GETTER(pdata),
+ update_custom_data_pointers};
+
+#undef MAKE_CONST_CUSTOM_DATA_GETTER
+#undef MAKE_MUTABLE_CUSTOM_DATA_GETTER
+
+ static BuiltinCustomDataLayerProvider position("position",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT3,
+ CD_MVERT,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable,
+ point_access,
+ make_vertex_position_read_attribute,
+ make_vertex_position_write_attribute);
+ static MeshUVsAttributeProvider uvs;
+ static VertexGroupsAttributeProvider vertex_groups;
+ static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access);
+ static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
+ static CustomDataAttributeProvider edge_custom_data(ATTR_DOMAIN_EDGE, edge_access);
+ static CustomDataAttributeProvider polygon_custom_data(ATTR_DOMAIN_POLYGON, polygon_access);
+
+ return ComponentAttributeProviders({&position},
+ {&uvs,
+ &corner_custom_data,
+ &vertex_groups,
+ &point_custom_data,
+ &edge_custom_data,
+ &polygon_custom_data});
}
-/** \} */
+/**
+ * In this function all the attribute providers for a point cloud component are created. Most data
+ * in this function is statically allocated, because it does not change over time.
+ */
+static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
+{
+ static auto update_custom_data_pointers = [](GeometryComponent &component) {
+ PointCloudComponent &pointcloud_component = static_cast<PointCloudComponent &>(component);
+ PointCloud *pointcloud = pointcloud_component.get_for_write();
+ if (pointcloud != nullptr) {
+ BKE_pointcloud_update_customdata_pointers(pointcloud);
+ }
+ };
+ static CustomDataAccessInfo point_access = {
+ [](GeometryComponent &component) -> CustomData * {
+ PointCloudComponent &pointcloud_component = static_cast<PointCloudComponent &>(component);
+ PointCloud *pointcloud = pointcloud_component.get_for_write();
+ return pointcloud ? &pointcloud->pdata : nullptr;
+ },
+ [](const GeometryComponent &component) -> const CustomData * {
+ const PointCloudComponent &pointcloud_component = static_cast<const PointCloudComponent &>(
+ component);
+ const PointCloud *pointcloud = pointcloud_component.get_for_read();
+ return pointcloud ? &pointcloud->pdata : nullptr;
+ },
+ update_custom_data_pointers};
+
+ static BuiltinCustomDataLayerProvider position(
+ "position",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT3,
+ CD_PROP_FLOAT3,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable,
+ point_access,
+ make_array_read_attribute<float3, ATTR_DOMAIN_POINT>,
+ make_array_write_attribute<float3, ATTR_DOMAIN_POINT>);
+ static BuiltinCustomDataLayerProvider radius(
+ "radius",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT,
+ CD_PROP_FLOAT,
+ BuiltinAttributeProvider::Creatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::Deletable,
+ point_access,
+ make_array_read_attribute<float, ATTR_DOMAIN_POINT>,
+ make_array_write_attribute<float, ATTR_DOMAIN_POINT>);
+ static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
+ return ComponentAttributeProviders({&position, &radius}, {&point_custom_data});
+}
+
+} // namespace blender::bke
/* -------------------------------------------------------------------- */
/** \name Geometry Component
* \{ */
-bool GeometryComponent::attribute_domain_supported(const AttributeDomain UNUSED(domain)) const
+const blender::bke::ComponentAttributeProviders *GeometryComponent::get_attribute_providers() const
{
- return false;
+ return nullptr;
}
-bool GeometryComponent::attribute_domain_with_type_supported(
- const AttributeDomain UNUSED(domain), const CustomDataType UNUSED(data_type)) const
+bool GeometryComponent::attribute_domain_supported(const AttributeDomain domain) const
{
- return false;
+ using namespace blender::bke;
+ const ComponentAttributeProviders *providers = this->get_attribute_providers();
+ if (providers == nullptr) {
+ return false;
+ }
+ return providers->supported_domains().contains(domain);
}
int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain)) const
@@ -638,14 +1321,26 @@ int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain
return 0;
}
-bool GeometryComponent::attribute_is_builtin(const StringRef UNUSED(attribute_name)) const
-{
- return true;
-}
-
ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
- const StringRef UNUSED(attribute_name)) const
+ const StringRef attribute_name) const
{
+ using namespace blender::bke;
+ const ComponentAttributeProviders *providers = this->get_attribute_providers();
+ if (providers == nullptr) {
+ return {};
+ }
+ const BuiltinAttributeProvider *builtin_provider =
+ providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
+ if (builtin_provider != nullptr) {
+ return builtin_provider->try_get_for_read(*this);
+ }
+ for (const DynamicAttributesProvider *dynamic_provider :
+ providers->dynamic_attribute_providers()) {
+ ReadAttributePtr attribute = dynamic_provider->try_get_for_read(*this, attribute_name);
+ if (attribute) {
+ return attribute;
+ }
+ }
return {};
}
@@ -658,27 +1353,95 @@ ReadAttributePtr GeometryComponent::attribute_try_adapt_domain(ReadAttributePtr
return {};
}
-WriteAttributePtr GeometryComponent::attribute_try_get_for_write(
- const StringRef UNUSED(attribute_name))
+WriteAttributePtr GeometryComponent::attribute_try_get_for_write(const StringRef attribute_name)
{
+ using namespace blender::bke;
+ const ComponentAttributeProviders *providers = this->get_attribute_providers();
+ if (providers == nullptr) {
+ return {};
+ }
+ const BuiltinAttributeProvider *builtin_provider =
+ providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
+ if (builtin_provider != nullptr) {
+ return builtin_provider->try_get_for_write(*this);
+ }
+ for (const DynamicAttributesProvider *dynamic_provider :
+ providers->dynamic_attribute_providers()) {
+ WriteAttributePtr attribute = dynamic_provider->try_get_for_write(*this, attribute_name);
+ if (attribute) {
+ return attribute;
+ }
+ }
return {};
}
-bool GeometryComponent::attribute_try_delete(const StringRef UNUSED(attribute_name))
+bool GeometryComponent::attribute_try_delete(const StringRef attribute_name)
{
- return false;
+ using namespace blender::bke;
+ const ComponentAttributeProviders *providers = this->get_attribute_providers();
+ if (providers == nullptr) {
+ return {};
+ }
+ const BuiltinAttributeProvider *builtin_provider =
+ providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
+ if (builtin_provider != nullptr) {
+ return builtin_provider->try_delete(*this);
+ }
+ bool success = false;
+ for (const DynamicAttributesProvider *dynamic_provider :
+ providers->dynamic_attribute_providers()) {
+ success = dynamic_provider->try_delete(*this, attribute_name) || success;
+ }
+ return success;
}
-bool GeometryComponent::attribute_try_create(const StringRef UNUSED(attribute_name),
- const AttributeDomain UNUSED(domain),
- const CustomDataType UNUSED(data_type))
+bool GeometryComponent::attribute_try_create(const StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type)
{
+ using namespace blender::bke;
+ const ComponentAttributeProviders *providers = this->get_attribute_providers();
+ if (providers == nullptr) {
+ return {};
+ }
+ const BuiltinAttributeProvider *builtin_provider =
+ providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
+ if (builtin_provider != nullptr) {
+ if (builtin_provider->domain() != domain) {
+ return false;
+ }
+ if (builtin_provider->data_type() != data_type) {
+ return false;
+ }
+ return builtin_provider->try_create(*this);
+ }
+ for (const DynamicAttributesProvider *dynamic_provider :
+ providers->dynamic_attribute_providers()) {
+ if (dynamic_provider->try_create(*this, attribute_name, domain, data_type)) {
+ return true;
+ }
+ }
return false;
}
Set<std::string> GeometryComponent::attribute_names() const
{
- return {};
+ using namespace blender::bke;
+ const ComponentAttributeProviders *providers = this->get_attribute_providers();
+ if (providers == nullptr) {
+ return {};
+ }
+ Set<std::string> names;
+ for (const BuiltinAttributeProvider *provider :
+ providers->builtin_attribute_providers().values()) {
+ if (provider->exists(*this)) {
+ names.add_new(provider->name());
+ }
+ }
+ for (const DynamicAttributesProvider *provider : providers->dynamic_attribute_providers()) {
+ provider->list(*this, names);
+ }
+ return names;
}
bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const
@@ -712,10 +1475,6 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
const AttributeDomain domain,
const CustomDataType data_type) const
{
- if (!this->attribute_domain_with_type_supported(domain, data_type)) {
- return {};
- }
-
ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name);
if (!attribute) {
return {};
@@ -767,8 +1526,6 @@ ReadAttributePtr GeometryComponent::attribute_get_for_read(const StringRef attri
const CustomDataType data_type,
const void *default_value) const
{
- BLI_assert(this->attribute_domain_with_type_supported(domain, data_type));
-
ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name, domain, data_type);
if (attribute) {
return attribute;
@@ -828,8 +1585,6 @@ OutputAttributePtr GeometryComponent::attribute_try_get_for_output(const StringR
const CustomDataType data_type,
const void *default_value)
{
- BLI_assert(this->attribute_domain_with_type_supported(domain, data_type));
-
const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
@@ -957,21 +1712,12 @@ void OutputAttributePtr::apply_span_and_save()
/** \name Point Cloud Component
* \{ */
-bool PointCloudComponent::attribute_domain_supported(const AttributeDomain domain) const
-{
- return domain == ATTR_DOMAIN_POINT;
-}
-
-bool PointCloudComponent::attribute_domain_with_type_supported(
- const AttributeDomain domain, const CustomDataType data_type) const
+const blender::bke::ComponentAttributeProviders *PointCloudComponent::get_attribute_providers()
+ const
{
- return domain == ATTR_DOMAIN_POINT && ELEM(data_type,
- CD_PROP_BOOL,
- CD_PROP_FLOAT,
- CD_PROP_FLOAT2,
- CD_PROP_FLOAT3,
- CD_PROP_INT32,
- CD_PROP_COLOR);
+ static blender::bke::ComponentAttributeProviders providers =
+ blender::bke::create_attribute_providers_for_point_cloud();
+ return &providers;
}
int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) const
@@ -984,119 +1730,17 @@ int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) con
return pointcloud_->totpoint;
}
-bool PointCloudComponent::attribute_is_builtin(const StringRef attribute_name) const
-{
- return attribute_name == "position";
-}
-
-ReadAttributePtr PointCloudComponent::attribute_try_get_for_read(
- const StringRef attribute_name) const
-{
- if (pointcloud_ == nullptr) {
- return {};
- }
-
- return read_attribute_from_custom_data(
- pointcloud_->pdata, pointcloud_->totpoint, attribute_name, ATTR_DOMAIN_POINT);
-}
-
-WriteAttributePtr PointCloudComponent::attribute_try_get_for_write(const StringRef attribute_name)
-{
- PointCloud *pointcloud = this->get_for_write();
- if (pointcloud == nullptr) {
- return {};
- }
-
- return write_attribute_from_custom_data(
- pointcloud->pdata, pointcloud->totpoint, attribute_name, ATTR_DOMAIN_POINT, [&]() {
- BKE_pointcloud_update_customdata_pointers(pointcloud);
- });
-}
-
-bool PointCloudComponent::attribute_try_delete(const StringRef attribute_name)
-{
- if (this->attribute_is_builtin(attribute_name)) {
- return false;
- }
- PointCloud *pointcloud = this->get_for_write();
- if (pointcloud == nullptr) {
- return false;
- }
- delete_named_custom_data_layer(pointcloud->pdata, attribute_name, pointcloud->totpoint);
- return true;
-}
-
-static bool custom_data_has_layer_with_name(const CustomData &custom_data, const StringRef name)
-{
- for (const CustomDataLayer &layer : blender::Span(custom_data.layers, custom_data.totlayer)) {
- if (layer.name == name) {
- return true;
- }
- }
- return false;
-}
-
-bool PointCloudComponent::attribute_try_create(const StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type)
-{
- if (this->attribute_is_builtin(attribute_name)) {
- return false;
- }
- if (!this->attribute_domain_with_type_supported(domain, data_type)) {
- return false;
- }
- PointCloud *pointcloud = this->get_for_write();
- if (pointcloud == nullptr) {
- return false;
- }
- if (custom_data_has_layer_with_name(pointcloud->pdata, attribute_name)) {
- return false;
- }
-
- char attribute_name_c[MAX_NAME];
- attribute_name.copy(attribute_name_c);
- CustomData_add_layer_named(
- &pointcloud->pdata, data_type, CD_DEFAULT, nullptr, pointcloud_->totpoint, attribute_name_c);
- return true;
-}
-
-Set<std::string> PointCloudComponent::attribute_names() const
-{
- if (pointcloud_ == nullptr) {
- return {};
- }
-
- Set<std::string> names;
- get_custom_data_layer_attribute_names(pointcloud_->pdata, *this, ATTR_DOMAIN_POINT, names);
- return names;
-}
-
/** \} */
/* -------------------------------------------------------------------- */
/** \name Mesh Component
* \{ */
-bool MeshComponent::attribute_domain_supported(const AttributeDomain domain) const
+const blender::bke::ComponentAttributeProviders *MeshComponent::get_attribute_providers() const
{
- return ELEM(
- domain, ATTR_DOMAIN_CORNER, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_POLYGON);
-}
-
-bool MeshComponent::attribute_domain_with_type_supported(const AttributeDomain domain,
- const CustomDataType data_type) const
-{
- if (!this->attribute_domain_supported(domain)) {
- return false;
- }
- return ELEM(data_type,
- CD_PROP_BOOL,
- CD_PROP_FLOAT,
- CD_PROP_FLOAT2,
- CD_PROP_FLOAT3,
- CD_PROP_INT32,
- CD_PROP_COLOR);
+ static blender::bke::ComponentAttributeProviders providers =
+ blender::bke::create_attribute_providers_for_mesh();
+ return &providers;
}
int MeshComponent::attribute_domain_size(const AttributeDomain domain) const
@@ -1121,228 +1765,4 @@ int MeshComponent::attribute_domain_size(const AttributeDomain domain) const
return 0;
}
-bool MeshComponent::attribute_is_builtin(const StringRef attribute_name) const
-{
- return attribute_name == "position";
-}
-
-ReadAttributePtr MeshComponent::attribute_try_get_for_read(const StringRef attribute_name) const
-{
- if (mesh_ == nullptr) {
- return {};
- }
-
- if (attribute_name == "position") {
- auto get_vertex_position = [](const MVert &vert) { return float3(vert.co); };
- return std::make_unique<
- blender::bke::DerivedArrayReadAttribute<MVert, float3, decltype(get_vertex_position)>>(
- ATTR_DOMAIN_POINT, blender::Span(mesh_->mvert, mesh_->totvert), get_vertex_position);
- }
-
- ReadAttributePtr corner_attribute = read_attribute_from_custom_data(
- mesh_->ldata, mesh_->totloop, attribute_name, ATTR_DOMAIN_CORNER);
- if (corner_attribute) {
- return corner_attribute;
- }
-
- const int vertex_group_index = vertex_group_names_.lookup_default(attribute_name, -1);
- if (vertex_group_index >= 0) {
- return std::make_unique<blender::bke::VertexWeightReadAttribute>(
- mesh_->dvert, mesh_->totvert, vertex_group_index);
- }
-
- ReadAttributePtr vertex_attribute = read_attribute_from_custom_data(
- mesh_->vdata, mesh_->totvert, attribute_name, ATTR_DOMAIN_POINT);
- if (vertex_attribute) {
- return vertex_attribute;
- }
-
- ReadAttributePtr edge_attribute = read_attribute_from_custom_data(
- mesh_->edata, mesh_->totedge, attribute_name, ATTR_DOMAIN_EDGE);
- if (edge_attribute) {
- return edge_attribute;
- }
-
- ReadAttributePtr polygon_attribute = read_attribute_from_custom_data(
- mesh_->pdata, mesh_->totpoly, attribute_name, ATTR_DOMAIN_POLYGON);
- if (polygon_attribute) {
- return polygon_attribute;
- }
-
- return {};
-}
-
-WriteAttributePtr MeshComponent::attribute_try_get_for_write(const StringRef attribute_name)
-{
- Mesh *mesh = this->get_for_write();
- if (mesh == nullptr) {
- return {};
- }
-
- const std::function<void()> update_mesh_pointers = [&]() {
- BKE_mesh_update_customdata_pointers(mesh, false);
- };
-
- if (attribute_name == "position") {
- CustomData_duplicate_referenced_layer(&mesh->vdata, CD_MVERT, mesh->totvert);
- update_mesh_pointers();
-
- auto get_vertex_position = [](const MVert &vert) { return float3(vert.co); };
- auto set_vertex_position = [](MVert &vert, const float3 &co) { copy_v3_v3(vert.co, co); };
- return std::make_unique<
- blender::bke::DerivedArrayWriteAttribute<MVert,
- float3,
- decltype(get_vertex_position),
- decltype(set_vertex_position)>>(
- ATTR_DOMAIN_POINT,
- blender::MutableSpan(mesh_->mvert, mesh_->totvert),
- get_vertex_position,
- set_vertex_position);
- }
-
- WriteAttributePtr corner_attribute = write_attribute_from_custom_data(
- mesh_->ldata, mesh_->totloop, attribute_name, ATTR_DOMAIN_CORNER, update_mesh_pointers);
- if (corner_attribute) {
- return corner_attribute;
- }
-
- const int vertex_group_index = vertex_group_names_.lookup_default_as(attribute_name, -1);
- if (vertex_group_index >= 0) {
- if (mesh_->dvert == nullptr) {
- BKE_object_defgroup_data_create(&mesh_->id);
- }
- else {
- /* Copy the data layer if it is shared with some other mesh. */
- mesh_->dvert = (MDeformVert *)CustomData_duplicate_referenced_layer(
- &mesh_->vdata, CD_MDEFORMVERT, mesh_->totvert);
- }
- return std::make_unique<blender::bke::VertexWeightWriteAttribute>(
- mesh_->dvert, mesh_->totvert, vertex_group_index);
- }
-
- WriteAttributePtr vertex_attribute = write_attribute_from_custom_data(
- mesh_->vdata, mesh_->totvert, attribute_name, ATTR_DOMAIN_POINT, update_mesh_pointers);
- if (vertex_attribute) {
- return vertex_attribute;
- }
-
- WriteAttributePtr edge_attribute = write_attribute_from_custom_data(
- mesh_->edata, mesh_->totedge, attribute_name, ATTR_DOMAIN_EDGE, update_mesh_pointers);
- if (edge_attribute) {
- return edge_attribute;
- }
-
- WriteAttributePtr polygon_attribute = write_attribute_from_custom_data(
- mesh_->pdata, mesh_->totpoly, attribute_name, ATTR_DOMAIN_POLYGON, update_mesh_pointers);
- if (polygon_attribute) {
- return polygon_attribute;
- }
-
- return {};
-}
-
-bool MeshComponent::attribute_try_delete(const StringRef attribute_name)
-{
- if (this->attribute_is_builtin(attribute_name)) {
- return false;
- }
- Mesh *mesh = this->get_for_write();
- if (mesh == nullptr) {
- return false;
- }
-
- delete_named_custom_data_layer(mesh_->ldata, attribute_name, mesh_->totloop);
- delete_named_custom_data_layer(mesh_->vdata, attribute_name, mesh_->totvert);
- delete_named_custom_data_layer(mesh_->edata, attribute_name, mesh_->totedge);
- delete_named_custom_data_layer(mesh_->pdata, attribute_name, mesh_->totpoly);
-
- const int vertex_group_index = vertex_group_names_.lookup_default_as(attribute_name, -1);
- if (vertex_group_index != -1) {
- for (MDeformVert &dvert : blender::MutableSpan(mesh_->dvert, mesh_->totvert)) {
- MDeformWeight *weight = BKE_defvert_find_index(&dvert, vertex_group_index);
- BKE_defvert_remove_group(&dvert, weight);
- }
- vertex_group_names_.remove_as(attribute_name);
- }
-
- return true;
-}
-
-bool MeshComponent::attribute_try_create(const StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type)
-{
- if (this->attribute_is_builtin(attribute_name)) {
- return false;
- }
- if (!this->attribute_domain_with_type_supported(domain, data_type)) {
- return false;
- }
- Mesh *mesh = this->get_for_write();
- if (mesh == nullptr) {
- return false;
- }
-
- char attribute_name_c[MAX_NAME];
- attribute_name.copy(attribute_name_c);
-
- switch (domain) {
- case ATTR_DOMAIN_CORNER: {
- if (custom_data_has_layer_with_name(mesh->ldata, attribute_name)) {
- return false;
- }
- CustomData_add_layer_named(
- &mesh->ldata, data_type, CD_DEFAULT, nullptr, mesh->totloop, attribute_name_c);
- return true;
- }
- case ATTR_DOMAIN_POINT: {
- if (custom_data_has_layer_with_name(mesh->vdata, attribute_name)) {
- return false;
- }
- if (vertex_group_names_.contains_as(attribute_name)) {
- return false;
- }
- CustomData_add_layer_named(
- &mesh->vdata, data_type, CD_DEFAULT, nullptr, mesh->totvert, attribute_name_c);
- return true;
- }
- case ATTR_DOMAIN_EDGE: {
- if (custom_data_has_layer_with_name(mesh->edata, attribute_name)) {
- return false;
- }
- CustomData_add_layer_named(
- &mesh->edata, data_type, CD_DEFAULT, nullptr, mesh->totedge, attribute_name_c);
- return true;
- }
- case ATTR_DOMAIN_POLYGON: {
- if (custom_data_has_layer_with_name(mesh->pdata, attribute_name)) {
- return false;
- }
- CustomData_add_layer_named(
- &mesh->pdata, data_type, CD_DEFAULT, nullptr, mesh->totpoly, attribute_name_c);
- return true;
- }
- default:
- return false;
- }
-}
-
-Set<std::string> MeshComponent::attribute_names() const
-{
- if (mesh_ == nullptr) {
- return {};
- }
-
- Set<std::string> names;
- names.add("position");
- for (StringRef name : vertex_group_names_.keys()) {
- names.add(name);
- }
- get_custom_data_layer_attribute_names(mesh_->ldata, *this, ATTR_DOMAIN_CORNER, names);
- get_custom_data_layer_attribute_names(mesh_->vdata, *this, ATTR_DOMAIN_POINT, names);
- get_custom_data_layer_attribute_names(mesh_->edata, *this, ATTR_DOMAIN_EDGE, names);
- get_custom_data_layer_attribute_names(mesh_->pdata, *this, ATTR_DOMAIN_POLYGON, names);
- return names;
-}
-
/** \} */
diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc
index dae0b757df0..2eaef8fc121 100644
--- a/source/blender/blenkernel/intern/geometry_set.cc
+++ b/source/blender/blenkernel/intern/geometry_set.cc
@@ -377,6 +377,17 @@ void MeshComponent::copy_vertex_group_names_from_object(const Object &object)
}
}
+const blender::Map<std::string, int> &MeshComponent::vertex_group_names() const
+{
+ return vertex_group_names_;
+}
+
+/* This is only exposed for the internal attribute API. */
+blender::Map<std::string, int> &MeshComponent::vertex_group_names()
+{
+ return vertex_group_names_;
+}
+
/* Get the mesh from this component. This method can be used by multiple threads at the same
* time. Therefore, the returned mesh should not be modified. No ownership is transferred. */
const Mesh *MeshComponent::get_for_read() const