diff options
Diffstat (limited to 'source/blender/blenkernel/BKE_attribute.hh')
-rw-r--r-- | source/blender/blenkernel/BKE_attribute.hh | 169 |
1 files changed, 130 insertions, 39 deletions
diff --git a/source/blender/blenkernel/BKE_attribute.hh b/source/blender/blenkernel/BKE_attribute.hh index b92d0d4326b..7b13b8a2b09 100644 --- a/source/blender/blenkernel/BKE_attribute.hh +++ b/source/blender/blenkernel/BKE_attribute.hh @@ -2,6 +2,8 @@ #pragma once +#include <optional> + #include "BLI_color.hh" #include "BLI_function_ref.hh" #include "BLI_generic_span.hh" @@ -14,6 +16,10 @@ struct Mesh; struct PointCloud; +namespace blender::fn { +class MultiFunction; +class GField; +} // namespace blender::fn namespace blender::bke { @@ -71,8 +77,13 @@ struct AttributeKind { */ struct AttributeInit { enum class Type { - Default, + /** #AttributeInitConstruct. */ + Construct, + /** #AttributeInitDefaultValue. */ + DefaultValue, + /** #AttributeInitVArray. */ VArray, + /** #AttributeInitMoveArray. */ MoveArray, }; Type type; @@ -82,11 +93,20 @@ struct AttributeInit { }; /** - * Create an attribute using the default value for the data type. - * The default values may depend on the attribute provider implementation. + * Default construct new attribute values. Does nothing for trivial types. This should be used + * if all attribute element values will be set by the caller after creating the attribute. + */ +struct AttributeInitConstruct : public AttributeInit { + AttributeInitConstruct() : AttributeInit(Type::Construct) + { + } +}; + +/** + * Create an attribute using the default value for the data type (almost always "zero"). */ -struct AttributeInitDefault : public AttributeInit { - AttributeInitDefault() : AttributeInit(Type::Default) +struct AttributeInitDefaultValue : public AttributeInit { + AttributeInitDefaultValue() : AttributeInit(Type::DefaultValue) { } }; @@ -94,14 +114,11 @@ struct AttributeInitDefault : public AttributeInit { /** * 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; + GVArray varray; - AttributeInitVArray(blender::GVArray varray) - : AttributeInit(Type::VArray), varray(std::move(varray)) + AttributeInitVArray(GVArray varray) : AttributeInit(Type::VArray), varray(std::move(varray)) { } }; @@ -117,10 +134,10 @@ struct AttributeInitVArray : public AttributeInit { * 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 { +struct AttributeInitMoveArray : public AttributeInit { void *data = nullptr; - AttributeInitMove(void *data) : AttributeInit(Type::MoveArray), data(data) + AttributeInitMoveArray(void *data) : AttributeInit(Type::MoveArray), data(data) { } }; @@ -150,7 +167,28 @@ template<typename T> struct AttributeReader { }; /** - * Result when looking up an attribute from some geometry with read an write access. After writing + * A utility to make sure attribute values are valid, for attributes like "material_index" which + * can only be positive, or attributes that represent enum options. This is usually only necessary + * when writing attributes from an untrusted/arbitrary user input. + */ +struct AttributeValidator { + /** + * Single input, single output function that corrects attribute values if necessary. + */ + const fn::MultiFunction *function; + + operator bool() const + { + return this->function != nullptr; + } + /** + * Return a field that creates corrected attribute values. + */ + fn::GField validate_field_if_necessary(const fn::GField &field) const; +}; + +/** + * Result when looking up an attribute from some geometry with read and write access. After writing * to the attribute, the #finish method has to be called. This may invalidate caches based on this * attribute. */ @@ -226,7 +264,9 @@ template<typename T> struct SpanAttributeWriter { */ void finish() { - this->span.save(); + if (this->span.varray()) { + this->span.save(); + } if (this->tag_modified_fn) { this->tag_modified_fn(); } @@ -301,7 +341,9 @@ struct GSpanAttributeWriter { void finish() { - this->span.save(); + if (this->span.varray()) { + this->span.save(); + } if (this->tag_modified_fn) { this->tag_modified_fn(); } @@ -330,7 +372,7 @@ struct AttributeAccessorFunctions { eAttrDomain to_domain); bool (*for_all)(const void *owner, FunctionRef<bool(const AttributeIDRef &, const AttributeMetaData &)> fn); - + AttributeValidator (*lookup_validator)(const void *owner, const AttributeIDRef &attribute_id); GAttributeWriter (*lookup_for_write)(void *owner, const AttributeIDRef &attribute_id); bool (*remove)(void *owner, const AttributeIDRef &attribute_id); bool (*add)(void *owner, @@ -351,8 +393,9 @@ class AttributeAccessor { /** * 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. + * Under some circumstances this can be null. In that case most methods can't be used. Allowed + * methods are #domain_size, #for_all and #is_builtin. We could potentially make these methods + * accessible without #AttributeAccessor and then #owner_ could always be non-null. * * \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. @@ -484,6 +527,14 @@ class AttributeAccessor { } /** + * Same as the generic version above, but should be used when the type is known at compile time. + */ + AttributeValidator lookup_validator(const AttributeIDRef &attribute_id) const + { + return fn_->lookup_validator(owner_, attribute_id); + } + + /** * Interpolate data from one domain to another. */ GVArray adapt_domain(const GVArray &varray, @@ -509,7 +560,10 @@ class AttributeAccessor { */ bool for_all(const AttributeForeachCallback fn) const { - return fn_->for_all(owner_, fn); + if (owner_ != nullptr) { + return fn_->for_all(owner_, fn); + } + return true; } /** @@ -533,10 +587,12 @@ class MutableAttributeAccessor : public AttributeAccessor { * 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); - } + GAttributeWriter lookup_for_write(const AttributeIDRef &attribute_id); + + /** + * Same as above, but returns a type that makes it easier to work with the attribute as a span. + */ + GSpanAttributeWriter lookup_for_write_span(const AttributeIDRef &attribute_id); /** * Get a writable attribute or non if it does not exist. @@ -555,6 +611,19 @@ class MutableAttributeAccessor : public AttributeAccessor { } /** + * Same as above, but returns a type that makes it easier to work with the attribute as a span. + */ + template<typename T> + SpanAttributeWriter<T> lookup_for_write_span(const AttributeIDRef &attribute_id) + { + AttributeWriter<T> attribute = this->lookup_for_write<T>(attribute_id); + if (attribute) { + return SpanAttributeWriter<T>{std::move(attribute), true}; + } + return {}; + } + + /** * 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. @@ -576,7 +645,7 @@ class MutableAttributeAccessor : public AttributeAccessor { const AttributeIDRef &attribute_id, const eAttrDomain domain, const eCustomDataType data_type, - const AttributeInit &initializer = AttributeInitDefault()); + const AttributeInit &initializer = AttributeInitDefaultValue()); /** * Same as above, but returns a type that makes it easier to work with the attribute as a span. @@ -587,7 +656,7 @@ class MutableAttributeAccessor : public AttributeAccessor { const AttributeIDRef &attribute_id, const eAttrDomain domain, const eCustomDataType data_type, - const AttributeInit &initializer = AttributeInitDefault()); + const AttributeInit &initializer = AttributeInitDefaultValue()); /** * Same as above, but should be used when the type is known at compile time. @@ -596,7 +665,7 @@ class MutableAttributeAccessor : public AttributeAccessor { AttributeWriter<T> lookup_or_add_for_write( const AttributeIDRef &attribute_id, const eAttrDomain domain, - const AttributeInit &initializer = AttributeInitDefault()) + const AttributeInit &initializer = AttributeInitDefaultValue()) { const CPPType &cpp_type = CPPType::get<T>(); const eCustomDataType data_type = cpp_type_to_custom_data_type(cpp_type); @@ -610,7 +679,7 @@ class MutableAttributeAccessor : public AttributeAccessor { SpanAttributeWriter<T> lookup_or_add_for_write_span( const AttributeIDRef &attribute_id, const eAttrDomain domain, - const AttributeInit &initializer = AttributeInitDefault()) + const AttributeInit &initializer = AttributeInitDefaultValue()) { AttributeWriter<T> attribute = this->lookup_or_add_for_write<T>( attribute_id, domain, initializer); @@ -627,6 +696,8 @@ class MutableAttributeAccessor : public AttributeAccessor { * 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. + * + * For trivial types, the values in a newly created attribute will not be initialized. */ GSpanAttributeWriter lookup_or_add_for_write_only_span(const AttributeIDRef &attribute_id, const eAttrDomain domain, @@ -639,7 +710,9 @@ class MutableAttributeAccessor : public AttributeAccessor { 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); + AttributeWriter<T> attribute = this->lookup_or_add_for_write<T>( + attribute_id, domain, AttributeInitConstruct()); + if (attribute) { return SpanAttributeWriter<T>{std::move(attribute), false}; } @@ -662,6 +735,35 @@ class MutableAttributeAccessor : public AttributeAccessor { void remove_anonymous(); }; +struct AttributeTransferData { + /* Expect that if an attribute exists, it is stored as a contiguous array internally anyway. */ + GVArraySpan src; + AttributeMetaData meta_data; + bke::GSpanAttributeWriter dst; +}; +/** + * Retrieve attribute arrays and writers for attributes that should be transferred between + * data-blocks of the same type. + */ +Vector<AttributeTransferData> retrieve_attributes_for_transfer( + const bke::AttributeAccessor src_attributes, + bke::MutableAttributeAccessor dst_attributes, + eAttrDomainMask domain_mask, + const Set<std::string> &skip = {}); + +/** + * Copy attributes for the domain based on the elementwise mask. + * + * \param mask_indices: Indexed elements to copy from the source data-block. + * \param domain: Attribute domain to transfer. + * \param skip: Named attributes to ignore/skip. + */ +void copy_attribute_domain(AttributeAccessor src_attributes, + MutableAttributeAccessor dst_attributes, + IndexMask selection, + eAttrDomain domain, + const Set<std::string> &skip = {}); + bool allow_procedural_attribute_access(StringRef attribute_name); extern const char *no_procedural_access_message; @@ -722,20 +824,9 @@ class CustomDataAttributes { 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 * \{ */ |