/* SPDX-FileCopyrightText: 2023 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "BLI_generic_pointer.hh" #include "BLI_map.hh" #include "BLI_span.hh" #include "BLI_string_ref.hh" #include "BLI_vector.hh" #include "BLI_vector_set.hh" #include "BKE_attribute_legacy_convert.hh" #include "BKE_geometry_set.hh" namespace blender::bke { /** * 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 *(*)(void *owner); using ConstCustomDataGetter = const CustomData *(*)(const void *owner); using GetElementNum = int (*)(const void *owner); using GetTagModifiedFunction = std::function (*)(void *owner, StringRef name); CustomDataGetter get_custom_data; ConstCustomDataGetter get_const_custom_data; GetElementNum get_element_num; GetTagModifiedFunction get_tag_modified_function; }; /** * 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: enum DeletableEnum { Deletable, NonDeletable, }; protected: const std::string name_; const AttrDomain domain_; const eCustomDataType data_type_; const DeletableEnum deletable_; const AttributeValidator validator_; const GPointer default_value_; public: BuiltinAttributeProvider(std::string name, const AttrDomain domain, const eCustomDataType data_type, const DeletableEnum deletable, AttributeValidator validator = {}, const GPointer default_value = {}) : name_(std::move(name)), domain_(domain), data_type_(data_type), deletable_(deletable), validator_(validator), default_value_(default_value) { } virtual GAttributeReader try_get_for_read(const void *owner) const = 0; virtual GAttributeWriter try_get_for_write(void *owner) const = 0; virtual bool try_delete(void *owner) const = 0; virtual bool try_create(void *owner, const AttributeInit &initializer) const = 0; virtual bool exists(const void *owner) const = 0; StringRefNull name() const { return name_; } AttrDomain domain() const { return domain_; } eCustomDataType data_type() const { return data_type_; } AttributeValidator validator() const { return validator_; } GPointer default_value() const { return default_value_; } }; /** * A #DynamicAttributesProvider manages a set of named attributes on a geometry component. Each * attribute has a name, domain and type. */ class DynamicAttributesProvider { public: virtual GAttributeReader try_get_for_read(const void *owner, StringRef attribute_id) const = 0; virtual GAttributeWriter try_get_for_write(void *owner, StringRef attribute_id) const = 0; virtual bool try_delete(void *owner, StringRef attribute_id) const = 0; virtual bool try_create(void *owner, const StringRef attribute_id, const AttrDomain domain, const eCustomDataType data_type, const AttributeInit &initializer) const { UNUSED_VARS(owner, attribute_id, domain, data_type, initializer); /* Some providers should not create new attributes. */ return false; }; /** * Return false when the iteration was stopped. */ virtual bool foreach_attribute(const void *owner, FunctionRef fn) const = 0; virtual void foreach_domain(const FunctionRef callback) const = 0; }; /** * 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_ALL; AttrDomain domain_; CustomDataAccessInfo custom_data_access_; public: CustomDataAttributeProvider(const AttrDomain domain, const CustomDataAccessInfo custom_data_access) : domain_(domain), custom_data_access_(custom_data_access) { } GAttributeReader try_get_for_read(const void *owner, StringRef attribute_id) const final; GAttributeWriter try_get_for_write(void *owner, StringRef attribute_id) const final; bool try_delete(void *owner, StringRef attribute_id) const final; bool try_create(void *owner, StringRef attribute_id, AttrDomain domain, const eCustomDataType data_type, const AttributeInit &initializer) const final; bool foreach_attribute(const void *owner, FunctionRef fn) const final; void foreach_domain(const FunctionRef callback) const final { callback(domain_); } private: bool type_is_supported(eCustomDataType data_type) const { return ((1ULL << data_type) & supported_types_mask) != 0; } }; /** * This provider is used to provide access to builtin attributes. It supports making internal types * available as different types. * * It also supports named builtin attributes, and will look up attributes in #CustomData by name * if the stored type is the same as the attribute type. */ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { using AttrUpdateOnChange = void (*)(void *owner); const CustomDataAccessInfo custom_data_access_; const AttrUpdateOnChange update_on_change_; public: BuiltinCustomDataLayerProvider(std::string attribute_name, const AttrDomain domain, const eCustomDataType data_type, const DeletableEnum deletable, const CustomDataAccessInfo custom_data_access, const AttrUpdateOnChange update_on_change, const AttributeValidator validator = {}, const GPointer default_value = {}) : BuiltinAttributeProvider( std::move(attribute_name), domain, data_type, deletable, validator, default_value), custom_data_access_(custom_data_access), update_on_change_(update_on_change) { } GAttributeReader try_get_for_read(const void *owner) const final; GAttributeWriter try_get_for_write(void *owner) const final; bool try_delete(void *owner) const final; bool try_create(void *owner, const AttributeInit &initializer) const final; bool exists(const void *owner) const final; private: bool layer_exists(const CustomData &custom_data) const; }; /** * This is a container for multiple attribute providers that are used by one geometry type * (e.g. there is a set of attribute providers for mesh components). */ class GeometryAttributeProviders { 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 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 dynamic_attribute_providers_; /** * All the domains that are supported by at least one of the providers above. */ VectorSet supported_domains_; public: GeometryAttributeProviders(Span builtin_attribute_providers, Span dynamic_attribute_providers) : dynamic_attribute_providers_(dynamic_attribute_providers) { 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_.add(provider->domain()); } for (const DynamicAttributesProvider *provider : dynamic_attribute_providers) { provider->foreach_domain([&](AttrDomain domain) { supported_domains_.add(domain); }); } } const Map &builtin_attribute_providers() const { return builtin_attribute_providers_; } Span dynamic_attribute_providers() const { return dynamic_attribute_providers_; } Span supported_domains() const { return supported_domains_; } }; namespace attribute_accessor_functions { template inline std::optional builtin_domain_and_type(const void * /*owner*/, const StringRef name) { if (const BuiltinAttributeProvider *provider = providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) { const AttrType data_type = *custom_data_type_to_attr_type(provider->data_type()); return AttributeDomainAndType{provider->domain(), data_type}; } return std::nullopt; } template inline GPointer builtin_default_value(const void * /*owner*/, const StringRef attribute_id) { if (const BuiltinAttributeProvider *provider = providers.builtin_attribute_providers().lookup_default_as(attribute_id, nullptr)) { return provider->default_value(); } return {}; } template inline GAttributeReader lookup(const void *owner, const StringRef name) { if (const BuiltinAttributeProvider *provider = providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) { return provider->try_get_for_read(owner); } for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) { GAttributeReader attribute = provider->try_get_for_read(owner, name); if (attribute) { return attribute; } } return {}; } template inline void foreach_attribute(const void *owner, const FunctionRef fn, const AttributeAccessor &accessor) { Set handled_attribute_ids; for (const BuiltinAttributeProvider *provider : providers.builtin_attribute_providers().values()) { if (provider->exists(owner)) { const auto get_fn = [&]() { return provider->try_get_for_read(owner); }; AttributeIter iter{provider->name(), provider->domain(), *custom_data_type_to_attr_type(provider->data_type()), get_fn}; iter.is_builtin = true; iter.accessor = &accessor; fn(iter); if (iter.is_stopped()) { return; } handled_attribute_ids.add(iter.name); } } for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) { const bool continue_loop = provider->foreach_attribute(owner, [&](const AttributeIter &iter) { if (handled_attribute_ids.add(iter.name)) { iter.accessor = &accessor; fn(iter); } }); if (!continue_loop) { return; } } } template inline AttributeValidator lookup_validator(const void * /*owner*/, const blender::StringRef name) { const BuiltinAttributeProvider *provider = providers.builtin_attribute_providers().lookup_default_as(name, nullptr); if (!provider) { return {}; } return provider->validator(); } template inline GAttributeWriter lookup_for_write(void *owner, const StringRef name) { if (const BuiltinAttributeProvider *provider = providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) { return provider->try_get_for_write(owner); } for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) { GAttributeWriter attribute = provider->try_get_for_write(owner, name); if (attribute) { return attribute; } } return {}; } template inline bool remove(void *owner, const StringRef name) { if (const BuiltinAttributeProvider *provider = providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) { return provider->try_delete(owner); } for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) { if (provider->try_delete(owner, name)) { return true; } } return false; } template inline bool add(void *owner, const StringRef name, const AttrDomain domain, const AttrType data_type, const AttributeInit &initializer) { const eCustomDataType custom_data_type = *attr_type_to_custom_data_type(data_type); if (const BuiltinAttributeProvider *provider = providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) { if (provider->domain() != domain) { return false; } if (provider->data_type() != custom_data_type) { return false; } return provider->try_create(owner, initializer); } for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) { if (provider->try_create(owner, name, domain, custom_data_type, initializer)) { return true; } } return false; } template inline AttributeAccessorFunctions accessor_functions_for_providers() { return AttributeAccessorFunctions{nullptr, nullptr, builtin_domain_and_type, builtin_default_value, lookup, nullptr, foreach_attribute, lookup_validator, lookup_for_write, remove, add}; } } // namespace attribute_accessor_functions } // namespace blender::bke