diff options
Diffstat (limited to 'source/blender/blenkernel')
21 files changed, 1246 insertions, 1189 deletions
diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index 120b4e08b9c..5feae1bf4ca 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -20,6 +20,7 @@ #include "FN_cpp_type.hh" #include "FN_generic_span.hh" +#include "FN_generic_virtual_array.hh" #include "BKE_attribute.h" @@ -30,6 +31,10 @@ namespace blender::bke { using fn::CPPType; +using fn::GVArray; +using fn::GVArrayPtr; +using fn::GVMutableArray; +using fn::GVMutableArrayPtr; const CPPType *custom_data_type_to_cpp_type(const CustomDataType type); CustomDataType cpp_type_to_custom_data_type(const CPPType &type); @@ -37,282 +42,193 @@ CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_ AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains); /** - * This class offers an indirection for reading an attribute. - * This is useful for the following reasons: - * - Blender does not store all attributes the same way. - * The simplest case are custom data layers with primitive types. - * A bit more complex are mesh attributes like the position of vertices, - * which are embedded into the MVert struct. - * Even more complex to access are vertex weights. - * - Sometimes attributes are stored on one domain, but we want to access - * the attribute on a different domain. Therefore, we have to interpolate - * between the domains. + * Used when looking up a "plain attribute" based on a name for reading from it. */ -class ReadAttribute { - protected: - const AttributeDomain domain_; - const CPPType &cpp_type_; - const CustomDataType custom_data_type_; - const int64_t size_; - - /* Protects the span below, so that no two threads initialize it at the same time. */ - mutable std::mutex span_mutex_; - /* When it is not null, it points to the attribute array or a temporary array that contains all - * the attribute values. */ - mutable void *array_buffer_ = nullptr; - /* Is true when the buffer above is owned by the attribute accessor. */ - mutable bool array_is_temporary_ = false; +struct ReadAttributeLookup { + /* The virtual array that is used to read from this attribute. */ + GVArrayPtr varray; + /* Domain the attribute lives on in the geometry. */ + AttributeDomain domain; - public: - ReadAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size) - : domain_(domain), - cpp_type_(cpp_type), - custom_data_type_(cpp_type_to_custom_data_type(cpp_type)), - size_(size) - { - } - - virtual ~ReadAttribute(); - - AttributeDomain domain() const + /* Convenience function to check if the attribute has been found. */ + operator bool() const { - return domain_; + return this->varray.get() != nullptr; } +}; - const CPPType &cpp_type() const - { - return cpp_type_; - } +/** + * 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. */ + GVMutableArrayPtr varray; + /* Domain the attributes lives on in the geometry. */ + AttributeDomain domain; - CustomDataType custom_data_type() const + /* Convenience function to check if the attribute has been found. */ + operator bool() const { - return custom_data_type_; + return this->varray.get() != nullptr; } +}; - int64_t size() const - { - return size_; - } +/** + * 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 + * VMutableArray_Span 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. + */ +class OutputAttribute { + public: + using SaveFn = std::function<void(OutputAttribute &)>; - void get(const int64_t index, void *r_value) const - { - BLI_assert(index < size_); - this->get_internal(index, r_value); - } + private: + GVMutableArrayPtr varray_; + AttributeDomain domain_; + SaveFn save_; + std::optional<fn::GVMutableArray_GSpan> optional_span_varray_; + bool ignore_old_values_ = false; + bool save_has_been_called_ = false; - /* Get a span that contains all attribute values. */ - fn::GSpan get_span() const; + public: + OutputAttribute() = default; - template<typename T> Span<T> get_span() const + OutputAttribute(GVMutableArrayPtr varray, + AttributeDomain domain, + SaveFn save, + const bool ignore_old_values) + : varray_(std::move(varray)), + domain_(domain), + save_(std::move(save)), + ignore_old_values_(ignore_old_values) { - return this->get_span().typed<T>(); } - protected: - /* r_value is expected to be uninitialized. */ - virtual void get_internal(const int64_t index, void *r_value) const = 0; + OutputAttribute(OutputAttribute &&other) = default; - virtual void initialize_span() const; -}; + ~OutputAttribute(); -/** - * This exists for similar reasons as the ReadAttribute class, except that - * it does not deal with interpolation between domains. - */ -class WriteAttribute { - protected: - const AttributeDomain domain_; - const CPPType &cpp_type_; - const CustomDataType custom_data_type_; - const int64_t size_; - - /* When not null, this points either to the attribute array or to a temporary array. */ - void *array_buffer_ = nullptr; - /* True, when the buffer points to a temporary array. */ - bool array_is_temporary_ = false; - /* This helps to protect against forgetting to apply changes done to the array. */ - bool array_should_be_applied_ = false; - - public: - WriteAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size) - : domain_(domain), - cpp_type_(cpp_type), - custom_data_type_(cpp_type_to_custom_data_type(cpp_type)), - size_(size) + operator bool() const { + return varray_.get() != nullptr; } - virtual ~WriteAttribute(); - - AttributeDomain domain() const + GVMutableArray &operator*() { - return domain_; + return *varray_; } - const CPPType &cpp_type() const + GVMutableArray *operator->() { - return cpp_type_; + return varray_.get(); } - CustomDataType custom_data_type() const + GVMutableArray &varray() { - return custom_data_type_; + return *varray_; } - int64_t size() const + AttributeDomain domain() const { - return size_; + return domain_; } - void get(const int64_t index, void *r_value) const + const CPPType &cpp_type() const { - BLI_assert(index < size_); - this->get_internal(index, r_value); + return varray_->type(); } - void set(const int64_t index, const void *value) + CustomDataType custom_data_type() const { - BLI_assert(index < size_); - this->set_internal(index, value); + return cpp_type_to_custom_data_type(this->cpp_type()); } - /* Get a span that new attribute values can be written into. When all values have been changed, - * #apply_span has to be called. */ - fn::GMutableSpan get_span(); - /* The span returned by this method might not contain the current attribute values. */ - fn::GMutableSpan get_span_for_write_only(); - /* Write the changes to the span into the actual attribute, if they aren't already. */ - void apply_span(); - - template<typename T> MutableSpan<T> get_span() + fn::GMutableSpan as_span() { - return this->get_span().typed<T>(); + if (!optional_span_varray_.has_value()) { + const bool materialize_old_values = !ignore_old_values_; + optional_span_varray_.emplace(*varray_, materialize_old_values); + } + fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_; + return span_varray; } - template<typename T> MutableSpan<T> get_span_for_write_only() + template<typename T> MutableSpan<T> as_span() { - return this->get_span_for_write_only().typed<T>(); + return this->as_span().typed<T>(); } - protected: - virtual void get_internal(const int64_t index, void *r_value) const = 0; - virtual void set_internal(const int64_t index, const void *value) = 0; - - virtual void initialize_span(const bool write_only); - virtual void apply_span_if_necessary(); + void save(); }; -using ReadAttributePtr = std::unique_ptr<ReadAttribute>; -using WriteAttributePtr = std::unique_ptr<WriteAttribute>; - -/* This provides type safe access to an attribute. - * The underlying ReadAttribute is owned optionally. */ -template<typename T> class TypedReadAttribute { +/** + * Same as OutputAttribute, but should be used when the data type is known at compile time. + */ +template<typename T> class OutputAttribute_Typed { private: - std::unique_ptr<const ReadAttribute> owned_attribute_; - const ReadAttribute *attribute_; + OutputAttribute attribute_; + std::optional<fn::GVMutableArray_Typed<T>> optional_varray_; + VMutableArray<T> *varray_ = nullptr; public: - TypedReadAttribute(ReadAttributePtr attribute) : TypedReadAttribute(*attribute) - { - owned_attribute_ = std::move(attribute); - BLI_assert(owned_attribute_); - } - - TypedReadAttribute(const ReadAttribute &attribute) : attribute_(&attribute) + OutputAttribute_Typed(OutputAttribute attribute) : attribute_(std::move(attribute)) { - BLI_assert(attribute_->cpp_type().is<T>()); + if (attribute_) { + optional_varray_.emplace(attribute_.varray()); + varray_ = &**optional_varray_; + } } - int64_t size() const + operator bool() const { - return attribute_->size(); + return varray_ != nullptr; } - T operator[](const int64_t index) const + VMutableArray<T> &operator*() { - BLI_assert(index < attribute_->size()); - T value; - value.~T(); - attribute_->get(index, &value); - return value; + return *varray_; } - /* Get a span to that contains all attribute values for faster and more convenient access. */ - Span<T> get_span() const + VMutableArray<T> *operator->() { - return attribute_->get_span().template typed<T>(); + return varray_; } -}; - -/* This provides type safe access to an attribute. - * The underlying WriteAttribute is owned optionally. */ -template<typename T> class TypedWriteAttribute { - private: - std::unique_ptr<WriteAttribute> owned_attribute_; - WriteAttribute *attribute_; - public: - TypedWriteAttribute(WriteAttributePtr attribute) : TypedWriteAttribute(*attribute) + VMutableArray<T> &varray() { - owned_attribute_ = std::move(attribute); - BLI_assert(owned_attribute_); + return *varray_; } - TypedWriteAttribute(WriteAttribute &attribute) : attribute_(&attribute) - { - BLI_assert(attribute_->cpp_type().is<T>()); - } - - int64_t size() const + AttributeDomain domain() const { - return attribute_->size(); + return attribute_.domain(); } - T operator[](const int64_t index) const + const CPPType &cpp_type() const { - BLI_assert(index < attribute_->size()); - T value; - value.~T(); - attribute_->get(index, &value); - return value; + return CPPType::get<T>(); } - void set(const int64_t index, const T &value) + CustomDataType custom_data_type() const { - attribute_->set(index, &value); + return cpp_type_to_custom_data_type(this->cpp_type()); } - /* Get a span that new values can be written into. Once all values have been updated #apply_span - * has to be called. */ - MutableSpan<T> get_span() + MutableSpan<T> as_span() { - return attribute_->get_span().typed<T>(); - } - /* The span returned by this method might not contain the current attribute values. */ - MutableSpan<T> get_span_for_write_only() - { - return attribute_->get_span_for_write_only().typed<T>(); + return attribute_.as_span<T>(); } - /* Write back all changes to the actual attribute, if necessary. */ - void apply_span() + void save() { - attribute_->apply_span(); + attribute_.save(); } }; -using BooleanReadAttribute = TypedReadAttribute<bool>; -using FloatReadAttribute = TypedReadAttribute<float>; -using Float2ReadAttribute = TypedReadAttribute<float2>; -using Float3ReadAttribute = TypedReadAttribute<float3>; -using Int32ReadAttribute = TypedReadAttribute<int>; -using Color4fReadAttribute = TypedReadAttribute<Color4f>; -using BooleanWriteAttribute = TypedWriteAttribute<bool>; -using FloatWriteAttribute = TypedWriteAttribute<float>; -using Float2WriteAttribute = TypedWriteAttribute<float2>; -using Float3WriteAttribute = TypedWriteAttribute<float3>; -using Int32WriteAttribute = TypedWriteAttribute<int>; -using Color4fWriteAttribute = TypedWriteAttribute<Color4f>; - } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh index 16fc0db60fb..65ac5b5bfa8 100644 --- a/source/blender/blenkernel/BKE_attribute_math.hh +++ b/source/blender/blenkernel/BKE_attribute_math.hh @@ -21,13 +21,17 @@ #include "DNA_customdata_types.h" +#include "FN_cpp_type.hh" + namespace blender::attribute_math { +using fn::CPPType; + /** * Utility function that simplifies calling a templated function based on a custom data type. */ template<typename Func> -void convert_to_static_type(const CustomDataType data_type, const Func &func) +inline void convert_to_static_type(const CustomDataType data_type, const Func &func) { switch (data_type) { case CD_PROP_FLOAT: @@ -54,6 +58,32 @@ void convert_to_static_type(const CustomDataType data_type, const Func &func) } } +template<typename Func> +inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func) +{ + if (cpp_type.is<float>()) { + func(float()); + } + else if (cpp_type.is<float2>()) { + func(float2()); + } + else if (cpp_type.is<float3>()) { + func(float3()); + } + else if (cpp_type.is<int>()) { + func(int()); + } + else if (cpp_type.is<bool>()) { + func(bool()); + } + else if (cpp_type.is<Color4f>()) { + func(Color4f()); + } + else { + BLI_assert_unreachable(); + } +} + /* -------------------------------------------------------------------- */ /** \name Mix three values of the same type. * diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 08aea043fc7..61489aa7494 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -31,15 +31,15 @@ extern "C" { */ /* Blender major and minor version. */ -#define BLENDER_VERSION 293 +#define BLENDER_VERSION 300 /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ -#define BLENDER_VERSION_CYCLE beta +#define BLENDER_VERSION_CYCLE alpha /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 19 +#define BLENDER_FILE_SUBVERSION 0 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index 3d30188e517..50aa6027840 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -206,8 +206,25 @@ void CTX_wm_area_set(bContext *C, struct ScrArea *area); void CTX_wm_region_set(bContext *C, struct ARegion *region); void CTX_wm_menu_set(bContext *C, struct ARegion *menu); void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup); -const char *CTX_wm_operator_poll_msg_get(struct bContext *C); + +/** + * Values to create the message that describes the reason poll failed. + * + * \note This must be called in the same context as the poll function that created it. + */ +struct bContextPollMsgDyn_Params { + /** The result is allocated . */ + char *(*get_fn)(bContext *C, void *user_data); + /** Optionally free the user-data. */ + void (*free_fn)(bContext *C, void *user_data); + void *user_data; +}; + +const char *CTX_wm_operator_poll_msg_get(struct bContext *C, bool *r_free); void CTX_wm_operator_poll_msg_set(struct bContext *C, const char *msg); +void CTX_wm_operator_poll_msg_set_dynamic(bContext *C, + const struct bContextPollMsgDyn_Params *params); +void CTX_wm_operator_poll_msg_clear(struct bContext *C); /* Data Context * diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index d94b2e7902b..027338a5d5c 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -56,72 +56,77 @@ class ComponentAttributeProviders; class GeometryComponent; /** - * An #OutputAttributePtr wraps a #WriteAttributePtr that might not be stored in its final - * destination yet. Therefore, once the attribute has been filled with data, the #save method has - * to be called, to store the attribute where it belongs (possibly by replacing an existing - * attribute with the same name). - * - * This is useful for example in the Attribute Color Ramp node, when the same attribute name is - * used as input and output. Typically the input is a float attribute, and the output is a color. - * Those two attributes cannot exist at the same time, due to a name collision. To handle this - * situation well, first the output colors have to be computed before the input floats are deleted. - * Therefore, the outputs have to be written to a temporary buffer that replaces the existing - * attribute once all computations are done. + * 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, ...). */ -class OutputAttributePtr { - private: - blender::bke::WriteAttributePtr attribute_; - - public: - OutputAttributePtr() = default; - OutputAttributePtr(blender::bke::WriteAttributePtr attribute); - OutputAttributePtr(GeometryComponent &component, - AttributeDomain domain, - std::string name, - CustomDataType data_type); +struct AttributeMetaData { + AttributeDomain domain; + CustomDataType data_type; +}; - ~OutputAttributePtr(); +/* Returns false when the iteration should be stopped. */ +using AttributeForeachCallback = blender::FunctionRef<bool(blender::StringRefNull attribute_name, + const AttributeMetaData &meta_data)>; - /* Returns false, when this wrapper is empty. */ - operator bool() const +/** + * Base class for the attribute intializer types described below. + */ +struct AttributeInit { + enum class Type { + Default, + VArray, + MoveArray, + }; + Type type; + AttributeInit(const Type type) : type(type) { - return static_cast<bool>(attribute_); } +}; - /* Get a reference to the underlying #WriteAttribute. */ - blender::bke::WriteAttribute &get() +/** + * 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) { - BLI_assert(attribute_); - return *attribute_; } +}; - blender::bke::WriteAttribute &operator*() - { - return *attribute_; - } +/** + * 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 { + const blender::fn::GVArray *varray; - blender::bke::WriteAttribute *operator->() + AttributeInitVArray(const blender::fn::GVArray *varray) + : AttributeInit(Type::VArray), varray(varray) { - return attribute_.get(); } - - void save(); - void apply_span_and_save(); }; /** - * 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, ...). + * 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 contigious + * 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 AttributeMetaData { - AttributeDomain domain; - CustomDataType data_type; -}; +struct AttributeInitMove : public AttributeInit { + void *data = nullptr; -/* Returns false when the iteration should be stopped. */ -using AttributeForeachCallback = blender::FunctionRef<bool(blender::StringRefNull attribute_name, - const AttributeMetaData &meta_data)>; + AttributeInitMove(void *data) : AttributeInit(Type::MoveArray), data(data) + { + } +}; /** * This is the base class for specialized geometry component types. @@ -156,26 +161,34 @@ class GeometryComponent { /* Return true when any attribute with this name exists, including built in attributes. */ bool attribute_exists(const blender::StringRef attribute_name) 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::StringRef attribute_name) const; + /* Returns true when the geometry component supports this attribute domain. */ bool attribute_domain_supported(const AttributeDomain domain) const; /* Can only be used with supported domain types. */ virtual int attribute_domain_size(const AttributeDomain domain) const; + bool attribute_is_builtin(const blender::StringRef attribute_name) const; + /* Get read-only access to the highest priority attribute with the given name. * Returns null if the attribute does not exist. */ - blender::bke::ReadAttributePtr attribute_try_get_for_read( + blender::bke::ReadAttributeLookup attribute_try_get_for_read( const blender::StringRef attribute_name) const; /* Get read and write access to the highest priority attribute with the given name. * Returns null if the attribute does not exist. */ - blender::bke::WriteAttributePtr attribute_try_get_for_write( + blender::bke::WriteAttributeLookup attribute_try_get_for_write( const blender::StringRef attribute_name); /* Get a read-only attribute for the domain based on the given attribute. This can be used to * interpolate from one domain to another. * Returns null if the interpolation is not implemented. */ - virtual blender::bke::ReadAttributePtr attribute_try_adapt_domain( - blender::bke::ReadAttributePtr attribute, const AttributeDomain new_domain) const; + virtual std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain( + std::unique_ptr<blender::fn::GVArray> varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const; /* Returns true when the attribute has been deleted. */ bool attribute_try_delete(const blender::StringRef attribute_name); @@ -183,82 +196,104 @@ class GeometryComponent { /* Returns true when the attribute has been created. */ bool attribute_try_create(const blender::StringRef attribute_name, const AttributeDomain domain, - const CustomDataType data_type); + const CustomDataType 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<std::string> attribute_names() const; bool attribute_foreach(const AttributeForeachCallback callback) const; virtual bool is_empty() const; - /* Get a read-only attribute for the given domain and data type. - * Returns null when it does not exist. */ - blender::bke::ReadAttributePtr attribute_try_get_for_read( + /* Get a virtual array to read the data of an attribute on the given domain and data type. + * Returns null when the attribute does not exist or cannot be converted to the requested domain + * and data type. */ + std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read( const blender::StringRef attribute_name, const AttributeDomain domain, const CustomDataType data_type) const; - /* Get a read-only attribute interpolated to the input domain, leaving the data type unchanged. - * Returns null when the attribute does not exist. */ - blender::bke::ReadAttributePtr attribute_try_get_for_read( + /* Get a virtual array to read the data of an attribute on the given domain. The data type is + * left unchanged. Returns null when the attribute does not exist or cannot be adapted to the + * requested domain. */ + std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read( const blender::StringRef attribute_name, const AttributeDomain domain) const; - /* Get a read-only attribute for the given domain and data type. - * Returns a constant attribute based on the default value if the attribute does not exist. - * Never returns null. */ - blender::bke::ReadAttributePtr attribute_get_for_read(const blender::StringRef attribute_name, - const AttributeDomain domain, - const CustomDataType data_type, - const void *default_value) const; + /* Get a virtual array to read data of an attribute with the given data type. The domain is + * left unchanged. Returns null when the attribute does not exist or cannot be converted to the + * requested data type. */ + blender::bke::ReadAttributeLookup attribute_try_get_for_read( + const blender::StringRef attribute_name, const CustomDataType data_type) const; - /* Get a typed read-only attribute for the given domain and type. */ - template<typename T> - blender::bke::TypedReadAttribute<T> attribute_get_for_read( + /* Get a virtual array to read the data of an attribute. If that is not possible, the returned + * virtual array will contain a default value. This never returns null. */ + std::unique_ptr<blender::fn::GVArray> attribute_get_for_read( const blender::StringRef attribute_name, const AttributeDomain domain, - const T &default_value) const + const CustomDataType data_type, + const void *default_value = nullptr) const; + + /* Should be used instead of the method above when the requested data type is known at compile + * time for better type safety. */ + template<typename T> + blender::fn::GVArray_Typed<T> attribute_get_for_read(const blender::StringRef attribute_name, + const AttributeDomain domain, + const T &default_value) const { const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>(); const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type); - return this->attribute_get_for_read(attribute_name, domain, type, &default_value); + std::unique_ptr varray = this->attribute_get_for_read( + attribute_name, domain, type, &default_value); + return blender::fn::GVArray_Typed<T>(std::move(varray)); } - /* Get a read-only dummy attribute that always returns the same value. */ - blender::bke::ReadAttributePtr attribute_get_constant_for_read(const AttributeDomain domain, - const CustomDataType data_type, - const void *value) const; + /** + * 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::StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type, + const void *default_value = nullptr); - /* Create a read-only dummy attribute that always returns the same value. - * The given value is converted to the correct type if necessary. */ - blender::bke::ReadAttributePtr attribute_get_constant_for_read_converted( + /* 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. Since values are not read + * from this attribute, no default value is necessary. */ + blender::bke::OutputAttribute attribute_try_get_for_output_only( + const blender::StringRef attribute_name, const AttributeDomain domain, - const CustomDataType in_data_type, - const CustomDataType out_data_type, - const void *value) const; + const CustomDataType data_type); - /* Get a read-only dummy attribute that always returns the same value. */ + /* Statically typed method corresponding to the equally named generic one. */ template<typename T> - blender::bke::TypedReadAttribute<T> attribute_get_constant_for_read(const AttributeDomain domain, - const T &value) const + blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output( + const blender::StringRef attribute_name, const AttributeDomain domain, const T default_value) { const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>(); - const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type); - return this->attribute_get_constant_for_read(domain, type, &value); + const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type); + return this->attribute_try_get_for_output(attribute_name, domain, data_type, &default_value); } - /** - * If an attribute with the given params exist, it is returned. - * If no attribute with the given name exists, create it and - * fill it with the default value if it is provided. - * If an attribute with the given name but different domain or type exists, a temporary attribute - * is created that has to be saved after the output has been computed. This avoids deleting - * another attribute, before a computation is finished. - * - * This might return no attribute when the attribute cannot exist on the component. - */ - OutputAttributePtr attribute_try_get_for_output(const blender::StringRef attribute_name, - const AttributeDomain domain, - const CustomDataType data_type, - const void *default_value = nullptr); + /* Statically typed method corresponding to the equally named generic one. */ + template<typename T> + blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output_only( + const blender::StringRef attribute_name, const AttributeDomain domain) + { + const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>(); + const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type); + return this->attribute_try_get_for_output_only(attribute_name, domain, data_type); + } private: virtual const blender::bke::ComponentAttributeProviders *get_attribute_providers() const; @@ -377,8 +412,10 @@ class MeshComponent : public GeometryComponent { Mesh *get_for_write(); int attribute_domain_size(const AttributeDomain domain) const final; - blender::bke::ReadAttributePtr attribute_try_adapt_domain( - blender::bke::ReadAttributePtr attribute, const AttributeDomain new_domain) const final; + std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain( + std::unique_ptr<blender::fn::GVArray> varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const final; bool is_empty() const final; diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh new file mode 100644 index 00000000000..f504650e349 --- /dev/null +++ b/source/blender/blenkernel/BKE_mesh_sample.hh @@ -0,0 +1,55 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bke + */ + +#include "FN_generic_virtual_array.hh" + +#include "BLI_float3.hh" + +#include "BKE_attribute.h" + +struct Mesh; + +namespace blender::bke::mesh_surface_sample { + +using fn::CPPType; +using fn::GMutableSpan; +using fn::GSpan; +using fn::GVArray; + +void sample_point_attribute(const Mesh &mesh, + Span<int> looptri_indices, + Span<float3> bary_coords, + const GVArray &data_in, + GMutableSpan data_out); + +void sample_corner_attribute(const Mesh &mesh, + Span<int> looptri_indices, + Span<float3> bary_coords, + const GVArray &data_in, + GMutableSpan data_out); + +void sample_face_attribute(const Mesh &mesh, + Span<int> looptri_indices, + const GVArray &data_in, + GMutableSpan data_out); + +} // namespace blender::bke::mesh_surface_sample diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index d6c4ad037e2..2a33c4819e3 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1413,6 +1413,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_ATTRIBUTE_MAP_RANGE 1040 #define GEO_NODE_ATTRIBUTE_CLAMP 1041 #define GEO_NODE_BOUNDING_BOX 1042 +#define GEO_NODE_SWITCH 1043 /** \} */ diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 59e2c74ead1..adf321da8f0 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -190,6 +190,7 @@ set(SRC intern/mesh_remap.c intern/mesh_remesh_voxel.c intern/mesh_runtime.c + intern/mesh_sample.cc intern/mesh_tangent.c intern/mesh_validate.c intern/mesh_validate.cc @@ -379,6 +380,7 @@ set(SRC BKE_mesh_remap.h BKE_mesh_remesh_voxel.h BKE_mesh_runtime.h + BKE_mesh_sample.hh BKE_mesh_tangent.h BKE_mesh_types.h BKE_mesh_wrapper.h diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index 1075a46e72b..bcfd34ab42f 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -137,7 +137,7 @@ static char *blender_version_decimal(const int version) { static char version_str[5]; BLI_assert(version < 1000); - BLI_snprintf(version_str, sizeof(version_str), "%d.%02d", version / 100, version % 100); + BLI_snprintf(version_str, sizeof(version_str), "%d.%d", version / 100, version % 100); return version_str; } diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index ac582fc30e7..5b2f588959f 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -44,196 +44,10 @@ using blender::float3; using blender::Set; using blender::StringRef; using blender::StringRefNull; -using blender::bke::ReadAttributePtr; -using blender::bke::WriteAttributePtr; using blender::fn::GMutableSpan; namespace blender::bke { -/* -------------------------------------------------------------------- */ -/** \name Attribute Accessor implementations - * \{ */ - -ReadAttribute::~ReadAttribute() -{ - if (array_is_temporary_ && array_buffer_ != nullptr) { - cpp_type_.destruct_n(array_buffer_, size_); - MEM_freeN(array_buffer_); - } -} - -fn::GSpan ReadAttribute::get_span() const -{ - if (size_ == 0) { - return fn::GSpan(cpp_type_); - } - if (array_buffer_ == nullptr) { - std::lock_guard lock{span_mutex_}; - if (array_buffer_ == nullptr) { - this->initialize_span(); - } - } - return fn::GSpan(cpp_type_, array_buffer_, size_); -} - -void ReadAttribute::initialize_span() const -{ - const int element_size = cpp_type_.size(); - array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__); - array_is_temporary_ = true; - for (const int i : IndexRange(size_)) { - this->get_internal(i, POINTER_OFFSET(array_buffer_, i * element_size)); - } -} - -WriteAttribute::~WriteAttribute() -{ - if (array_should_be_applied_) { - CLOG_ERROR(&LOG, "Forgot to call apply_span."); - } - if (array_is_temporary_ && array_buffer_ != nullptr) { - cpp_type_.destruct_n(array_buffer_, size_); - MEM_freeN(array_buffer_); - } -} - -/** - * Get a mutable span that can be modified. When all modifications to the attribute are done, - * #apply_span should be called. */ -fn::GMutableSpan WriteAttribute::get_span() -{ - if (size_ == 0) { - return fn::GMutableSpan(cpp_type_); - } - if (array_buffer_ == nullptr) { - this->initialize_span(false); - } - array_should_be_applied_ = true; - return fn::GMutableSpan(cpp_type_, array_buffer_, size_); -} - -fn::GMutableSpan WriteAttribute::get_span_for_write_only() -{ - if (size_ == 0) { - return fn::GMutableSpan(cpp_type_); - } - if (array_buffer_ == nullptr) { - this->initialize_span(true); - } - array_should_be_applied_ = true; - return fn::GMutableSpan(cpp_type_, array_buffer_, size_); -} - -void WriteAttribute::initialize_span(const bool write_only) -{ - const int element_size = cpp_type_.size(); - array_buffer_ = MEM_mallocN_aligned(element_size * size_, cpp_type_.alignment(), __func__); - array_is_temporary_ = true; - if (write_only) { - /* This does nothing for trivial types, but is necessary for general correctness. */ - cpp_type_.construct_default_n(array_buffer_, size_); - } - else { - for (const int i : IndexRange(size_)) { - this->get(i, POINTER_OFFSET(array_buffer_, i * element_size)); - } - } -} - -void WriteAttribute::apply_span() -{ - this->apply_span_if_necessary(); - array_should_be_applied_ = false; -} - -void WriteAttribute::apply_span_if_necessary() -{ - /* Only works when the span has been initialized beforehand. */ - BLI_assert(array_buffer_ != nullptr); - - const int element_size = cpp_type_.size(); - for (const int i : IndexRange(size_)) { - this->set_internal(i, POINTER_OFFSET(array_buffer_, i * element_size)); - } -} - -/* This is used by the #OutputAttributePtr class. */ -class TemporaryWriteAttribute final : public WriteAttribute { - public: - GMutableSpan data; - GeometryComponent &component; - std::string final_name; - - TemporaryWriteAttribute(AttributeDomain domain, - GMutableSpan data, - GeometryComponent &component, - std::string final_name) - : WriteAttribute(domain, data.type(), data.size()), - data(data), - component(component), - final_name(std::move(final_name)) - { - } - - ~TemporaryWriteAttribute() override - { - if (data.data() != nullptr) { - cpp_type_.destruct_n(data.data(), data.size()); - MEM_freeN(data.data()); - } - } - - void get_internal(const int64_t index, void *r_value) const override - { - data.type().copy_to_uninitialized(data[index], r_value); - } - - void set_internal(const int64_t index, const void *value) override - { - data.type().copy_to_initialized(value, data[index]); - } - - void initialize_span(const bool UNUSED(write_only)) override - { - array_buffer_ = data.data(); - array_is_temporary_ = false; - } - - void apply_span_if_necessary() override - { - /* Do nothing, because the span contains the attribute itself already. */ - } -}; - -class ConvertedReadAttribute final : public ReadAttribute { - private: - const CPPType &from_type_; - const CPPType &to_type_; - ReadAttributePtr base_attribute_; - void (*convert_)(const void *src, void *dst); - - public: - ConvertedReadAttribute(ReadAttributePtr base_attribute, const CPPType &to_type) - : ReadAttribute(base_attribute->domain(), to_type, base_attribute->size()), - from_type_(base_attribute->cpp_type()), - to_type_(to_type), - base_attribute_(std::move(base_attribute)) - { - const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions(); - convert_ = conversions.get_conversion_functions(base_attribute_->cpp_type(), to_type) - ->convert_single_to_uninitialized; - } - - void get_internal(const int64_t index, void *r_value) const override - { - BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer); - base_attribute_->get(index, buffer); - convert_(buffer, r_value); - } -}; - -/** \} */ - const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType type) { switch (type) { @@ -368,7 +182,27 @@ AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains) return highest_priority_domain; } -ReadAttributePtr BuiltinCustomDataLayerProvider::try_get_for_read( +void OutputAttribute::save() +{ + save_has_been_called_ = true; + if (optional_span_varray_.has_value()) { + 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"; + } + } +} + +GVArrayPtr BuiltinCustomDataLayerProvider::try_get_for_read( const GeometryComponent &component) const { const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); @@ -384,7 +218,7 @@ ReadAttributePtr BuiltinCustomDataLayerProvider::try_get_for_read( return as_read_attribute_(data, domain_size); } -WriteAttributePtr BuiltinCustomDataLayerProvider::try_get_for_write( +GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write( GeometryComponent &component) const { if (writable_ != Writable) { @@ -430,7 +264,43 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co return delete_success; } -bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component) const +static bool add_custom_data_layer_from_attribute_init(CustomData &custom_data, + const CustomDataType data_type, + const int domain_size, + const AttributeInit &initializer) +{ + switch (initializer.type) { + case AttributeInit::Type::Default: { + void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size); + return data != nullptr; + } + case AttributeInit::Type::VArray: { + void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size); + if (data == nullptr) { + return false; + } + const GVArray *varray = static_cast<const AttributeInitVArray &>(initializer).varray; + varray->materialize_to_uninitialized(IndexRange(varray->size()), data); + return true; + } + case AttributeInit::Type::MoveArray: { + void *source_data = static_cast<const AttributeInitMove &>(initializer).data; + void *data = CustomData_add_layer( + &custom_data, data_type, CD_ASSIGN, source_data, domain_size); + if (data == nullptr) { + MEM_freeN(source_data); + return false; + } + return true; + } + } + + BLI_assert_unreachable(); + return false; +} + +bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, + const AttributeInit &initializer) const { if (createable_ != Creatable) { return false; @@ -443,10 +313,10 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component) co /* Exists already. */ return false; } + const int domain_size = component.attribute_domain_size(domain_); - const void *data = CustomData_add_layer( - custom_data, stored_type_, CD_DEFAULT, nullptr, domain_size); - const bool success = data != nullptr; + const bool success = add_custom_data_layer_from_attribute_init( + *custom_data, stored_type_, domain_size, initializer); if (success) { custom_data_access_.update_custom_data_pointers(component); } @@ -463,7 +333,7 @@ bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component) return data != nullptr; } -ReadAttributePtr CustomDataAttributeProvider::try_get_for_read( +ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( const GeometryComponent &component, const StringRef attribute_name) const { const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); @@ -496,7 +366,7 @@ ReadAttributePtr CustomDataAttributeProvider::try_get_for_read( return {}; } -WriteAttributePtr CustomDataAttributeProvider::try_get_for_write( +WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( GeometryComponent &component, const StringRef attribute_name) const { CustomData *custom_data = custom_data_access_.get_custom_data(component); @@ -548,10 +418,52 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component, return false; } +static bool add_named_custom_data_layer_from_attribute_init(const StringRef attribute_name, + CustomData &custom_data, + const CustomDataType data_type, + const int domain_size, + const AttributeInit &initializer) +{ + char attribute_name_c[MAX_NAME]; + attribute_name.copy(attribute_name_c); + + switch (initializer.type) { + case AttributeInit::Type::Default: { + void *data = CustomData_add_layer_named( + &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c); + return data != nullptr; + } + case AttributeInit::Type::VArray: { + void *data = CustomData_add_layer_named( + &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c); + if (data == nullptr) { + return false; + } + const GVArray *varray = static_cast<const AttributeInitVArray &>(initializer).varray; + varray->materialize_to_uninitialized(IndexRange(varray->size()), data); + return true; + } + case AttributeInit::Type::MoveArray: { + void *source_data = static_cast<const AttributeInitMove &>(initializer).data; + void *data = CustomData_add_layer_named( + &custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_name_c); + if (data == nullptr) { + MEM_freeN(source_data); + return false; + } + return true; + } + } + + BLI_assert_unreachable(); + return false; +} + bool CustomDataAttributeProvider::try_create(GeometryComponent &component, const StringRef attribute_name, const AttributeDomain domain, - const CustomDataType data_type) const + const CustomDataType data_type, + const AttributeInit &initializer) const { if (domain_ != domain) { return false; @@ -569,10 +481,8 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component, } } const int domain_size = component.attribute_domain_size(domain_); - char attribute_name_c[MAX_NAME]; - attribute_name.copy(attribute_name_c); - CustomData_add_layer_named( - custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c); + add_named_custom_data_layer_from_attribute_init( + attribute_name, *custom_data, data_type, domain_size, initializer); return true; } @@ -595,7 +505,7 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com return true; } -ReadAttributePtr NamedLegacyCustomDataProvider::try_get_for_read( +ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( const GeometryComponent &component, const StringRef attribute_name) const { const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); @@ -606,14 +516,14 @@ ReadAttributePtr NamedLegacyCustomDataProvider::try_get_for_read( if (layer.type == stored_type_) { if (layer.name == attribute_name) { const int domain_size = component.attribute_domain_size(domain_); - return as_read_attribute_(layer.data, domain_size); + return {as_read_attribute_(layer.data, domain_size), domain_}; } } } return {}; } -WriteAttributePtr NamedLegacyCustomDataProvider::try_get_for_write( +WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write( GeometryComponent &component, const StringRef attribute_name) const { CustomData *custom_data = custom_data_access_.get_custom_data(component); @@ -630,7 +540,7 @@ WriteAttributePtr NamedLegacyCustomDataProvider::try_get_for_write( if (data_old != data_new) { custom_data_access_.update_custom_data_pointers(component); } - return as_write_attribute_(layer.data, domain_size); + return {as_write_attribute_(layer.data, domain_size), domain_}; } } } @@ -708,7 +618,17 @@ int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain return 0; } -ReadAttributePtr GeometryComponent::attribute_try_get_for_read( +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); +} + +blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( const StringRef attribute_name) const { using namespace blender::bke; @@ -719,11 +639,11 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read( const BuiltinAttributeProvider *builtin_provider = providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); if (builtin_provider != nullptr) { - return builtin_provider->try_get_for_read(*this); + return {builtin_provider->try_get_for_read(*this), builtin_provider->domain()}; } for (const DynamicAttributesProvider *dynamic_provider : providers->dynamic_attribute_providers()) { - ReadAttributePtr attribute = dynamic_provider->try_get_for_read(*this, attribute_name); + ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_name); if (attribute) { return attribute; } @@ -731,16 +651,19 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read( return {}; } -ReadAttributePtr GeometryComponent::attribute_try_adapt_domain( - ReadAttributePtr attribute, const AttributeDomain new_domain) const +std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_adapt_domain( + std::unique_ptr<blender::fn::GVArray> varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const { - if (attribute && attribute->domain() == new_domain) { - return attribute; + if (from_domain == to_domain) { + return varray; } return {}; } -WriteAttributePtr GeometryComponent::attribute_try_get_for_write(const StringRef attribute_name) +blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_write( + const StringRef attribute_name) { using namespace blender::bke; const ComponentAttributeProviders *providers = this->get_attribute_providers(); @@ -750,11 +673,11 @@ WriteAttributePtr GeometryComponent::attribute_try_get_for_write(const StringRef const BuiltinAttributeProvider *builtin_provider = providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); if (builtin_provider != nullptr) { - return builtin_provider->try_get_for_write(*this); + return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()}; } for (const DynamicAttributesProvider *dynamic_provider : providers->dynamic_attribute_providers()) { - WriteAttributePtr attribute = dynamic_provider->try_get_for_write(*this, attribute_name); + WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_name); if (attribute) { return attribute; } @@ -784,7 +707,8 @@ bool GeometryComponent::attribute_try_delete(const StringRef attribute_name) bool GeometryComponent::attribute_try_create(const StringRef attribute_name, const AttributeDomain domain, - const CustomDataType data_type) + const CustomDataType data_type, + const AttributeInit &initializer) { using namespace blender::bke; if (attribute_name.is_empty()) { @@ -803,17 +727,36 @@ bool GeometryComponent::attribute_try_create(const StringRef attribute_name, if (builtin_provider->data_type() != data_type) { return false; } - return builtin_provider->try_create(*this); + return builtin_provider->try_create(*this, initializer); } for (const DynamicAttributesProvider *dynamic_provider : providers->dynamic_attribute_providers()) { - if (dynamic_provider->try_create(*this, attribute_name, domain, data_type)) { + if (dynamic_provider->try_create(*this, attribute_name, 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<std::string> GeometryComponent::attribute_names() const { Set<std::string> attributes; @@ -867,264 +810,283 @@ bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const { - ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name); + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); if (attribute) { return true; } return false; } -static ReadAttributePtr try_adapt_data_type(ReadAttributePtr attribute, - const blender::fn::CPPType &to_type) +std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data( + const StringRef attribute_name) const { - const blender::fn::CPPType &from_type = attribute->cpp_type(); - if (from_type == to_type) { - return attribute; - } + std::optional<AttributeMetaData> result{std::nullopt}; + this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { + if (attribute_name == name) { + result = meta_data; + return false; + } + return true; + }); + return result; +} +static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type( + std::unique_ptr<blender::fn::GVArray> varray, const blender::fn::CPPType &to_type) +{ const blender::nodes::DataTypeConversions &conversions = blender::nodes::get_implicit_type_conversions(); - if (!conversions.is_convertible(from_type, to_type)) { - return {}; - } - - return std::make_unique<blender::bke::ConvertedReadAttribute>(std::move(attribute), to_type); + return conversions.try_convert(std::move(varray), to_type); } -ReadAttributePtr GeometryComponent::attribute_try_get_for_read( +std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_read( const StringRef attribute_name, const AttributeDomain domain, const CustomDataType data_type) const { - ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name); + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); if (!attribute) { return {}; } - if (domain != ATTR_DOMAIN_AUTO && attribute->domain() != domain) { - attribute = this->attribute_try_adapt_domain(std::move(attribute), domain); - if (!attribute) { + std::unique_ptr<blender::fn::GVArray> varray = std::move(attribute.varray); + if (domain != ATTR_DOMAIN_AUTO && attribute.domain != domain) { + varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain); + if (!varray) { return {}; } } const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); BLI_assert(cpp_type != nullptr); - if (attribute->cpp_type() != *cpp_type) { - attribute = try_adapt_data_type(std::move(attribute), *cpp_type); - if (!attribute) { + if (varray->type() != *cpp_type) { + varray = try_adapt_data_type(std::move(varray), *cpp_type); + if (!varray) { return {}; } } - return attribute; + return varray; } -ReadAttributePtr GeometryComponent::attribute_try_get_for_read(const StringRef attribute_name, - const AttributeDomain domain) const +std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_try_get_for_read( + const StringRef attribute_name, const AttributeDomain domain) const { if (!this->attribute_domain_supported(domain)) { return {}; } - ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name); + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); if (!attribute) { return {}; } - if (attribute->domain() != domain) { - attribute = this->attribute_try_adapt_domain(std::move(attribute), domain); - if (!attribute) { - return {}; - } + if (attribute.domain != domain) { + return this->attribute_try_adapt_domain(std::move(attribute.varray), attribute.domain, domain); } - return attribute; + return std::move(attribute.varray); } -ReadAttributePtr GeometryComponent::attribute_get_for_read(const StringRef attribute_name, - const AttributeDomain domain, - const CustomDataType data_type, - const void *default_value) const +blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( + const blender::StringRef attribute_name, const CustomDataType data_type) const { - ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name, domain, data_type); - if (attribute) { - return attribute; + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); + if (!attribute) { + return {}; } - return this->attribute_get_constant_for_read(domain, data_type, default_value); -} - -blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read( - const AttributeDomain domain, const CustomDataType data_type, const void *value) const -{ - BLI_assert(this->attribute_domain_supported(domain)); - const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); - BLI_assert(cpp_type != nullptr); - if (value == nullptr) { - value = cpp_type->default_value(); + const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type); + BLI_assert(type != nullptr); + if (attribute.varray->type() == *type) { + return attribute; } - const int domain_size = this->attribute_domain_size(domain); - return std::make_unique<blender::bke::ConstantReadAttribute>( - domain, domain_size, *cpp_type, value); + const blender::nodes::DataTypeConversions &conversions = + blender::nodes::get_implicit_type_conversions(); + return {conversions.try_convert(std::move(attribute.varray), *type), attribute.domain}; } -blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read_converted( +std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_get_for_read( + const StringRef attribute_name, const AttributeDomain domain, - const CustomDataType in_data_type, - const CustomDataType out_data_type, - const void *value) const + const CustomDataType data_type, + const void *default_value) const { - BLI_assert(this->attribute_domain_supported(domain)); - if (value == nullptr || in_data_type == out_data_type) { - return this->attribute_get_constant_for_read(domain, out_data_type, value); + std::unique_ptr<blender::bke::GVArray> varray = this->attribute_try_get_for_read( + attribute_name, domain, data_type); + if (varray) { + return varray; + } + const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type); + if (default_value == nullptr) { + default_value = type->default_value(); } - - const blender::fn::CPPType *in_cpp_type = blender::bke::custom_data_type_to_cpp_type( - in_data_type); - const blender::fn::CPPType *out_cpp_type = blender::bke::custom_data_type_to_cpp_type( - out_data_type); - BLI_assert(in_cpp_type != nullptr); - BLI_assert(out_cpp_type != nullptr); - - const blender::nodes::DataTypeConversions &conversions = - blender::nodes::get_implicit_type_conversions(); - BLI_assert(conversions.is_convertible(*in_cpp_type, *out_cpp_type)); - - void *out_value = alloca(out_cpp_type->size()); - conversions.convert_to_uninitialized(*in_cpp_type, *out_cpp_type, value, out_value); - const int domain_size = this->attribute_domain_size(domain); - blender::bke::ReadAttributePtr attribute = std::make_unique<blender::bke::ConstantReadAttribute>( - domain, domain_size, *out_cpp_type, out_value); - - out_cpp_type->destruct(out_value); - return attribute; + return std::make_unique<blender::fn::GVArray_For_SingleValue>(*type, domain_size, default_value); } -OutputAttributePtr GeometryComponent::attribute_try_get_for_output(const StringRef attribute_name, - const AttributeDomain domain, - const CustomDataType data_type, - const void *default_value) -{ - const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); - BLI_assert(cpp_type != nullptr); - - WriteAttributePtr attribute = this->attribute_try_get_for_write(attribute_name); +class GVMutableAttribute_For_OutputAttribute + : public blender::fn::GVMutableArray_For_GMutableSpan { + public: + GeometryComponent *component; + std::string final_name; - /* If the attribute doesn't exist, make a new one with the correct type. */ - if (!attribute) { - this->attribute_try_create(attribute_name, domain, data_type); - attribute = this->attribute_try_get_for_write(attribute_name); - if (attribute && default_value != nullptr) { - void *data = attribute->get_span_for_write_only().data(); - cpp_type->fill_initialized(default_value, data, attribute->size()); - attribute->apply_span(); - } - return OutputAttributePtr(std::move(attribute)); + GVMutableAttribute_For_OutputAttribute(GMutableSpan data, + GeometryComponent &component, + std::string final_name) + : blender::fn::GVMutableArray_For_GMutableSpan(data), + component(&component), + final_name(std::move(final_name)) + { } - /* If an existing attribute has a matching domain and type, just use that. */ - if (attribute->domain() == domain && attribute->cpp_type() == *cpp_type) { - return OutputAttributePtr(std::move(attribute)); + ~GVMutableAttribute_For_OutputAttribute() override + { + type_->destruct_n(data_, size_); + MEM_freeN(data_); } +}; - /* Otherwise create a temporary buffer to use before saving the new attribute. */ - return OutputAttributePtr(*this, domain, attribute_name, data_type); -} - -/* Construct from an attribute that already exists in the geometry component. */ -OutputAttributePtr::OutputAttributePtr(WriteAttributePtr attribute) - : attribute_(std::move(attribute)) +static void save_output_attribute(blender::bke::OutputAttribute &output_attribute) { -} + using namespace blender; + using namespace blender::fn; + using namespace blender::bke; -/* Construct a temporary attribute that has to replace an existing one later on. */ -OutputAttributePtr::OutputAttributePtr(GeometryComponent &component, - AttributeDomain domain, - std::string final_name, - CustomDataType data_type) -{ - const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type); - BLI_assert(cpp_type != nullptr); + GVMutableAttribute_For_OutputAttribute &varray = + dynamic_cast<GVMutableAttribute_For_OutputAttribute &>(output_attribute.varray()); - const int domain_size = component.attribute_domain_size(domain); - void *buffer = MEM_malloc_arrayN(domain_size, cpp_type->size(), __func__); - GMutableSpan new_span{*cpp_type, buffer, domain_size}; + GeometryComponent &component = *varray.component; + const StringRefNull name = varray.final_name; + const AttributeDomain domain = output_attribute.domain(); + const CustomDataType data_type = output_attribute.custom_data_type(); + const CPPType &cpp_type = output_attribute.cpp_type(); - /* Copy converted values from conflicting attribute, in case the value is read. - * TODO: An optimization could be to not do this, when the caller says that the attribute will - * only be written. */ - ReadAttributePtr src_attribute = component.attribute_get_for_read( - final_name, domain, data_type, nullptr); - for (const int i : blender::IndexRange(domain_size)) { - src_attribute->get(i, new_span[i]); + component.attribute_try_delete(name); + if (!component.attribute_try_create( + varray.final_name, domain, data_type, AttributeInitDefault())) { + CLOG_WARN(&LOG, + "Could not create the '%s' attribute with type '%s'.", + name.c_str(), + cpp_type.name().c_str()); + return; + } + WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(name); + 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); } - - attribute_ = std::make_unique<blender::bke::TemporaryWriteAttribute>( - domain, new_span, component, std::move(final_name)); } -/* Store the computed attribute. If it was stored from the beginning already, nothing is done. This - * might delete another attribute with the same name. */ -void OutputAttributePtr::save() +static blender::bke::OutputAttribute create_output_attribute( + GeometryComponent &component, + const blender::StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type, + const bool ignore_old_values, + const void *default_value) { - if (!attribute_) { - CLOG_WARN(&LOG, "Trying to save an attribute that does not exist anymore."); - return; + using namespace blender; + using namespace blender::fn; + using namespace blender::bke; + + if (attribute_name.is_empty()) { + return {}; } - blender::bke::TemporaryWriteAttribute *attribute = - dynamic_cast<blender::bke::TemporaryWriteAttribute *>(attribute_.get()); + const CPPType *cpp_type = custom_data_type_to_cpp_type(data_type); + BLI_assert(cpp_type != nullptr); + const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions(); - if (attribute == nullptr) { - /* The attribute is saved already. */ - attribute_.reset(); - return; + if (component.attribute_is_builtin(attribute_name)) { + WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); + if (!attribute) { + if (default_value) { + const int64_t domain_size = component.attribute_domain_size(domain); + const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value}; + component.attribute_try_create_builtin(attribute_name, + AttributeInitVArray(&default_varray)); + } + 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 {}; + } + GVMutableArrayPtr varray = std::move(attribute.varray); + if (varray->type() == *cpp_type) { + /* Builtin attribute matches exactly. */ + return OutputAttribute(std::move(varray), domain, {}, 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, {}, ignore_old_values); } - StringRefNull name = attribute->final_name; - const blender::fn::CPPType &cpp_type = attribute->cpp_type(); + const int domain_size = component.attribute_domain_size(domain); - /* Delete an existing attribute with the same name if necessary. */ - attribute->component.attribute_try_delete(name); + WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); + if (!attribute) { + if (default_value) { + const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value}; + component.attribute_try_create( + attribute_name, domain, data_type, AttributeInitVArray(&default_varray)); + } + else { + component.attribute_try_create(attribute_name, domain, data_type, AttributeInitDefault()); + } - if (!attribute->component.attribute_try_create( - name, attribute_->domain(), attribute_->custom_data_type())) { - /* Cannot create the target attribute for some reason. */ - CLOG_WARN(&LOG, - "Creating the '%s' attribute with type '%s' failed.", - name.c_str(), - cpp_type.name().c_str()); - attribute_.reset(); - return; + attribute = component.attribute_try_get_for_write(attribute_name); + 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, {}, ignore_old_values); } - WriteAttributePtr new_attribute = attribute->component.attribute_try_get_for_write(name); - - GMutableSpan temp_span = attribute->data; - GMutableSpan new_span = new_attribute->get_span_for_write_only(); - BLI_assert(temp_span.size() == new_span.size()); - - /* Currently we copy over the attribute. In the future we want to reuse the buffer. */ - cpp_type.move_to_initialized_n(temp_span.data(), new_span.data(), new_span.size()); - new_attribute->apply_span(); + /* 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_size, cpp_type->alignment(), __func__); + if (ignore_old_values) { + /* This does nothing for trivially constructible types, but is necessary for correctness. */ + cpp_type->construct_default_n(data, domain); + } + else { + /* Fill the temporary array with values from the existing attribute. */ + GVArrayPtr old_varray = component.attribute_get_for_read( + attribute_name, domain, data_type, default_value); + old_varray->materialize_to_uninitialized(IndexRange(domain_size), data); + } + GVMutableArrayPtr varray = std::make_unique<GVMutableAttribute_For_OutputAttribute>( + GMutableSpan{*cpp_type, data, domain_size}, component, attribute_name); - attribute_.reset(); + return OutputAttribute(std::move(varray), domain, save_output_attribute, true); } -OutputAttributePtr::~OutputAttributePtr() +blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output( + const StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type, + const void *default_value) { - if (attribute_) { - CLOG_ERROR(&LOG, "Forgot to call #save or #apply_span_and_save."); - } + return create_output_attribute(*this, attribute_name, domain, data_type, false, default_value); } -/* Utility function to call #apply_span and #save in the right order. */ -void OutputAttributePtr::apply_span_and_save() +blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output_only( + const blender::StringRef attribute_name, + const AttributeDomain domain, + const CustomDataType data_type) { - BLI_assert(attribute_); - attribute_->apply_span(); - this->save(); + return create_output_attribute(*this, attribute_name, domain, data_type, true, nullptr); } - -/** \} */ diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index 806d10e9e89..b3a795faa30 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -24,166 +24,8 @@ namespace blender::bke { -class ConstantReadAttribute final : public ReadAttribute { - private: - void *value_; - - public: - ConstantReadAttribute(AttributeDomain domain, - const int64_t size, - const CPPType &type, - const void *value) - : ReadAttribute(domain, type, size) - { - value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__); - type.copy_to_uninitialized(value, value_); - } - - ~ConstantReadAttribute() override - { - this->cpp_type_.destruct(value_); - MEM_freeN(value_); - } - - void get_internal(const int64_t UNUSED(index), void *r_value) const override - { - this->cpp_type_.copy_to_uninitialized(value_, r_value); - } - - void initialize_span() const override - { - const int element_size = cpp_type_.size(); - array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__); - array_is_temporary_ = true; - cpp_type_.fill_uninitialized(value_, array_buffer_, size_); - } -}; - -template<typename T> class ArrayReadAttribute final : public ReadAttribute { - private: - Span<T> data_; - - public: - ArrayReadAttribute(AttributeDomain domain, Span<T> data) - : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(data) - { - } - - void get_internal(const int64_t index, void *r_value) const override - { - new (r_value) T(data_[index]); - } - - void initialize_span() const override - { - /* The data will not be modified, so this const_cast is fine. */ - array_buffer_ = const_cast<T *>(data_.data()); - array_is_temporary_ = false; - } -}; - -template<typename T> class OwnedArrayReadAttribute final : public ReadAttribute { - private: - Array<T> data_; - - public: - OwnedArrayReadAttribute(AttributeDomain domain, Array<T> data) - : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(std::move(data)) - { - } - - void get_internal(const int64_t index, void *r_value) const override - { - new (r_value) T(data_[index]); - } - - void initialize_span() const override - { - /* The data will not be modified, so this const_cast is fine. */ - array_buffer_ = const_cast<T *>(data_.data()); - array_is_temporary_ = false; - } -}; - -template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> -class DerivedArrayReadAttribute final : public ReadAttribute { - private: - Span<StructT> data_; - - public: - DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data) - : ReadAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data) - { - } - - void get_internal(const int64_t index, void *r_value) const override - { - const StructT &struct_value = data_[index]; - const ElemT value = GetFunc(struct_value); - new (r_value) ElemT(value); - } -}; - -template<typename T> class ArrayWriteAttribute final : public WriteAttribute { - private: - MutableSpan<T> data_; - - public: - ArrayWriteAttribute(AttributeDomain domain, MutableSpan<T> data) - : WriteAttribute(domain, CPPType::get<T>(), data.size()), data_(data) - { - } - - void get_internal(const int64_t index, void *r_value) const override - { - new (r_value) T(data_[index]); - } - - void set_internal(const int64_t index, const void *value) override - { - data_[index] = *reinterpret_cast<const T *>(value); - } - - void initialize_span(const bool UNUSED(write_only)) override - { - array_buffer_ = data_.data(); - array_is_temporary_ = false; - } - - void apply_span_if_necessary() override - { - /* Do nothing, because the span contains the attribute itself already. */ - } -}; - -template<typename StructT, - typename ElemT, - ElemT (*GetFunc)(const StructT &), - void (*SetFunc)(StructT &, const ElemT &)> -class DerivedArrayWriteAttribute final : public WriteAttribute { - private: - MutableSpan<StructT> data_; - - public: - DerivedArrayWriteAttribute(AttributeDomain domain, MutableSpan<StructT> data) - : WriteAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data) - { - } - - void get_internal(const int64_t index, void *r_value) const override - { - const StructT &struct_value = data_[index]; - const ElemT value = GetFunc(struct_value); - new (r_value) ElemT(value); - } - - void set_internal(const int64_t index, const void *value) override - { - StructT &struct_value = data_[index]; - const ElemT &typed_value = *reinterpret_cast<const ElemT *>(value); - SetFunc(struct_value, typed_value); - } -}; +using fn::GVArrayPtr; +using fn::GVMutableArrayPtr; /** * Utility to group together multiple functions that are used to access custom data on geometry @@ -244,10 +86,11 @@ class BuiltinAttributeProvider { { } - virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component) const = 0; - virtual WriteAttributePtr try_get_for_write(GeometryComponent &component) const = 0; + virtual GVArrayPtr try_get_for_read(const GeometryComponent &component) const = 0; + virtual GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const = 0; virtual bool try_delete(GeometryComponent &component) const = 0; - virtual bool try_create(GeometryComponent &UNUSED(component)) const = 0; + virtual bool try_create(GeometryComponent &UNUSED(component), + const AttributeInit &UNUSED(initializer)) const = 0; virtual bool exists(const GeometryComponent &component) const = 0; StringRefNull name() const @@ -272,15 +115,16 @@ class BuiltinAttributeProvider { */ class DynamicAttributesProvider { public: - virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const = 0; - virtual WriteAttributePtr try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const = 0; + virtual ReadAttributeLookup try_get_for_read(const GeometryComponent &component, + const StringRef attribute_name) const = 0; + virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component, + const StringRef attribute_name) const = 0; virtual bool try_delete(GeometryComponent &component, const StringRef attribute_name) const = 0; virtual bool try_create(GeometryComponent &UNUSED(component), const StringRef UNUSED(attribute_name), const AttributeDomain UNUSED(domain), - const CustomDataType UNUSED(data_type)) const + const CustomDataType UNUSED(data_type), + const AttributeInit &UNUSED(initializer)) const { /* Some providers should not create new attributes. */ return false; @@ -309,18 +153,19 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { { } - ReadAttributePtr try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final; + ReadAttributeLookup try_get_for_read(const GeometryComponent &component, + const StringRef attribute_name) const final; - WriteAttributePtr try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const final; + WriteAttributeLookup try_get_for_write(GeometryComponent &component, + const StringRef attribute_name) const final; bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final; bool try_create(GeometryComponent &component, const StringRef attribute_name, const AttributeDomain domain, - const CustomDataType data_type) const final; + const CustomDataType data_type, + const AttributeInit &initializer) const final; bool foreach_attribute(const GeometryComponent &component, const AttributeForeachCallback callback) const final; @@ -332,18 +177,21 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { private: template<typename T> - ReadAttributePtr layer_to_read_attribute(const CustomDataLayer &layer, - const int domain_size) const + ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer, + const int domain_size) const { - return std::make_unique<ArrayReadAttribute<T>>( - domain_, Span(static_cast<const T *>(layer.data), domain_size)); + return {std::make_unique<fn::GVArray_For_Span<T>>( + Span(static_cast<const T *>(layer.data), domain_size)), + domain_}; } template<typename T> - WriteAttributePtr layer_to_write_attribute(CustomDataLayer &layer, const int domain_size) const + WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer, + const int domain_size) const { - return std::make_unique<ArrayWriteAttribute<T>>( - domain_, MutableSpan(static_cast<T *>(layer.data), domain_size)); + return {std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>( + MutableSpan(static_cast<T *>(layer.data), domain_size)), + domain_}; } bool type_is_supported(CustomDataType data_type) const @@ -357,8 +205,8 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { */ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { private: - using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size); - using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size); + using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size); + using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size); const AttributeDomain domain_; const CustomDataType attribute_type_; const CustomDataType stored_type_; @@ -382,10 +230,10 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { { } - ReadAttributePtr try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final; - WriteAttributePtr try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const final; + ReadAttributeLookup try_get_for_read(const GeometryComponent &component, + const StringRef attribute_name) const final; + WriteAttributeLookup try_get_for_write(GeometryComponent &component, + const StringRef attribute_name) const final; bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final; bool foreach_attribute(const GeometryComponent &component, const AttributeForeachCallback callback) const final; @@ -398,8 +246,8 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { * the #MVert struct, but is exposed as float3 attribute. */ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { - using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size); - using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size); + using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size); + using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size); using UpdateOnRead = void (*)(const GeometryComponent &component); using UpdateOnWrite = void (*)(GeometryComponent &component); const CustomDataType stored_type_; @@ -430,10 +278,10 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { { } - ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final; - WriteAttributePtr try_get_for_write(GeometryComponent &component) const final; + GVArrayPtr try_get_for_read(const GeometryComponent &component) const final; + GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final; bool try_delete(GeometryComponent &component) const final; - bool try_create(GeometryComponent &component) const final; + bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final; bool exists(const GeometryComponent &component) const final; }; diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index e8879cdda8f..e84b485c466 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -132,7 +132,7 @@ static void blender_version_init(void) BLI_snprintf(blender_version_string, ARRAY_SIZE(blender_version_string), - "%d.%02d.%d%s", + "%d.%01d.%d%s", BLENDER_VERSION / 100, BLENDER_VERSION % 100, BLENDER_VERSION_PATCH, diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index cbf7a4483c0..81830f5bb61 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -80,7 +80,17 @@ struct bContext { struct ARegion *menu; struct wmGizmoGroup *gizmo_group; struct bContextStore *store; - const char *operator_poll_msg; /* reason for poll failing */ + + /* Operator poll. */ + /** + * Store the reason the poll function fails (static string, not allocated). + * For more advanced formatting use `operator_poll_msg_dyn_params`. + */ + const char *operator_poll_msg; + /** + * Store values to dynamically to create the string (called when a tool-tip is shown). + */ + struct bContextPollMsgDyn_Params operator_poll_msg_dyn_params; } wm; /* data context */ @@ -113,11 +123,16 @@ bContext *CTX_copy(const bContext *C) { bContext *newC = MEM_dupallocN((void *)C); + memset(&newC->wm.operator_poll_msg_dyn_params, 0, sizeof(newC->wm.operator_poll_msg_dyn_params)); + return newC; } void CTX_free(bContext *C) { + /* This may contain a dynamically allocated message, free. */ + CTX_wm_operator_poll_msg_clear(C); + MEM_freeN(C); } @@ -1003,13 +1018,45 @@ void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup) C->wm.gizmo_group = gzgroup; } +void CTX_wm_operator_poll_msg_clear(bContext *C) +{ + struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params; + if (params->free_fn != NULL) { + params->free_fn(C, params->user_data); + } + params->get_fn = NULL; + params->free_fn = NULL; + params->user_data = NULL; + + C->wm.operator_poll_msg = NULL; +} void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg) { + CTX_wm_operator_poll_msg_clear(C); + C->wm.operator_poll_msg = msg; } -const char *CTX_wm_operator_poll_msg_get(bContext *C) +void CTX_wm_operator_poll_msg_set_dynamic(bContext *C, + const struct bContextPollMsgDyn_Params *params) { + CTX_wm_operator_poll_msg_clear(C); + + C->wm.operator_poll_msg_dyn_params = *params; +} + +const char *CTX_wm_operator_poll_msg_get(bContext *C, bool *r_free) +{ + struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params; + if (params->get_fn != NULL) { + char *msg = params->get_fn(C, params->user_data); + if (msg != NULL) { + *r_free = true; + } + return msg; + } + + *r_free = false; return IFACE_(C->wm.operator_poll_msg); } diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 5697fb2ccde..2ecd0e6bd85 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -32,7 +32,7 @@ /* Can't include BKE_object_deform.h right now, due to an enum forward declaration. */ extern "C" MDeformVert *BKE_object_defgroup_data_create(ID *id); -using blender::bke::ReadAttributePtr; +using blender::fn::GVArray; /* -------------------------------------------------------------------- */ /** \name Geometry Component Implementation @@ -201,14 +201,14 @@ namespace blender::bke { template<typename T> static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, - const TypedReadAttribute<T> &attribute, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totvert); attribute_math::DefaultMixer<T> mixer(r_values); for (const int loop_index : IndexRange(mesh.totloop)) { - const T value = attribute[loop_index]; + const T value = old_values[loop_index]; const MLoop &loop = mesh.mloop[loop_index]; const int point_index = loop.v; mixer.mix_in(point_index, value); @@ -216,43 +216,40 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { /* We compute all interpolated values at once, because for this interpolation, one has to * iterate over all loops anyway. */ Array<T> values(mesh.totvert); - adapt_mesh_domain_corner_to_point_impl<T>(mesh, *attribute, values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_corner_to_point_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh, - const TypedReadAttribute<T> &attribute, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totloop); for (const int loop_index : IndexRange(mesh.totloop)) { const int vertex_index = mesh.mloop[loop_index].v; - r_values[loop_index] = attribute[vertex_index]; + r_values[loop_index] = old_values[vertex_index]; } } -static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); /* It is not strictly necessary to compute the value for all corners here. Instead one could @@ -260,11 +257,10 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, * when an algorithm only accesses very few of the corner values. However, for the algorithms * we currently have, precomputing the array is fine. Also, it is easier to implement. */ Array<T> values(mesh.totloop); - adapt_mesh_domain_point_to_corner_impl<T>(mesh, *attribute, values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_CORNER, - std::move(values)); + adapt_mesh_domain_point_to_corner_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); }); - return new_attribute; + return new_varray; } /** @@ -274,7 +270,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, */ template<typename T> static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh, - Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totpoly); @@ -291,26 +287,24 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totpoly); - adapt_mesh_domain_corner_to_face_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_corner_to_face_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, - Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totedge); @@ -332,26 +326,24 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totedge); - adapt_mesh_domain_corner_to_edge_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_corner_to_edge_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, - Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totvert); @@ -370,26 +362,24 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_face_to_point(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totvert); - adapt_mesh_domain_face_to_point_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_face_to_point_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totloop); @@ -401,26 +391,24 @@ void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh, } } -static ReadAttributePtr adapt_mesh_domain_face_to_corner(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_face_to_corner(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totloop); - adapt_mesh_domain_face_to_corner_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_face_to_corner_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totedge); @@ -437,21 +425,19 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totedge); - adapt_mesh_domain_face_to_edge_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_face_to_edge_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } /** @@ -461,7 +447,7 @@ static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, */ template<typename T> static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totpoly); @@ -478,21 +464,19 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_point_to_face(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totpoly); - adapt_mesh_domain_point_to_face_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_point_to_face_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } /** @@ -502,7 +486,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh, */ template<typename T> static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totedge); @@ -517,26 +501,24 @@ static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totedge); - adapt_mesh_domain_point_to_edge_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_point_to_edge_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totloop); @@ -558,26 +540,24 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totloop); - adapt_mesh_domain_edge_to_corner_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_edge_to_corner_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } template<typename T> static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totvert); @@ -593,21 +573,19 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totvert); - adapt_mesh_domain_edge_to_point_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_edge_to_point_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } /** @@ -617,7 +595,7 @@ static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, */ template<typename T> static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh, - const Span<T> old_values, + const VArray<T> &old_values, MutableSpan<T> r_values) { BLI_assert(r_values.size() == mesh.totpoly); @@ -634,87 +612,86 @@ static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh, mixer.finalize(); } -static ReadAttributePtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, - ReadAttributePtr attribute) +static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr varray) { - ReadAttributePtr new_attribute; - const CustomDataType data_type = attribute->custom_data_type(); + GVArrayPtr new_varray; + const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type()); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { Array<T> values(mesh.totpoly); - adapt_mesh_domain_edge_to_face_impl<T>(mesh, attribute->get_span<T>(), values); - new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT, - std::move(values)); + adapt_mesh_domain_edge_to_face_impl<T>(mesh, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); } }); - return new_attribute; + return new_varray; } } // namespace blender::bke -ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attribute, - const AttributeDomain new_domain) const +blender::fn::GVArrayPtr MeshComponent::attribute_try_adapt_domain( + blender::fn::GVArrayPtr varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const { - if (!attribute) { + if (!varray) { return {}; } - if (attribute->size() == 0) { + if (varray->size() == 0) { return {}; } - const AttributeDomain old_domain = attribute->domain(); - if (old_domain == new_domain) { - return attribute; + if (from_domain == to_domain) { + return varray; } - switch (old_domain) { + switch (from_domain) { case ATTR_DOMAIN_CORNER: { - switch (new_domain) { + switch (to_domain) { case ATTR_DOMAIN_POINT: - return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(varray)); case ATTR_DOMAIN_FACE: - return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(varray)); case ATTR_DOMAIN_EDGE: - return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(varray)); default: break; } break; } case ATTR_DOMAIN_POINT: { - switch (new_domain) { + switch (to_domain) { case ATTR_DOMAIN_CORNER: - return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(varray)); case ATTR_DOMAIN_FACE: - return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(varray)); case ATTR_DOMAIN_EDGE: - return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(varray)); default: break; } break; } case ATTR_DOMAIN_FACE: { - switch (new_domain) { + switch (to_domain) { case ATTR_DOMAIN_POINT: - return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(varray)); case ATTR_DOMAIN_CORNER: - return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(varray)); case ATTR_DOMAIN_EDGE: - return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(varray)); default: break; } break; } case ATTR_DOMAIN_EDGE: { - switch (new_domain) { + switch (to_domain) { case ATTR_DOMAIN_CORNER: - return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(varray)); case ATTR_DOMAIN_POINT: - return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(varray)); case ATTR_DOMAIN_FACE: - return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(attribute)); + return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(varray)); default: break; } @@ -743,25 +720,21 @@ static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &com namespace blender::bke { -template<typename StructT, - typename ElemT, - ElemT (*GetFunc)(const StructT &), - AttributeDomain Domain> -static ReadAttributePtr make_derived_read_attribute(const void *data, const int domain_size) +template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> +static GVArrayPtr make_derived_read_attribute(const void *data, const int domain_size) { - return std::make_unique<DerivedArrayReadAttribute<StructT, ElemT, GetFunc>>( - Domain, Span<StructT>((const StructT *)data, domain_size)); + return std::make_unique<fn::GVArray_For_DerivedSpan<StructT, ElemT, GetFunc>>( + Span<StructT>((const StructT *)data, domain_size)); } template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &), - void (*SetFunc)(StructT &, const ElemT &), - AttributeDomain Domain> -static WriteAttributePtr make_derived_write_attribute(void *data, const int domain_size) + void (*SetFunc)(StructT &, ElemT)> +static GVMutableArrayPtr make_derived_write_attribute(void *data, const int domain_size) { - return std::make_unique<DerivedArrayWriteAttribute<StructT, ElemT, GetFunc, SetFunc>>( - Domain, MutableSpan<StructT>((StructT *)data, domain_size)); + return std::make_unique<fn::GVMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>>( + MutableSpan<StructT>((StructT *)data, domain_size)); } static float3 get_vertex_position(const MVert &vert) @@ -769,7 +742,7 @@ static float3 get_vertex_position(const MVert &vert) return float3(vert.co); } -static void set_vertex_position(MVert &vert, const float3 &position) +static void set_vertex_position(MVert &vert, float3 position) { copy_v3_v3(vert.co, position); } @@ -787,7 +760,7 @@ static int get_material_index(const MPoly &mpoly) return static_cast<int>(mpoly.mat_nr); } -static void set_material_index(MPoly &mpoly, const int &index) +static void set_material_index(MPoly &mpoly, int index) { mpoly.mat_nr = static_cast<short>(std::clamp(index, 0, SHRT_MAX)); } @@ -797,7 +770,7 @@ static bool get_shade_smooth(const MPoly &mpoly) return mpoly.flag & ME_SMOOTH; } -static void set_shade_smooth(MPoly &mpoly, const bool &value) +static void set_shade_smooth(MPoly &mpoly, bool value) { SET_FLAG_FROM_TEST(mpoly.flag, value, ME_SMOOTH); } @@ -807,7 +780,7 @@ static float2 get_loop_uv(const MLoopUV &uv) return float2(uv.uv); } -static void set_loop_uv(MLoopUV &uv, const float2 &co) +static void set_loop_uv(MLoopUV &uv, float2 co) { copy_v2_v2(uv.uv, co); } @@ -821,7 +794,7 @@ static Color4f get_loop_color(const MLoopCol &col) return linear_color; } -static void set_loop_color(MLoopCol &col, const Color4f &linear_color) +static void set_loop_color(MLoopCol &col, Color4f linear_color) { linearrgb_to_srgb_uchar4(&col.r, linear_color); } @@ -831,71 +804,62 @@ static float get_crease(const MEdge &edge) return edge.crease / 255.0f; } -static void set_crease(MEdge &edge, const float &value) +static void set_crease(MEdge &edge, float value) { edge.crease = round_fl_to_uchar_clamp(value * 255.0f); } -class VertexWeightWriteAttribute final : public WriteAttribute { +class VMutableArray_For_VertexWeights final : public VMutableArray<float> { private: MDeformVert *dverts_; const int dvert_index_; public: - VertexWeightWriteAttribute(MDeformVert *dverts, const int totvert, const int dvert_index) - : WriteAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert), - dverts_(dverts), - dvert_index_(dvert_index) + VMutableArray_For_VertexWeights(MDeformVert *dverts, const int totvert, const int dvert_index) + : VMutableArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index) { } - void get_internal(const int64_t index, void *r_value) const override + float get_impl(const int64_t index) const override { - get_internal(dverts_, dvert_index_, index, r_value); + return get_internal(dverts_, dvert_index_, index); } - void set_internal(const int64_t index, const void *value) override + void set_impl(const int64_t index, const float value) override { MDeformWeight *weight = BKE_defvert_ensure_index(&dverts_[index], dvert_index_); - weight->weight = *reinterpret_cast<const float *>(value); + weight->weight = value; } - static void get_internal(const MDeformVert *dverts, - const int dvert_index, - const int64_t index, - void *r_value) + static float get_internal(const MDeformVert *dverts, const int dvert_index, const int64_t index) { if (dverts == nullptr) { - *(float *)r_value = 0.0f; - return; + return 0.0f; } const MDeformVert &dvert = dverts[index]; for (const MDeformWeight &weight : Span(dvert.dw, dvert.totweight)) { if (weight.def_nr == dvert_index) { - *(float *)r_value = weight.weight; - return; + return weight.weight; } } - *(float *)r_value = 0.0f; + return 0.0f; } }; -class VertexWeightReadAttribute final : public ReadAttribute { +class VArray_For_VertexWeights final : public VArray<float> { private: const MDeformVert *dverts_; const int dvert_index_; public: - VertexWeightReadAttribute(const MDeformVert *dverts, const int totvert, const int dvert_index) - : ReadAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert), - dverts_(dverts), - dvert_index_(dvert_index) + VArray_For_VertexWeights(const MDeformVert *dverts, const int totvert, const int dvert_index) + : VArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index) { } - void get_internal(const int64_t index, void *r_value) const override + float get_impl(const int64_t index) const override { - VertexWeightWriteAttribute::get_internal(dverts_, dvert_index_, index, r_value); + return VMutableArray_For_VertexWeights::get_internal(dverts_, dvert_index_, index); } }; @@ -904,8 +868,8 @@ class VertexWeightReadAttribute final : public ReadAttribute { */ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { public: - ReadAttributePtr try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final + ReadAttributeLookup try_get_for_read(const GeometryComponent &component, + const StringRef attribute_name) const final { BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); @@ -917,15 +881,17 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { } if (mesh == nullptr || mesh->dvert == nullptr) { static const float default_value = 0.0f; - return std::make_unique<ConstantReadAttribute>( - ATTR_DOMAIN_POINT, mesh->totvert, CPPType::get<float>(), &default_value); + return {std::make_unique<fn::GVArray_For_SingleValueRef>( + CPPType::get<float>(), mesh->totvert, &default_value), + ATTR_DOMAIN_POINT}; } - return std::make_unique<VertexWeightReadAttribute>( - mesh->dvert, mesh->totvert, vertex_group_index); + return {std::make_unique<fn::GVArray_For_EmbeddedVArray<float, VArray_For_VertexWeights>>( + mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index), + ATTR_DOMAIN_POINT}; } - WriteAttributePtr try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const final + WriteAttributeLookup try_get_for_write(GeometryComponent &component, + const StringRef attribute_name) const final { BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); MeshComponent &mesh_component = static_cast<MeshComponent &>(component); @@ -946,8 +912,11 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { mesh->dvert = (MDeformVert *)CustomData_duplicate_referenced_layer( &mesh->vdata, CD_MDEFORMVERT, mesh->totvert); } - return std::make_unique<blender::bke::VertexWeightWriteAttribute>( - mesh->dvert, mesh->totvert, vertex_group_index); + return { + std::make_unique< + fn::GVMutableArray_For_EmbeddedVMutableArray<float, VMutableArray_For_VertexWeights>>( + mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index), + ATTR_DOMAIN_POINT}; } bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final @@ -1009,7 +978,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { { } - ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final + GVArrayPtr try_get_for_read(const GeometryComponent &component) const final { const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); const Mesh *mesh = mesh_component.get_for_read(); @@ -1022,8 +991,8 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { CustomData_has_layer(&mesh->pdata, CD_NORMAL)) { const void *data = CustomData_get_layer(&mesh->pdata, CD_NORMAL); - return std::make_unique<ArrayReadAttribute<float3>>( - ATTR_DOMAIN_FACE, Span<float3>((const float3 *)data, mesh->totpoly)); + return std::make_unique<fn::GVArray_For_Span<float3>>( + Span<float3>((const float3 *)data, mesh->totpoly)); } Array<float3> normals(mesh->totpoly); @@ -1032,10 +1001,10 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { BKE_mesh_calc_poly_normal(poly, &mesh->mloop[poly->loopstart], mesh->mvert, normals[i]); } - return std::make_unique<OwnedArrayReadAttribute<float3>>(ATTR_DOMAIN_FACE, std::move(normals)); + return std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals)); } - WriteAttributePtr try_get_for_write(GeometryComponent &UNUSED(component)) const final + GVMutableArrayPtr try_get_for_write(GeometryComponent &UNUSED(component)) const final { return {}; } @@ -1045,7 +1014,8 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { return false; } - bool try_create(GeometryComponent &UNUSED(component)) const final + bool try_create(GeometryComponent &UNUSED(component), + const AttributeInit &UNUSED(initializer)) const final { return false; } @@ -1105,12 +1075,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::NonDeletable, point_access, - make_derived_read_attribute<MVert, float3, get_vertex_position, ATTR_DOMAIN_POINT>, - make_derived_write_attribute<MVert, - float3, - get_vertex_position, - set_vertex_position, - ATTR_DOMAIN_POINT>, + make_derived_read_attribute<MVert, float3, get_vertex_position>, + make_derived_write_attribute<MVert, float3, get_vertex_position, set_vertex_position>, tag_normals_dirty_when_writing_position); static NormalAttributeProvider normal; @@ -1124,12 +1090,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::NonDeletable, face_access, - make_derived_read_attribute<MPoly, int, get_material_index, ATTR_DOMAIN_FACE>, - make_derived_write_attribute<MPoly, - int, - get_material_index, - set_material_index, - ATTR_DOMAIN_FACE>, + make_derived_read_attribute<MPoly, int, get_material_index>, + make_derived_write_attribute<MPoly, int, get_material_index, set_material_index>, nullptr); static BuiltinCustomDataLayerProvider shade_smooth( @@ -1141,12 +1103,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::NonDeletable, face_access, - make_derived_read_attribute<MPoly, bool, get_shade_smooth, ATTR_DOMAIN_FACE>, - make_derived_write_attribute<MPoly, - bool, - get_shade_smooth, - set_shade_smooth, - ATTR_DOMAIN_FACE>, + make_derived_read_attribute<MPoly, bool, get_shade_smooth>, + make_derived_write_attribute<MPoly, bool, get_shade_smooth, set_shade_smooth>, nullptr); static BuiltinCustomDataLayerProvider crease( @@ -1158,8 +1116,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() BuiltinAttributeProvider::Writable, BuiltinAttributeProvider::NonDeletable, edge_access, - make_derived_read_attribute<MEdge, float, get_crease, ATTR_DOMAIN_EDGE>, - make_derived_write_attribute<MEdge, float, get_crease, set_crease, ATTR_DOMAIN_EDGE>, + make_derived_read_attribute<MEdge, float, get_crease>, + make_derived_write_attribute<MEdge, float, get_crease, set_crease>, nullptr); static NamedLegacyCustomDataProvider uvs( @@ -1167,20 +1125,16 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() CD_PROP_FLOAT2, CD_MLOOPUV, corner_access, - make_derived_read_attribute<MLoopUV, float2, get_loop_uv, ATTR_DOMAIN_CORNER>, - make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv, ATTR_DOMAIN_CORNER>); + make_derived_read_attribute<MLoopUV, float2, get_loop_uv>, + make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv>); static NamedLegacyCustomDataProvider vertex_colors( ATTR_DOMAIN_CORNER, CD_PROP_COLOR, CD_MLOOPCOL, corner_access, - make_derived_read_attribute<MLoopCol, Color4f, get_loop_color, ATTR_DOMAIN_CORNER>, - make_derived_write_attribute<MLoopCol, - Color4f, - get_loop_color, - set_loop_color, - ATTR_DOMAIN_CORNER>); + make_derived_read_attribute<MLoopCol, Color4f, get_loop_color>, + make_derived_write_attribute<MLoopCol, Color4f, get_loop_color, set_loop_color>); static VertexGroupsAttributeProvider vertex_groups; static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access); diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc index 135de14b4f7..6c4af7a6d23 100644 --- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc +++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc @@ -140,16 +140,17 @@ int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) con namespace blender::bke { -template<typename T, AttributeDomain Domain> -static ReadAttributePtr make_array_read_attribute(const void *data, const int domain_size) +template<typename T> +static GVArrayPtr make_array_read_attribute(const void *data, const int domain_size) { - return std::make_unique<ArrayReadAttribute<T>>(Domain, Span<T>((const T *)data, domain_size)); + return std::make_unique<fn::GVArray_For_Span<T>>(Span<T>((const T *)data, domain_size)); } -template<typename T, AttributeDomain Domain> -static WriteAttributePtr make_array_write_attribute(void *data, const int domain_size) +template<typename T> +static GVMutableArrayPtr make_array_write_attribute(void *data, const int domain_size) { - return std::make_unique<ArrayWriteAttribute<T>>(Domain, MutableSpan<T>((T *)data, domain_size)); + return std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>( + MutableSpan<T>((T *)data, domain_size)); } /** @@ -179,30 +180,28 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud() }, update_custom_data_pointers}; - static BuiltinCustomDataLayerProvider position( - "position", - ATTR_DOMAIN_POINT, - CD_PROP_FLOAT3, - CD_PROP_FLOAT3, - BuiltinAttributeProvider::NonCreatable, - BuiltinAttributeProvider::Writable, - BuiltinAttributeProvider::NonDeletable, - point_access, - make_array_read_attribute<float3, ATTR_DOMAIN_POINT>, - make_array_write_attribute<float3, ATTR_DOMAIN_POINT>, - nullptr); - static BuiltinCustomDataLayerProvider radius( - "radius", - ATTR_DOMAIN_POINT, - CD_PROP_FLOAT, - CD_PROP_FLOAT, - BuiltinAttributeProvider::Creatable, - BuiltinAttributeProvider::Writable, - BuiltinAttributeProvider::Deletable, - point_access, - make_array_read_attribute<float, ATTR_DOMAIN_POINT>, - make_array_write_attribute<float, ATTR_DOMAIN_POINT>, - nullptr); + static BuiltinCustomDataLayerProvider position("position", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT3, + CD_PROP_FLOAT3, + BuiltinAttributeProvider::NonCreatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::NonDeletable, + point_access, + make_array_read_attribute<float3>, + make_array_write_attribute<float3>, + nullptr); + static BuiltinCustomDataLayerProvider radius("radius", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT, + CD_PROP_FLOAT, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute<float>, + make_array_write_attribute<float>, + nullptr); static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access); return ComponentAttributeProviders({&position, &radius}, {&point_custom_data}); } diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 07d0e520c93..47db5d1f901 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -449,13 +449,15 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups, const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output); BLI_assert(cpp_type != nullptr); - result.attribute_try_create(entry.key, domain_output, data_type_output); - WriteAttributePtr write_attribute = result.attribute_try_get_for_write(name); - if (!write_attribute || &write_attribute->cpp_type() != cpp_type || - write_attribute->domain() != domain_output) { + result.attribute_try_create( + entry.key, domain_output, data_type_output, AttributeInitDefault()); + WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(name); + if (!write_attribute || &write_attribute.varray->type() != cpp_type || + write_attribute.domain != domain_output) { continue; } - fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only(); + + fn::GVMutableArray_GSpan dst_span{*write_attribute.varray}; int offset = 0; for (const GeometryInstanceGroup &set_group : set_groups) { @@ -467,11 +469,11 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups, if (domain_size == 0) { continue; /* Domain size is 0, so no need to increment the offset. */ } - ReadAttributePtr source_attribute = component.attribute_try_get_for_read( + GVArrayPtr source_attribute = component.attribute_try_get_for_read( name, domain_output, data_type_output); if (source_attribute) { - fn::GSpan src_span = source_attribute->get_span(); + fn::GVArray_GSpan src_span{*source_attribute}; const void *src_buffer = src_span.data(); for (const int UNUSED(i) : set_group.transforms.index_range()) { void *dst_buffer = dst_span[offset]; @@ -486,7 +488,7 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups, } } - write_attribute->apply_span(); + dst_span.save(); } } diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c index 729c38ae7cf..1ec3b6a1cbf 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.c @@ -1542,7 +1542,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, * check whether it is still true with Mesh */ Mesh tmp = *mesh_dst; int totvert, totedge /*, totface */ /* UNUSED */, totloop, totpoly; - int did_shapekeys = 0; + bool did_shapekeys = false; eCDAllocType alloctype = CD_DUPLICATE; if (take_ownership /* && dm->type == DM_TYPE_CDDM && dm->needsFree */) { @@ -1597,7 +1597,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, } shapekey_layers_to_keyblocks(mesh_src, mesh_dst, uid); - did_shapekeys = 1; + did_shapekeys = true; } /* copy texture space */ @@ -1626,13 +1626,18 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, totedge); } if (!CustomData_has_layer(&tmp.pdata, CD_MPOLY)) { - /* TODO(Sybren): assignment to tmp.mxxx is probably not necessary due to the - * BKE_mesh_update_customdata_pointers() call below. */ - tmp.mloop = (alloctype == CD_ASSIGN) ? mesh_src->mloop : MEM_dupallocN(mesh_src->mloop); - tmp.mpoly = (alloctype == CD_ASSIGN) ? mesh_src->mpoly : MEM_dupallocN(mesh_src->mpoly); - - CustomData_add_layer(&tmp.ldata, CD_MLOOP, CD_ASSIGN, tmp.mloop, tmp.totloop); - CustomData_add_layer(&tmp.pdata, CD_MPOLY, CD_ASSIGN, tmp.mpoly, tmp.totpoly); + CustomData_add_layer(&tmp.ldata, + CD_MLOOP, + CD_ASSIGN, + (alloctype == CD_ASSIGN) ? mesh_src->mloop : + MEM_dupallocN(mesh_src->mloop), + tmp.totloop); + CustomData_add_layer(&tmp.pdata, + CD_MPOLY, + CD_ASSIGN, + (alloctype == CD_ASSIGN) ? mesh_src->mpoly : + MEM_dupallocN(mesh_src->mpoly), + tmp.totpoly); } /* object had got displacement layer, should copy this layer to save sculpted data */ @@ -1651,9 +1656,6 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, /* yes, must be before _and_ after tessellate */ BKE_mesh_update_customdata_pointers(&tmp, false); - /* since 2.65 caller must do! */ - // BKE_mesh_tessface_calc(&tmp); - CustomData_free(&mesh_dst->vdata, mesh_dst->totvert); CustomData_free(&mesh_dst->edata, mesh_dst->totedge); CustomData_free(&mesh_dst->fdata, mesh_dst->totface); diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc new file mode 100644 index 00000000000..91c9951ae89 --- /dev/null +++ b/source/blender/blenkernel/intern/mesh_sample.cc @@ -0,0 +1,158 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_attribute_math.hh" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_sample.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +namespace blender::bke::mesh_surface_sample { + +static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh) +{ + /* This only updates a cache and can be considered to be logically const. */ + const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh)); + const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh); + return {looptris, looptris_len}; +} + +template<typename T> +BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh, + const Span<int> looptri_indices, + const Span<float3> bary_coords, + const VArray<T> &data_in, + const MutableSpan<T> data_out) +{ + const Span<MLoopTri> looptris = get_mesh_looptris(mesh); + + for (const int i : bary_coords.index_range()) { + const int looptri_index = looptri_indices[i]; + const MLoopTri &looptri = looptris[looptri_index]; + const float3 &bary_coord = bary_coords[i]; + + const int v0_index = mesh.mloop[looptri.tri[0]].v; + const int v1_index = mesh.mloop[looptri.tri[1]].v; + const int v2_index = mesh.mloop[looptri.tri[2]].v; + + const T v0 = data_in[v0_index]; + const T v1 = data_in[v1_index]; + const T v2 = data_in[v2_index]; + + const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2); + data_out[i] = interpolated_value; + } +} + +void sample_point_attribute(const Mesh &mesh, + const Span<int> looptri_indices, + const Span<float3> bary_coords, + const GVArray &data_in, + const GMutableSpan data_out) +{ + BLI_assert(data_out.size() == looptri_indices.size()); + BLI_assert(data_out.size() == bary_coords.size()); + BLI_assert(data_in.size() == mesh.totvert); + BLI_assert(data_in.type() == data_out.type()); + + const CPPType &type = data_in.type(); + attribute_math::convert_to_static_type(type, [&](auto dummy) { + using T = decltype(dummy); + sample_point_attribute<T>( + mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>()); + }); +} + +template<typename T> +BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh, + const Span<int> looptri_indices, + const Span<float3> bary_coords, + const VArray<T> &data_in, + const MutableSpan<T> data_out) +{ + Span<MLoopTri> looptris = get_mesh_looptris(mesh); + + for (const int i : bary_coords.index_range()) { + const int looptri_index = looptri_indices[i]; + const MLoopTri &looptri = looptris[looptri_index]; + const float3 &bary_coord = bary_coords[i]; + + const int loop_index_0 = looptri.tri[0]; + const int loop_index_1 = looptri.tri[1]; + const int loop_index_2 = looptri.tri[2]; + + const T v0 = data_in[loop_index_0]; + const T v1 = data_in[loop_index_1]; + const T v2 = data_in[loop_index_2]; + + const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2); + data_out[i] = interpolated_value; + } +} + +void sample_corner_attribute(const Mesh &mesh, + const Span<int> looptri_indices, + const Span<float3> bary_coords, + const GVArray &data_in, + const GMutableSpan data_out) +{ + BLI_assert(data_out.size() == looptri_indices.size()); + BLI_assert(data_out.size() == bary_coords.size()); + BLI_assert(data_in.size() == mesh.totloop); + BLI_assert(data_in.type() == data_out.type()); + + const CPPType &type = data_in.type(); + attribute_math::convert_to_static_type(type, [&](auto dummy) { + using T = decltype(dummy); + sample_corner_attribute<T>( + mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>()); + }); +} + +template<typename T> +void sample_face_attribute(const Mesh &mesh, + const Span<int> looptri_indices, + const VArray<T> &data_in, + const MutableSpan<T> data_out) +{ + Span<MLoopTri> looptris = get_mesh_looptris(mesh); + + for (const int i : data_out.index_range()) { + const int looptri_index = looptri_indices[i]; + const MLoopTri &looptri = looptris[looptri_index]; + const int poly_index = looptri.poly; + data_out[i] = data_in[poly_index]; + } +} + +void sample_face_attribute(const Mesh &mesh, + const Span<int> looptri_indices, + const GVArray &data_in, + const GMutableSpan data_out) +{ + BLI_assert(data_out.size() == looptri_indices.size()); + BLI_assert(data_in.size() == mesh.totpoly); + BLI_assert(data_in.type() == data_out.type()); + + const CPPType &type = data_in.type(); + attribute_math::convert_to_static_type(type, [&](auto dummy) { + using T = decltype(dummy); + sample_face_attribute<T>(mesh, looptri_indices, data_in.typed<T>(), data_out.typed<T>()); + }); +} + +} // namespace blender::bke::mesh_surface_sample diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 02195e0d60f..473be34d69a 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4968,6 +4968,7 @@ static void registerGeometryNodes() register_node_type_geo_sample_texture(); register_node_type_geo_subdivide(); register_node_type_geo_subdivision_surface(); + register_node_type_geo_switch(); register_node_type_geo_transform(); register_node_type_geo_triangulate(); register_node_type_geo_volume_to_mesh(); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 160fb93b835..522b4549f57 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1336,7 +1336,7 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) return (mti->modifyGeometrySet != NULL); } if (ob->type == OB_VOLUME) { - return (mti->modifyVolume != NULL); + return (mti->modifyVolume != NULL) || (mti->modifyGeometrySet != NULL); } if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly) == 0) { diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 7b03839f659..93d5e116131 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -39,6 +39,7 @@ #include "BLI_utildefines.h" #include "BKE_anim_data.h" +#include "BKE_geometry_set.hh" #include "BKE_global.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" @@ -1003,13 +1004,12 @@ static void volume_update_simplify_level(Volume *volume, const Depsgraph *depsgr #endif } -static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph, - struct Scene *scene, - Object *object, - Volume *volume_input) +static void volume_evaluate_modifiers(struct Depsgraph *depsgraph, + struct Scene *scene, + Object *object, + Volume *volume_input, + GeometrySet &geometry_set) { - Volume *volume = volume_input; - /* Modifier evaluation modes. */ const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; @@ -1030,25 +1030,22 @@ static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph, continue; } - if (mti->modifyVolume) { - /* Ensure we are not modifying the input. */ - if (volume == volume_input) { - volume = BKE_volume_copy_for_eval(volume, true); + if (mti->modifyGeometrySet) { + mti->modifyGeometrySet(md, &mectx, &geometry_set); + } + else if (mti->modifyVolume) { + VolumeComponent &volume_component = geometry_set.get_component_for_write<VolumeComponent>(); + Volume *volume_old = volume_component.get_for_write(); + if (volume_old == nullptr) { + volume_old = BKE_volume_new_for_eval(volume_input); + volume_component.replace(volume_old); } - - Volume *volume_next = mti->modifyVolume(md, &mectx, volume); - - if (volume_next && volume_next != volume) { - /* If the modifier returned a new volume, release the old one. */ - if (volume != volume_input) { - BKE_id_free(nullptr, volume); - } - volume = volume_next; + Volume *volume_new = mti->modifyVolume(md, &mectx, volume_old); + if (volume_new != volume_old) { + volume_component.replace(volume_new); } } } - - return volume; } void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume) @@ -1072,6 +1069,24 @@ void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume) } } +static Volume *take_volume_ownership_from_geometry_set(GeometrySet &geometry_set) +{ + if (!geometry_set.has<VolumeComponent>()) { + return nullptr; + } + VolumeComponent &volume_component = geometry_set.get_component_for_write<VolumeComponent>(); + Volume *volume = volume_component.release(); + if (volume != nullptr) { + /* Add back, but only as read-only non-owning component. */ + volume_component.replace(volume, GeometryOwnershipType::ReadOnly); + } + else { + /* The component was empty, we can remove it. */ + geometry_set.remove<VolumeComponent>(); + } + return volume; +} + void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object) { /* Free any evaluated data and restore original data. */ @@ -1079,11 +1094,22 @@ void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Ob /* Evaluate modifiers. */ Volume *volume = (Volume *)object->data; - Volume *volume_eval = volume_evaluate_modifiers(depsgraph, scene, object, volume); + GeometrySet geometry_set; + VolumeComponent &volume_component = geometry_set.get_component_for_write<VolumeComponent>(); + volume_component.replace(volume, GeometryOwnershipType::ReadOnly); + volume_evaluate_modifiers(depsgraph, scene, object, volume, geometry_set); + + Volume *volume_eval = take_volume_ownership_from_geometry_set(geometry_set); + + /* If the geometry set did not contain a volume, we still create an empty one. */ + if (volume_eval == nullptr) { + volume_eval = BKE_volume_new_for_eval(volume); + } /* Assign evaluated object. */ - const bool is_owned = (volume != volume_eval); - BKE_object_eval_assign_data(object, &volume_eval->id, is_owned); + const bool eval_is_owned = (volume != volume_eval); + BKE_object_eval_assign_data(object, &volume_eval->id, eval_is_owned); + object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set)); } void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, const char *filepath) |