diff options
author | Jacques Lucke <jacques@blender.org> | 2022-07-08 17:16:56 +0300 |
---|---|---|
committer | Jacques Lucke <jacques@blender.org> | 2022-07-08 17:16:56 +0300 |
commit | b876ce2a4a4638142439a7cf265a0780491ae4cc (patch) | |
tree | 871d71eb6d1cf215869fc941c831c81bcacc6433 /source/blender/blenkernel | |
parent | f391e8f316bd29b700cef874a59cf3b64203d70c (diff) |
Geometry Nodes: new geometry attribute API
Currently, there are two attribute API. The first, defined in `BKE_attribute.h` is
accessible from RNA and C code. The second is implemented with `GeometryComponent`
and is only accessible in C++ code. The second is widely used, but only being
accessible through the `GeometrySet` API makes it awkward to use, and even impossible
for types that don't correspond directly to a geometry component like `CurvesGeometry`.
This patch adds a new attribute API, designed to replace the `GeometryComponent`
attribute API now, and to eventually replace or be the basis of the other one.
The basic idea is that there is an `AttributeAccessor` class that allows code to
interact with a set of attributes owned by some geometry. The accessor itself has
no ownership. `AttributeAccessor` is a simple type that can be passed around by
value. That makes it easy to return it from functions and to store it in containers.
For const-correctness, there is also a `MutableAttributeAccessor` that allows
changing individual and can add or remove attributes.
Currently, `AttributeAccessor` is composed of two pointers. The first is a pointer
to the owner of the attribute data. The second is a pointer to a struct with
function pointers, that is similar to a virtual function table. The functions
know how to access attributes on the owner.
The actual attribute access for geometries is still implemented with the `AttributeProvider`
pattern, which makes it easy to support different sources of attributes on a
geometry and simplifies dealing with built-in attributes.
There are different ways to get an attribute accessor for a geometry:
* `GeometryComponent.attributes()`
* `CurvesGeometry.attributes()`
* `bke::mesh_attributes(const Mesh &)`
* `bke::pointcloud_attributes(const PointCloud &)`
All of these also have a `_for_write` variant that returns a `MutabelAttributeAccessor`.
Differential Revision: https://developer.blender.org/D15280
Diffstat (limited to 'source/blender/blenkernel')
22 files changed, 1889 insertions, 1969 deletions
diff --git a/source/blender/blenkernel/BKE_attribute.hh b/source/blender/blenkernel/BKE_attribute.hh new file mode 100644 index 00000000000..b92d0d4326b --- /dev/null +++ b/source/blender/blenkernel/BKE_attribute.hh @@ -0,0 +1,814 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_color.hh" +#include "BLI_function_ref.hh" +#include "BLI_generic_span.hh" +#include "BLI_generic_virtual_array.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_set.hh" + +#include "BKE_anonymous_attribute.hh" +#include "BKE_attribute.h" + +struct Mesh; +struct PointCloud; + +namespace blender::bke { + +/** + * Identifies an attribute that is either named or anonymous. + * It does not own the identifier, so it is just a reference. + */ +class AttributeIDRef { + private: + StringRef name_; + const AnonymousAttributeID *anonymous_id_ = nullptr; + + public: + AttributeIDRef(); + AttributeIDRef(StringRef name); + AttributeIDRef(StringRefNull name); + AttributeIDRef(const char *name); + AttributeIDRef(const std::string &name); + AttributeIDRef(const AnonymousAttributeID *anonymous_id); + + operator bool() const; + uint64_t hash() const; + bool is_named() const; + bool is_anonymous() const; + StringRef name() const; + const AnonymousAttributeID &anonymous_id() const; + bool should_be_kept() const; + + friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b); + friend std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id); +}; + +/** + * Contains information about an attribute in a geometry component. + * More information can be added in the future. E.g. whether the attribute is builtin and how it is + * stored (uv map, vertex group, ...). + */ +struct AttributeMetaData { + eAttrDomain domain; + eCustomDataType data_type; + + constexpr friend bool operator==(AttributeMetaData a, AttributeMetaData b) + { + return (a.domain == b.domain) && (a.data_type == b.data_type); + } +}; + +struct AttributeKind { + eAttrDomain domain; + eCustomDataType data_type; +}; + +/** + * Base class for the attribute initializer types described below. + */ +struct AttributeInit { + enum class Type { + Default, + VArray, + MoveArray, + }; + Type type; + AttributeInit(const Type type) : type(type) + { + } +}; + +/** + * Create an attribute using the default value for the data type. + * The default values may depend on the attribute provider implementation. + */ +struct AttributeInitDefault : public AttributeInit { + AttributeInitDefault() : AttributeInit(Type::Default) + { + } +}; + +/** + * Create an attribute by copying data from an existing virtual array. The virtual array + * must have the same type as the newly created attribute. + * + * Note that this can be used to fill the new attribute with the default + */ +struct AttributeInitVArray : public AttributeInit { + blender::GVArray varray; + + AttributeInitVArray(blender::GVArray varray) + : AttributeInit(Type::VArray), varray(std::move(varray)) + { + } +}; + +/** + * Create an attribute with a by passing ownership of a pre-allocated contiguous array of data. + * Sometimes data is created before a geometry component is available. In that case, it's + * preferable to move data directly to the created attribute to avoid a new allocation and a copy. + * + * Note that this will only have a benefit for attributes that are stored directly as contiguous + * arrays, so not for some built-in attributes. + * + * The array must be allocated with MEM_*, since `attribute_try_create` will free the array if it + * can't be used directly, and that is generally how Blender expects custom data to be allocated. + */ +struct AttributeInitMove : public AttributeInit { + void *data = nullptr; + + AttributeInitMove(void *data) : AttributeInit(Type::MoveArray), data(data) + { + } +}; + +/* Returns false when the iteration should be stopped. */ +using AttributeForeachCallback = + FunctionRef<bool(const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data)>; + +/** + * Result when looking up an attribute from some geometry with the intention of only reading from + * it. + */ +template<typename T> struct AttributeReader { + /** + * Virtual array that provides access to the attribute data. This may be empty. + */ + VArray<T> varray; + /** + * Domain where the attribute is stored. This also determines the size of the virtual array. + */ + eAttrDomain domain; + + operator bool() const + { + return this->varray; + } +}; + +/** + * Result when looking up an attribute from some geometry with read an write access. After writing + * to the attribute, the #finish method has to be called. This may invalidate caches based on this + * attribute. + */ +template<typename T> struct AttributeWriter { + /** + * Virtual array giving read and write access to the attribute. This may be empty. + * Consider using #SpanAttributeWriter when you want to access the virtual array as a span. + */ + VMutableArray<T> varray; + /** + * Domain where the attribute is stored on the geometry. Also determines the size of the virtual + * array. + */ + eAttrDomain domain; + /** + * A function that has to be called after the attribute has been edited. This may be empty. + */ + std::function<void()> tag_modified_fn; + + operator bool() const + { + return this->varray; + } + + /** + * Has to be called after the attribute has been modified. + */ + void finish() + { + if (this->tag_modified_fn) { + this->tag_modified_fn(); + } + } +}; + +/** + * A version of #AttributeWriter for the common case when the user of the attribute wants to write + * to a span instead of a virtual array. Since most attributes are spans internally, this can + * result in better performance and also simplifies code. + */ +template<typename T> struct SpanAttributeWriter { + /** + * A span based on the virtual array that contains the attribute data. This may be empty. + */ + MutableVArraySpan<T> span; + /** + * Domain of the attribute. Also determines the size of the span. + */ + eAttrDomain domain; + /** + * Has to be called after writing to the span. + */ + std::function<void()> tag_modified_fn; + + SpanAttributeWriter() = default; + + SpanAttributeWriter(AttributeWriter<T> &&other, const bool copy_values_to_span) + : span(std::move(other.varray), copy_values_to_span), + domain(other.domain), + tag_modified_fn(std::move(other.tag_modified_fn)) + { + } + + operator bool() const + { + return span.varray(); + } + + /** + * Has to be called when done writing to the attribute. This makes sure that the data is copied + * to the underlying attribute if it was not stored as an array. Furthermore, this may invalidate + * other data depending on the modified attribute. + */ + void finish() + { + this->span.save(); + if (this->tag_modified_fn) { + this->tag_modified_fn(); + } + } +}; + +/** + * A generic version of #AttributeReader. + */ +struct GAttributeReader { + GVArray varray; + eAttrDomain domain; + + operator bool() const + { + return this->varray; + } + + template<typename T> AttributeReader<T> typed() const + { + return {varray.typed<T>(), domain}; + } +}; + +/** + * A generic version of #AttributeWriter. + */ +struct GAttributeWriter { + GVMutableArray varray; + eAttrDomain domain; + std::function<void()> tag_modified_fn; + + operator bool() const + { + return this->varray; + } + + void finish() + { + if (this->tag_modified_fn) { + this->tag_modified_fn(); + } + } + + template<typename T> AttributeWriter<T> typed() const + { + return {varray.typed<T>(), domain, tag_modified_fn}; + } +}; + +/** + * A generic version of #SpanAttributeWriter. + */ +struct GSpanAttributeWriter { + GMutableVArraySpan span; + eAttrDomain domain; + std::function<void()> tag_modified_fn; + + GSpanAttributeWriter() = default; + + GSpanAttributeWriter(GAttributeWriter &&other, const bool copy_values_to_span) + : span(std::move(other.varray), copy_values_to_span), + domain(other.domain), + tag_modified_fn(std::move(other.tag_modified_fn)) + { + } + + operator bool() const + { + return span.varray(); + } + + void finish() + { + this->span.save(); + if (this->tag_modified_fn) { + this->tag_modified_fn(); + } + } +}; + +/** + * Core functions which make up the attribute API. They should not be called directly, but through + * #AttributesAccessor or #MutableAttributesAccessor. + * + * This is similar to a virtual function table. A struct of function pointers is used instead, + * because this way the attribute accessors can be trivial and can be passed around by value. This + * makes it easy to return the attribute accessor for a geometry from a function. + */ +struct AttributeAccessorFunctions { + bool (*contains)(const void *owner, const AttributeIDRef &attribute_id); + std::optional<AttributeMetaData> (*lookup_meta_data)(const void *owner, + const AttributeIDRef &attribute_id); + bool (*domain_supported)(const void *owner, eAttrDomain domain); + int (*domain_size)(const void *owner, eAttrDomain domain); + bool (*is_builtin)(const void *owner, const AttributeIDRef &attribute_id); + GAttributeReader (*lookup)(const void *owner, const AttributeIDRef &attribute_id); + GVArray (*adapt_domain)(const void *owner, + const GVArray &varray, + eAttrDomain from_domain, + eAttrDomain to_domain); + bool (*for_all)(const void *owner, + FunctionRef<bool(const AttributeIDRef &, const AttributeMetaData &)> fn); + + GAttributeWriter (*lookup_for_write)(void *owner, const AttributeIDRef &attribute_id); + bool (*remove)(void *owner, const AttributeIDRef &attribute_id); + bool (*add)(void *owner, + const AttributeIDRef &attribute_id, + eAttrDomain domain, + eCustomDataType data_type, + const AttributeInit &initializer); +}; + +/** + * Provides read-only access to the set of attributes on some geometry. + * + * Note, this does not own the attributes. When the owner is freed, it is invalid to access its + * attributes. + */ +class AttributeAccessor { + protected: + /** + * The data that actually owns the attributes, for example, a pointer to a #Mesh or #PointCloud + * Most commonly this is a pointer to a #Mesh or #PointCloud. + * Under some circumstances this can be null. In that case most methods can't be used. Just e.g. + * the #domain_size method works and returns 0 for every domain. + * + * \note This class cannot modify the owner's attributes, but the pointer is still non-const, so + * this class can be a base class for the mutable version. + */ + void *owner_; + /** + * Functions that know how to access the attributes stored in the owner above. + */ + const AttributeAccessorFunctions *fn_; + + public: + AttributeAccessor(const void *owner, const AttributeAccessorFunctions &fn) + : owner_(const_cast<void *>(owner)), fn_(&fn) + { + } + + /** + * \return True, when the attribute is available. + */ + bool contains(const AttributeIDRef &attribute_id) const + { + return fn_->contains(owner_, attribute_id); + } + + /** + * \return Information about the attribute if it exists. + */ + std::optional<AttributeMetaData> lookup_meta_data(const AttributeIDRef &attribute_id) const + { + return fn_->lookup_meta_data(owner_, attribute_id); + } + + /** + * \return True, when attributes can exist on that domain. + */ + bool domain_supported(const eAttrDomain domain) const + { + return fn_->domain_supported(owner_, domain); + } + + /** + * \return Number of elements in the given domain. + */ + int domain_size(const eAttrDomain domain) const + { + return fn_->domain_size(owner_, domain); + } + + /** + * \return True, when the attribute has a special meaning for Blender and can't be used for + * arbitrary things. + */ + bool is_builtin(const AttributeIDRef &attribute_id) const + { + return fn_->is_builtin(owner_, attribute_id); + } + + /** + * Get read-only access to the attribute. If the attribute does not exist, the return value is + * empty. + */ + GAttributeReader lookup(const AttributeIDRef &attribute_id) const + { + return fn_->lookup(owner_, attribute_id); + } + + /** + * Get read-only access to the attribute. If necessary, the attribute is interpolated to the + * given domain, and converted to the given type, in that order. The result may be empty. + */ + GVArray lookup(const AttributeIDRef &attribute_id, + const std::optional<eAttrDomain> domain, + const std::optional<eCustomDataType> data_type) const; + + /** + * Get read-only access to the attribute whereby the attribute is interpolated to the given + * domain. The result may be empty. + */ + GVArray lookup(const AttributeIDRef &attribute_id, const eAttrDomain domain) const + { + return this->lookup(attribute_id, domain, std::nullopt); + } + + /** + * Get read-only access to the attribute whereby the attribute is converted to the given type. + * The result may be empty. + */ + GVArray lookup(const AttributeIDRef &attribute_id, const eCustomDataType data_type) const + { + return this->lookup(attribute_id, std::nullopt, data_type); + } + + /** + * Get read-only access to the attribute. If necessary, the attribute is interpolated to the + * given domain and then converted to the given type, in that order. The result may be empty. + */ + template<typename T> + VArray<T> lookup(const AttributeIDRef &attribute_id, + const std::optional<eAttrDomain> domain = std::nullopt) const + { + const CPPType &cpp_type = CPPType::get<T>(); + const eCustomDataType data_type = cpp_type_to_custom_data_type(cpp_type); + return this->lookup(attribute_id, domain, data_type).typed<T>(); + } + + /** + * Get read-only access to the attribute. If necessary, the attribute is interpolated to the + * given domain and then converted to the given data type, in that order. + * If the attribute does not exist, a virtual array with the given default value is returned. + * If the passed in default value is null, the default value of the type is used (generally 0). + */ + GVArray lookup_or_default(const AttributeIDRef &attribute_id, + const eAttrDomain domain, + const eCustomDataType data_type, + const void *default_value = nullptr) const; + + /** + * Same as the generic version above, but should be used when the type is known at compile time. + */ + template<typename T> + VArray<T> lookup_or_default(const AttributeIDRef &attribute_id, + const eAttrDomain domain, + const T &default_value) const + { + if (VArray<T> varray = this->lookup<T>(attribute_id, domain)) { + return varray; + } + return VArray<T>::ForSingle(default_value, this->domain_size(domain)); + } + + /** + * Interpolate data from one domain to another. + */ + GVArray adapt_domain(const GVArray &varray, + const eAttrDomain from_domain, + const eAttrDomain to_domain) const + { + return fn_->adapt_domain(owner_, varray, from_domain, to_domain); + } + + /** + * Interpolate data from one domain to another. + */ + template<typename T> + VArray<T> adapt_domain(const VArray<T> &varray, + const eAttrDomain from_domain, + const eAttrDomain to_domain) const + { + return this->adapt_domain(GVArray(varray), from_domain, to_domain).typed<T>(); + } + + /** + * Run the provided function for every attribute. + */ + bool for_all(const AttributeForeachCallback fn) const + { + return fn_->for_all(owner_, fn); + } + + /** + * Get a set of all attributes. + */ + Set<AttributeIDRef> all_ids() const; +}; + +/** + * Extends #AttributeAccessor with methods that allow modifying individual attributes as well as + * the set of attributes. + */ +class MutableAttributeAccessor : public AttributeAccessor { + public: + MutableAttributeAccessor(void *owner, const AttributeAccessorFunctions &fn) + : AttributeAccessor(owner, fn) + { + } + + /** + * Get a writable attribute or none if it does not exist. + * Make sure to call #finish after changes are done. + */ + GAttributeWriter lookup_for_write(const AttributeIDRef &attribute_id) + { + return fn_->lookup_for_write(owner_, attribute_id); + } + + /** + * Get a writable attribute or non if it does not exist. + * Make sure to call #finish after changes are done. + */ + template<typename T> AttributeWriter<T> lookup_for_write(const AttributeIDRef &attribute_id) + { + GAttributeWriter attribute = this->lookup_for_write(attribute_id); + if (!attribute) { + return {}; + } + if (!attribute.varray.type().is<T>()) { + return {}; + } + return attribute.typed<T>(); + } + + /** + * Create a new attribute. + * \return True, when a new attribute has been created. False, when it's not possible to create + * this attribute or there is already an attribute with that id. + */ + bool add(const AttributeIDRef &attribute_id, + const eAttrDomain domain, + const eCustomDataType data_type, + const AttributeInit &initializer) + { + return fn_->add(owner_, attribute_id, domain, data_type, initializer); + } + + /** + * Find an attribute with the given id, domain and data type. If it does not exist, create a new + * attribute. If the attribute does not exist and can't be created (e.g. because it already + * exists on a different domain or with a different type), none is returned. + */ + GAttributeWriter lookup_or_add_for_write( + const AttributeIDRef &attribute_id, + const eAttrDomain domain, + const eCustomDataType data_type, + const AttributeInit &initializer = AttributeInitDefault()); + + /** + * Same as above, but returns a type that makes it easier to work with the attribute as a span. + * If the caller newly initializes the attribute, it's better to use + * #lookup_or_add_for_write_only_span. + */ + GSpanAttributeWriter lookup_or_add_for_write_span( + const AttributeIDRef &attribute_id, + const eAttrDomain domain, + const eCustomDataType data_type, + const AttributeInit &initializer = AttributeInitDefault()); + + /** + * Same as above, but should be used when the type is known at compile time. + */ + template<typename T> + AttributeWriter<T> lookup_or_add_for_write( + const AttributeIDRef &attribute_id, + const eAttrDomain domain, + const AttributeInit &initializer = AttributeInitDefault()) + { + const CPPType &cpp_type = CPPType::get<T>(); + const eCustomDataType data_type = cpp_type_to_custom_data_type(cpp_type); + return this->lookup_or_add_for_write(attribute_id, domain, data_type, initializer).typed<T>(); + } + + /** + * Same as above, but should be used when the type is known at compile time. + */ + template<typename T> + SpanAttributeWriter<T> lookup_or_add_for_write_span( + const AttributeIDRef &attribute_id, + const eAttrDomain domain, + const AttributeInit &initializer = AttributeInitDefault()) + { + AttributeWriter<T> attribute = this->lookup_or_add_for_write<T>( + attribute_id, domain, initializer); + if (attribute) { + return SpanAttributeWriter<T>{std::move(attribute), true}; + } + return {}; + } + + /** + * Find an attribute with the given id, domain and data type. If it does not exist, create a new + * attribute. If the attribute does not exist and can't be created, none is returned. + * + * The "only" in the name indicates that the caller should not read existing values from the + * span. If the attribute is not stored as span internally, the existing values won't be copied + * over to the span. + */ + GSpanAttributeWriter lookup_or_add_for_write_only_span(const AttributeIDRef &attribute_id, + const eAttrDomain domain, + const eCustomDataType data_type); + + /** + * Same as above, but should be used when the type is known at compile time. + */ + template<typename T> + SpanAttributeWriter<T> lookup_or_add_for_write_only_span(const AttributeIDRef &attribute_id, + const eAttrDomain domain) + { + AttributeWriter<T> attribute = this->lookup_or_add_for_write<T>(attribute_id, domain); + if (attribute) { + return SpanAttributeWriter<T>{std::move(attribute), false}; + } + return {}; + } + + /** + * Remove an attribute. + * \return True, when the attribute has been deleted. False, when it's not possible to delete + * this attribute or if there is no attribute with that id. + */ + bool remove(const AttributeIDRef &attribute_id) + { + return fn_->remove(owner_, attribute_id); + } + + /** + * Remove all anonymous attributes. + */ + void remove_anonymous(); +}; + +bool allow_procedural_attribute_access(StringRef attribute_name); +extern const char *no_procedural_access_message; + +eCustomDataType attribute_data_type_highest_complexity(Span<eCustomDataType> data_types); +/** + * Domains with a higher "information density" have a higher priority, + * in order to choose a domain that will not lose data through domain conversion. + */ +eAttrDomain attribute_domain_highest_priority(Span<eAttrDomain> domains); + +/** + * A basic container around DNA CustomData so that its users + * don't have to implement special copy and move constructors. + */ +class CustomDataAttributes { + /** + * #CustomData needs a size to be freed, and unfortunately it isn't stored in the struct + * itself, so keep track of the size here so this class can implement its own destructor. + * If the implementation of the attribute storage changes, this could be removed. + */ + int size_; + + public: + CustomData data; + + CustomDataAttributes(); + ~CustomDataAttributes(); + CustomDataAttributes(const CustomDataAttributes &other); + CustomDataAttributes(CustomDataAttributes &&other); + CustomDataAttributes &operator=(const CustomDataAttributes &other); + + void reallocate(int size); + + void clear(); + + std::optional<blender::GSpan> get_for_read(const AttributeIDRef &attribute_id) const; + + /** + * Return a virtual array for a stored attribute, or a single value virtual array with the + * default value if the attribute doesn't exist. If no default value is provided, the default + * value for the type will be used. + */ + blender::GVArray get_for_read(const AttributeIDRef &attribute_id, + eCustomDataType data_type, + const void *default_value) const; + + template<typename T> + blender::VArray<T> get_for_read(const AttributeIDRef &attribute_id, const T &default_value) const + { + const blender::CPPType &cpp_type = blender::CPPType::get<T>(); + const eCustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type); + GVArray varray = this->get_for_read(attribute_id, type, &default_value); + return varray.typed<T>(); + } + + std::optional<blender::GMutableSpan> get_for_write(const AttributeIDRef &attribute_id); + bool create(const AttributeIDRef &attribute_id, eCustomDataType data_type); + bool create_by_move(const AttributeIDRef &attribute_id, eCustomDataType data_type, void *buffer); + bool remove(const AttributeIDRef &attribute_id); + + /** + * Change the order of the attributes to match the order of IDs in the argument. + */ + void reorder(Span<AttributeIDRef> new_order); + + bool foreach_attribute(const AttributeForeachCallback callback, eAttrDomain domain) const; +}; + +AttributeAccessor mesh_attributes(const Mesh &mesh); +MutableAttributeAccessor mesh_attributes_for_write(Mesh &mesh); + +AttributeAccessor pointcloud_attributes(const PointCloud &pointcloud); +MutableAttributeAccessor pointcloud_attributes_for_write(PointCloud &pointcloud); + +/* -------------------------------------------------------------------- */ +/** \name #AttributeIDRef Inline Methods + * \{ */ + +inline AttributeIDRef::AttributeIDRef() = default; + +inline AttributeIDRef::AttributeIDRef(StringRef name) : name_(name) +{ +} + +inline AttributeIDRef::AttributeIDRef(StringRefNull name) : name_(name) +{ +} + +inline AttributeIDRef::AttributeIDRef(const char *name) : name_(name) +{ +} + +inline AttributeIDRef::AttributeIDRef(const std::string &name) : name_(name) +{ +} + +/* The anonymous id is only borrowed, the caller has to keep a reference to it. */ +inline AttributeIDRef::AttributeIDRef(const AnonymousAttributeID *anonymous_id) + : anonymous_id_(anonymous_id) +{ +} + +inline bool operator==(const AttributeIDRef &a, const AttributeIDRef &b) +{ + return a.anonymous_id_ == b.anonymous_id_ && a.name_ == b.name_; +} + +inline AttributeIDRef::operator bool() const +{ + return this->is_named() || this->is_anonymous(); +} + +inline uint64_t AttributeIDRef::hash() const +{ + return get_default_hash_2(name_, anonymous_id_); +} + +inline bool AttributeIDRef::is_named() const +{ + return !name_.is_empty(); +} + +inline bool AttributeIDRef::is_anonymous() const +{ + return anonymous_id_ != nullptr; +} + +inline StringRef AttributeIDRef::name() const +{ + BLI_assert(this->is_named()); + return name_; +} + +inline const AnonymousAttributeID &AttributeIDRef::anonymous_id() const +{ + BLI_assert(this->is_anonymous()); + return *anonymous_id_; +} + +/** + * \return True if the attribute should not be removed automatically as an optimization during + * processing or copying. Anonymous attributes can be removed when they no longer have any + * references. + */ +inline bool AttributeIDRef::should_be_kept() const +{ + return this->is_named() || BKE_anonymous_attribute_id_has_strong_references(anonymous_id_); +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh deleted file mode 100644 index 9648b5b7cde..00000000000 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ /dev/null @@ -1,541 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#pragma once - -#include <mutex> - -#include "BKE_anonymous_attribute.hh" -#include "BKE_attribute.h" - -#include "BLI_color.hh" -#include "BLI_function_ref.hh" -#include "BLI_generic_span.hh" -#include "BLI_generic_virtual_array.hh" -#include "BLI_math_vec_types.hh" - -/** - * This file defines classes that help to provide access to attribute data on a #GeometryComponent. - * The API for retrieving attributes is defined in `BKE_geometry_set.hh`, but this comment has some - * general comments about the system. - * - * Attributes are stored in geometry data, though they can also be stored in instances. Their - * storage is often tied to `CustomData`, which is a system to store "layers" of data with specific - * types and names. However, since `CustomData` was added to Blender before attributes were - * conceptualized, it combines the "legacy" style of task-specific attribute types with generic - * types like "Float". The attribute API here only provides access to generic types. - * - * Attributes are retrieved from geometry components by providing an "id" (#AttributeIDRef). This - * is most commonly just an attribute name. The attribute API on geometry components has some more - * advanced capabilities: - * 1. Read-only access: With a `const` geometry component, an attribute on the geometry cannot be - * modified, so the `for_write` and `for_output` versions of the API are not available. This is - * extremely important for writing coherent bug-free code. When an attribute is retrieved with - * write access, via #WriteAttributeLookup or #OutputAttribute, the geometry component must be - * tagged to clear caches that depend on the changed data. - * 2. Domain interpolation: When retrieving an attribute, a domain (#eAttrDomain) can be - * provided. If the attribute is stored on a different domain and conversion is possible, a - * version of the data interpolated to the requested domain will be provided. These conversions - * are implemented in each #GeometryComponent by `attribute_try_adapt_domain_impl`. - * 3. Implicit type conversion: In addition to interpolating domains, attribute types can be - * converted, using the conversions in `BKE_type_conversions.hh`. The #VArray / #GVArray system - * makes it possible to only convert necessary indices on-demand. - * 4. Anonymous attributes: The "id" used to look up an attribute can also be an anonymous - * attribute reference. Currently anonymous attributes are only used in geometry nodes. - * 5. Abstracted storage: Since the data returned from the API is usually a virtual array, - * it doesn't have to be stored contiguously (even though that is generally preferred). This - * allows accessing "legacy" attributes like `material_index`, which is stored in `MPoly`. - */ - -namespace blender::bke { - -/** - * Identifies an attribute that is either named or anonymous. - * It does not own the identifier, so it is just a reference. - */ -class AttributeIDRef { - private: - StringRef name_; - const AnonymousAttributeID *anonymous_id_ = nullptr; - - public: - AttributeIDRef(); - AttributeIDRef(StringRef name); - AttributeIDRef(StringRefNull name); - AttributeIDRef(const char *name); - AttributeIDRef(const std::string &name); - AttributeIDRef(const AnonymousAttributeID *anonymous_id); - - operator bool() const; - uint64_t hash() const; - bool is_named() const; - bool is_anonymous() const; - StringRef name() const; - const AnonymousAttributeID &anonymous_id() const; - bool should_be_kept() const; - - friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b); - friend std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id); -}; - -bool allow_procedural_attribute_access(StringRef attribute_name); -extern const char *no_procedural_access_message; - -} // namespace blender::bke - -/** - * Contains information about an attribute in a geometry component. - * More information can be added in the future. E.g. whether the attribute is builtin and how it is - * stored (uv map, vertex group, ...). - */ -struct AttributeMetaData { - eAttrDomain domain; - eCustomDataType data_type; - - constexpr friend bool operator==(AttributeMetaData a, AttributeMetaData b) - { - return (a.domain == b.domain) && (a.data_type == b.data_type); - } -}; - -struct AttributeKind { - eAttrDomain domain; - eCustomDataType data_type; -}; - -/** - * Base class for the attribute initializer types described below. - */ -struct AttributeInit { - enum class Type { - Default, - VArray, - MoveArray, - }; - Type type; - AttributeInit(const Type type) : type(type) - { - } -}; - -/** - * Create an attribute using the default value for the data type. - * The default values may depend on the attribute provider implementation. - */ -struct AttributeInitDefault : public AttributeInit { - AttributeInitDefault() : AttributeInit(Type::Default) - { - } -}; - -/** - * Create an attribute by copying data from an existing virtual array. The virtual array - * must have the same type as the newly created attribute. - * - * Note that this can be used to fill the new attribute with the default - */ -struct AttributeInitVArray : public AttributeInit { - blender::GVArray varray; - - AttributeInitVArray(blender::GVArray varray) - : AttributeInit(Type::VArray), varray(std::move(varray)) - { - } -}; - -/** - * Create an attribute with a by passing ownership of a pre-allocated contiguous array of data. - * Sometimes data is created before a geometry component is available. In that case, it's - * preferable to move data directly to the created attribute to avoid a new allocation and a copy. - * - * Note that this will only have a benefit for attributes that are stored directly as contiguous - * arrays, so not for some built-in attributes. - * - * The array must be allocated with MEM_*, since `attribute_try_create` will free the array if it - * can't be used directly, and that is generally how Blender expects custom data to be allocated. - */ -struct AttributeInitMove : public AttributeInit { - void *data = nullptr; - - AttributeInitMove(void *data) : AttributeInit(Type::MoveArray), data(data) - { - } -}; - -/* Returns false when the iteration should be stopped. */ -using AttributeForeachCallback = blender::FunctionRef<bool( - const blender::bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data)>; - -namespace blender::bke { - -eCustomDataType attribute_data_type_highest_complexity(Span<eCustomDataType> data_types); -/** - * Domains with a higher "information density" have a higher priority, - * in order to choose a domain that will not lose data through domain conversion. - */ -eAttrDomain attribute_domain_highest_priority(Span<eAttrDomain> domains); - -/** - * Used when looking up a "plain attribute" based on a name for reading from it. - */ -struct ReadAttributeLookup { - /* The virtual array that is used to read from this attribute. */ - GVArray varray; - /* Domain the attribute lives on in the geometry. */ - eAttrDomain domain; - - /* Convenience function to check if the attribute has been found. */ - operator bool() const - { - return this->varray; - } -}; - -/** - * Used when looking up a "plain attribute" based on a name for reading from it and writing to it. - */ -struct WriteAttributeLookup { - /** The virtual array that is used to read from and write to the attribute. */ - GVMutableArray varray; - /** Domain the attributes lives on in the geometry component. */ - eAttrDomain domain; - /** - * Call this after changing the attribute to invalidate caches that depend on this attribute. - * \note Do not call this after the component the attribute is from has been destructed. - */ - std::function<void()> tag_modified_fn; - - /* Convenience function to check if the attribute has been found. */ - operator bool() const - { - return this->varray; - } -}; - -/** - * An output attribute allows writing to an attribute (and optionally reading as well). It adds - * some convenience features on top of `GVMutableArray` that are very commonly used. - * - * Supported convenience features: - * - Implicit type conversion when writing to builtin attributes. - * - Supports simple access to a span containing the attribute values (that avoids the use of - * MutableVArraySpan in many cases). - * - An output attribute can live side by side with an existing attribute with a different domain - * or data type. The old attribute will only be overwritten when the #save function is called. - * - * \note The lifetime of an output attribute should not be longer than the lifetime of the - * geometry component it comes from, since it can keep a reference to the component for use in - * the #save method. - */ -class OutputAttribute { - public: - using SaveFn = std::function<void(OutputAttribute &)>; - - private: - GVMutableArray varray_; - eAttrDomain domain_ = ATTR_DOMAIN_AUTO; - SaveFn save_; - std::unique_ptr<GMutableVArraySpan> optional_span_varray_; - bool ignore_old_values_ = false; - bool save_has_been_called_ = false; - - public: - OutputAttribute(); - OutputAttribute(OutputAttribute &&other); - OutputAttribute(GVMutableArray varray, eAttrDomain domain, SaveFn save, bool ignore_old_values); - - ~OutputAttribute(); - - operator bool() const; - - GVMutableArray &operator*(); - GVMutableArray *operator->(); - GVMutableArray &varray(); - eAttrDomain domain() const; - const CPPType &cpp_type() const; - eCustomDataType custom_data_type() const; - - GMutableSpan as_span(); - template<typename T> MutableSpan<T> as_span(); - - void save(); -}; - -/** - * Same as OutputAttribute, but should be used when the data type is known at compile time. - */ -template<typename T> class OutputAttribute_Typed { - private: - OutputAttribute attribute_; - VMutableArray<T> varray_; - - public: - OutputAttribute_Typed(); - OutputAttribute_Typed(OutputAttribute attribute) : attribute_(std::move(attribute)) - { - if (attribute_) { - varray_ = attribute_.varray().template typed<T>(); - } - } - - OutputAttribute_Typed(OutputAttribute_Typed &&other); - ~OutputAttribute_Typed(); - - OutputAttribute_Typed &operator=(OutputAttribute_Typed &&other) - { - if (this == &other) { - return *this; - } - this->~OutputAttribute_Typed(); - new (this) OutputAttribute_Typed(std::move(other)); - return *this; - } - - operator bool() const - { - return varray_; - } - - VMutableArray<T> &operator*() - { - return varray_; - } - - VMutableArray<T> *operator->() - { - return &varray_; - } - - VMutableArray<T> &varray() - { - return varray_; - } - - eAttrDomain domain() const - { - return attribute_.domain(); - } - - const CPPType &cpp_type() const - { - return CPPType::get<T>(); - } - - eCustomDataType custom_data_type() const - { - return cpp_type_to_custom_data_type(this->cpp_type()); - } - - MutableSpan<T> as_span() - { - return attribute_.as_span<T>(); - } - - void save() - { - attribute_.save(); - } -}; - -/* These are not defined in the class directly, because when defining them there, the external - * template instantiation does not work, resulting in longer compile times. */ -template<typename T> inline OutputAttribute_Typed<T>::OutputAttribute_Typed() = default; -template<typename T> -inline OutputAttribute_Typed<T>::OutputAttribute_Typed(OutputAttribute_Typed &&other) = default; -template<typename T> inline OutputAttribute_Typed<T>::~OutputAttribute_Typed() = default; - -/** - * A basic container around DNA CustomData so that its users - * don't have to implement special copy and move constructors. - */ -class CustomDataAttributes { - /** - * #CustomData needs a size to be freed, and unfortunately it isn't stored in the struct - * itself, so keep track of the size here so this class can implement its own destructor. - * If the implementation of the attribute storage changes, this could be removed. - */ - int size_; - - public: - CustomData data; - - CustomDataAttributes(); - ~CustomDataAttributes(); - CustomDataAttributes(const CustomDataAttributes &other); - CustomDataAttributes(CustomDataAttributes &&other); - CustomDataAttributes &operator=(const CustomDataAttributes &other); - - void reallocate(int size); - - void clear(); - - std::optional<blender::GSpan> get_for_read(const AttributeIDRef &attribute_id) const; - - /** - * Return a virtual array for a stored attribute, or a single value virtual array with the - * default value if the attribute doesn't exist. If no default value is provided, the default - * value for the type will be used. - */ - blender::GVArray get_for_read(const AttributeIDRef &attribute_id, - eCustomDataType data_type, - const void *default_value) const; - - template<typename T> - blender::VArray<T> get_for_read(const AttributeIDRef &attribute_id, const T &default_value) const - { - const blender::CPPType &cpp_type = blender::CPPType::get<T>(); - const eCustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type); - GVArray varray = this->get_for_read(attribute_id, type, &default_value); - return varray.typed<T>(); - } - - std::optional<blender::GMutableSpan> get_for_write(const AttributeIDRef &attribute_id); - bool create(const AttributeIDRef &attribute_id, eCustomDataType data_type); - bool create_by_move(const AttributeIDRef &attribute_id, eCustomDataType data_type, void *buffer); - bool remove(const AttributeIDRef &attribute_id); - - /** - * Change the order of the attributes to match the order of IDs in the argument. - */ - void reorder(Span<AttributeIDRef> new_order); - - bool foreach_attribute(const AttributeForeachCallback callback, eAttrDomain domain) const; -}; - -/* -------------------------------------------------------------------- */ -/** \name #AttributeIDRef Inline Methods - * \{ */ - -inline AttributeIDRef::AttributeIDRef() = default; - -inline AttributeIDRef::AttributeIDRef(StringRef name) : name_(name) -{ -} - -inline AttributeIDRef::AttributeIDRef(StringRefNull name) : name_(name) -{ -} - -inline AttributeIDRef::AttributeIDRef(const char *name) : name_(name) -{ -} - -inline AttributeIDRef::AttributeIDRef(const std::string &name) : name_(name) -{ -} - -/* The anonymous id is only borrowed, the caller has to keep a reference to it. */ -inline AttributeIDRef::AttributeIDRef(const AnonymousAttributeID *anonymous_id) - : anonymous_id_(anonymous_id) -{ -} - -inline bool operator==(const AttributeIDRef &a, const AttributeIDRef &b) -{ - return a.anonymous_id_ == b.anonymous_id_ && a.name_ == b.name_; -} - -inline AttributeIDRef::operator bool() const -{ - return this->is_named() || this->is_anonymous(); -} - -inline uint64_t AttributeIDRef::hash() const -{ - return get_default_hash_2(name_, anonymous_id_); -} - -inline bool AttributeIDRef::is_named() const -{ - return !name_.is_empty(); -} - -inline bool AttributeIDRef::is_anonymous() const -{ - return anonymous_id_ != nullptr; -} - -inline StringRef AttributeIDRef::name() const -{ - BLI_assert(this->is_named()); - return name_; -} - -inline const AnonymousAttributeID &AttributeIDRef::anonymous_id() const -{ - BLI_assert(this->is_anonymous()); - return *anonymous_id_; -} - -/** - * \return True if the attribute should not be removed automatically as an optimization during - * processing or copying. Anonymous attributes can be removed when they no longer have any - * references. - */ -inline bool AttributeIDRef::should_be_kept() const -{ - return this->is_named() || BKE_anonymous_attribute_id_has_strong_references(anonymous_id_); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name #OutputAttribute Inline Methods - * \{ */ - -inline OutputAttribute::OutputAttribute() = default; -inline OutputAttribute::OutputAttribute(OutputAttribute &&other) = default; - -inline OutputAttribute::OutputAttribute(GVMutableArray varray, - eAttrDomain domain, - SaveFn save, - const bool ignore_old_values) - : varray_(std::move(varray)), - domain_(domain), - save_(std::move(save)), - ignore_old_values_(ignore_old_values) -{ -} - -inline OutputAttribute::operator bool() const -{ - return varray_; -} - -inline GVMutableArray &OutputAttribute::operator*() -{ - return varray_; -} - -inline GVMutableArray *OutputAttribute::operator->() -{ - return &varray_; -} - -inline GVMutableArray &OutputAttribute::varray() -{ - return varray_; -} - -inline eAttrDomain OutputAttribute::domain() const -{ - return domain_; -} - -inline const CPPType &OutputAttribute::cpp_type() const -{ - return varray_.type(); -} - -inline eCustomDataType OutputAttribute::custom_data_type() const -{ - return cpp_type_to_custom_data_type(this->cpp_type()); -} - -template<typename T> inline MutableSpan<T> OutputAttribute::as_span() -{ - return this->as_span().typed<T>(); -} - -/** \} */ - -} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index 3e00dc78b74..fb97e52f6da 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -20,7 +20,7 @@ #include "BLI_vector.hh" #include "BLI_virtual_array.hh" -#include "BKE_attribute_access.hh" +#include "BKE_attribute.hh" namespace blender::bke { @@ -401,6 +401,9 @@ class CurvesGeometry : public ::CurvesGeometry { */ void remove_attributes_based_on_types(); + AttributeAccessor attributes() const; + MutableAttributeAccessor attributes_for_write(); + /* -------------------------------------------------------------------- * Attributes. */ diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 1d4291473bb..4108e2f7e2e 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -8,6 +8,7 @@ #include <atomic> #include <iostream> +#include <mutex> #include "BLI_float4x4.hh" #include "BLI_function_ref.hh" @@ -19,7 +20,7 @@ #include "BLI_vector_set.hh" #include "BKE_anonymous_attribute.hh" -#include "BKE_attribute_access.hh" +#include "BKE_attribute.hh" #include "BKE_geometry_set.h" struct Curves; @@ -63,6 +64,15 @@ class GeometryComponent { virtual ~GeometryComponent() = default; static GeometryComponent *create(GeometryComponentType component_type); + int attribute_domain_size(eAttrDomain domain) const; + + /** + * Get access to the attributes in this geometry component. May return none if the geometry does + * not support the attribute system. + */ + virtual std::optional<blender::bke::AttributeAccessor> attributes() const; + virtual std::optional<blender::bke::MutableAttributeAccessor> attributes_for_write(); + /* The returned component should be of the same type as the type this is called on. */ virtual GeometryComponent *copy() const = 0; @@ -78,203 +88,7 @@ class GeometryComponent { GeometryComponentType type() const; - /** - * Return true when any attribute with this name exists, including built in attributes. - */ - bool attribute_exists(const blender::bke::AttributeIDRef &attribute_id) const; - - /** - * Return the data type and domain of an attribute with the given name if it exists. - */ - std::optional<AttributeMetaData> attribute_get_meta_data( - const blender::bke::AttributeIDRef &attribute_id) const; - - /** - * Return true when the geometry component supports this attribute domain. - * \note Conceptually this function is static, the result is always the same for different - * instances of the same geometry component type. - */ - bool attribute_domain_supported(eAttrDomain domain) const; - /** - * Return the length of a specific domain, or 0 if the domain is not supported. - */ - virtual int attribute_domain_num(eAttrDomain domain) const; - - /** - * Return true if the attribute name corresponds to a built-in attribute with a hardcoded domain - * and data type. - */ - bool attribute_is_builtin(const blender::StringRef attribute_name) const; - bool attribute_is_builtin(const blender::bke::AttributeIDRef &attribute_id) const; - - /** - * Get read-only access to an attribute with the given name or id, on the highest priority domain - * if there is a name collision. - * \return null if the attribute does not exist. - */ - blender::bke::ReadAttributeLookup attribute_try_get_for_read( - const blender::bke::AttributeIDRef &attribute_id) const; - - /** - * Get read and write access to an attribute with the given name or id, on the highest priority - * domain if there is a name collision. - * \note #WriteAttributeLookup.tag_modified_fn must be called after modifying data. - * \return null if the attribute does not exist - */ - blender::bke::WriteAttributeLookup attribute_try_get_for_write( - const blender::bke::AttributeIDRef &attribute_id); - - /** - * Get a read-only attribute for the domain based on the given attribute. This can be used to - * interpolate from one domain to another. - * \return null if the interpolation is not implemented. - */ - blender::GVArray attribute_try_adapt_domain(const blender::GVArray &varray, - const eAttrDomain from_domain, - const eAttrDomain to_domain) const - { - return this->attribute_try_adapt_domain_impl(varray, from_domain, to_domain); - } - /* Use instead of the method above when the type is known at compile time for type safety. */ - template<typename T> - blender::VArray<T> attribute_try_adapt_domain(const blender::VArray<T> &varray, - const eAttrDomain from_domain, - const eAttrDomain to_domain) const - { - return this->attribute_try_adapt_domain_impl(varray, from_domain, to_domain) - .template typed<T>(); - } - - /** Returns true when the attribute has been deleted. */ - bool attribute_try_delete(const blender::bke::AttributeIDRef &attribute_id); - - /** - * Remove any anonymous attributes on the geometry (they generally shouldn't exist on original - * geometry). - */ - void attributes_remove_anonymous(); - - /** Returns true when the attribute has been created. */ - bool attribute_try_create(const blender::bke::AttributeIDRef &attribute_id, - eAttrDomain domain, - eCustomDataType data_type, - const AttributeInit &initializer); - - /** - * Try to create the builtin attribute with the given name. No data type or domain has to be - * provided, because those are fixed for builtin attributes. - */ - bool attribute_try_create_builtin(const blender::StringRef attribute_name, - const AttributeInit &initializer); - - blender::Set<blender::bke::AttributeIDRef> attribute_ids() const; - /** - * \return False if the callback explicitly returned false at any point, otherwise true, - * meaning the callback made it all the way through. - */ - bool attribute_foreach(const AttributeForeachCallback callback) const; - virtual bool is_empty() const; - - /** - * Get a virtual array that refers to the data of an attribute, interpolated to the given domain - * and converted to the data type. Returns null when the attribute does not exist or cannot be - * interpolated or converted. - */ - blender::GVArray attribute_try_get_for_read(const blender::bke::AttributeIDRef &attribute_id, - eAttrDomain domain, - eCustomDataType data_type) const; - - /** - * Get a virtual array that refers to the data of an attribute, interpolated to the given domain. - * The data type is left unchanged. Returns null when the attribute does not exist or cannot be - * interpolated. - */ - blender::GVArray attribute_try_get_for_read(const blender::bke::AttributeIDRef &attribute_id, - eAttrDomain domain) const; - - /** - * Get a virtual array that refers to the data of an attribute converted to the given data type. - * The attribute's domain is left unchanged. Returns null when the attribute does not exist or - * cannot be converted. - */ - blender::bke::ReadAttributeLookup attribute_try_get_for_read( - const blender::bke::AttributeIDRef &attribute_id, eCustomDataType data_type) const; - - /** - * Get a virtual array that refers to the data of an attribute, interpolated to the given domain - * and converted to the data type. If that is not possible, the returned virtual array will - * contain a default value. This never returns null. - */ - blender::GVArray attribute_get_for_read(const blender::bke::AttributeIDRef &attribute_id, - eAttrDomain domain, - eCustomDataType data_type, - const void *default_value = nullptr) const; - /* Use instead of the method above when the type is known at compile time for type safety. */ - template<typename T> - blender::VArray<T> attribute_get_for_read(const blender::bke::AttributeIDRef &attribute_id, - const eAttrDomain domain, - const T &default_value) const - { - const blender::CPPType &cpp_type = blender::CPPType::get<T>(); - const eCustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type); - return this->attribute_get_for_read(attribute_id, domain, type, &default_value) - .template typed<T>(); - } - - /** - * Returns an "output attribute", which is essentially a mutable virtual array with some commonly - * used convince features. The returned output attribute might be empty if requested attribute - * cannot exist on the geometry. - * - * The included convenience features are: - * - Implicit type conversion when writing to builtin attributes. - * - If the attribute name exists already, but has a different type/domain, a temporary attribute - * is created that will overwrite the existing attribute in the end. - */ - blender::bke::OutputAttribute attribute_try_get_for_output( - const blender::bke::AttributeIDRef &attribute_id, - eAttrDomain domain, - eCustomDataType data_type, - const void *default_value = nullptr); - /* Use instead of the method above when the type is known at compile time for type safety. */ - template<typename T> - blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output( - const blender::bke::AttributeIDRef &attribute_id, - const eAttrDomain domain, - const T default_value) - { - const blender::CPPType &cpp_type = blender::CPPType::get<T>(); - const eCustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type); - return this->attribute_try_get_for_output(attribute_id, domain, data_type, &default_value); - } - - /** - * Same as #attribute_try_get_for_output, but should be used when the original values in the - * attributes are not read, i.e. the attribute is used only for output. The can be faster because - * it can avoid interpolation and conversion of existing values. Since values are not read from - * this attribute, no default value is necessary. - */ - blender::bke::OutputAttribute attribute_try_get_for_output_only( - const blender::bke::AttributeIDRef &attribute_id, - eAttrDomain domain, - eCustomDataType data_type); - /* Use instead of the method above when the type is known at compile time for type safety. */ - template<typename T> - blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output_only( - const blender::bke::AttributeIDRef &attribute_id, const eAttrDomain domain) - { - const blender::CPPType &cpp_type = blender::CPPType::get<T>(); - const eCustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type); - return this->attribute_try_get_for_output_only(attribute_id, domain, data_type); - } - - private: - virtual const blender::bke::ComponentAttributeProviders *get_attribute_providers() const; - - virtual blender::GVArray attribute_try_adapt_domain_impl(const blender::GVArray &varray, - eAttrDomain from_domain, - eAttrDomain to_domain) const; }; template<typename T> @@ -381,7 +195,7 @@ struct GeometrySet { using AttributeForeachCallback = blender::FunctionRef<void(const blender::bke::AttributeIDRef &attribute_id, - const AttributeMetaData &meta_data, + const blender::bke::AttributeMetaData &meta_data, const GeometryComponent &component)>; void attribute_foreach(blender::Span<GeometryComponentType> component_types, @@ -392,7 +206,7 @@ struct GeometrySet { blender::Span<GeometryComponentType> component_types, GeometryComponentType dst_component_type, bool include_instances, - blender::Map<blender::bke::AttributeIDRef, AttributeKind> &r_attributes) const; + blender::Map<blender::bke::AttributeIDRef, blender::bke::AttributeKind> &r_attributes) const; blender::Vector<GeometryComponentType> gather_component_types(bool include_instances, bool ignore_empty) const; @@ -565,8 +379,6 @@ class MeshComponent : public GeometryComponent { */ Mesh *get_for_write(); - int attribute_domain_num(eAttrDomain domain) const final; - bool is_empty() const final; bool owns_direct_data() const override; @@ -574,12 +386,8 @@ class MeshComponent : public GeometryComponent { static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_MESH; - private: - const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final; - - blender::GVArray attribute_try_adapt_domain_impl(const blender::GVArray &varray, - eAttrDomain from_domain, - eAttrDomain to_domain) const final; + std::optional<blender::bke::AttributeAccessor> attributes() const final; + std::optional<blender::bke::MutableAttributeAccessor> attributes_for_write() final; }; /** @@ -628,17 +436,17 @@ class PointCloudComponent : public GeometryComponent { */ PointCloud *get_for_write(); - int attribute_domain_num(eAttrDomain domain) const final; - bool is_empty() const final; bool owns_direct_data() const override; void ensure_owns_direct_data() override; + std::optional<blender::bke::AttributeAccessor> attributes() const final; + std::optional<blender::bke::MutableAttributeAccessor> attributes_for_write() final; + static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_POINT_CLOUD; private: - const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final; }; /** @@ -669,21 +477,15 @@ class CurveComponentLegacy : public GeometryComponent { const CurveEval *get_for_read() const; CurveEval *get_for_write(); - int attribute_domain_num(eAttrDomain domain) const final; - bool is_empty() const final; bool owns_direct_data() const override; void ensure_owns_direct_data() override; - static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE; + std::optional<blender::bke::AttributeAccessor> attributes() const final; + std::optional<blender::bke::MutableAttributeAccessor> attributes_for_write() final; - private: - const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final; - - blender::GVArray attribute_try_adapt_domain_impl(const blender::GVArray &varray, - eAttrDomain from_domain, - eAttrDomain to_domain) const final; + static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE; }; /** @@ -721,8 +523,6 @@ class CurveComponent : public GeometryComponent { const Curves *get_for_read() const; Curves *get_for_write(); - int attribute_domain_num(eAttrDomain domain) const final; - bool is_empty() const final; bool owns_direct_data() const override; @@ -734,14 +534,10 @@ class CurveComponent : public GeometryComponent { */ const Curve *get_curve_for_render() const; - static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE; + std::optional<blender::bke::AttributeAccessor> attributes() const final; + std::optional<blender::bke::MutableAttributeAccessor> attributes_for_write() final; - private: - const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final; - - blender::GVArray attribute_try_adapt_domain_impl(const blender::GVArray &varray, - eAttrDomain from_domain, - eAttrDomain to_domain) const final; + static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE; }; /** @@ -966,10 +762,11 @@ class InstancesComponent : public GeometryComponent { blender::Span<int> almost_unique_ids() const; - blender::bke::CustomDataAttributes &attributes(); - const blender::bke::CustomDataAttributes &attributes() const; + blender::bke::CustomDataAttributes &instance_attributes(); + const blender::bke::CustomDataAttributes &instance_attributes() const; - int attribute_domain_num(eAttrDomain domain) const final; + std::optional<blender::bke::AttributeAccessor> attributes() const final; + std::optional<blender::bke::MutableAttributeAccessor> attributes_for_write() final; void foreach_referenced_geometry( blender::FunctionRef<void(const GeometrySet &geometry_set)> callback) const; @@ -982,7 +779,6 @@ class InstancesComponent : public GeometryComponent { static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_INSTANCES; private: - const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final; }; /** diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh index cbbd0249f4f..356709d8942 100644 --- a/source/blender/blenkernel/BKE_mesh_sample.hh +++ b/source/blender/blenkernel/BKE_mesh_sample.hh @@ -13,6 +13,7 @@ #include "DNA_meshdata_types.h" #include "BKE_attribute.h" +#include "BKE_attribute.hh" struct Mesh; struct BVHTreeFromMesh; @@ -21,11 +22,6 @@ namespace blender { class RandomNumberGenerator; } -namespace blender::bke { -struct ReadAttributeLookup; -class OutputAttribute; -} // namespace blender::bke - namespace blender::bke::mesh_surface_sample { void sample_point_attribute(const Mesh &mesh, @@ -81,8 +77,8 @@ class MeshAttributeInterpolator { eAttributeMapMode mode, const GMutableSpan dst); - void sample_attribute(const ReadAttributeLookup &src_attribute, - OutputAttribute &dst_attribute, + void sample_attribute(const GAttributeReader &src_attribute, + GSpanAttributeWriter &dst_attribute, eAttributeMapMode mode); protected: diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 28f326a4ad4..767018ae0bb 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -15,7 +15,7 @@ #include "BLI_math_vec_types.hh" #include "BLI_vector.hh" -#include "BKE_attribute_access.hh" +#include "BKE_attribute.hh" #include "BKE_attribute_math.hh" struct Curve; @@ -646,6 +646,8 @@ struct CurveEval { void transform(const blender::float4x4 &matrix); bool bounds_min_max(blender::float3 &min, blender::float3 &max, bool use_evaluated) const; + blender::bke::MutableAttributeAccessor attributes_for_write(); + /** * Return the start indices for each of the curve spline's control points, if they were part * of a flattened array. This can be used to facilitate parallelism by avoiding the need to diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index b2307c0e129..d0f9a67f167 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -325,7 +325,7 @@ set(SRC BKE_asset_library.h BKE_asset_library.hh BKE_attribute.h - BKE_attribute_access.hh + BKE_attribute.hh BKE_attribute_math.hh BKE_autoexec.h BKE_blender.h diff --git a/source/blender/blenkernel/intern/attribute.cc b/source/blender/blenkernel/intern/attribute.cc index 7c09b4a4ce3..030e4941874 100644 --- a/source/blender/blenkernel/intern/attribute.cc +++ b/source/blender/blenkernel/intern/attribute.cc @@ -23,7 +23,7 @@ #include "BLI_string_utils.h" #include "BKE_attribute.h" -#include "BKE_attribute_access.hh" +#include "BKE_attribute.hh" #include "BKE_curves.h" #include "BKE_customdata.h" #include "BKE_editmesh.h" diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index ee9358eb97e..ac1ee19927c 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -2,7 +2,6 @@ #include <utility> -#include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_customdata.h" #include "BKE_deform.h" @@ -26,8 +25,6 @@ #include "attribute_access_intern.hh" -static CLG_LogRef LOG = {"bke.attribute_access"}; - using blender::float3; using blender::GMutableSpan; using blender::GSpan; @@ -36,7 +33,6 @@ using blender::Set; using blender::StringRef; using blender::StringRefNull; using blender::bke::AttributeIDRef; -using blender::bke::OutputAttribute; namespace blender::bke { @@ -151,36 +147,6 @@ eAttrDomain attribute_domain_highest_priority(Span<eAttrDomain> domains) return highest_priority_domain; } -GMutableSpan OutputAttribute::as_span() -{ - if (!optional_span_varray_) { - const bool materialize_old_values = !ignore_old_values_; - optional_span_varray_ = std::make_unique<GMutableVArraySpan>(varray_, materialize_old_values); - } - GMutableVArraySpan &span_varray = *optional_span_varray_; - return span_varray; -} - -void OutputAttribute::save() -{ - save_has_been_called_ = true; - if (optional_span_varray_) { - optional_span_varray_->save(); - } - if (save_) { - save_(*this); - } -} - -OutputAttribute::~OutputAttribute() -{ - if (!save_has_been_called_) { - if (varray_) { - std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n"; - } - } -} - static AttributeIDRef attribute_id_from_custom_data_layer(const CustomDataLayer &layer) { if (layer.anonymous_id != nullptr) { @@ -292,9 +258,9 @@ static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer, return layer.name == attribute_id.name(); } -GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const GeometryComponent &component) const +GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const void *owner) const { - const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); + const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner); if (custom_data == nullptr) { return {}; } @@ -310,21 +276,20 @@ GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const GeometryComponent return {}; } - const int domain_num = component.attribute_domain_num(domain_); - return as_read_attribute_(data, domain_num); + const int element_num = custom_data_access_.get_element_num(owner); + return as_read_attribute_(data, element_num); } -WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( - GeometryComponent &component) const +GAttributeWriter BuiltinCustomDataLayerProvider::try_get_for_write(void *owner) const { if (writable_ != Writable) { return {}; } - CustomData *custom_data = custom_data_access_.get_custom_data(component); + CustomData *custom_data = custom_data_access_.get_custom_data(owner); if (custom_data == nullptr) { return {}; } - const int domain_num = component.attribute_domain_num(domain_); + const int element_num = custom_data_access_.get_element_num(owner); void *data; if (stored_as_named_attribute_) { @@ -340,44 +305,42 @@ WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( void *new_data; if (stored_as_named_attribute_) { new_data = CustomData_duplicate_referenced_layer_named( - custom_data, stored_type_, name_.c_str(), domain_num); + custom_data, stored_type_, name_.c_str(), element_num); } else { - new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_num); + new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, element_num); } if (data != new_data) { if (custom_data_access_.update_custom_data_pointers) { - custom_data_access_.update_custom_data_pointers(component); + custom_data_access_.update_custom_data_pointers(owner); } data = new_data; } std::function<void()> tag_modified_fn; if (update_on_write_ != nullptr) { - tag_modified_fn = [component = &component, update = update_on_write_]() { - update(*component); - }; + tag_modified_fn = [owner, update = update_on_write_]() { update(owner); }; } - return {as_write_attribute_(data, domain_num), domain_, std::move(tag_modified_fn)}; + return {as_write_attribute_(data, element_num), domain_, std::move(tag_modified_fn)}; } -bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) const +bool BuiltinCustomDataLayerProvider::try_delete(void *owner) const { if (deletable_ != Deletable) { return false; } - CustomData *custom_data = custom_data_access_.get_custom_data(component); + CustomData *custom_data = custom_data_access_.get_custom_data(owner); if (custom_data == nullptr) { return {}; } - const int domain_num = component.attribute_domain_num(domain_); + 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(), domain_num)) { + if (CustomData_free_layer_named(custom_data, name_.c_str(), element_num)) { if (custom_data_access_.update_custom_data_pointers) { - custom_data_access_.update_custom_data_pointers(component); + custom_data_access_.update_custom_data_pointers(owner); } return true; } @@ -385,27 +348,27 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co } const int layer_index = CustomData_get_layer_index(custom_data, stored_type_); - if (CustomData_free_layer(custom_data, stored_type_, domain_num, layer_index)) { + if (CustomData_free_layer(custom_data, stored_type_, element_num, layer_index)) { if (custom_data_access_.update_custom_data_pointers) { - custom_data_access_.update_custom_data_pointers(component); + custom_data_access_.update_custom_data_pointers(owner); } return true; } return false; } -bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, +bool BuiltinCustomDataLayerProvider::try_create(void *owner, const AttributeInit &initializer) const { if (createable_ != Creatable) { return false; } - CustomData *custom_data = custom_data_access_.get_custom_data(component); + CustomData *custom_data = custom_data_access_.get_custom_data(owner); if (custom_data == nullptr) { return false; } - const int domain_num = component.attribute_domain_num(domain_); + const int element_num = custom_data_access_.get_element_num(owner); bool success; if (stored_as_named_attribute_) { if (CustomData_get_layer_named(custom_data, data_type_, name_.c_str())) { @@ -413,7 +376,7 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, return false; } success = add_custom_data_layer_from_attribute_init( - name_, *custom_data, stored_type_, domain_num, initializer); + name_, *custom_data, stored_type_, element_num, initializer); } else { if (CustomData_get_layer(custom_data, stored_type_) != nullptr) { @@ -421,19 +384,19 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, return false; } success = add_builtin_type_custom_data_layer_from_init( - *custom_data, stored_type_, domain_num, initializer); + *custom_data, stored_type_, element_num, initializer); } if (success) { if (custom_data_access_.update_custom_data_pointers) { - custom_data_access_.update_custom_data_pointers(component); + custom_data_access_.update_custom_data_pointers(owner); } } return success; } -bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component) const +bool BuiltinCustomDataLayerProvider::exists(const void *owner) const { - const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); + const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner); if (custom_data == nullptr) { return false; } @@ -443,14 +406,14 @@ bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component) return CustomData_get_layer(custom_data, stored_type_) != nullptr; } -ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( - const GeometryComponent &component, const AttributeIDRef &attribute_id) const +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(component); + const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner); if (custom_data == nullptr) { return {}; } - const int domain_num = component.attribute_domain_num(domain_); + 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; @@ -459,61 +422,62 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( if (type == nullptr) { continue; } - GSpan data{*type, layer.data, domain_num}; + GSpan data{*type, layer.data, element_num}; return {GVArray::ForSpan(data), domain_}; } return {}; } -WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( - GeometryComponent &component, const AttributeIDRef &attribute_id) const +GAttributeWriter CustomDataAttributeProvider::try_get_for_write( + void *owner, const AttributeIDRef &attribute_id) const { - CustomData *custom_data = custom_data_access_.get_custom_data(component); + CustomData *custom_data = custom_data_access_.get_custom_data(owner); if (custom_data == nullptr) { return {}; } - const int domain_num = component.attribute_domain_num(domain_); + 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, domain_num); + 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(), domain_num); + 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, domain_num}; + GMutableSpan data{*type, layer.data, element_num}; return {GVMutableArray::ForSpan(data), domain_}; } return {}; } -bool CustomDataAttributeProvider::try_delete(GeometryComponent &component, - const AttributeIDRef &attribute_id) const +bool CustomDataAttributeProvider::try_delete(void *owner, const AttributeIDRef &attribute_id) const { - CustomData *custom_data = custom_data_access_.get_custom_data(component); + CustomData *custom_data = custom_data_access_.get_custom_data(owner); if (custom_data == nullptr) { return false; } - const int domain_num = component.attribute_domain_num(domain_); + 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, domain_num, i); + CustomData_free_layer(custom_data, layer.type, element_num, i); return true; } } return false; } -bool CustomDataAttributeProvider::try_create(GeometryComponent &component, +bool CustomDataAttributeProvider::try_create(void *owner, const AttributeIDRef &attribute_id, const eAttrDomain domain, const eCustomDataType data_type, @@ -525,7 +489,7 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component, if (!this->type_is_supported(data_type)) { return false; } - CustomData *custom_data = custom_data_access_.get_custom_data(component); + CustomData *custom_data = custom_data_access_.get_custom_data(owner); if (custom_data == nullptr) { return false; } @@ -534,16 +498,16 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component, return false; } } - const int domain_num = component.attribute_domain_num(domain_); + const int element_num = custom_data_access_.get_element_num(owner); add_custom_data_layer_from_attribute_init( - attribute_id, *custom_data, data_type, domain_num, initializer); + attribute_id, *custom_data, data_type, element_num, initializer); return true; } -bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &component, +bool CustomDataAttributeProvider::foreach_attribute(const void *owner, const AttributeForeachCallback callback) const { - const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); + const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner); if (custom_data == nullptr) { return true; } @@ -560,17 +524,17 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com return true; } -ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( - const GeometryComponent &component, const AttributeIDRef &attribute_id) const +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(component); + 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 = component.attribute_domain_num(domain_); + const int domain_num = custom_data_access_.get_element_num(owner); return {as_read_attribute_(layer.data, domain_num), domain_}; } } @@ -578,36 +542,36 @@ ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( return {}; } -WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write( - GeometryComponent &component, const AttributeIDRef &attribute_id) const +GAttributeWriter NamedLegacyCustomDataProvider::try_get_for_write( + void *owner, const AttributeIDRef &attribute_id) const { - CustomData *custom_data = custom_data_access_.get_custom_data(component); + 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 domain_num = component.attribute_domain_num(domain_); + const int element_num = custom_data_access_.get_element_num(owner); void *data_old = layer.data; void *data_new = CustomData_duplicate_referenced_layer_named( - custom_data, stored_type_, layer.name, domain_num); + custom_data, stored_type_, layer.name, element_num); if (data_old != data_new) { if (custom_data_access_.update_custom_data_pointers) { - custom_data_access_.update_custom_data_pointers(component); + custom_data_access_.update_custom_data_pointers(owner); } } - return {as_write_attribute_(layer.data, domain_num), domain_}; + return {as_write_attribute_(layer.data, element_num), domain_}; } } } return {}; } -bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component, +bool NamedLegacyCustomDataProvider::try_delete(void *owner, const AttributeIDRef &attribute_id) const { - CustomData *custom_data = custom_data_access_.get_custom_data(component); + CustomData *custom_data = custom_data_access_.get_custom_data(owner); if (custom_data == nullptr) { return false; } @@ -615,10 +579,10 @@ bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component, const CustomDataLayer &layer = custom_data->layers[i]; if (layer.type == stored_type_) { if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { - const int domain_num = component.attribute_domain_num(domain_); - CustomData_free_layer(custom_data, stored_type_, domain_num, i); + const int element_num = custom_data_access_.get_element_num(owner); + CustomData_free_layer(custom_data, stored_type_, element_num, i); if (custom_data_access_.update_custom_data_pointers) { - custom_data_access_.update_custom_data_pointers(component); + custom_data_access_.update_custom_data_pointers(owner); } return true; } @@ -628,9 +592,9 @@ bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component, } bool NamedLegacyCustomDataProvider::foreach_attribute( - const GeometryComponent &component, const AttributeForeachCallback callback) const + const void *owner, const AttributeForeachCallback callback) const { - const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); + const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner); if (custom_data == nullptr) { return true; } @@ -804,275 +768,10 @@ void CustomDataAttributes::reorder(Span<AttributeIDRef> new_order) CustomData_update_typemap(&data); } -} // namespace blender::bke - /* -------------------------------------------------------------------- */ /** \name Geometry Component * \{ */ -const blender::bke::ComponentAttributeProviders *GeometryComponent::get_attribute_providers() const -{ - return nullptr; -} - -bool GeometryComponent::attribute_domain_supported(const eAttrDomain domain) const -{ - 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_num(const eAttrDomain UNUSED(domain)) const -{ - return 0; -} - -bool GeometryComponent::attribute_is_builtin(const blender::StringRef attribute_name) const -{ - using namespace blender::bke; - const ComponentAttributeProviders *providers = this->get_attribute_providers(); - if (providers == nullptr) { - return false; - } - return providers->builtin_attribute_providers().contains_as(attribute_name); -} - -bool GeometryComponent::attribute_is_builtin(const AttributeIDRef &attribute_id) const -{ - /* Anonymous attributes cannot be built-in. */ - return attribute_id.is_named() && this->attribute_is_builtin(attribute_id.name()); -} - -blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( - const AttributeIDRef &attribute_id) const -{ - using namespace blender::bke; - const ComponentAttributeProviders *providers = this->get_attribute_providers(); - if (providers == nullptr) { - return {}; - } - if (attribute_id.is_named()) { - const BuiltinAttributeProvider *builtin_provider = - providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); - if (builtin_provider != nullptr) { - return {builtin_provider->try_get_for_read(*this), builtin_provider->domain()}; - } - } - for (const DynamicAttributesProvider *dynamic_provider : - providers->dynamic_attribute_providers()) { - ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_id); - if (attribute) { - return attribute; - } - } - return {}; -} - -blender::GVArray GeometryComponent::attribute_try_adapt_domain_impl( - const blender::GVArray &varray, - const eAttrDomain from_domain, - const eAttrDomain to_domain) const -{ - if (from_domain == to_domain) { - return varray; - } - return {}; -} - -blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_write( - const AttributeIDRef &attribute_id) -{ - using namespace blender::bke; - const ComponentAttributeProviders *providers = this->get_attribute_providers(); - if (providers == nullptr) { - return {}; - } - if (attribute_id.is_named()) { - const BuiltinAttributeProvider *builtin_provider = - providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); - if (builtin_provider != nullptr) { - return builtin_provider->try_get_for_write(*this); - } - } - for (const DynamicAttributesProvider *dynamic_provider : - providers->dynamic_attribute_providers()) { - WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_id); - if (attribute) { - return attribute; - } - } - return {}; -} - -bool GeometryComponent::attribute_try_delete(const AttributeIDRef &attribute_id) -{ - using namespace blender::bke; - const ComponentAttributeProviders *providers = this->get_attribute_providers(); - if (providers == nullptr) { - return {}; - } - if (attribute_id.is_named()) { - const BuiltinAttributeProvider *builtin_provider = - providers->builtin_attribute_providers().lookup_default_as(attribute_id.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_id) || success; - } - return success; -} - -void GeometryComponent::attributes_remove_anonymous() -{ - using namespace blender; - Vector<const AnonymousAttributeID *> anonymous_ids; - for (const AttributeIDRef &id : this->attribute_ids()) { - if (id.is_anonymous()) { - anonymous_ids.append(&id.anonymous_id()); - } - } - - while (!anonymous_ids.is_empty()) { - this->attribute_try_delete(anonymous_ids.pop_last()); - } -} - -bool GeometryComponent::attribute_try_create(const AttributeIDRef &attribute_id, - const eAttrDomain domain, - const eCustomDataType data_type, - const AttributeInit &initializer) -{ - using namespace blender::bke; - if (!attribute_id) { - return false; - } - const ComponentAttributeProviders *providers = this->get_attribute_providers(); - if (providers == nullptr) { - return false; - } - if (this->attribute_exists(attribute_id)) { - return false; - } - if (attribute_id.is_named()) { - const BuiltinAttributeProvider *builtin_provider = - providers->builtin_attribute_providers().lookup_default_as(attribute_id.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, initializer); - } - } - for (const DynamicAttributesProvider *dynamic_provider : - providers->dynamic_attribute_providers()) { - if (dynamic_provider->try_create(*this, attribute_id, domain, data_type, initializer)) { - return true; - } - } - return false; -} - -bool GeometryComponent::attribute_try_create_builtin(const blender::StringRef attribute_name, - const AttributeInit &initializer) -{ - using namespace blender::bke; - if (attribute_name.is_empty()) { - return false; - } - const ComponentAttributeProviders *providers = this->get_attribute_providers(); - if (providers == nullptr) { - return false; - } - const BuiltinAttributeProvider *builtin_provider = - providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); - if (builtin_provider == nullptr) { - return false; - } - return builtin_provider->try_create(*this, initializer); -} - -Set<AttributeIDRef> GeometryComponent::attribute_ids() const -{ - Set<AttributeIDRef> attributes; - this->attribute_foreach( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { - attributes.add(attribute_id); - return true; - }); - return attributes; -} - -bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callback) const -{ - using namespace blender::bke; - const ComponentAttributeProviders *providers = this->get_attribute_providers(); - if (providers == nullptr) { - return true; - } - - /* Keep track handled attribute names to make sure that we do not return the same name twice. */ - Set<std::string> handled_attribute_names; - - for (const BuiltinAttributeProvider *provider : - providers->builtin_attribute_providers().values()) { - if (provider->exists(*this)) { - AttributeMetaData meta_data{provider->domain(), provider->data_type()}; - if (!callback(provider->name(), meta_data)) { - return false; - } - handled_attribute_names.add_new(provider->name()); - } - } - for (const DynamicAttributesProvider *provider : providers->dynamic_attribute_providers()) { - const bool continue_loop = provider->foreach_attribute( - *this, [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - if (attribute_id.is_anonymous() || handled_attribute_names.add(attribute_id.name())) { - return callback(attribute_id, meta_data); - } - return true; - }); - if (!continue_loop) { - return false; - } - } - - return true; -} - -bool GeometryComponent::attribute_exists(const AttributeIDRef &attribute_id) const -{ - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); - if (attribute) { - return true; - } - return false; -} - -std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data( - const AttributeIDRef &attribute_id) const -{ - std::optional<AttributeMetaData> result{std::nullopt}; - this->attribute_foreach( - [&](const AttributeIDRef ¤t_attribute_id, const AttributeMetaData &meta_data) { - if (attribute_id == current_attribute_id) { - result = meta_data; - return false; - } - return true; - }); - return result; -} - static blender::GVArray try_adapt_data_type(blender::GVArray varray, const blender::CPPType &to_type) { @@ -1081,294 +780,6 @@ static blender::GVArray try_adapt_data_type(blender::GVArray varray, return conversions.try_convert(std::move(varray), to_type); } -blender::GVArray GeometryComponent::attribute_try_get_for_read( - const AttributeIDRef &attribute_id, - const eAttrDomain domain, - const eCustomDataType data_type) const -{ - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); - if (!attribute) { - return {}; - } - - blender::GVArray varray = std::move(attribute.varray); - if (!ELEM(domain, ATTR_DOMAIN_AUTO, attribute.domain)) { - varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain); - if (!varray) { - return {}; - } - } - - const blender::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); - BLI_assert(cpp_type != nullptr); - if (varray.type() != *cpp_type) { - varray = try_adapt_data_type(std::move(varray), *cpp_type); - if (!varray) { - return {}; - } - } - - return varray; -} - -blender::GVArray GeometryComponent::attribute_try_get_for_read(const AttributeIDRef &attribute_id, - const eAttrDomain domain) const -{ - if (!this->attribute_domain_supported(domain)) { - return {}; - } - - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); - if (!attribute) { - return {}; - } - - if (attribute.domain != domain) { - return this->attribute_try_adapt_domain(std::move(attribute.varray), attribute.domain, domain); - } - - return std::move(attribute.varray); -} - -blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( - const AttributeIDRef &attribute_id, const eCustomDataType data_type) const -{ - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); - if (!attribute) { - return {}; - } - const blender::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type); - BLI_assert(type != nullptr); - if (attribute.varray.type() == *type) { - return attribute; - } - const blender::bke::DataTypeConversions &conversions = - blender::bke::get_implicit_type_conversions(); - return {conversions.try_convert(std::move(attribute.varray), *type), attribute.domain}; -} - -blender::GVArray GeometryComponent::attribute_get_for_read(const AttributeIDRef &attribute_id, - const eAttrDomain domain, - const eCustomDataType data_type, - const void *default_value) const -{ - blender::GVArray varray = this->attribute_try_get_for_read(attribute_id, domain, data_type); - if (varray) { - return varray; - } - const blender::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type); - if (default_value == nullptr) { - default_value = type->default_value(); - } - const int domain_num = this->attribute_domain_num(domain); - return blender::GVArray::ForSingle(*type, domain_num, default_value); -} - -class GVMutableAttribute_For_OutputAttribute : public blender::GVArrayImpl_For_GSpan { - public: - GeometryComponent *component; - std::string attribute_name; - blender::bke::WeakAnonymousAttributeID anonymous_attribute_id; - - GVMutableAttribute_For_OutputAttribute(GMutableSpan data, - GeometryComponent &component, - const AttributeIDRef &attribute_id) - : blender::GVArrayImpl_For_GSpan(data), component(&component) - { - if (attribute_id.is_named()) { - this->attribute_name = attribute_id.name(); - } - else { - const AnonymousAttributeID *anonymous_id = &attribute_id.anonymous_id(); - BKE_anonymous_attribute_id_increment_weak(anonymous_id); - this->anonymous_attribute_id = blender::bke::WeakAnonymousAttributeID{anonymous_id}; - } - } - - ~GVMutableAttribute_For_OutputAttribute() override - { - type_->destruct_n(data_, size_); - MEM_freeN(data_); - } -}; - -static void save_output_attribute(OutputAttribute &output_attribute) -{ - using namespace blender; - using namespace blender::fn; - using namespace blender::bke; - - GVMutableAttribute_For_OutputAttribute &varray = - dynamic_cast<GVMutableAttribute_For_OutputAttribute &>( - *output_attribute.varray().get_implementation()); - - GeometryComponent &component = *varray.component; - AttributeIDRef attribute_id; - if (!varray.attribute_name.empty()) { - attribute_id = varray.attribute_name; - } - else { - attribute_id = varray.anonymous_attribute_id.extract(); - } - const eAttrDomain domain = output_attribute.domain(); - const eCustomDataType data_type = output_attribute.custom_data_type(); - const CPPType &cpp_type = output_attribute.cpp_type(); - - component.attribute_try_delete(attribute_id); - if (!component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault())) { - if (!varray.attribute_name.empty()) { - CLOG_WARN(&LOG, - "Could not create the '%s' attribute with type '%s'.", - varray.attribute_name.c_str(), - cpp_type.name().c_str()); - } - return; - } - WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(attribute_id); - BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer); - for (const int i : IndexRange(varray.size())) { - varray.get(i, buffer); - write_attribute.varray.set_by_relocate(i, buffer); - } - if (write_attribute.tag_modified_fn) { - write_attribute.tag_modified_fn(); - } -} - -static std::function<void(OutputAttribute &)> get_simple_output_attribute_save_method( - const blender::bke::WriteAttributeLookup &attribute) -{ - if (!attribute.tag_modified_fn) { - return {}; - } - return [tag_modified_fn = attribute.tag_modified_fn](OutputAttribute &UNUSED(attribute)) { - tag_modified_fn(); - }; -} - -static OutputAttribute create_output_attribute(GeometryComponent &component, - const AttributeIDRef &attribute_id, - const eAttrDomain domain, - const eCustomDataType data_type, - const bool ignore_old_values, - const void *default_value) -{ - using namespace blender; - using namespace blender::fn; - using namespace blender::bke; - - if (!attribute_id) { - return {}; - } - - const CPPType *cpp_type = custom_data_type_to_cpp_type(data_type); - BLI_assert(cpp_type != nullptr); - const DataTypeConversions &conversions = get_implicit_type_conversions(); - - if (component.attribute_is_builtin(attribute_id)) { - const StringRef attribute_name = attribute_id.name(); - WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); - if (!attribute) { - if (default_value) { - const int64_t domain_num = component.attribute_domain_num(domain); - component.attribute_try_create_builtin( - attribute_name, - AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_num, default_value))); - } - else { - component.attribute_try_create_builtin(attribute_name, AttributeInitDefault()); - } - attribute = component.attribute_try_get_for_write(attribute_name); - if (!attribute) { - /* Builtin attribute does not exist and can't be created. */ - return {}; - } - } - if (attribute.domain != domain) { - /* Builtin attribute is on different domain. */ - return {}; - } - GVMutableArray varray = std::move(attribute.varray); - if (varray.type() == *cpp_type) { - /* Builtin attribute matches exactly. */ - return OutputAttribute(std::move(varray), - domain, - get_simple_output_attribute_save_method(attribute), - ignore_old_values); - } - /* Builtin attribute is on the same domain but has a different data type. */ - varray = conversions.try_convert(std::move(varray), *cpp_type); - return OutputAttribute(std::move(varray), - domain, - get_simple_output_attribute_save_method(attribute), - ignore_old_values); - } - - const int domain_num = component.attribute_domain_num(domain); - - WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_id); - if (!attribute) { - if (default_value) { - component.attribute_try_create( - attribute_id, - domain, - data_type, - AttributeInitVArray(GVArray::ForSingleRef(*cpp_type, domain_num, default_value))); - } - else { - component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault()); - } - - attribute = component.attribute_try_get_for_write(attribute_id); - if (!attribute) { - /* Can't create the attribute. */ - return {}; - } - } - if (attribute.domain == domain && attribute.varray.type() == *cpp_type) { - /* Existing generic attribute matches exactly. */ - - return OutputAttribute(std::move(attribute.varray), - domain, - get_simple_output_attribute_save_method(attribute), - ignore_old_values); - } - - /* Allocate a new array that lives next to the existing attribute. It will overwrite the existing - * attribute after processing is done. */ - void *data = MEM_mallocN_aligned(cpp_type->size() * domain_num, cpp_type->alignment(), __func__); - if (ignore_old_values) { - /* This does nothing for trivially constructible types, but is necessary for correctness. */ - cpp_type->default_construct_n(data, domain); - } - else { - /* Fill the temporary array with values from the existing attribute. */ - GVArray old_varray = component.attribute_get_for_read( - attribute_id, domain, data_type, default_value); - old_varray.materialize_to_uninitialized(IndexRange(domain_num), data); - } - GVMutableArray varray = GVMutableArray::For<GVMutableAttribute_For_OutputAttribute>( - GMutableSpan{*cpp_type, data, domain_num}, component, attribute_id); - - return OutputAttribute(std::move(varray), domain, save_output_attribute, true); -} - -OutputAttribute GeometryComponent::attribute_try_get_for_output(const AttributeIDRef &attribute_id, - const eAttrDomain domain, - const eCustomDataType data_type, - const void *default_value) -{ - return create_output_attribute(*this, attribute_id, domain, data_type, false, default_value); -} - -OutputAttribute GeometryComponent::attribute_try_get_for_output_only( - const AttributeIDRef &attribute_id, const eAttrDomain domain, const eCustomDataType data_type) -{ - return create_output_attribute(*this, attribute_id, domain, data_type, true, nullptr); -} - -namespace blender::bke { - GVArray GeometryFieldInput::get_varray_for_context(const fn::FieldContext &context, IndexMask mask, ResourceScope &UNUSED(scope)) const @@ -1387,7 +798,10 @@ GVArray AttributeFieldInput::get_varray_for_context(const GeometryComponent &com IndexMask UNUSED(mask)) const { const eCustomDataType data_type = cpp_type_to_custom_data_type(*type_); - return component.attribute_try_get_for_read(name_, domain, data_type); + if (auto attributes = component.attributes()) { + return attributes->lookup(name_, domain, data_type); + } + return {}; } std::string AttributeFieldInput::socket_inspection_name() const @@ -1427,10 +841,10 @@ GVArray IDAttributeFieldInput::get_varray_for_context(const GeometryComponent &c { const StringRef name = get_random_id_attribute_name(domain); - GVArray attribute = component.attribute_try_get_for_read(name, domain, CD_PROP_INT32); - if (attribute) { - BLI_assert(attribute.size() == component.attribute_domain_num(domain)); - return attribute; + if (auto attributes = component.attributes()) { + if (GVArray attribute = attributes->lookup(name, domain, CD_PROP_INT32)) { + return attribute; + } } /* Use the index as the fallback if no random ID attribute exists. */ @@ -1459,7 +873,7 @@ GVArray AnonymousAttributeFieldInput::get_varray_for_context(const GeometryCompo IndexMask UNUSED(mask)) const { const eCustomDataType data_type = cpp_type_to_custom_data_type(*type_); - return component.attribute_try_get_for_read(anonymous_id_.get(), domain, data_type); + return component.attributes()->lookup(anonymous_id_.get(), domain, data_type); } std::string AnonymousAttributeFieldInput::socket_inspection_name() const @@ -1483,6 +897,120 @@ bool AnonymousAttributeFieldInput::is_equal_to(const fn::FieldNode &other) const return false; } +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()); + } +} + +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); + if (attribute) { + return GSpanAttributeWriter{std::move(attribute), false}; + } + return {}; +} + } // namespace blender::bke /** \} */ diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index ac43754dd1a..17432fa2726 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -15,12 +15,14 @@ namespace blender::bke { * components in a generic way. */ struct CustomDataAccessInfo { - using CustomDataGetter = CustomData *(*)(GeometryComponent &component); - using ConstCustomDataGetter = const CustomData *(*)(const GeometryComponent &component); - using UpdateCustomDataPointers = void (*)(GeometryComponent &component); + using CustomDataGetter = CustomData *(*)(void *owner); + using ConstCustomDataGetter = const CustomData *(*)(const void *owner); + using GetElementNum = int (*)(const void *owner); + using UpdateCustomDataPointers = void (*)(void *owner); CustomDataGetter get_custom_data; ConstCustomDataGetter get_const_custom_data; + GetElementNum get_element_num; UpdateCustomDataPointers update_custom_data_pointers; }; @@ -69,12 +71,11 @@ class BuiltinAttributeProvider { { } - virtual GVArray try_get_for_read(const GeometryComponent &component) const = 0; - virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component) const = 0; - virtual bool try_delete(GeometryComponent &component) const = 0; - virtual bool try_create(GeometryComponent &UNUSED(component), - const AttributeInit &UNUSED(initializer)) const = 0; - virtual bool exists(const GeometryComponent &component) const = 0; + virtual GVArray 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 *onwer, const AttributeInit &initializer) const = 0; + virtual bool exists(const void *owner) const = 0; StringRefNull name() const { @@ -98,23 +99,23 @@ class BuiltinAttributeProvider { */ class DynamicAttributesProvider { public: - virtual ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const AttributeIDRef &attribute_id) const = 0; - virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const AttributeIDRef &attribute_id) const = 0; - virtual bool try_delete(GeometryComponent &component, - const AttributeIDRef &attribute_id) const = 0; - virtual bool try_create(GeometryComponent &UNUSED(component), - const AttributeIDRef &UNUSED(attribute_id), - const eAttrDomain UNUSED(domain), - const eCustomDataType UNUSED(data_type), - const AttributeInit &UNUSED(initializer)) const + virtual GAttributeReader try_get_for_read(const void *owner, + const AttributeIDRef &attribute_id) const = 0; + virtual GAttributeWriter try_get_for_write(void *owner, + const AttributeIDRef &attribute_id) const = 0; + virtual bool try_delete(void *owner, const AttributeIDRef &attribute_id) const = 0; + virtual bool try_create(void *owner, + const AttributeIDRef &attribute_id, + const eAttrDomain 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; }; - virtual bool foreach_attribute(const GeometryComponent &component, + virtual bool foreach_attribute(const void *owner, const AttributeForeachCallback callback) const = 0; virtual void foreach_domain(const FunctionRef<void(eAttrDomain)> callback) const = 0; }; @@ -138,22 +139,20 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { { } - ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const AttributeIDRef &attribute_id) const final; + GAttributeReader try_get_for_read(const void *owner, + const AttributeIDRef &attribute_id) const final; - WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const AttributeIDRef &attribute_id) const final; + GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final; - bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final; + bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final; - bool try_create(GeometryComponent &component, + bool try_create(void *owner, const AttributeIDRef &attribute_id, eAttrDomain domain, const eCustomDataType data_type, const AttributeInit &initializer) const final; - bool foreach_attribute(const GeometryComponent &component, - const AttributeForeachCallback callback) const final; + bool foreach_attribute(const void *owner, const AttributeForeachCallback callback) const final; void foreach_domain(const FunctionRef<void(eAttrDomain)> callback) const final { @@ -197,13 +196,11 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { { } - ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const AttributeIDRef &attribute_id) const final; - WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const AttributeIDRef &attribute_id) const final; - bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final; - bool foreach_attribute(const GeometryComponent &component, - const AttributeForeachCallback callback) const final; + GAttributeReader try_get_for_read(const void *owner, + const AttributeIDRef &attribute_id) const final; + GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final; + bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final; + bool foreach_attribute(const void *owner, const AttributeForeachCallback callback) const final; void foreach_domain(const FunctionRef<void(eAttrDomain)> callback) const final; }; @@ -226,10 +223,10 @@ template<typename T> GVMutableArray make_array_write_attribute(void *data, const * if the stored type is the same as the attribute type. */ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { - using AsReadAttribute = GVArray (*)(const void *data, int domain_num); - using AsWriteAttribute = GVMutableArray (*)(void *data, int domain_num); - using UpdateOnRead = void (*)(const GeometryComponent &component); - using UpdateOnWrite = void (*)(GeometryComponent &component); + using AsReadAttribute = GVArray (*)(const void *data, int element_num); + using AsWriteAttribute = GVMutableArray (*)(void *data, int element_num); + using UpdateOnRead = void (*)(const void *owner); + using UpdateOnWrite = void (*)(void *owner); const eCustomDataType stored_type_; const CustomDataAccessInfo custom_data_access_; const AsReadAttribute as_read_attribute_; @@ -260,11 +257,11 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { { } - GVArray try_get_for_read(const GeometryComponent &component) const final; - WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final; - bool try_delete(GeometryComponent &component) const final; - bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final; - bool exists(const GeometryComponent &component) const final; + GVArray 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; }; /** @@ -321,4 +318,183 @@ class ComponentAttributeProviders { } }; +namespace attribute_accessor_functions { + +template<const ComponentAttributeProviders &providers> +inline bool is_builtin(const void *UNUSED(owner), const AttributeIDRef &attribute_id) +{ + if (!attribute_id.is_named()) { + return false; + } + const StringRef name = attribute_id.name(); + return providers.builtin_attribute_providers().contains_as(name); +} + +template<const ComponentAttributeProviders &providers> +inline GAttributeReader lookup(const void *owner, const AttributeIDRef &attribute_id) +{ + if (attribute_id.is_named()) { + const StringRef name = attribute_id.name(); + if (const BuiltinAttributeProvider *provider = + providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) { + return {provider->try_get_for_read(owner), provider->domain()}; + } + } + for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) { + GAttributeReader attribute = provider->try_get_for_read(owner, attribute_id); + if (attribute) { + return attribute; + } + } + return {}; +} + +template<const ComponentAttributeProviders &providers> +inline bool for_all(const void *owner, + FunctionRef<bool(const AttributeIDRef &, const AttributeMetaData &)> fn) +{ + Set<AttributeIDRef> handled_attribute_ids; + for (const BuiltinAttributeProvider *provider : + providers.builtin_attribute_providers().values()) { + if (provider->exists(owner)) { + AttributeMetaData meta_data{provider->domain(), provider->data_type()}; + if (!fn(provider->name(), meta_data)) { + return false; + } + handled_attribute_ids.add_new(provider->name()); + } + } + for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) { + const bool continue_loop = provider->foreach_attribute( + owner, [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (handled_attribute_ids.add(attribute_id)) { + return fn(attribute_id, meta_data); + } + return true; + }); + if (!continue_loop) { + return false; + } + } + return true; +} + +template<const ComponentAttributeProviders &providers> +inline bool contains(const void *owner, const blender::bke::AttributeIDRef &attribute_id) +{ + bool found = false; + for_all<providers>( + owner, + [&](const AttributeIDRef &other_attribute_id, const AttributeMetaData & /* meta_data */) { + if (attribute_id == other_attribute_id) { + found = true; + return false; + } + return true; + }); + return found; +} + +template<const ComponentAttributeProviders &providers> +inline std::optional<AttributeMetaData> lookup_meta_data(const void *owner, + const AttributeIDRef &attribute_id) +{ + std::optional<AttributeMetaData> meta_data; + for_all<providers>( + owner, + [&](const AttributeIDRef &other_attribute_id, const AttributeMetaData &other_meta_data) { + if (attribute_id == other_attribute_id) { + meta_data = other_meta_data; + return false; + } + return true; + }); + return meta_data; +} + +template<const ComponentAttributeProviders &providers> +inline GAttributeWriter lookup_for_write(void *owner, const AttributeIDRef &attribute_id) +{ + if (attribute_id.is_named()) { + const StringRef name = attribute_id.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, attribute_id); + if (attribute) { + return attribute; + } + } + return {}; +} + +template<const ComponentAttributeProviders &providers> +inline bool remove(void *owner, const AttributeIDRef &attribute_id) +{ + if (attribute_id.is_named()) { + const StringRef name = attribute_id.name(); + if (const BuiltinAttributeProvider *provider = + providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) { + return provider->try_delete(owner); + } + } + bool success = false; + for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) { + success = provider->try_delete(owner, attribute_id) || success; + } + return success; +} + +template<const ComponentAttributeProviders &providers> +inline bool add(void *owner, + const AttributeIDRef &attribute_id, + eAttrDomain domain, + eCustomDataType data_type, + const AttributeInit &initializer) +{ + if (contains<providers>(owner, attribute_id)) { + return false; + } + if (attribute_id.is_named()) { + const StringRef name = attribute_id.name(); + if (const BuiltinAttributeProvider *provider = + providers.builtin_attribute_providers().lookup_default_as(name, nullptr)) { + if (provider->domain() != domain) { + return false; + } + if (provider->data_type() != data_type) { + return false; + } + return provider->try_create(owner, initializer); + } + } + for (const DynamicAttributesProvider *provider : providers.dynamic_attribute_providers()) { + if (provider->try_create(owner, attribute_id, domain, data_type, initializer)) { + return true; + } + } + return false; +} + +template<const ComponentAttributeProviders &providers> +inline AttributeAccessorFunctions accessor_functions_for_providers() +{ + return AttributeAccessorFunctions{contains<providers>, + lookup_meta_data<providers>, + nullptr, + nullptr, + is_builtin<providers>, + lookup<providers>, + nullptr, + for_all<providers>, + lookup_for_write<providers>, + remove<providers>, + add<providers>}; +} + +} // namespace attribute_accessor_functions + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index c01a184c6f9..424fa311dc7 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -31,9 +31,7 @@ using blender::VArray; using blender::VArraySpan; using blender::Vector; using blender::bke::AttributeIDRef; -using blender::bke::OutputAttribute; -using blender::bke::OutputAttribute_Typed; -using blender::bke::ReadAttributeLookup; +using blender::bke::AttributeMetaData; blender::Span<SplinePtr> CurveEval::splines() const { @@ -345,32 +343,31 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve) return curve_eval_from_dna_curve(dna_curve, *BKE_curve_nurbs_get_for_read(&dna_curve)); } -static void copy_attributes_between_components(const GeometryComponent &src_component, - GeometryComponent &dst_component, - Span<std::string> skip) +static void copy_attributes_between_components( + const blender::bke::AttributeAccessor &src_attributes, + blender::bke::MutableAttributeAccessor &dst_attributes, + Span<std::string> skip) { - src_component.attribute_foreach( - [&](const AttributeIDRef &id, const AttributeMetaData meta_data) { - if (id.is_named() && skip.contains(id.name())) { - return true; - } + src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { + if (id.is_named() && skip.contains(id.name())) { + return true; + } - GVArray src_attribute = src_component.attribute_try_get_for_read( - id, meta_data.domain, meta_data.data_type); - if (!src_attribute) { - return true; - } - GVArraySpan src_attribute_data{src_attribute}; + GVArray src_attribute = src_attributes.lookup(id, meta_data.domain, meta_data.data_type); + if (!src_attribute) { + return true; + } + GVArraySpan src_attribute_data{src_attribute}; - OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( - id, meta_data.domain, meta_data.data_type); - if (!dst_attribute) { - return true; - } - dst_attribute.varray().set_all(src_attribute_data.data()); - dst_attribute.save(); - return true; - }); + blender::bke::GAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write( + id, meta_data.domain, meta_data.data_type); + if (!dst_attribute) { + return true; + } + dst_attribute.varray.set_all(src_attribute_data.data()); + dst_attribute.finish(); + return true; + }); } std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves_id) @@ -379,21 +376,22 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves_id) src_component.replace(&const_cast<Curves &>(curves_id), GeometryOwnershipType::ReadOnly); const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap( curves_id.geometry); + const blender::bke::AttributeAccessor src_attributes = curves.attributes(); VArray<int> resolution = curves.resolution(); VArray<int8_t> normal_mode = curves.normal_mode(); VArraySpan<float> nurbs_weights{ - src_component.attribute_get_for_read<float>("nurbs_weight", ATTR_DOMAIN_POINT, 0.0f)}; + src_attributes.lookup_or_default<float>("nurbs_weight", ATTR_DOMAIN_POINT, 0.0f)}; VArraySpan<int8_t> nurbs_orders{ - src_component.attribute_get_for_read<int8_t>("nurbs_order", ATTR_DOMAIN_CURVE, 4)}; + src_attributes.lookup_or_default<int8_t>("nurbs_order", ATTR_DOMAIN_CURVE, 4)}; VArraySpan<int8_t> nurbs_knots_modes{ - src_component.attribute_get_for_read<int8_t>("knots_mode", ATTR_DOMAIN_CURVE, 0)}; + src_attributes.lookup_or_default<int8_t>("knots_mode", ATTR_DOMAIN_CURVE, 0)}; VArraySpan<int8_t> handle_types_right{ - src_component.attribute_get_for_read<int8_t>("handle_type_right", ATTR_DOMAIN_POINT, 0)}; + src_attributes.lookup_or_default<int8_t>("handle_type_right", ATTR_DOMAIN_POINT, 0)}; VArraySpan<int8_t> handle_types_left{ - src_component.attribute_get_for_read<int8_t>("handle_type_left", ATTR_DOMAIN_POINT, 0)}; + src_attributes.lookup_or_default<int8_t>("handle_type_left", ATTR_DOMAIN_POINT, 0)}; /* Create splines with the correct size and type. */ VArray<int8_t> curve_types = curves.curve_types(); @@ -446,9 +444,10 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves_id) CurveComponentLegacy dst_component; dst_component.replace(curve_eval.get(), GeometryOwnershipType::Editable); + blender::bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write(); - copy_attributes_between_components(src_component, - dst_component, + copy_attributes_between_components(src_attributes, + dst_attributes, {"curve_type", "resolution", "normal_mode", @@ -467,37 +466,38 @@ Curves *curve_eval_to_curves(const CurveEval &curve_eval) curve_eval.splines().size()); CurveComponent dst_component; dst_component.replace(curves_id, GeometryOwnershipType::Editable); + blender::bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write(); blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap(curves_id->geometry); curves.offsets_for_write().copy_from(curve_eval.control_point_offsets()); MutableSpan<int8_t> curve_types = curves.curve_types_for_write(); - OutputAttribute_Typed<int8_t> normal_mode = - dst_component.attribute_try_get_for_output_only<int8_t>("normal_mode", ATTR_DOMAIN_CURVE); - OutputAttribute_Typed<float> nurbs_weight; - OutputAttribute_Typed<int> nurbs_order; - OutputAttribute_Typed<int8_t> nurbs_knots_mode; + blender::bke::SpanAttributeWriter<int8_t> normal_mode = + dst_attributes.lookup_or_add_for_write_only_span<int8_t>("normal_mode", ATTR_DOMAIN_CURVE); + blender::bke::SpanAttributeWriter<float> nurbs_weight; + blender::bke::SpanAttributeWriter<int> nurbs_order; + blender::bke::SpanAttributeWriter<int8_t> nurbs_knots_mode; if (curve_eval.has_spline_with_type(CURVE_TYPE_NURBS)) { - nurbs_weight = dst_component.attribute_try_get_for_output_only<float>("nurbs_weight", - ATTR_DOMAIN_POINT); - nurbs_order = dst_component.attribute_try_get_for_output_only<int>("nurbs_order", - ATTR_DOMAIN_CURVE); - nurbs_knots_mode = dst_component.attribute_try_get_for_output_only<int8_t>("knots_mode", - ATTR_DOMAIN_CURVE); + nurbs_weight = dst_attributes.lookup_or_add_for_write_only_span<float>("nurbs_weight", + ATTR_DOMAIN_POINT); + nurbs_order = dst_attributes.lookup_or_add_for_write_only_span<int>("nurbs_order", + ATTR_DOMAIN_CURVE); + nurbs_knots_mode = dst_attributes.lookup_or_add_for_write_only_span<int8_t>("knots_mode", + ATTR_DOMAIN_CURVE); } - OutputAttribute_Typed<int8_t> handle_type_right; - OutputAttribute_Typed<int8_t> handle_type_left; + blender::bke::SpanAttributeWriter<int8_t> handle_type_right; + blender::bke::SpanAttributeWriter<int8_t> handle_type_left; if (curve_eval.has_spline_with_type(CURVE_TYPE_BEZIER)) { - handle_type_right = dst_component.attribute_try_get_for_output_only<int8_t>( + handle_type_right = dst_attributes.lookup_or_add_for_write_only_span<int8_t>( "handle_type_right", ATTR_DOMAIN_POINT); - handle_type_left = dst_component.attribute_try_get_for_output_only<int8_t>("handle_type_left", - ATTR_DOMAIN_POINT); + handle_type_left = dst_attributes.lookup_or_add_for_write_only_span<int8_t>("handle_type_left", + ATTR_DOMAIN_POINT); } for (const int curve_index : curve_eval.splines().index_range()) { const Spline &spline = *curve_eval.splines()[curve_index]; curve_types[curve_index] = curve_eval.splines()[curve_index]->type(); - normal_mode.as_span()[curve_index] = curve_eval.splines()[curve_index]->normal_mode; + normal_mode.span[curve_index] = curve_eval.splines()[curve_index]->normal_mode; const IndexRange points = curves.points_for_curve(curve_index); switch (spline.type()) { @@ -505,15 +505,15 @@ Curves *curve_eval_to_curves(const CurveEval &curve_eval) break; case CURVE_TYPE_BEZIER: { const BezierSpline &src = static_cast<const BezierSpline &>(spline); - handle_type_right.as_span().slice(points).copy_from(src.handle_types_right()); - handle_type_left.as_span().slice(points).copy_from(src.handle_types_left()); + handle_type_right.span.slice(points).copy_from(src.handle_types_right()); + handle_type_left.span.slice(points).copy_from(src.handle_types_left()); break; } case CURVE_TYPE_NURBS: { const NURBSpline &src = static_cast<const NURBSpline &>(spline); - nurbs_knots_mode.as_span()[curve_index] = static_cast<int8_t>(src.knots_mode); - nurbs_order.as_span()[curve_index] = src.order(); - nurbs_weight.as_span().slice(points).copy_from(src.weights()); + nurbs_knots_mode.span[curve_index] = static_cast<int8_t>(src.knots_mode); + nurbs_order.span[curve_index] = src.order(); + nurbs_weight.span.slice(points).copy_from(src.weights()); break; } case CURVE_TYPE_CATMULL_ROM: { @@ -525,17 +525,18 @@ Curves *curve_eval_to_curves(const CurveEval &curve_eval) curves.update_curve_types(); - normal_mode.save(); - nurbs_weight.save(); - nurbs_order.save(); - nurbs_knots_mode.save(); - handle_type_right.save(); - handle_type_left.save(); + normal_mode.finish(); + nurbs_weight.finish(); + nurbs_order.finish(); + nurbs_knots_mode.finish(); + handle_type_right.finish(); + handle_type_left.finish(); CurveComponentLegacy src_component; src_component.replace(&const_cast<CurveEval &>(curve_eval), GeometryOwnershipType::ReadOnly); + const blender::bke::AttributeAccessor src_attributes = *src_component.attributes(); - copy_attributes_between_components(src_component, dst_component, {}); + copy_attributes_between_components(src_attributes, dst_attributes, {}); return curves_id; } diff --git a/source/blender/blenkernel/intern/curve_legacy_convert.cc b/source/blender/blenkernel/intern/curve_legacy_convert.cc index 299a0fbb44c..ff5bbc32afe 100644 --- a/source/blender/blenkernel/intern/curve_legacy_convert.cc +++ b/source/blender/blenkernel/intern/curve_legacy_convert.cc @@ -83,8 +83,7 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_ Curves *curves_id = curves_new_nomain(0, src_curves.size()); CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry); - CurveComponent component; - component.replace(curves_id, GeometryOwnershipType::Editable); + MutableAttributeAccessor curves_attributes = curves.attributes_for_write(); MutableSpan<int8_t> types = curves.curve_types_for_write(); MutableSpan<bool> cyclic = curves.cyclic_for_write(); @@ -110,9 +109,9 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_ } MutableSpan<float3> positions = curves.positions_for_write(); - OutputAttribute_Typed<float> radius_attribute = - component.attribute_try_get_for_output_only<float>("radius", ATTR_DOMAIN_POINT); - MutableSpan<float> radii = radius_attribute.as_span(); + SpanAttributeWriter<float> radius_attribute = + curves_attributes.lookup_or_add_for_write_only_span<float>("radius", ATTR_DOMAIN_POINT); + MutableSpan<float> radii = radius_attribute.span; MutableSpan<float> tilts = curves.tilt_for_write(); auto create_poly = [&](IndexMask selection) { @@ -203,7 +202,7 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_ curves.normal_mode_for_write().fill(normal_mode_from_legacy(curve_legacy.twist_mode)); - radius_attribute.save(); + radius_attribute.finish(); return curves_id; } diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index baf56c0c350..7f051b683b4 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -8,7 +8,6 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" -#include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_curves.hh" #include "BKE_geometry_set.hh" @@ -315,15 +314,15 @@ static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool return result; } -static eAttrDomain get_attribute_domain_for_mesh(const MeshComponent &mesh, +static eAttrDomain get_attribute_domain_for_mesh(const AttributeAccessor &mesh_attributes, const AttributeIDRef &attribute_id) { /* Only use a different domain if it is builtin and must only exist on one domain. */ - if (!mesh.attribute_is_builtin(attribute_id)) { + if (!mesh_attributes.is_builtin(attribute_id)) { return ATTR_DOMAIN_POINT; } - std::optional<AttributeMetaData> meta_data = mesh.attribute_get_meta_data(attribute_id); + std::optional<AttributeMetaData> meta_data = mesh_attributes.lookup_meta_data(attribute_id); if (!meta_data) { return ATTR_DOMAIN_POINT; } @@ -331,16 +330,17 @@ static eAttrDomain get_attribute_domain_for_mesh(const MeshComponent &mesh, return meta_data->domain; } -static bool should_add_attribute_to_mesh(const CurveComponent &curve_component, - const MeshComponent &mesh_component, +static bool should_add_attribute_to_mesh(const AttributeAccessor &curve_attributes, + const AttributeAccessor &mesh_attributes, const AttributeIDRef &id) { + /* The position attribute has special non-generic evaluation. */ if (id.is_named() && id.name() == "position") { return false; } /* Don't propagate built-in curves attributes that are not built-in on meshes. */ - if (curve_component.attribute_is_builtin(id) && !mesh_component.attribute_is_builtin(id)) { + if (curve_attributes.is_builtin(id) && !mesh_attributes.is_builtin(id)) { return false; } if (!id.should_be_kept()) { @@ -667,20 +667,13 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, Vector<std::byte> eval_buffer; - Curves main_id = {{nullptr}}; - main_id.geometry = reinterpret_cast<const ::CurvesGeometry &>(main); - CurveComponent main_component; - main_component.replace(&main_id, GeometryOwnershipType::Editable); - - Curves profile_id = {{nullptr}}; - profile_id.geometry = reinterpret_cast<const ::CurvesGeometry &>(profile); - CurveComponent profile_component; - profile_component.replace(&profile_id, GeometryOwnershipType::Editable); + const AttributeAccessor main_attributes = main.attributes(); + const AttributeAccessor profile_attributes = profile.attributes(); Span<float> radii = {}; - if (main_component.attribute_exists("radius")) { + if (main_attributes.contains("radius")) { radii = evaluated_attribute_if_necessary( - main_component.attribute_get_for_read<float>("radius", ATTR_DOMAIN_POINT, 1.0f), + main_attributes.lookup_or_default<float>("radius", ATTR_DOMAIN_POINT, 1.0f), main, main.curve_type_counts(), eval_buffer) @@ -716,24 +709,23 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, }); } - Set<AttributeIDRef> main_attributes; + Set<AttributeIDRef> main_attributes_set; - MeshComponent mesh_component; - mesh_component.replace(mesh, GeometryOwnershipType::Editable); + MutableAttributeAccessor mesh_attributes = bke::mesh_attributes_for_write(*mesh); - main_component.attribute_foreach([&](const AttributeIDRef &id, - const AttributeMetaData meta_data) { - if (!should_add_attribute_to_mesh(main_component, mesh_component, id)) { + main_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { + if (!should_add_attribute_to_mesh(main_attributes, mesh_attributes, id)) { return true; } - main_attributes.add_new(id); + main_attributes_set.add_new(id); const eAttrDomain src_domain = meta_data.domain; const eCustomDataType type = meta_data.data_type; - GVArray src = main_component.attribute_try_get_for_read(id, src_domain, type); + GVArray src = main_attributes.lookup(id, src_domain, type); - const eAttrDomain dst_domain = get_attribute_domain_for_mesh(mesh_component, id); - OutputAttribute dst = mesh_component.attribute_try_get_for_output_only(id, dst_domain, type); + const eAttrDomain dst_domain = get_attribute_domain_for_mesh(mesh_attributes, id); + GSpanAttributeWriter dst = mesh_attributes.lookup_or_add_for_write_only_span( + id, dst_domain, type); if (!dst) { return true; } @@ -744,31 +736,31 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, offsets, dst_domain, evaluated_attribute_if_necessary(src, main, main.curve_type_counts(), eval_buffer), - dst.as_span()); + dst.span); } else if (src_domain == ATTR_DOMAIN_CURVE) { copy_curve_domain_attribute_to_mesh( - offsets, offsets.main_indices, dst_domain, src, dst.as_span()); + offsets, offsets.main_indices, dst_domain, src, dst.span); } - dst.save(); + dst.finish(); return true; }); - profile_component.attribute_foreach([&](const AttributeIDRef &id, - const AttributeMetaData meta_data) { + profile_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { if (main_attributes.contains(id)) { return true; } - if (!should_add_attribute_to_mesh(profile_component, mesh_component, id)) { + if (!should_add_attribute_to_mesh(profile_attributes, mesh_attributes, id)) { return true; } const eAttrDomain src_domain = meta_data.domain; const eCustomDataType type = meta_data.data_type; - GVArray src = profile_component.attribute_try_get_for_read(id, src_domain, type); + GVArray src = profile_attributes.lookup(id, src_domain, type); - const eAttrDomain dst_domain = get_attribute_domain_for_mesh(mesh_component, id); - OutputAttribute dst = mesh_component.attribute_try_get_for_output_only(id, dst_domain, type); + const eAttrDomain dst_domain = get_attribute_domain_for_mesh(mesh_attributes, id); + GSpanAttributeWriter dst = mesh_attributes.lookup_or_add_for_write_only_span( + id, dst_domain, type); if (!dst) { return true; } @@ -779,14 +771,14 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, offsets, dst_domain, evaluated_attribute_if_necessary(src, profile, profile.curve_type_counts(), eval_buffer), - dst.as_span()); + dst.span); } else if (src_domain == ATTR_DOMAIN_CURVE) { copy_curve_domain_attribute_to_mesh( - offsets, offsets.profile_indices, dst_domain, src, dst.as_span()); + offsets, offsets.profile_indices, dst_domain, src, dst.span); } - dst.save(); + dst.finish(); return true; }); diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index 5725c6043b2..0d899ec7b06 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -5,7 +5,6 @@ #include "DNA_ID_enums.h" #include "DNA_curve_types.h" -#include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_curve.h" #include "BKE_geometry_set.hh" @@ -114,24 +113,6 @@ void CurveComponentLegacy::ensure_owns_direct_data() /** \name Attribute Access Helper Functions * \{ */ -int CurveComponentLegacy::attribute_domain_num(const eAttrDomain domain) const -{ - if (curve_ == nullptr) { - return 0; - } - if (domain == ATTR_DOMAIN_POINT) { - int total = 0; - for (const SplinePtr &spline : curve_->splines()) { - total += spline->size(); - } - return total; - } - if (domain == ATTR_DOMAIN_CURVE) { - return curve_->splines().size(); - } - return 0; -} - namespace blender::bke { namespace { @@ -308,9 +289,10 @@ static GVArray adapt_curve_domain_spline_to_point(const CurveEval &curve, GVArra } // namespace blender::bke -GVArray CurveComponentLegacy::attribute_try_adapt_domain_impl(const GVArray &varray, - const eAttrDomain from_domain, - const eAttrDomain to_domain) const +static GVArray adapt_curve_attribute_domain(const CurveEval &curve, + const GVArray &varray, + const eAttrDomain from_domain, + const eAttrDomain to_domain) { if (!varray) { return {}; @@ -323,30 +305,15 @@ GVArray CurveComponentLegacy::attribute_try_adapt_domain_impl(const GVArray &var } if (from_domain == ATTR_DOMAIN_POINT && to_domain == ATTR_DOMAIN_CURVE) { - return blender::bke::adapt_curve_domain_point_to_spline(*curve_, std::move(varray)); + return blender::bke::adapt_curve_domain_point_to_spline(curve, std::move(varray)); } if (from_domain == ATTR_DOMAIN_CURVE && to_domain == ATTR_DOMAIN_POINT) { - return blender::bke::adapt_curve_domain_spline_to_point(*curve_, std::move(varray)); + return blender::bke::adapt_curve_domain_spline_to_point(curve, std::move(varray)); } return {}; } -static CurveEval *get_curve_from_component_for_write(GeometryComponent &component) -{ - BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); - CurveComponentLegacy &curve_component = static_cast<CurveComponentLegacy &>(component); - return curve_component.get_for_write(); -} - -static const CurveEval *get_curve_from_component_for_read(const GeometryComponent &component) -{ - BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); - const CurveComponentLegacy &curve_component = static_cast<const CurveComponentLegacy &>( - component); - return curve_component.get_for_read(); -} - /** \} */ namespace blender::bke { @@ -380,41 +347,41 @@ class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider { { } - GVArray try_get_for_read(const GeometryComponent &component) const final + GVArray try_get_for_read(const void *owner) const final { - const CurveEval *curve = get_curve_from_component_for_read(component); + const CurveEval *curve = static_cast<const CurveEval *>(owner); if (curve == nullptr) { return {}; } return as_read_attribute_(*curve); } - WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final + GAttributeWriter try_get_for_write(void *owner) const final { if (writable_ != Writable) { return {}; } - CurveEval *curve = get_curve_from_component_for_write(component); + CurveEval *curve = static_cast<CurveEval *>(owner); if (curve == nullptr) { return {}; } return {as_write_attribute_(*curve), domain_}; } - bool try_delete(GeometryComponent &UNUSED(component)) const final + bool try_delete(void *UNUSED(owner)) const final { return false; } - bool try_create(GeometryComponent &UNUSED(component), - const AttributeInit &UNUSED(initializer)) const final + bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final { return false; } - bool exists(const GeometryComponent &component) const final + bool exists(const void *owner) const final { - return component.attribute_domain_num(ATTR_DOMAIN_CURVE) != 0; + const CurveEval *curve = static_cast<const CurveEval *>(owner); + return !curve->splines().is_empty(); } }; @@ -600,12 +567,11 @@ static GVArray varray_from_initializer(const AttributeInit &initializer, return {}; } -static bool create_point_attribute(GeometryComponent &component, +static bool create_point_attribute(CurveEval *curve, const AttributeIDRef &attribute_id, const AttributeInit &initializer, const eCustomDataType data_type) { - CurveEval *curve = get_curve_from_component_for_write(component); if (curve == nullptr || curve->splines().size() == 0) { return false; } @@ -638,7 +604,7 @@ static bool create_point_attribute(GeometryComponent &component, return true; } - WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(attribute_id); + GAttributeWriter write_attribute = curve->attributes_for_write().lookup_for_write(attribute_id); /* We just created the attribute, it should exist. */ BLI_assert(write_attribute); @@ -647,6 +613,7 @@ static bool create_point_attribute(GeometryComponent &component, * this theoretically unnecessary materialize step could be removed. */ GVArraySpan source_VArraySpan{source_varray}; write_attribute.varray.set_all(source_VArraySpan.data()); + write_attribute.finish(); if (initializer.type == AttributeInit::Type::MoveArray) { MEM_freeN(static_cast<const AttributeInitMove &>(initializer).data); @@ -655,10 +622,8 @@ static bool create_point_attribute(GeometryComponent &component, return true; } -static bool remove_point_attribute(GeometryComponent &component, - const AttributeIDRef &attribute_id) +static bool remove_point_attribute(CurveEval *curve, const AttributeIDRef &attribute_id) { - CurveEval *curve = get_curve_from_component_for_write(component); if (curve == nullptr) { return false; } @@ -934,14 +899,14 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu { } - GVArray try_get_for_read(const GeometryComponent &component) const override + GVArray try_get_for_read(const void *owner) const override { - const CurveEval *curve = get_curve_from_component_for_read(component); + const CurveEval *curve = static_cast<const CurveEval *>(owner); if (curve == nullptr) { return {}; } - if (!this->exists(component)) { + if (!this->exists(owner)) { return {}; } @@ -962,14 +927,14 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu return point_data_varray(spans, offsets); } - WriteAttributeLookup try_get_for_write(GeometryComponent &component) const override + GAttributeWriter try_get_for_write(void *owner) const override { - CurveEval *curve = get_curve_from_component_for_write(component); + CurveEval *curve = static_cast<CurveEval *>(owner); if (curve == nullptr) { return {}; } - if (!this->exists(component)) { + if (!this->exists(owner)) { return {}; } @@ -998,25 +963,27 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu return {point_data_varray_mutable(spans, offsets), domain_, tag_modified_fn}; } - bool try_delete(GeometryComponent &component) const final + bool try_delete(void *owner) const final { if (deletable_ == DeletableEnum::NonDeletable) { return false; } - return remove_point_attribute(component, name_); + CurveEval *curve = static_cast<CurveEval *>(owner); + return remove_point_attribute(curve, name_); } - bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final + bool try_create(void *owner, const AttributeInit &initializer) const final { if (createable_ == CreatableEnum::NonCreatable) { return false; } - return create_point_attribute(component, name_, initializer, CD_PROP_INT32); + CurveEval *curve = static_cast<CurveEval *>(owner); + return create_point_attribute(curve, name_, initializer, CD_PROP_INT32); } - bool exists(const GeometryComponent &component) const final + bool exists(const void *owner) const final { - const CurveEval *curve = get_curve_from_component_for_read(component); + const CurveEval *curve = static_cast<const CurveEval *>(owner); if (curve == nullptr) { return false; } @@ -1067,9 +1034,9 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo { } - WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final + GAttributeWriter try_get_for_write(void *owner) const final { - CurveEval *curve = get_curve_from_component_for_write(component); + CurveEval *curve = static_cast<CurveEval *>(owner); if (curve == nullptr) { return {}; } @@ -1077,7 +1044,7 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo /* Use the regular position virtual array when there aren't any Bezier splines * to avoid the overhead of checking the spline type for every point. */ if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) { - return BuiltinPointAttributeProvider<float3>::try_get_for_write(component); + return BuiltinPointAttributeProvider<float3>::try_get_for_write(owner); } auto tag_modified_fn = [curve]() { @@ -1110,9 +1077,9 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { { } - GVArray try_get_for_read(const GeometryComponent &component) const override + GVArray try_get_for_read(const void *owner) const override { - const CurveEval *curve = get_curve_from_component_for_read(component); + const CurveEval *curve = static_cast<const CurveEval *>(owner); if (curve == nullptr) { return {}; } @@ -1128,9 +1095,9 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { const_cast<CurveEval *>(curve)->splines(), std::move(offsets), is_right_); } - WriteAttributeLookup try_get_for_write(GeometryComponent &component) const override + GAttributeWriter try_get_for_write(void *owner) const override { - CurveEval *curve = get_curve_from_component_for_write(component); + CurveEval *curve = static_cast<CurveEval *>(owner); if (curve == nullptr) { return {}; } @@ -1148,26 +1115,27 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { tag_modified_fn}; } - bool try_delete(GeometryComponent &UNUSED(component)) const final + bool try_delete(void *UNUSED(owner)) const final { return false; } - bool try_create(GeometryComponent &UNUSED(component), - const AttributeInit &UNUSED(initializer)) const final + bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final { return false; } - bool exists(const GeometryComponent &component) const final + bool exists(const void *owner) const final { - const CurveEval *curve = get_curve_from_component_for_read(component); + const CurveEval *curve = static_cast<const CurveEval *>(owner); if (curve == nullptr) { return false; } - return curve->has_spline_with_type(CURVE_TYPE_BEZIER) && - component.attribute_domain_num(ATTR_DOMAIN_POINT) != 0; + CurveComponentLegacy component; + component.replace(const_cast<CurveEval *>(curve), GeometryOwnershipType::ReadOnly); + + return curve->has_spline_with_type(CURVE_TYPE_BEZIER) && !curve->splines().is_empty(); } }; @@ -1190,10 +1158,10 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { CD_MASK_PROP_INT8; public: - ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const AttributeIDRef &attribute_id) const final + GAttributeReader try_get_for_read(const void *owner, + const AttributeIDRef &attribute_id) const final { - const CurveEval *curve = get_curve_from_component_for_read(component); + const CurveEval *curve = static_cast<const CurveEval *>(owner); if (curve == nullptr || curve->splines().size() == 0) { return {}; } @@ -1228,7 +1196,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { return {GVArray::ForSpan(spans.first()), ATTR_DOMAIN_POINT}; } - ReadAttributeLookup attribute = {}; + GAttributeReader attribute = {}; Array<int> offsets = curve->control_point_offsets(); attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) { using T = decltype(dummy); @@ -1246,10 +1214,9 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { } /* This function is almost the same as #try_get_for_read, but without const. */ - WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const AttributeIDRef &attribute_id) const final + GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final { - CurveEval *curve = get_curve_from_component_for_write(component); + CurveEval *curve = static_cast<CurveEval *>(owner); if (curve == nullptr || curve->splines().size() == 0) { return {}; } @@ -1284,7 +1251,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { return {GVMutableArray::ForSpan(spans.first()), ATTR_DOMAIN_POINT}; } - WriteAttributeLookup attribute = {}; + GAttributeWriter attribute = {}; Array<int> offsets = curve->control_point_offsets(); attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) { using T = decltype(dummy); @@ -1298,12 +1265,13 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { return attribute; } - bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final + bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final { - return remove_point_attribute(component, attribute_id); + CurveEval *curve = static_cast<CurveEval *>(owner); + return remove_point_attribute(curve, attribute_id); } - bool try_create(GeometryComponent &component, + bool try_create(void *owner, const AttributeIDRef &attribute_id, const eAttrDomain domain, const eCustomDataType data_type, @@ -1313,13 +1281,13 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { if (domain != ATTR_DOMAIN_POINT) { return false; } - return create_point_attribute(component, attribute_id, initializer, data_type); + CurveEval *curve = static_cast<CurveEval *>(owner); + return create_point_attribute(curve, attribute_id, initializer, data_type); } - bool foreach_attribute(const GeometryComponent &component, - const AttributeForeachCallback callback) const final + bool foreach_attribute(const void *owner, const AttributeForeachCallback callback) const final { - const CurveEval *curve = get_curve_from_component_for_read(component); + const CurveEval *curve = static_cast<const CurveEval *>(owner); if (curve == nullptr || curve->splines().size() == 0) { return false; } @@ -1371,14 +1339,18 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() make_cyclic_write_attribute); static CustomDataAccessInfo spline_custom_data_access = { - [](GeometryComponent &component) -> CustomData * { - CurveEval *curve = get_curve_from_component_for_write(component); + [](void *owner) -> CustomData * { + CurveEval *curve = static_cast<CurveEval *>(owner); return curve ? &curve->attributes.data : nullptr; }, - [](const GeometryComponent &component) -> const CustomData * { - const CurveEval *curve = get_curve_from_component_for_read(component); + [](const void *owner) -> const CustomData * { + const CurveEval *curve = static_cast<const CurveEval *>(owner); return curve ? &curve->attributes.data : nullptr; }, + [](const void *owner) -> int { + const CurveEval *curve = static_cast<const CurveEval *>(owner); + return curve->splines().size(); + }, nullptr}; static CustomDataAttributeProvider spline_custom_data(ATTR_DOMAIN_CURVE, @@ -1430,12 +1402,62 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() /** \} */ +static AttributeAccessorFunctions get_curve_accessor_functions() +{ + static const ComponentAttributeProviders providers = create_attribute_providers_for_curve(); + AttributeAccessorFunctions fn = + attribute_accessor_functions::accessor_functions_for_providers<providers>(); + fn.domain_size = [](const void *owner, const eAttrDomain domain) -> int { + if (owner == nullptr) { + return 0; + } + const CurveEval &curve_eval = *static_cast<const CurveEval *>(owner); + switch (domain) { + case ATTR_DOMAIN_POINT: + return curve_eval.total_control_point_num(); + case ATTR_DOMAIN_CURVE: + return curve_eval.splines().size(); + default: + return 0; + } + }; + fn.domain_supported = [](const void *UNUSED(owner), const eAttrDomain domain) { + return ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); + }; + fn.adapt_domain = [](const void *owner, + const blender::GVArray &varray, + const eAttrDomain from_domain, + const eAttrDomain to_domain) -> GVArray { + if (owner == nullptr) { + return {}; + } + const CurveEval &curve_eval = *static_cast<const CurveEval *>(owner); + return adapt_curve_attribute_domain(curve_eval, varray, from_domain, to_domain); + }; + return fn; +} + +static const AttributeAccessorFunctions &get_curve_accessor_functions_ref() +{ + static const AttributeAccessorFunctions fn = get_curve_accessor_functions(); + return fn; +} + } // namespace blender::bke -const blender::bke::ComponentAttributeProviders *CurveComponentLegacy::get_attribute_providers() - const +std::optional<blender::bke::AttributeAccessor> CurveComponentLegacy::attributes() const +{ + return blender::bke::AttributeAccessor(curve_, blender::bke::get_curve_accessor_functions_ref()); +} + +std::optional<blender::bke::MutableAttributeAccessor> CurveComponentLegacy::attributes_for_write() +{ + return blender::bke::MutableAttributeAccessor(curve_, + blender::bke::get_curve_accessor_functions_ref()); +} + +blender::bke::MutableAttributeAccessor CurveEval::attributes_for_write() { - static blender::bke::ComponentAttributeProviders providers = - blender::bke::create_attribute_providers_for_curve(); - return &providers; + return blender::bke::MutableAttributeAccessor(this, + blender::bke::get_curve_accessor_functions_ref()); } diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc index af058534f68..34c17bedc2c 100644 --- a/source/blender/blenkernel/intern/geometry_component_curves.cc +++ b/source/blender/blenkernel/intern/geometry_component_curves.cc @@ -5,7 +5,6 @@ #include "DNA_ID_enums.h" #include "DNA_curve_types.h" -#include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_curve.h" #include "BKE_curves.hh" @@ -218,7 +217,7 @@ VArray<float3> curve_normals_varray(const CurveComponent &component, const eAttr const VArray<int8_t> types = curves.curve_types(); if (curves.is_single_type(CURVE_TYPE_POLY)) { - return component.attribute_try_adapt_domain<float3>( + return component.attributes()->adapt_domain<float3>( VArray<float3>::ForSpan(curves.evaluated_normals()), ATTR_DOMAIN_POINT, domain); } @@ -229,7 +228,7 @@ VArray<float3> curve_normals_varray(const CurveComponent &component, const eAttr } if (domain == ATTR_DOMAIN_CURVE) { - return component.attribute_try_adapt_domain<float3>( + return component.attributes()->adapt_domain<float3>( VArray<float3>::ForContainer(std::move(normals)), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); } @@ -264,7 +263,7 @@ static VArray<float> construct_curve_length_gvarray(const CurveComponent &compon } if (domain == ATTR_DOMAIN_POINT) { - return component.attribute_try_adapt_domain<float>( + return component.attributes()->adapt_domain<float>( std::move(lengths), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); } @@ -307,75 +306,29 @@ bool CurveLengthFieldInput::is_equal_to(const fn::FieldNode &other) const /** \name Attribute Access Helper Functions * \{ */ -int CurveComponent::attribute_domain_num(const eAttrDomain domain) const +static void tag_component_topology_changed(void *owner) { - if (curves_ == nullptr) { - return 0; - } - const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap( - curves_->geometry); - if (domain == ATTR_DOMAIN_POINT) { - return curves.points_num(); - } - if (domain == ATTR_DOMAIN_CURVE) { - return curves.curves_num(); - } - return 0; -} - -GVArray CurveComponent::attribute_try_adapt_domain_impl(const GVArray &varray, - const eAttrDomain from_domain, - const eAttrDomain to_domain) const -{ - return blender::bke::CurvesGeometry::wrap(curves_->geometry) - .adapt_domain(varray, from_domain, to_domain); -} - -static Curves *get_curves_from_component_for_write(GeometryComponent &component) -{ - BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); - CurveComponent &curve_component = static_cast<CurveComponent &>(component); - return curve_component.get_for_write(); -} - -static const Curves *get_curves_from_component_for_read(const GeometryComponent &component) -{ - BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - return curve_component.get_for_read(); + blender::bke::CurvesGeometry &curves = *static_cast<blender::bke::CurvesGeometry *>(owner); + curves.tag_topology_changed(); } -static void tag_component_topology_changed(GeometryComponent &component) +static void tag_component_curve_types_changed(void *owner) { - Curves *curves = get_curves_from_component_for_write(component); - if (curves) { - blender::bke::CurvesGeometry::wrap(curves->geometry).tag_topology_changed(); - } + blender::bke::CurvesGeometry &curves = *static_cast<blender::bke::CurvesGeometry *>(owner); + curves.update_curve_types(); + curves.tag_topology_changed(); } -static void tag_component_curve_types_changed(GeometryComponent &component) +static void tag_component_positions_changed(void *owner) { - Curves *curves = get_curves_from_component_for_write(component); - if (curves) { - blender::bke::CurvesGeometry::wrap(curves->geometry).update_curve_types(); - blender::bke::CurvesGeometry::wrap(curves->geometry).tag_topology_changed(); - } + blender::bke::CurvesGeometry &curves = *static_cast<blender::bke::CurvesGeometry *>(owner); + curves.tag_positions_changed(); } -static void tag_component_positions_changed(GeometryComponent &component) +static void tag_component_normals_changed(void *owner) { - Curves *curves = get_curves_from_component_for_write(component); - if (curves) { - blender::bke::CurvesGeometry::wrap(curves->geometry).tag_positions_changed(); - } -} - -static void tag_component_normals_changed(GeometryComponent &component) -{ - Curves *curves = get_curves_from_component_for_write(component); - if (curves) { - blender::bke::CurvesGeometry::wrap(curves->geometry).tag_normals_changed(); - } + blender::bke::CurvesGeometry &curves = *static_cast<blender::bke::CurvesGeometry *>(owner); + curves.tag_normals_changed(); } /** \} */ @@ -393,34 +346,38 @@ namespace blender::bke { static ComponentAttributeProviders create_attribute_providers_for_curve() { static CustomDataAccessInfo curve_access = { - [](GeometryComponent &component) -> CustomData * { - Curves *curves = get_curves_from_component_for_write(component); - return curves ? &curves->geometry.curve_data : nullptr; + [](void *owner) -> CustomData * { + CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner); + return &curves.curve_data; }, - [](const GeometryComponent &component) -> const CustomData * { - const Curves *curves = get_curves_from_component_for_read(component); - return curves ? &curves->geometry.curve_data : nullptr; + [](const void *owner) -> const CustomData * { + const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner); + return &curves.curve_data; }, - [](GeometryComponent &component) { - Curves *curves = get_curves_from_component_for_write(component); - if (curves) { - blender::bke::CurvesGeometry::wrap(curves->geometry).update_customdata_pointers(); - } + [](const void *owner) -> int { + const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner); + return curves.curves_num(); + }, + [](void *owner) { + CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner); + curves.update_customdata_pointers(); }}; static CustomDataAccessInfo point_access = { - [](GeometryComponent &component) -> CustomData * { - Curves *curves = get_curves_from_component_for_write(component); - return curves ? &curves->geometry.point_data : nullptr; + [](void *owner) -> CustomData * { + CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner); + return &curves.point_data; }, - [](const GeometryComponent &component) -> const CustomData * { - const Curves *curves = get_curves_from_component_for_read(component); - return curves ? &curves->geometry.point_data : nullptr; + [](const void *owner) -> const CustomData * { + const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner); + return &curves.point_data; }, - [](GeometryComponent &component) { - Curves *curves = get_curves_from_component_for_write(component); - if (curves) { - blender::bke::CurvesGeometry::wrap(curves->geometry).update_customdata_pointers(); - } + [](const void *owner) -> int { + const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner); + return curves.points_num(); + }, + [](void *owner) { + CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner); + curves.update_customdata_pointers(); }}; static BuiltinCustomDataLayerProvider position("position", @@ -626,11 +583,67 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() /** \} */ +static AttributeAccessorFunctions get_curves_accessor_functions() +{ + static const ComponentAttributeProviders providers = create_attribute_providers_for_curve(); + AttributeAccessorFunctions fn = + attribute_accessor_functions::accessor_functions_for_providers<providers>(); + fn.domain_size = [](const void *owner, const eAttrDomain domain) { + if (owner == nullptr) { + return 0; + } + const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner); + switch (domain) { + case ATTR_DOMAIN_POINT: + return curves.points_num(); + case ATTR_DOMAIN_CURVE: + return curves.curves_num(); + default: + return 0; + } + }; + fn.domain_supported = [](const void *UNUSED(owner), const eAttrDomain domain) { + return ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); + }; + fn.adapt_domain = [](const void *owner, + const blender::GVArray &varray, + const eAttrDomain from_domain, + const eAttrDomain to_domain) -> GVArray { + if (owner == nullptr) { + return {}; + } + const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner); + return curves.adapt_domain(varray, from_domain, to_domain); + }; + return fn; +} + +static const AttributeAccessorFunctions &get_curves_accessor_functions_ref() +{ + static const AttributeAccessorFunctions fn = get_curves_accessor_functions(); + return fn; +} + +AttributeAccessor CurvesGeometry::attributes() const +{ + return AttributeAccessor(this, get_curves_accessor_functions_ref()); +} + +MutableAttributeAccessor CurvesGeometry::attributes_for_write() +{ + return MutableAttributeAccessor(this, get_curves_accessor_functions_ref()); +} + } // namespace blender::bke -const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const +std::optional<blender::bke::AttributeAccessor> CurveComponent::attributes() const +{ + return blender::bke::AttributeAccessor(curves_ ? &curves_->geometry : nullptr, + blender::bke::get_curves_accessor_functions_ref()); +} + +std::optional<blender::bke::MutableAttributeAccessor> CurveComponent::attributes_for_write() { - static blender::bke::ComponentAttributeProviders providers = - blender::bke::create_attribute_providers_for_curve(); - return &providers; + return blender::bke::MutableAttributeAccessor(curves_ ? &curves_->geometry : nullptr, + blender::bke::get_curves_accessor_functions_ref()); } diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index 653be03b991..c16311945ba 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -13,7 +13,6 @@ #include "DNA_collection_types.h" -#include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_geometry_set.hh" #include "BKE_geometry_set_instances.hh" @@ -157,7 +156,7 @@ void InstancesComponent::remove_instances(const IndexMask mask) dst_attributes.reallocate(mask.size()); src_attributes.foreach_attribute( - [&](const bke::AttributeIDRef &id, const AttributeMetaData &meta_data) { + [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData &meta_data) { if (!id.should_be_kept()) { return true; } @@ -366,20 +365,12 @@ blender::Span<int> InstancesComponent::almost_unique_ids() const return almost_unique_ids_; } -int InstancesComponent::attribute_domain_num(const eAttrDomain domain) const -{ - if (domain != ATTR_DOMAIN_INSTANCE) { - return 0; - } - return this->instances_num(); -} - -blender::bke::CustomDataAttributes &InstancesComponent::attributes() +blender::bke::CustomDataAttributes &InstancesComponent::instance_attributes() { return this->attributes_; } -const blender::bke::CustomDataAttributes &InstancesComponent::attributes() const +const blender::bke::CustomDataAttributes &InstancesComponent::instance_attributes() const { return this->attributes_; } @@ -404,17 +395,17 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider { } - GVArray try_get_for_read(const GeometryComponent &component) const final + GVArray try_get_for_read(const void *owner) const final { - const InstancesComponent &instances_component = static_cast<const InstancesComponent &>( - component); + const InstancesComponent &instances_component = *static_cast<const InstancesComponent *>( + owner); Span<float4x4> transforms = instances_component.instance_transforms(); return VArray<float3>::ForDerivedSpan<float4x4, get_transform_position>(transforms); } - WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final + GAttributeWriter try_get_for_write(void *owner) const final { - InstancesComponent &instances_component = static_cast<InstancesComponent &>(component); + InstancesComponent &instances_component = *static_cast<InstancesComponent *>(owner); MutableSpan<float4x4> transforms = instances_component.instance_transforms(); return {VMutableArray<float3>::ForDerivedSpan<float4x4, get_transform_position, @@ -422,18 +413,17 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider domain_}; } - bool try_delete(GeometryComponent &UNUSED(component)) const final + bool try_delete(void *UNUSED(owner)) const final { return false; } - bool try_create(GeometryComponent &UNUSED(component), - const AttributeInit &UNUSED(initializer)) const final + bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final { return false; } - bool exists(const GeometryComponent &UNUSED(component)) const final + bool exists(const void *UNUSED(owner)) const final { return true; } @@ -443,13 +433,17 @@ static ComponentAttributeProviders create_attribute_providers_for_instances() { static InstancePositionAttributeProvider position; static CustomDataAccessInfo instance_custom_data_access = { - [](GeometryComponent &component) -> CustomData * { - InstancesComponent &inst = static_cast<InstancesComponent &>(component); - return &inst.attributes().data; + [](void *owner) -> CustomData * { + InstancesComponent &inst = *static_cast<InstancesComponent *>(owner); + return &inst.instance_attributes().data; + }, + [](const void *owner) -> const CustomData * { + const InstancesComponent &inst = *static_cast<const InstancesComponent *>(owner); + return &inst.instance_attributes().data; }, - [](const GeometryComponent &component) -> const CustomData * { - const InstancesComponent &inst = static_cast<const InstancesComponent &>(component); - return &inst.attributes().data; + [](const void *owner) -> int { + const InstancesComponent &inst = *static_cast<const InstancesComponent *>(owner); + return inst.instances_num(); }, nullptr}; @@ -476,14 +470,57 @@ static ComponentAttributeProviders create_attribute_providers_for_instances() return ComponentAttributeProviders({&position, &id}, {&instance_custom_data}); } + +static AttributeAccessorFunctions get_instances_accessor_functions() +{ + static const ComponentAttributeProviders providers = create_attribute_providers_for_instances(); + AttributeAccessorFunctions fn = + attribute_accessor_functions::accessor_functions_for_providers<providers>(); + fn.domain_size = [](const void *owner, const eAttrDomain domain) { + if (owner == nullptr) { + return 0; + } + const InstancesComponent &instances = *static_cast<const InstancesComponent *>(owner); + switch (domain) { + case ATTR_DOMAIN_INSTANCE: + return instances.instances_num(); + default: + return 0; + } + }; + fn.domain_supported = [](const void *UNUSED(owner), const eAttrDomain domain) { + return domain == ATTR_DOMAIN_INSTANCE; + }; + fn.adapt_domain = [](const void *UNUSED(owner), + const blender::GVArray &varray, + const eAttrDomain from_domain, + const eAttrDomain to_domain) { + if (from_domain == to_domain && from_domain == ATTR_DOMAIN_INSTANCE) { + return varray; + } + return blender::GVArray{}; + }; + return fn; +} + +static const AttributeAccessorFunctions &get_instances_accessor_functions_ref() +{ + static const AttributeAccessorFunctions fn = get_instances_accessor_functions(); + return fn; +} + } // namespace blender::bke -const blender::bke::ComponentAttributeProviders *InstancesComponent::get_attribute_providers() - const +std::optional<blender::bke::AttributeAccessor> InstancesComponent::attributes() const +{ + return blender::bke::AttributeAccessor(this, + blender::bke::get_instances_accessor_functions_ref()); +} + +std::optional<blender::bke::MutableAttributeAccessor> InstancesComponent::attributes_for_write() { - static blender::bke::ComponentAttributeProviders providers = - blender::bke::create_attribute_providers_for_instances(); - return &providers; + return blender::bke::MutableAttributeAccessor( + this, blender::bke::get_instances_accessor_functions_ref()); } /** \} */ diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 9e64acf218b..cb36b9b19f7 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -7,7 +7,6 @@ #include "DNA_meshdata_types.h" #include "DNA_object_types.h" -#include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_deform.h" #include "BKE_geometry_fields.hh" @@ -151,7 +150,7 @@ VArray<float3> mesh_normals_varray(const MeshComponent &mesh_component, * array and copy the face normal for each of its corners. In this case using the mesh * component's generic domain interpolation is fine, the data will still be normalized, * since the face normal is just copied to every corner. */ - return mesh_component.attribute_try_adapt_domain( + return mesh_component.attributes()->adapt_domain( VArray<float3>::ForSpan({(float3 *)BKE_mesh_poly_normals_ensure(&mesh), mesh.totpoly}), ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER); @@ -169,26 +168,6 @@ VArray<float3> mesh_normals_varray(const MeshComponent &mesh_component, /** \name Attribute Access * \{ */ -int MeshComponent::attribute_domain_num(const eAttrDomain domain) const -{ - if (mesh_ == nullptr) { - return 0; - } - switch (domain) { - case ATTR_DOMAIN_CORNER: - return mesh_->totloop; - case ATTR_DOMAIN_POINT: - return mesh_->totvert; - case ATTR_DOMAIN_EDGE: - return mesh_->totedge; - case ATTR_DOMAIN_FACE: - return mesh_->totpoly; - default: - break; - } - return 0; -} - namespace blender::bke { template<typename T> @@ -747,9 +726,10 @@ static GVArray adapt_mesh_domain_edge_to_face(const Mesh &mesh, const GVArray &v } // namespace blender::bke -blender::GVArray MeshComponent::attribute_try_adapt_domain_impl(const blender::GVArray &varray, - const eAttrDomain from_domain, - const eAttrDomain to_domain) const +static blender::GVArray adapt_mesh_attribute_domain(const Mesh &mesh, + const blender::GVArray &varray, + const eAttrDomain from_domain, + const eAttrDomain to_domain) { if (!varray) { return {}; @@ -765,11 +745,11 @@ blender::GVArray MeshComponent::attribute_try_adapt_domain_impl(const blender::G case ATTR_DOMAIN_CORNER: { switch (to_domain) { case ATTR_DOMAIN_POINT: - return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, varray); + return blender::bke::adapt_mesh_domain_corner_to_point(mesh, varray); case ATTR_DOMAIN_FACE: - return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, varray); + return blender::bke::adapt_mesh_domain_corner_to_face(mesh, varray); case ATTR_DOMAIN_EDGE: - return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, varray); + return blender::bke::adapt_mesh_domain_corner_to_edge(mesh, varray); default: break; } @@ -778,11 +758,11 @@ blender::GVArray MeshComponent::attribute_try_adapt_domain_impl(const blender::G case ATTR_DOMAIN_POINT: { switch (to_domain) { case ATTR_DOMAIN_CORNER: - return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, varray); + return blender::bke::adapt_mesh_domain_point_to_corner(mesh, varray); case ATTR_DOMAIN_FACE: - return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, varray); + return blender::bke::adapt_mesh_domain_point_to_face(mesh, varray); case ATTR_DOMAIN_EDGE: - return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, varray); + return blender::bke::adapt_mesh_domain_point_to_edge(mesh, varray); default: break; } @@ -791,11 +771,11 @@ blender::GVArray MeshComponent::attribute_try_adapt_domain_impl(const blender::G case ATTR_DOMAIN_FACE: { switch (to_domain) { case ATTR_DOMAIN_POINT: - return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, varray); + return blender::bke::adapt_mesh_domain_face_to_point(mesh, varray); case ATTR_DOMAIN_CORNER: - return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, varray); + return blender::bke::adapt_mesh_domain_face_to_corner(mesh, varray); case ATTR_DOMAIN_EDGE: - return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, varray); + return blender::bke::adapt_mesh_domain_face_to_edge(mesh, varray); default: break; } @@ -804,11 +784,11 @@ blender::GVArray MeshComponent::attribute_try_adapt_domain_impl(const blender::G case ATTR_DOMAIN_EDGE: { switch (to_domain) { case ATTR_DOMAIN_CORNER: - return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, varray); + return blender::bke::adapt_mesh_domain_edge_to_corner(mesh, varray); case ATTR_DOMAIN_POINT: - return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, varray); + return blender::bke::adapt_mesh_domain_edge_to_point(mesh, varray); case ATTR_DOMAIN_FACE: - return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, varray); + return blender::bke::adapt_mesh_domain_edge_to_face(mesh, varray); default: break; } @@ -821,20 +801,6 @@ blender::GVArray MeshComponent::attribute_try_adapt_domain_impl(const blender::G return {}; } -static Mesh *get_mesh_from_component_for_write(GeometryComponent &component) -{ - BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); - MeshComponent &mesh_component = static_cast<MeshComponent &>(component); - return mesh_component.get_for_write(); -} - -static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &component) -{ - BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return mesh_component.get_for_read(); -} - namespace blender::bke { template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> @@ -864,9 +830,9 @@ static void set_vertex_position(MVert &vert, float3 position) copy_v3_v3(vert.co, position); } -static void tag_component_positions_changed(GeometryComponent &component) +static void tag_component_positions_changed(void *owner) { - Mesh *mesh = get_mesh_from_component_for_write(component); + Mesh *mesh = static_cast<Mesh *>(owner); if (mesh != nullptr) { BKE_mesh_tag_coords_changed(mesh); } @@ -1001,15 +967,13 @@ class VArrayImpl_For_VertexWeights final : public VMutableArrayImpl<float> { */ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { public: - ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const AttributeIDRef &attribute_id) const final + GAttributeReader try_get_for_read(const void *owner, + const AttributeIDRef &attribute_id) const final { - BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); if (!attribute_id.is_named()) { return {}; } - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); + const Mesh *mesh = static_cast<const Mesh *>(owner); if (mesh == nullptr) { return {}; } @@ -1028,15 +992,12 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { ATTR_DOMAIN_POINT}; } - WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const AttributeIDRef &attribute_id) const final + GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final { - BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); if (!attribute_id.is_named()) { return {}; } - MeshComponent &mesh_component = static_cast<MeshComponent &>(component); - Mesh *mesh = mesh_component.get_for_write(); + Mesh *mesh = static_cast<Mesh *>(owner); if (mesh == nullptr) { return {}; } @@ -1060,14 +1021,12 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { ATTR_DOMAIN_POINT}; } - bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final + bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final { - BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); if (!attribute_id.is_named()) { return false; } - MeshComponent &mesh_component = static_cast<MeshComponent &>(component); - Mesh *mesh = mesh_component.get_for_write(); + Mesh *mesh = static_cast<Mesh *>(owner); if (mesh == nullptr) { return true; } @@ -1101,12 +1060,9 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { return true; } - bool foreach_attribute(const GeometryComponent &component, - const AttributeForeachCallback callback) const final + bool foreach_attribute(const void *owner, const AttributeForeachCallback callback) const final { - BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); + const Mesh *mesh = static_cast<const Mesh *>(owner); if (mesh == nullptr) { return true; } @@ -1136,35 +1092,34 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { { } - GVArray try_get_for_read(const GeometryComponent &component) const final + GVArray try_get_for_read(const void *owner) const final { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); + const Mesh *mesh = static_cast<const Mesh *>(owner); if (mesh == nullptr || mesh->totpoly == 0) { return {}; } return VArray<float3>::ForSpan({(float3 *)BKE_mesh_poly_normals_ensure(mesh), mesh->totpoly}); } - WriteAttributeLookup try_get_for_write(GeometryComponent &UNUSED(component)) const final + GAttributeWriter try_get_for_write(void *UNUSED(owner)) const final { return {}; } - bool try_delete(GeometryComponent &UNUSED(component)) const final + bool try_delete(void *UNUSED(owner)) const final { return false; } - bool try_create(GeometryComponent &UNUSED(component), - const AttributeInit &UNUSED(initializer)) const final + bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final { return false; } - bool exists(const GeometryComponent &component) const final + bool exists(const void *owner) const final { - return component.attribute_domain_num(ATTR_DOMAIN_FACE) != 0; + const Mesh *mesh = static_cast<const Mesh *>(owner); + return mesh->totpoly != 0; } }; @@ -1174,34 +1129,42 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { */ static ComponentAttributeProviders create_attribute_providers_for_mesh() { - static auto update_custom_data_pointers = [](GeometryComponent &component) { - if (Mesh *mesh = get_mesh_from_component_for_write(component)) { - BKE_mesh_update_customdata_pointers(mesh, false); - } + static auto update_custom_data_pointers = [](void *owner) { + Mesh *mesh = static_cast<Mesh *>(owner); + 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; \ + [](void *owner) -> CustomData * { \ + Mesh *mesh = static_cast<Mesh *>(owner); \ + return &mesh->NAME; \ } #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; \ + [](const void *owner) -> const CustomData * { \ + const Mesh *mesh = static_cast<const Mesh *>(owner); \ + return &mesh->NAME; \ + } +#define MAKE_GET_ELEMENT_NUM_GETTER(NAME) \ + [](const void *owner) -> int { \ + const Mesh *mesh = static_cast<const Mesh *>(owner); \ + return mesh->NAME; \ } static CustomDataAccessInfo corner_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(ldata), MAKE_CONST_CUSTOM_DATA_GETTER(ldata), + MAKE_GET_ELEMENT_NUM_GETTER(totloop), update_custom_data_pointers}; static CustomDataAccessInfo point_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(vdata), MAKE_CONST_CUSTOM_DATA_GETTER(vdata), + MAKE_GET_ELEMENT_NUM_GETTER(totvert), update_custom_data_pointers}; static CustomDataAccessInfo edge_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(edata), MAKE_CONST_CUSTOM_DATA_GETTER(edata), + MAKE_GET_ELEMENT_NUM_GETTER(totedge), update_custom_data_pointers}; static CustomDataAccessInfo face_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(pdata), MAKE_CONST_CUSTOM_DATA_GETTER(pdata), + MAKE_GET_ELEMENT_NUM_GETTER(totpoly), update_custom_data_pointers}; #undef MAKE_CONST_CUSTOM_DATA_GETTER @@ -1297,13 +1260,72 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() &face_custom_data}); } +static AttributeAccessorFunctions get_mesh_accessor_functions() +{ + static const ComponentAttributeProviders providers = create_attribute_providers_for_mesh(); + AttributeAccessorFunctions fn = + attribute_accessor_functions::accessor_functions_for_providers<providers>(); + fn.domain_size = [](const void *owner, const eAttrDomain domain) { + if (owner == nullptr) { + return 0; + } + const Mesh &mesh = *static_cast<const Mesh *>(owner); + switch (domain) { + case ATTR_DOMAIN_POINT: + return mesh.totvert; + case ATTR_DOMAIN_EDGE: + return mesh.totedge; + case ATTR_DOMAIN_FACE: + return mesh.totpoly; + case ATTR_DOMAIN_CORNER: + return mesh.totloop; + default: + return 0; + } + }; + fn.domain_supported = [](const void *UNUSED(owner), const eAttrDomain domain) { + return ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER); + }; + fn.adapt_domain = [](const void *owner, + const blender::GVArray &varray, + const eAttrDomain from_domain, + const eAttrDomain to_domain) -> blender::GVArray { + if (owner == nullptr) { + return {}; + } + const Mesh &mesh = *static_cast<const Mesh *>(owner); + return adapt_mesh_attribute_domain(mesh, varray, from_domain, to_domain); + }; + return fn; +} + +static const AttributeAccessorFunctions &get_mesh_accessor_functions_ref() +{ + static const AttributeAccessorFunctions fn = get_mesh_accessor_functions(); + return fn; +} + +AttributeAccessor mesh_attributes(const Mesh &mesh) +{ + return AttributeAccessor(&mesh, get_mesh_accessor_functions_ref()); +} + +MutableAttributeAccessor mesh_attributes_for_write(Mesh &mesh) +{ + return MutableAttributeAccessor(&mesh, get_mesh_accessor_functions_ref()); +} + } // namespace blender::bke -const blender::bke::ComponentAttributeProviders *MeshComponent::get_attribute_providers() const +std::optional<blender::bke::AttributeAccessor> MeshComponent::attributes() const +{ + return blender::bke::AttributeAccessor(mesh_, blender::bke::get_mesh_accessor_functions_ref()); +} + +std::optional<blender::bke::MutableAttributeAccessor> MeshComponent::attributes_for_write() { - static blender::bke::ComponentAttributeProviders providers = - blender::bke::create_attribute_providers_for_mesh(); - return &providers; + return blender::bke::MutableAttributeAccessor(mesh_, + blender::bke::get_mesh_accessor_functions_ref()); } /** \} */ diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc index facdbed265d..b439a9ba7f8 100644 --- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc +++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc @@ -2,7 +2,6 @@ #include "DNA_pointcloud_types.h" -#include "BKE_attribute_access.hh" #include "BKE_geometry_set.hh" #include "BKE_lib_id.h" #include "BKE_pointcloud.h" @@ -104,17 +103,6 @@ void PointCloudComponent::ensure_owns_direct_data() /** \name Attribute Access * \{ */ -int PointCloudComponent::attribute_domain_num(const eAttrDomain domain) const -{ - if (pointcloud_ == nullptr) { - return 0; - } - if (domain != ATTR_DOMAIN_POINT) { - return 0; - } - return pointcloud_->totpoint; -} - namespace blender::bke { /** @@ -123,23 +111,22 @@ namespace blender::bke { */ static ComponentAttributeProviders create_attribute_providers_for_point_cloud() { - static auto update_custom_data_pointers = [](GeometryComponent &component) { - PointCloudComponent &pointcloud_component = static_cast<PointCloudComponent &>(component); - if (PointCloud *pointcloud = pointcloud_component.get_for_write()) { - BKE_pointcloud_update_customdata_pointers(pointcloud); - } + static auto update_custom_data_pointers = [](void *owner) { + PointCloud *pointcloud = static_cast<PointCloud *>(owner); + 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; + [](void *owner) -> CustomData * { + PointCloud *pointcloud = static_cast<PointCloud *>(owner); + return &pointcloud->pdata; + }, + [](const void *owner) -> const CustomData * { + const PointCloud *pointcloud = static_cast<const PointCloud *>(owner); + return &pointcloud->pdata; }, - [](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; + [](const void *owner) -> int { + const PointCloud *pointcloud = static_cast<const PointCloud *>(owner); + return pointcloud->totpoint; }, update_custom_data_pointers}; @@ -180,14 +167,67 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud() return ComponentAttributeProviders({&position, &radius, &id}, {&point_custom_data}); } +static AttributeAccessorFunctions get_pointcloud_accessor_functions() +{ + static const ComponentAttributeProviders providers = + create_attribute_providers_for_point_cloud(); + AttributeAccessorFunctions fn = + attribute_accessor_functions::accessor_functions_for_providers<providers>(); + fn.domain_size = [](const void *owner, const eAttrDomain domain) { + if (owner == nullptr) { + return 0; + } + const PointCloud &pointcloud = *static_cast<const PointCloud *>(owner); + switch (domain) { + case ATTR_DOMAIN_POINT: + return pointcloud.totpoint; + default: + return 0; + } + }; + fn.domain_supported = [](const void *UNUSED(owner), const eAttrDomain domain) { + return domain == ATTR_DOMAIN_POINT; + }; + fn.adapt_domain = [](const void *UNUSED(owner), + const blender::GVArray &varray, + const eAttrDomain from_domain, + const eAttrDomain to_domain) { + if (from_domain == to_domain && from_domain == ATTR_DOMAIN_POINT) { + return varray; + } + return blender::GVArray{}; + }; + return fn; +} + +static const AttributeAccessorFunctions &get_pointcloud_accessor_functions_ref() +{ + static const AttributeAccessorFunctions fn = get_pointcloud_accessor_functions(); + return fn; +} + +AttributeAccessor pointcloud_attributes(const PointCloud &pointcloud) +{ + return AttributeAccessor(&pointcloud, get_pointcloud_accessor_functions_ref()); +} + +MutableAttributeAccessor pointcloud_attributes_for_write(PointCloud &pointcloud) +{ + return MutableAttributeAccessor(&pointcloud, get_pointcloud_accessor_functions_ref()); +} + } // namespace blender::bke -const blender::bke::ComponentAttributeProviders *PointCloudComponent::get_attribute_providers() - const +std::optional<blender::bke::AttributeAccessor> PointCloudComponent::attributes() const +{ + return blender::bke::AttributeAccessor(pointcloud_, + blender::bke::get_pointcloud_accessor_functions_ref()); +} + +std::optional<blender::bke::MutableAttributeAccessor> PointCloudComponent::attributes_for_write() { - static blender::bke::ComponentAttributeProviders providers = - blender::bke::create_attribute_providers_for_point_cloud(); - return &providers; + return blender::bke::MutableAttributeAccessor( + pointcloud_, blender::bke::get_pointcloud_accessor_functions_ref()); } /** \} */ diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 70a39acf620..c6fe8eebc7f 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -7,7 +7,6 @@ #include "BLT_translation.h" #include "BKE_attribute.h" -#include "BKE_attribute_access.hh" #include "BKE_curves.hh" #include "BKE_geometry_fields.hh" #include "BKE_geometry_set.hh" @@ -59,6 +58,27 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ return nullptr; } +int GeometryComponent::attribute_domain_size(const eAttrDomain domain) const +{ + if (this->is_empty()) { + return 0; + } + const std::optional<blender::bke::AttributeAccessor> attributes = this->attributes(); + if (attributes.has_value()) { + return attributes->domain_size(domain); + } + return 0; +} + +std::optional<blender::bke::AttributeAccessor> GeometryComponent::attributes() const +{ + return std::nullopt; +}; +std::optional<blender::bke::MutableAttributeAccessor> GeometryComponent::attributes_for_write() +{ + return std::nullopt; +} + void GeometryComponent::user_add() const { users_.fetch_add(1); @@ -444,11 +464,14 @@ void GeometrySet::attribute_foreach(const Span<GeometryComponentType> component_ continue; } const GeometryComponent &component = *this->get_component_for_read(component_type); - component.attribute_foreach( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - callback(attribute_id, meta_data, component); - return true; - }); + const std::optional<AttributeAccessor> attributes = component.attributes(); + if (attributes.has_value()) { + attributes->for_all( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + callback(attribute_id, meta_data, component); + return true; + }); + } } if (include_instances && this->has_instances()) { const InstancesComponent &instances = *this->get_component_for_read<InstancesComponent>(); @@ -462,7 +485,7 @@ void GeometrySet::gather_attributes_for_propagation( const Span<GeometryComponentType> component_types, const GeometryComponentType dst_component_type, bool include_instances, - blender::Map<blender::bke::AttributeIDRef, AttributeKind> &r_attributes) const + blender::Map<blender::bke::AttributeIDRef, blender::bke::AttributeKind> &r_attributes) const { using namespace blender; using namespace blender::bke; @@ -475,8 +498,8 @@ void GeometrySet::gather_attributes_for_propagation( [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data, const GeometryComponent &component) { - if (component.attribute_is_builtin(attribute_id)) { - if (!dummy_component->attribute_is_builtin(attribute_id)) { + if (component.attributes()->is_builtin(attribute_id)) { + if (!dummy_component->attributes()->is_builtin(attribute_id)) { /* Don't propagate built-in attributes that are not built-in on the destination * component. */ return; diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index 9cb3e684667..7ebb3e25fd4 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -1209,9 +1209,7 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain, BKE_mesh_nomain_to_mesh(mesh, mesh_in_bmain, nullptr, &CD_MASK_MESH, true); /* Anonymous attributes shouldn't exist on original data. */ - MeshComponent component; - component.replace(mesh_in_bmain, GeometryOwnershipType::Editable); - component.attributes_remove_anonymous(); + blender::bke::mesh_attributes_for_write(*mesh_in_bmain).remove_anonymous(); /* User-count is required because so far mesh was in a limbo, where library management does * not perform any user management (i.e. copy of a mesh will not increase users of materials). */ diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc index b792c5c98b9..dd09a3d6917 100644 --- a/source/blender/blenkernel/intern/mesh_sample.cc +++ b/source/blender/blenkernel/intern/mesh_sample.cc @@ -1,6 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_bvhutils.h" #include "BKE_mesh_runtime.h" @@ -253,12 +252,12 @@ void MeshAttributeInterpolator::sample_data(const GVArray &src, } } -void MeshAttributeInterpolator::sample_attribute(const ReadAttributeLookup &src_attribute, - OutputAttribute &dst_attribute, +void MeshAttributeInterpolator::sample_attribute(const GAttributeReader &src_attribute, + GSpanAttributeWriter &dst_attribute, eAttributeMapMode mode) { if (src_attribute && dst_attribute) { - this->sample_data(src_attribute.varray, src_attribute.domain, mode, dst_attribute.as_span()); + this->sample_data(src_attribute.varray, src_attribute.domain, mode, dst_attribute.span); } } diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index e8c7aff75d1..a674bf7800a 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -6,7 +6,6 @@ #include "BLI_task.hh" #include "BLI_timeit.hh" -#include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_spline.hh" @@ -21,6 +20,7 @@ using blender::Span; using blender::VArray; using blender::attribute_math::convert_to_static_type; using blender::bke::AttributeIDRef; +using blender::bke::AttributeMetaData; CurveType Spline::type() const { |