diff options
Diffstat (limited to 'source/blender/blenkernel')
17 files changed, 945 insertions, 238 deletions
diff --git a/source/blender/blenkernel/BKE_anonymous_attribute.h b/source/blender/blenkernel/BKE_anonymous_attribute.h new file mode 100644 index 00000000000..ebdb0b05160 --- /dev/null +++ b/source/blender/blenkernel/BKE_anonymous_attribute.h @@ -0,0 +1,43 @@ +/* + * 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 + * + * An #AnonymousAttributeID is used to identify attributes that are not explicitly named. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct AnonymousAttributeID AnonymousAttributeID; + +AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name); +AnonymousAttributeID *BKE_anonymous_attribute_id_new_strong(const char *debug_name); +bool BKE_anonymous_attribute_id_has_strong_references(const AnonymousAttributeID *anonymous_id); +void BKE_anonymous_attribute_id_increment_weak(const AnonymousAttributeID *anonymous_id); +void BKE_anonymous_attribute_id_increment_strong(const AnonymousAttributeID *anonymous_id); +void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anonymous_id); +void BKE_anonymous_attribute_id_decrement_strong(const AnonymousAttributeID *anonymous_id); +const char *BKE_anonymous_attribute_id_debug_name(const AnonymousAttributeID *anonymous_id); +const char *BKE_anonymous_attribute_id_internal_name(const AnonymousAttributeID *anonymous_id); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/BKE_anonymous_attribute.hh b/source/blender/blenkernel/BKE_anonymous_attribute.hh new file mode 100644 index 00000000000..201fa2b2f52 --- /dev/null +++ b/source/blender/blenkernel/BKE_anonymous_attribute.hh @@ -0,0 +1,169 @@ +/* + * 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 + +#include <atomic> +#include <string> + +#include "BLI_hash.hh" +#include "BLI_string_ref.hh" + +#include "BKE_anonymous_attribute.h" + +namespace blender::bke { + +/** + * Wrapper for #AnonymousAttributeID with RAII semantics. + * This class should typically not be used directly. Instead use #StrongAnonymousAttributeID or + * #WeakAnonymousAttributeID. + */ +template<bool IsStrongReference> class OwnedAnonymousAttributeID { + private: + const AnonymousAttributeID *data_ = nullptr; + + template<bool OtherIsStrongReference> friend class OwnedAnonymousAttributeID; + + public: + OwnedAnonymousAttributeID() = default; + + /** Create a new anonymous attribute id. */ + explicit OwnedAnonymousAttributeID(StringRefNull debug_name) + { + if constexpr (IsStrongReference) { + data_ = BKE_anonymous_attribute_id_new_strong(debug_name.c_str()); + } + else { + data_ = BKE_anonymous_attribute_id_new_weak(debug_name.c_str()); + } + } + + /** + * This transfers ownership, so no incref is necessary. + * The caller has to make sure that it owned the anonymous id. + */ + explicit OwnedAnonymousAttributeID(const AnonymousAttributeID *anonymous_id) + : data_(anonymous_id) + { + } + + template<bool OtherIsStrong> + OwnedAnonymousAttributeID(const OwnedAnonymousAttributeID<OtherIsStrong> &other) + { + data_ = other.data_; + this->incref(); + } + + template<bool OtherIsStrong> + OwnedAnonymousAttributeID(OwnedAnonymousAttributeID<OtherIsStrong> &&other) + { + data_ = other.data_; + this->incref(); + other.decref(); + other.data_ = nullptr; + } + + ~OwnedAnonymousAttributeID() + { + this->decref(); + } + + template<bool OtherIsStrong> + OwnedAnonymousAttributeID &operator=(const OwnedAnonymousAttributeID<OtherIsStrong> &other) + { + if (this == &other) { + return *this; + } + this->~OwnedAnonymousAttributeID(); + new (this) OwnedAnonymousAttributeID(other); + return *this; + } + + template<bool OtherIsStrong> + OwnedAnonymousAttributeID &operator=(OwnedAnonymousAttributeID<OtherIsStrong> &&other) + { + if (this == &other) { + return *this; + } + this->~OwnedAnonymousAttributeID(); + new (this) OwnedAnonymousAttributeID(std::move(other)); + return *this; + } + + operator bool() const + { + return data_ != nullptr; + } + + StringRefNull debug_name() const + { + BLI_assert(data_ != nullptr); + return BKE_anonymous_attribute_id_debug_name(data_); + } + + bool has_strong_references() const + { + BLI_assert(data_ != nullptr); + return BKE_anonymous_attribute_id_has_strong_references(data_); + } + + /** Extract the onwership of the currently wrapped anonymous id. */ + const AnonymousAttributeID *extract() + { + const AnonymousAttributeID *extracted_data = data_; + /* Don't decref because the caller becomes the new owner. */ + data_ = nullptr; + return extracted_data; + } + + /** Get the wrapped anonymous id, without taking ownership. */ + const AnonymousAttributeID *get() const + { + return data_; + } + + private: + void incref() + { + if (data_ == nullptr) { + return; + } + if constexpr (IsStrongReference) { + BKE_anonymous_attribute_id_increment_strong(data_); + } + else { + BKE_anonymous_attribute_id_increment_weak(data_); + } + } + + void decref() + { + if (data_ == nullptr) { + return; + } + if constexpr (IsStrongReference) { + BKE_anonymous_attribute_id_decrement_strong(data_); + } + else { + BKE_anonymous_attribute_id_decrement_weak(data_); + } + } +}; + +using StrongAnonymousAttributeID = OwnedAnonymousAttributeID<true>; +using WeakAnonymousAttributeID = OwnedAnonymousAttributeID<false>; + +} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index c3f7dbd4bd9..9d309d8a1c1 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -22,6 +22,7 @@ #include "FN_generic_span.hh" #include "FN_generic_virtual_array.hh" +#include "BKE_anonymous_attribute.hh" #include "BKE_attribute.h" #include "BLI_color.hh" @@ -29,6 +30,81 @@ #include "BLI_float3.hh" #include "BLI_function_ref.hh" +namespace blender::bke { + +/** + * Identifies an attribute that is either named or anonymous. + * It does not own the identifier, so it is just a reference. + */ +class AttributeIDRef { + private: + StringRef name_; + const AnonymousAttributeID *anonymous_id_ = nullptr; + + public: + AttributeIDRef() = default; + + AttributeIDRef(StringRef name) : name_(name) + { + } + + AttributeIDRef(StringRefNull name) : name_(name) + { + } + + AttributeIDRef(const char *name) : name_(name) + { + } + + AttributeIDRef(const std::string &name) : name_(name) + { + } + + /* The anonymous id is only borrowed, the caller has to keep a reference to it. */ + AttributeIDRef(const AnonymousAttributeID *anonymous_id) : anonymous_id_(anonymous_id) + { + } + + operator bool() const + { + return this->is_named() || this->is_anonymous(); + } + + friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b) + { + return a.anonymous_id_ == b.anonymous_id_ && a.name_ == b.name_; + } + + uint64_t hash() const + { + return get_default_hash_2(name_, anonymous_id_); + } + + bool is_named() const + { + return !name_.is_empty(); + } + + bool is_anonymous() const + { + return anonymous_id_ != nullptr; + } + + StringRef name() const + { + BLI_assert(this->is_named()); + return name_; + } + + const AnonymousAttributeID &anonymous_id() const + { + BLI_assert(this->is_anonymous()); + return *anonymous_id_; + } +}; + +} // namespace blender::bke + /** * Contains information about an attribute in a geometry component. * More information can be added in the future. E.g. whether the attribute is builtin and how it is @@ -104,8 +180,8 @@ struct AttributeInitMove : public AttributeInit { }; /* Returns false when the iteration should be stopped. */ -using AttributeForeachCallback = blender::FunctionRef<bool(blender::StringRefNull attribute_name, - const AttributeMetaData &meta_data)>; +using AttributeForeachCallback = blender::FunctionRef<bool( + const blender::bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data)>; namespace blender::bke { @@ -333,26 +409,28 @@ class CustomDataAttributes { void reallocate(const int size); - std::optional<blender::fn::GSpan> get_for_read(const blender::StringRef name) const; + std::optional<blender::fn::GSpan> get_for_read(const AttributeIDRef &attribute_id) const; - blender::fn::GVArrayPtr get_for_read(const StringRef name, + blender::fn::GVArrayPtr get_for_read(const AttributeIDRef &attribute_id, const CustomDataType data_type, const void *default_value) const; template<typename T> - blender::fn::GVArray_Typed<T> get_for_read(const blender::StringRef name, + blender::fn::GVArray_Typed<T> get_for_read(const AttributeIDRef &attribute_id, 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); - GVArrayPtr varray = this->get_for_read(name, type, &default_value); + GVArrayPtr varray = this->get_for_read(attribute_id, type, &default_value); return blender::fn::GVArray_Typed<T>(std::move(varray)); } - std::optional<blender::fn::GMutableSpan> get_for_write(const blender::StringRef name); - bool create(const blender::StringRef name, const CustomDataType data_type); - bool create_by_move(const blender::StringRef name, const CustomDataType data_type, void *buffer); - bool remove(const blender::StringRef name); + std::optional<blender::fn::GMutableSpan> get_for_write(const AttributeIDRef &attribute_id); + bool create(const AttributeIDRef &attribute_id, const CustomDataType data_type); + bool create_by_move(const AttributeIDRef &attribute_id, + const CustomDataType data_type, + void *buffer); + bool remove(const AttributeIDRef &attribute_id); bool foreach_attribute(const AttributeForeachCallback callback, const AttributeDomain domain) const; diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 7a44553c565..0732f1e5190 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -33,6 +33,7 @@ extern "C" { #endif +struct AnonymousAttributeID; struct BMesh; struct BlendDataReader; struct BlendWriter; @@ -193,6 +194,12 @@ void *CustomData_add_layer_named(struct CustomData *data, void *layer, int totelem, const char *name); +void *CustomData_add_layer_anonymous(struct CustomData *data, + int type, + eCDAllocType alloctype, + void *layer, + int totelem, + const struct AnonymousAttributeID *anonymous_id); /* frees the active or first data layer with the give type. * returns 1 on success, 0 if no layer with the given type is found @@ -231,6 +238,11 @@ void *CustomData_duplicate_referenced_layer_named(struct CustomData *data, const int type, const char *name, const int totelem); +void *CustomData_duplicate_referenced_layer_anonymous( + CustomData *data, + const int type, + const struct AnonymousAttributeID *anonymous_id, + const int totelem); bool CustomData_is_referenced_layer(struct CustomData *data, int type); /* Duplicate all the layers with flag NOFREE, and remove the flag from duplicated layers. */ diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 08b6cb951a8..c3d594d7dcd 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -31,9 +31,12 @@ #include "BLI_user_counter.hh" #include "BLI_vector_set.hh" +#include "BKE_anonymous_attribute.hh" #include "BKE_attribute_access.hh" #include "BKE_geometry_set.h" +#include "FN_field.hh" + struct Collection; struct Curve; struct CurveEval; @@ -88,11 +91,11 @@ class GeometryComponent { GeometryComponentType type() const; /* Return true when any attribute with this name exists, including built in attributes. */ - bool attribute_exists(const blender::StringRef attribute_name) const; + bool attribute_exists(const blender::bke::AttributeIDRef &attribute_id) const; /* Return the data type and domain of an attribute with the given name if it exists. */ std::optional<AttributeMetaData> attribute_get_meta_data( - const blender::StringRef attribute_name) const; + const blender::bke::AttributeIDRef &attribute_id) const; /* Returns true when the geometry component supports this attribute domain. */ bool attribute_domain_supported(const AttributeDomain domain) const; @@ -104,12 +107,12 @@ class GeometryComponent { /* Get read-only access to the highest priority attribute with the given name. * Returns null if the attribute does not exist. */ blender::bke::ReadAttributeLookup attribute_try_get_for_read( - const blender::StringRef attribute_name) const; + const blender::bke::AttributeIDRef &attribute_id) 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::WriteAttributeLookup attribute_try_get_for_write( - const blender::StringRef attribute_name); + const blender::bke::AttributeIDRef &attribute_id); /* Get a read-only attribute for the domain based on the given attribute. This can be used to * interpolate from one domain to another. @@ -120,10 +123,10 @@ class GeometryComponent { const AttributeDomain to_domain) const; /* Returns true when the attribute has been deleted. */ - bool attribute_try_delete(const blender::StringRef attribute_name); + bool attribute_try_delete(const blender::bke::AttributeIDRef &attribute_id); /* Returns true when the attribute has been created. */ - bool attribute_try_create(const blender::StringRef attribute_name, + bool attribute_try_create(const blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const AttributeInit &initializer); @@ -133,7 +136,7 @@ class GeometryComponent { bool attribute_try_create_builtin(const blender::StringRef attribute_name, const AttributeInit &initializer); - blender::Set<std::string> attribute_names() const; + blender::Set<blender::bke::AttributeIDRef> attribute_ids() const; bool attribute_foreach(const AttributeForeachCallback callback) const; virtual bool is_empty() const; @@ -142,7 +145,7 @@ class GeometryComponent { * 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 blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type) const; @@ -150,18 +153,18 @@ class GeometryComponent { * 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; + const blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain) 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; + const blender::bke::AttributeIDRef &attribute_id, const CustomDataType data_type) const; /* 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 blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const void *default_value = nullptr) const; @@ -169,14 +172,15 @@ class GeometryComponent { /* 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 + blender::fn::GVArray_Typed<T> attribute_get_for_read( + const blender::bke::AttributeIDRef &attribute_id, + 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); std::unique_ptr varray = this->attribute_get_for_read( - attribute_name, domain, type, &default_value); + attribute_id, domain, type, &default_value); return blender::fn::GVArray_Typed<T>(std::move(varray)); } @@ -191,7 +195,7 @@ class GeometryComponent { * 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 blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const void *default_value = nullptr); @@ -200,28 +204,30 @@ class GeometryComponent { * 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 blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type); /* Statically typed method corresponding to the equally named generic one. */ template<typename T> blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output( - const blender::StringRef attribute_name, const AttributeDomain domain, const T default_value) + const blender::bke::AttributeIDRef &attribute_id, + const AttributeDomain domain, + const T default_value) { 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(attribute_name, domain, data_type, &default_value); + return this->attribute_try_get_for_output(attribute_id, domain, data_type, &default_value); } /* 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::bke::AttributeIDRef &attribute_id, 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); + return this->attribute_try_get_for_output_only(attribute_id, domain, data_type); } private: @@ -616,3 +622,69 @@ class VolumeComponent : public GeometryComponent { static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_VOLUME; }; + +namespace blender::bke { + +class GeometryComponentFieldContext : public fn::FieldContext { + private: + const GeometryComponent &component_; + const AttributeDomain domain_; + + public: + GeometryComponentFieldContext(const GeometryComponent &component, const AttributeDomain domain) + : component_(component), domain_(domain) + { + } + + const GeometryComponent &geometry_component() const + { + return component_; + } + + AttributeDomain domain() const + { + return domain_; + } +}; + +class AttributeFieldInput : public fn::FieldInput { + private: + std::string name_; + + public: + AttributeFieldInput(std::string name, const CPPType &type) + : fn::FieldInput(type, name), name_(std::move(name)) + { + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask mask, + ResourceScope &scope) const override; + + uint64_t hash() const override; + bool is_equal_to(const fn::FieldNode &other) const override; +}; + +class AnonymousAttributeFieldInput : public fn::FieldInput { + private: + /** + * A strong reference is required to make sure that the referenced attribute is not removed + * automatically. + */ + StrongAnonymousAttributeID anonymous_id_; + + public: + AnonymousAttributeFieldInput(StrongAnonymousAttributeID anonymous_id, const CPPType &type) + : fn::FieldInput(type, anonymous_id.debug_name()), anonymous_id_(std::move(anonymous_id)) + { + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask mask, + ResourceScope &scope) const override; + + uint64_t hash() const override; + bool is_equal_to(const fn::FieldNode &other) const override; +}; + +} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_geometry_set_instances.hh b/source/blender/blenkernel/BKE_geometry_set_instances.hh index 25876296a47..44a0ee30c4c 100644 --- a/source/blender/blenkernel/BKE_geometry_set_instances.hh +++ b/source/blender/blenkernel/BKE_geometry_set_instances.hh @@ -59,9 +59,10 @@ struct AttributeKind { * will contain the highest complexity data type and the highest priority domain among every * attribute with the given name on all of the input components. */ -void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> set_groups, - Span<GeometryComponentType> component_types, - const Set<std::string> &ignored_attributes, - Map<std::string, AttributeKind> &r_attributes); +void geometry_set_gather_instances_attribute_info( + Span<GeometryInstanceGroup> set_groups, + Span<GeometryComponentType> component_types, + const Set<std::string> &ignored_attributes, + Map<AttributeIDRef, AttributeKind> &r_attributes); } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index b41fbae6075..0d70e812ecb 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1484,6 +1484,11 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_SPLINE_TYPE 1073 #define GEO_NODE_CURVE_SELECT_HANDLES 1074 #define GEO_NODE_CURVE_FILL 1075 +#define GEO_NODE_INPUT_POSITION 1076 +#define GEO_NODE_SET_POSITION 1077 +#define GEO_NODE_INPUT_INDEX 1078 +#define GEO_NODE_INPUT_NORMAL 1079 +#define GEO_NODE_ATTRIBUTE_CAPTURE 1080 /** \} */ diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 64cfbd8f491..26d81ec3b34 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -76,6 +76,7 @@ set(SRC intern/anim_path.c intern/anim_sys.c intern/anim_visualization.c + intern/anonymous_attribute.cc intern/appdir.c intern/armature.c intern/armature_deform.c @@ -295,6 +296,8 @@ set(SRC BKE_anim_path.h BKE_anim_visualization.h BKE_animsys.h + BKE_anonymous_attribute.h + BKE_anonymous_attribute.hh BKE_appdir.h BKE_armature.h BKE_armature.hh diff --git a/source/blender/blenkernel/intern/anonymous_attribute.cc b/source/blender/blenkernel/intern/anonymous_attribute.cc new file mode 100644 index 00000000000..67611053d83 --- /dev/null +++ b/source/blender/blenkernel/intern/anonymous_attribute.cc @@ -0,0 +1,118 @@ +/* + * 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_anonymous_attribute.hh" + +using namespace blender::bke; + +/** + * A struct that identifies an attribute. It's lifetime is managed by an atomic reference count. + * + * Additionally, this struct can be strongly or weakly owned. The difference is that strong + * ownership means that attributes with this id will be kept around. Weak ownership just makes sure + * that the struct itself stays alive, but corresponding attributes might still be removed + * automatically. + */ +struct AnonymousAttributeID { + /** + * Total number of references to this attribute id. Once this reaches zero, the struct can be + * freed. This includes strong and weak references. + */ + mutable std::atomic<int> refcount_tot = 0; + + /** + * Number of strong references to this attribute id. When this is zero, the corresponding + * attributes can be removed from geometries automatically. + */ + mutable std::atomic<int> refcount_strong = 0; + + /** + * Only used to identify this struct in a debugging session. + */ + std::string debug_name; + + /** + * Unique name of the this attribute id during the current session. + */ + std::string internal_name; +}; + +/** Every time this function is called, it outputs a different name. */ +static std::string get_new_internal_name() +{ + static std::atomic<int> index = 0; + const int next_index = index.fetch_add(1); + return "anonymous_attribute_" + std::to_string(next_index); +} + +AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name) +{ + AnonymousAttributeID *anonymous_id = new AnonymousAttributeID(); + anonymous_id->debug_name = debug_name; + anonymous_id->internal_name = get_new_internal_name(); + anonymous_id->refcount_tot.store(1); + return anonymous_id; +} + +AnonymousAttributeID *BKE_anonymous_attribute_id_new_strong(const char *debug_name) +{ + AnonymousAttributeID *anonymous_id = new AnonymousAttributeID(); + anonymous_id->debug_name = debug_name; + anonymous_id->internal_name = get_new_internal_name(); + anonymous_id->refcount_tot.store(1); + anonymous_id->refcount_strong.store(1); + return anonymous_id; +} + +bool BKE_anonymous_attribute_id_has_strong_references(const AnonymousAttributeID *anonymous_id) +{ + return anonymous_id->refcount_strong.load() >= 1; +} + +void BKE_anonymous_attribute_id_increment_weak(const AnonymousAttributeID *anonymous_id) +{ + anonymous_id->refcount_tot.fetch_add(1); +} + +void BKE_anonymous_attribute_id_increment_strong(const AnonymousAttributeID *anonymous_id) +{ + anonymous_id->refcount_tot.fetch_add(1); + anonymous_id->refcount_strong.fetch_add(1); +} + +void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anonymous_id) +{ + const int new_refcount = anonymous_id->refcount_tot.fetch_sub(1) - 1; + if (new_refcount == 0) { + delete anonymous_id; + } +} + +void BKE_anonymous_attribute_id_decrement_strong(const AnonymousAttributeID *anonymous_id) +{ + anonymous_id->refcount_strong.fetch_sub(1); + BKE_anonymous_attribute_id_decrement_weak(anonymous_id); +} + +const char *BKE_anonymous_attribute_id_debug_name(const AnonymousAttributeID *anonymous_id) +{ + return anonymous_id->debug_name.c_str(); +} + +const char *BKE_anonymous_attribute_id_internal_name(const AnonymousAttributeID *anonymous_id) +{ + return anonymous_id->internal_name.c_str(); +} diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index aa0af294bc3..08bd5dc5981 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -44,6 +44,8 @@ using blender::float3; using blender::Set; using blender::StringRef; using blender::StringRefNull; +using blender::bke::AttributeIDRef; +using blender::bke::OutputAttribute; using blender::fn::GMutableSpan; using blender::fn::GSpan; using blender::fn::GVArray_For_GSpan; @@ -334,8 +336,20 @@ bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component) return data != nullptr; } +static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer, + const AttributeIDRef &attribute_id) +{ + if (!attribute_id) { + return false; + } + if (attribute_id.is_anonymous()) { + return layer.anonymous_id == &attribute_id.anonymous_id(); + } + return layer.name == attribute_id.name(); +} + ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( - const GeometryComponent &component, const StringRef attribute_name) const + const GeometryComponent &component, const AttributeIDRef &attribute_id) const { const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); if (custom_data == nullptr) { @@ -343,7 +357,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( } const int domain_size = component.attribute_domain_size(domain_); for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { - if (layer.name != attribute_name) { + if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { continue; } const CustomDataType data_type = (CustomDataType)layer.type; @@ -368,7 +382,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( } WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( - GeometryComponent &component, const StringRef attribute_name) const + GeometryComponent &component, const AttributeIDRef &attribute_id) const { CustomData *custom_data = custom_data_access_.get_custom_data(component); if (custom_data == nullptr) { @@ -376,10 +390,17 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( } const int domain_size = component.attribute_domain_size(domain_); for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { - if (layer.name != attribute_name) { + if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { continue; } - CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_size); + if (attribute_id.is_named()) { + CustomData_duplicate_referenced_layer_named( + custom_data, layer.type, layer.name, domain_size); + } + else { + CustomData_duplicate_referenced_layer_anonymous( + custom_data, layer.type, &attribute_id.anonymous_id(), domain_size); + } const CustomDataType data_type = (CustomDataType)layer.type; switch (data_type) { case CD_PROP_FLOAT: @@ -402,7 +423,7 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( } bool CustomDataAttributeProvider::try_delete(GeometryComponent &component, - const StringRef attribute_name) const + const AttributeIDRef &attribute_id) const { CustomData *custom_data = custom_data_access_.get_custom_data(component); if (custom_data == nullptr) { @@ -411,7 +432,8 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component, const int domain_size = component.attribute_domain_size(domain_); for (const int i : IndexRange(custom_data->totlayer)) { const CustomDataLayer &layer = custom_data->layers[i]; - if (this->type_is_supported((CustomDataType)layer.type) && layer.name == attribute_name) { + if (this->type_is_supported((CustomDataType)layer.type) && + custom_data_layer_matches_attribute_id(layer, attribute_id)) { CustomData_free_layer(custom_data, layer.type, domain_size, i); return true; } @@ -419,24 +441,39 @@ 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) +static void *add_generic_custom_data_layer(CustomData &custom_data, + const CustomDataType data_type, + const eCDAllocType alloctype, + void *layer_data, + const int domain_size, + const AttributeIDRef &attribute_id) { - char attribute_name_c[MAX_NAME]; - attribute_name.copy(attribute_name_c); + if (attribute_id.is_named()) { + char attribute_name_c[MAX_NAME]; + attribute_id.name().copy(attribute_name_c); + return CustomData_add_layer_named( + &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c); + } + const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id(); + return CustomData_add_layer_anonymous( + &custom_data, data_type, alloctype, layer_data, domain_size, &anonymous_id); +} +static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attribute_id, + 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_named( - &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c); + void *data = add_generic_custom_data_layer( + custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id); 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); + void *data = add_generic_custom_data_layer( + custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id); if (data == nullptr) { return false; } @@ -446,8 +483,8 @@ static bool add_named_custom_data_layer_from_attribute_init(const StringRef attr } 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); + void *data = add_generic_custom_data_layer( + custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_id); if (data == nullptr) { MEM_freeN(source_data); return false; @@ -461,7 +498,7 @@ static bool add_named_custom_data_layer_from_attribute_init(const StringRef attr } bool CustomDataAttributeProvider::try_create(GeometryComponent &component, - const StringRef attribute_name, + const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const AttributeInit &initializer) const @@ -477,13 +514,13 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component, return false; } for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { - if (layer.name == attribute_name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { return false; } } const int domain_size = component.attribute_domain_size(domain_); - add_named_custom_data_layer_from_attribute_init( - attribute_name, *custom_data, data_type, domain_size, initializer); + add_custom_data_layer_from_attribute_init( + attribute_id, *custom_data, data_type, domain_size, initializer); return true; } @@ -498,7 +535,14 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com const CustomDataType data_type = (CustomDataType)layer.type; if (this->type_is_supported(data_type)) { AttributeMetaData meta_data{domain_, data_type}; - if (!callback(layer.name, meta_data)) { + AttributeIDRef attribute_id; + if (layer.anonymous_id != nullptr) { + attribute_id = layer.anonymous_id; + } + else { + attribute_id = layer.name; + } + if (!callback(attribute_id, meta_data)) { return false; } } @@ -507,7 +551,7 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com } ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( - const GeometryComponent &component, const StringRef attribute_name) const + const GeometryComponent &component, const AttributeIDRef &attribute_id) const { const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); if (custom_data == nullptr) { @@ -515,7 +559,7 @@ ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( } for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { if (layer.type == stored_type_) { - if (layer.name == attribute_name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { const int domain_size = component.attribute_domain_size(domain_); return {as_read_attribute_(layer.data, domain_size), domain_}; } @@ -525,7 +569,7 @@ ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( } WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write( - GeometryComponent &component, const StringRef attribute_name) const + GeometryComponent &component, const AttributeIDRef &attribute_id) const { CustomData *custom_data = custom_data_access_.get_custom_data(component); if (custom_data == nullptr) { @@ -533,7 +577,7 @@ WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write( } for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { if (layer.type == stored_type_) { - if (layer.name == attribute_name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { const int domain_size = component.attribute_domain_size(domain_); void *data_old = layer.data; void *data_new = CustomData_duplicate_referenced_layer_named( @@ -549,7 +593,7 @@ WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write( } bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component, - const StringRef attribute_name) const + const AttributeIDRef &attribute_id) const { CustomData *custom_data = custom_data_access_.get_custom_data(component); if (custom_data == nullptr) { @@ -558,7 +602,7 @@ bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component, for (const int i : IndexRange(custom_data->totlayer)) { const CustomDataLayer &layer = custom_data->layers[i]; if (layer.type == stored_type_) { - if (layer.name == attribute_name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { const int domain_size = component.attribute_domain_size(domain_); CustomData_free_layer(custom_data, stored_type_, domain_size, i); custom_data_access_.update_custom_data_pointers(component); @@ -627,11 +671,11 @@ CustomDataAttributes &CustomDataAttributes::operator=(const CustomDataAttributes return *this; } -std::optional<GSpan> CustomDataAttributes::get_for_read(const StringRef name) const +std::optional<GSpan> CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id) const { BLI_assert(size_ != 0); for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) { - if (layer.name == name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type); BLI_assert(cpp_type != nullptr); return GSpan(*cpp_type, layer.data, size_); @@ -645,13 +689,13 @@ std::optional<GSpan> CustomDataAttributes::get_for_read(const StringRef name) co * value if the attribute doesn't exist. If no default value is provided, the default value for the * type will be used. */ -GVArrayPtr CustomDataAttributes::get_for_read(const StringRef name, +GVArrayPtr CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id, const CustomDataType data_type, const void *default_value) const { const CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type); - std::optional<GSpan> attribute = this->get_for_read(name); + std::optional<GSpan> attribute = this->get_for_read(attribute_id); if (!attribute) { const int domain_size = this->size_; return std::make_unique<GVArray_For_SingleValue>( @@ -666,12 +710,12 @@ GVArrayPtr CustomDataAttributes::get_for_read(const StringRef name, return conversions.try_convert(std::make_unique<GVArray_For_GSpan>(*attribute), *type); } -std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const StringRef name) +std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const AttributeIDRef &attribute_id) { /* If this assert hits, it most likely means that #reallocate was not called at some point. */ BLI_assert(size_ != 0); for (CustomDataLayer &layer : MutableSpan(data.layers, data.totlayer)) { - if (layer.name == name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type); BLI_assert(cpp_type != nullptr); return GMutableSpan(*cpp_type, layer.data, size_); @@ -680,30 +724,29 @@ std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const StringRef return {}; } -bool CustomDataAttributes::create(const StringRef name, const CustomDataType data_type) +bool CustomDataAttributes::create(const AttributeIDRef &attribute_id, + const CustomDataType data_type) { - char name_c[MAX_NAME]; - name.copy(name_c); - void *result = CustomData_add_layer_named(&data, data_type, CD_DEFAULT, nullptr, size_, name_c); + void *result = add_generic_custom_data_layer( + data, data_type, CD_DEFAULT, nullptr, size_, attribute_id); return result != nullptr; } -bool CustomDataAttributes::create_by_move(const blender::StringRef name, +bool CustomDataAttributes::create_by_move(const AttributeIDRef &attribute_id, const CustomDataType data_type, void *buffer) { - char name_c[MAX_NAME]; - name.copy(name_c); - void *result = CustomData_add_layer_named(&data, data_type, CD_ASSIGN, buffer, size_, name_c); + void *result = add_generic_custom_data_layer( + data, data_type, CD_ASSIGN, buffer, size_, attribute_id); return result != nullptr; } -bool CustomDataAttributes::remove(const blender::StringRef name) +bool CustomDataAttributes::remove(const AttributeIDRef &attribute_id) { bool result = false; for (const int i : IndexRange(data.totlayer)) { const CustomDataLayer &layer = data.layers[i]; - if (layer.name == name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { CustomData_free_layer(&data, layer.type, size_, i); result = true; } @@ -722,7 +765,14 @@ bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback call { for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) { AttributeMetaData meta_data{domain, (CustomDataType)layer.type}; - if (!callback(layer.name, meta_data)) { + AttributeIDRef attribute_id; + if (layer.anonymous_id != nullptr) { + attribute_id = layer.anonymous_id; + } + else { + attribute_id = layer.name; + } + if (!callback(attribute_id, meta_data)) { return false; } } @@ -766,21 +816,23 @@ bool GeometryComponent::attribute_is_builtin(const blender::StringRef attribute_ } blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( - const StringRef attribute_name) const + const AttributeIDRef &attribute_id) const { using namespace blender::bke; const ComponentAttributeProviders *providers = this->get_attribute_providers(); if (providers == nullptr) { return {}; } - const BuiltinAttributeProvider *builtin_provider = - providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); - if (builtin_provider != nullptr) { - return {builtin_provider->try_get_for_read(*this), builtin_provider->domain()}; + if (attribute_id.is_named()) { + const BuiltinAttributeProvider *builtin_provider = + providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); + if (builtin_provider != nullptr) { + return {builtin_provider->try_get_for_read(*this), builtin_provider->domain()}; + } } for (const DynamicAttributesProvider *dynamic_provider : providers->dynamic_attribute_providers()) { - ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_name); + ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_id); if (attribute) { return attribute; } @@ -800,21 +852,23 @@ std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_adapt_dom } blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_write( - const StringRef attribute_name) + const AttributeIDRef &attribute_id) { using namespace blender::bke; const ComponentAttributeProviders *providers = this->get_attribute_providers(); if (providers == nullptr) { return {}; } - const BuiltinAttributeProvider *builtin_provider = - providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); - if (builtin_provider != nullptr) { - return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()}; + if (attribute_id.is_named()) { + const BuiltinAttributeProvider *builtin_provider = + providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); + if (builtin_provider != nullptr) { + return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()}; + } } for (const DynamicAttributesProvider *dynamic_provider : providers->dynamic_attribute_providers()) { - WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_name); + WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_id); if (attribute) { return attribute; } @@ -822,53 +876,57 @@ blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_writ return {}; } -bool GeometryComponent::attribute_try_delete(const StringRef attribute_name) +bool GeometryComponent::attribute_try_delete(const AttributeIDRef &attribute_id) { using namespace blender::bke; const ComponentAttributeProviders *providers = this->get_attribute_providers(); if (providers == nullptr) { return {}; } - const BuiltinAttributeProvider *builtin_provider = - providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); - if (builtin_provider != nullptr) { - return builtin_provider->try_delete(*this); + if (attribute_id.is_named()) { + const BuiltinAttributeProvider *builtin_provider = + providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); + if (builtin_provider != nullptr) { + return builtin_provider->try_delete(*this); + } } bool success = false; for (const DynamicAttributesProvider *dynamic_provider : providers->dynamic_attribute_providers()) { - success = dynamic_provider->try_delete(*this, attribute_name) || success; + success = dynamic_provider->try_delete(*this, attribute_id) || success; } return success; } -bool GeometryComponent::attribute_try_create(const StringRef attribute_name, +bool GeometryComponent::attribute_try_create(const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const AttributeInit &initializer) { using namespace blender::bke; - if (attribute_name.is_empty()) { + if (!attribute_id) { 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) { - if (builtin_provider->domain() != domain) { - return false; - } - if (builtin_provider->data_type() != data_type) { - return false; + if (attribute_id.is_named()) { + const BuiltinAttributeProvider *builtin_provider = + providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); + if (builtin_provider != nullptr) { + if (builtin_provider->domain() != domain) { + return false; + } + if (builtin_provider->data_type() != data_type) { + return false; + } + return builtin_provider->try_create(*this, initializer); } - 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, initializer)) { + if (dynamic_provider->try_create(*this, attribute_id, domain, data_type, initializer)) { return true; } } @@ -894,13 +952,14 @@ bool GeometryComponent::attribute_try_create_builtin(const blender::StringRef at return builtin_provider->try_create(*this, initializer); } -Set<std::string> GeometryComponent::attribute_names() const +Set<AttributeIDRef> GeometryComponent::attribute_ids() const { - Set<std::string> attributes; - this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) { - attributes.add(name); - return true; - }); + Set<AttributeIDRef> attributes; + this->attribute_foreach( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { + attributes.add(attribute_id); + return true; + }); return attributes; } @@ -931,9 +990,9 @@ bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac } for (const DynamicAttributesProvider *provider : providers->dynamic_attribute_providers()) { const bool continue_loop = provider->foreach_attribute( - *this, [&](StringRefNull name, const AttributeMetaData &meta_data) { - if (handled_attribute_names.add(name)) { - return callback(name, meta_data); + *this, [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (attribute_id.is_anonymous() || handled_attribute_names.add(attribute_id.name())) { + return callback(attribute_id, meta_data); } return true; }); @@ -945,9 +1004,9 @@ bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac return true; } -bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const +bool GeometryComponent::attribute_exists(const AttributeIDRef &attribute_id) const { - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); if (attribute) { return true; } @@ -955,16 +1014,17 @@ bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name } std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data( - const StringRef attribute_name) const + const AttributeIDRef &attribute_id) const { 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; - }); + this->attribute_foreach( + [&](const AttributeIDRef ¤t_attribute_id, const AttributeMetaData &meta_data) { + if (attribute_id == current_attribute_id) { + result = meta_data; + return false; + } + return true; + }); return result; } @@ -977,11 +1037,11 @@ static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type( } std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_read( - const StringRef attribute_name, + const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type) const { - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); if (!attribute) { return {}; } @@ -1007,13 +1067,13 @@ std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_r } std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_try_get_for_read( - const StringRef attribute_name, const AttributeDomain domain) const + const AttributeIDRef &attribute_id, const AttributeDomain domain) const { if (!this->attribute_domain_supported(domain)) { return {}; } - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); if (!attribute) { return {}; } @@ -1026,9 +1086,9 @@ std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_try_get_for_ } blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( - const blender::StringRef attribute_name, const CustomDataType data_type) const + const AttributeIDRef &attribute_id, const CustomDataType data_type) const { - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); if (!attribute) { return {}; } @@ -1043,13 +1103,13 @@ blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( } std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_get_for_read( - const StringRef attribute_name, + const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const void *default_value) const { std::unique_ptr<blender::bke::GVArray> varray = this->attribute_try_get_for_read( - attribute_name, domain, data_type); + attribute_id, domain, data_type); if (varray) { return varray; } @@ -1065,15 +1125,22 @@ class GVMutableAttribute_For_OutputAttribute : public blender::fn::GVMutableArray_For_GMutableSpan { public: GeometryComponent *component; - std::string final_name; + std::string attribute_name; + blender::bke::WeakAnonymousAttributeID anonymous_attribute_id; 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)) + const AttributeIDRef &attribute_id) + : blender::fn::GVMutableArray_For_GMutableSpan(data), component(&component) { + if (attribute_id.is_named()) { + this->attribute_name = attribute_id.name(); + } + else { + const AnonymousAttributeID *anonymous_id = &attribute_id.anonymous_id(); + BKE_anonymous_attribute_id_increment_weak(anonymous_id); + this->anonymous_attribute_id = blender::bke::WeakAnonymousAttributeID{anonymous_id}; + } } ~GVMutableAttribute_For_OutputAttribute() override @@ -1083,7 +1150,7 @@ class GVMutableAttribute_For_OutputAttribute } }; -static void save_output_attribute(blender::bke::OutputAttribute &output_attribute) +static void save_output_attribute(OutputAttribute &output_attribute) { using namespace blender; using namespace blender::fn; @@ -1093,21 +1160,28 @@ static void save_output_attribute(blender::bke::OutputAttribute &output_attribut dynamic_cast<GVMutableAttribute_For_OutputAttribute &>(output_attribute.varray()); GeometryComponent &component = *varray.component; - const StringRefNull name = varray.final_name; + AttributeIDRef attribute_id; + if (!varray.attribute_name.empty()) { + attribute_id = varray.attribute_name; + } + else { + attribute_id = varray.anonymous_attribute_id.extract(); + } const AttributeDomain domain = output_attribute.domain(); const CustomDataType data_type = output_attribute.custom_data_type(); const CPPType &cpp_type = output_attribute.cpp_type(); - 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()); + component.attribute_try_delete(attribute_id); + if (!component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault())) { + if (!varray.attribute_name.empty()) { + CLOG_WARN(&LOG, + "Could not create the '%s' attribute with type '%s'.", + varray.attribute_name.c_str(), + cpp_type.name().c_str()); + } return; } - WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(name); + WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(attribute_id); BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer); for (const int i : IndexRange(varray.size())) { varray.get(i, buffer); @@ -1115,19 +1189,18 @@ static void save_output_attribute(blender::bke::OutputAttribute &output_attribut } } -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) +static OutputAttribute create_output_attribute(GeometryComponent &component, + const AttributeIDRef &attribute_id, + const AttributeDomain domain, + const CustomDataType data_type, + const bool ignore_old_values, + const void *default_value) { using namespace blender; using namespace blender::fn; using namespace blender::bke; - if (attribute_name.is_empty()) { + if (!attribute_id) { return {}; } @@ -1135,7 +1208,8 @@ static blender::bke::OutputAttribute create_output_attribute( BLI_assert(cpp_type != nullptr); const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions(); - if (component.attribute_is_builtin(attribute_name)) { + if (attribute_id.is_named() && component.attribute_is_builtin(attribute_id.name())) { + const StringRef attribute_name = attribute_id.name(); WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); if (!attribute) { if (default_value) { @@ -1169,18 +1243,18 @@ static blender::bke::OutputAttribute create_output_attribute( const int domain_size = component.attribute_domain_size(domain); - WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); + WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_id); 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)); + attribute_id, domain, data_type, AttributeInitVArray(&default_varray)); } else { - component.attribute_try_create(attribute_name, domain, data_type, AttributeInitDefault()); + component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault()); } - attribute = component.attribute_try_get_for_write(attribute_name); + attribute = component.attribute_try_get_for_write(attribute_id); if (!attribute) { /* Can't create the attribute. */ return {}; @@ -1202,28 +1276,88 @@ static blender::bke::OutputAttribute create_output_attribute( 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); + attribute_id, 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); + GMutableSpan{*cpp_type, data, domain_size}, component, attribute_id); return OutputAttribute(std::move(varray), domain, save_output_attribute, true); } -blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output( - const StringRef attribute_name, - const AttributeDomain domain, - const CustomDataType data_type, - const void *default_value) +OutputAttribute GeometryComponent::attribute_try_get_for_output(const AttributeIDRef &attribute_id, + const AttributeDomain domain, + const CustomDataType data_type, + const void *default_value) { - return create_output_attribute(*this, attribute_name, domain, data_type, false, default_value); + return create_output_attribute(*this, attribute_id, domain, data_type, false, default_value); } -blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output_only( - const blender::StringRef attribute_name, +OutputAttribute GeometryComponent::attribute_try_get_for_output_only( + const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type) { - return create_output_attribute(*this, attribute_name, domain, data_type, true, nullptr); + return create_output_attribute(*this, attribute_id, domain, data_type, true, nullptr); +} + +namespace blender::bke { + +const GVArray *AttributeFieldInput::get_varray_for_context(const fn::FieldContext &context, + IndexMask UNUSED(mask), + ResourceScope &scope) const +{ + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast<const GeometryComponentFieldContext *>(&context)) { + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + const CustomDataType data_type = cpp_type_to_custom_data_type(*type_); + GVArrayPtr attribute = component.attribute_try_get_for_read(name_, domain, data_type); + return scope.add(std::move(attribute), __func__); + } + return nullptr; +} + +uint64_t AttributeFieldInput::hash() const +{ + return get_default_hash_2(name_, type_); +} + +bool AttributeFieldInput::is_equal_to(const fn::FieldNode &other) const +{ + if (const AttributeFieldInput *other_typed = dynamic_cast<const AttributeFieldInput *>(&other)) { + return name_ == other_typed->name_ && type_ == other_typed->type_; + } + return false; +} + +const GVArray *AnonymousAttributeFieldInput::get_varray_for_context( + const fn::FieldContext &context, IndexMask UNUSED(mask), ResourceScope &scope) const +{ + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast<const GeometryComponentFieldContext *>(&context)) { + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + const CustomDataType data_type = cpp_type_to_custom_data_type(*type_); + GVArrayPtr attribute = component.attribute_try_get_for_read( + anonymous_id_.get(), domain, data_type); + return scope.add(std::move(attribute), __func__); + } + return nullptr; +} + +uint64_t AnonymousAttributeFieldInput::hash() const +{ + return get_default_hash_2(anonymous_id_.get(), type_); } + +bool AnonymousAttributeFieldInput::is_equal_to(const fn::FieldNode &other) const +{ + if (const AnonymousAttributeFieldInput *other_typed = + dynamic_cast<const AnonymousAttributeFieldInput *>(&other)) { + return anonymous_id_.get() == other_typed->anonymous_id_.get() && type_ == other_typed->type_; + } + return false; +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index b3a795faa30..261cb26d4e5 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -116,12 +116,13 @@ class BuiltinAttributeProvider { class DynamicAttributesProvider { public: virtual ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const = 0; + const AttributeIDRef &attribute_id) 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; + const AttributeIDRef &attribute_id) const = 0; + virtual bool try_delete(GeometryComponent &component, + const AttributeIDRef &attribute_id) const = 0; virtual bool try_create(GeometryComponent &UNUSED(component), - const StringRef UNUSED(attribute_name), + const AttributeIDRef &UNUSED(attribute_id), const AttributeDomain UNUSED(domain), const CustomDataType UNUSED(data_type), const AttributeInit &UNUSED(initializer)) const @@ -154,15 +155,15 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { } ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final; + const AttributeIDRef &attribute_id) const final; WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const final; + const AttributeIDRef &attribute_id) const final; - bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final; + bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final; bool try_create(GeometryComponent &component, - const StringRef attribute_name, + const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const AttributeInit &initializer) const final; @@ -231,10 +232,10 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { } ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final; + const AttributeIDRef &attribute_id) 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; + const AttributeIDRef &attribute_id) const final; + bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final; bool foreach_attribute(const GeometryComponent &component, const AttributeForeachCallback callback) const final; void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final; diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 5c18f6f3807..1c4f9c5a6ab 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -25,6 +25,7 @@ #include "DNA_curve_types.h" +#include "BKE_anonymous_attribute.hh" #include "BKE_curve.h" #include "BKE_spline.hh" @@ -37,6 +38,7 @@ using blender::MutableSpan; using blender::Span; using blender::StringRefNull; using blender::Vector; +using blender::bke::AttributeIDRef; blender::Span<SplinePtr> CurveEval::splines() const { @@ -330,13 +332,13 @@ void CurveEval::assert_valid_point_attributes() const return; } const int layer_len = splines_.first()->attributes.data.totlayer; - Map<StringRefNull, AttributeMetaData> map; + Map<AttributeIDRef, AttributeMetaData> map; for (const SplinePtr &spline : splines_) { BLI_assert(spline->attributes.data.totlayer == layer_len); spline->attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &meta_data) { + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { map.add_or_modify( - name, + attribute_id, [&](AttributeMetaData *map_data) { /* All unique attribute names should be added on the first spline. */ BLI_assert(spline == splines_.first()); diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 1a3200a9b6c..ad2d5d267d5 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -46,6 +46,7 @@ #include "BLT_translation.h" +#include "BKE_anonymous_attribute.h" #include "BKE_customdata.h" #include "BKE_customdata_file.h" #include "BKE_deform.h" @@ -2127,6 +2128,11 @@ bool CustomData_merge(const struct CustomData *source, if (flag & CD_FLAG_NOCOPY) { continue; } + if (layer->anonymous_id && + !BKE_anonymous_attribute_id_has_strong_references(layer->anonymous_id)) { + /* This attribute is not referenced anymore, so it can be treated as if it didn't exist. */ + continue; + } if (!(mask & CD_TYPE_AS_MASK(type))) { continue; } @@ -2166,6 +2172,11 @@ bool CustomData_merge(const struct CustomData *source, newlayer->active_mask = lastmask; newlayer->flag |= flag & (CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY); changed = true; + + if (layer->anonymous_id != NULL) { + BKE_anonymous_attribute_id_increment_weak(layer->anonymous_id); + newlayer->anonymous_id = layer->anonymous_id; + } } } @@ -2206,6 +2217,10 @@ static void customData_free_layer__internal(CustomDataLayer *layer, int totelem) { const LayerTypeInfo *typeInfo; + if (layer->anonymous_id != NULL) { + BKE_anonymous_attribute_id_decrement_weak(layer->anonymous_id); + layer->anonymous_id = NULL; + } if (!(layer->flag & CD_FLAG_NOFREE) && layer->data) { typeInfo = layerType_getInfo(layer->type); @@ -2649,6 +2664,27 @@ void *CustomData_add_layer_named(CustomData *data, return NULL; } +void *CustomData_add_layer_anonymous(struct CustomData *data, + int type, + eCDAllocType alloctype, + void *layerdata, + int totelem, + const AnonymousAttributeID *anonymous_id) +{ + const char *name = BKE_anonymous_attribute_id_internal_name(anonymous_id); + CustomDataLayer *layer = customData_add_layer__internal( + data, type, alloctype, layerdata, totelem, name); + CustomData_update_typemap(data); + + if (layer == NULL) { + return NULL; + } + + BKE_anonymous_attribute_id_increment_weak(anonymous_id); + layer->anonymous_id = anonymous_id; + return layer->data; +} + bool CustomData_free_layer(CustomData *data, int type, int totelem, int index) { const int index_first = CustomData_get_layer_index(data, type); @@ -2812,6 +2848,20 @@ void *CustomData_duplicate_referenced_layer_named(CustomData *data, return customData_duplicate_referenced_layer_index(data, layer_index, totelem); } +void *CustomData_duplicate_referenced_layer_anonymous(CustomData *data, + const int UNUSED(type), + const AnonymousAttributeID *anonymous_id, + const int totelem) +{ + for (int i = 0; i < data->totlayer; i++) { + if (data->layers[i].anonymous_id == anonymous_id) { + return customData_duplicate_referenced_layer_index(data, i, totelem); + } + } + BLI_assert_unreachable(); + return NULL; +} + void CustomData_duplicate_referenced_layers(CustomData *data, int totelem) { for (int i = 0; i < data->totlayer; i++) { @@ -4244,7 +4294,8 @@ void CustomData_blend_write_prepare(CustomData *data, for (i = 0, j = 0; i < totlayer; i++) { CustomDataLayer *layer = &data->layers[i]; - if (layer->flag & CD_FLAG_NOCOPY) { /* Layers with this flag set are not written to file. */ + /* Layers with this flag set are not written to file. */ + if ((layer->flag & CD_FLAG_NOCOPY) || layer->anonymous_id != NULL) { data->totlayer--; // CLOG_WARN(&LOG, "skipping layer %p (%s)", layer, layer->name); } diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index 0b6ba966974..a24b60ee288 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -892,7 +892,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { public: ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final + const AttributeIDRef &attribute_id) const final { const CurveEval *curve = get_curve_from_component_for_read(component); if (curve == nullptr || curve->splines().size() == 0) { @@ -902,13 +902,13 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { Span<SplinePtr> splines = curve->splines(); Vector<GSpan> spans; /* GSpan has no default constructor. */ spans.reserve(splines.size()); - std::optional<GSpan> first_span = splines[0]->attributes.get_for_read(attribute_name); + std::optional<GSpan> first_span = splines[0]->attributes.get_for_read(attribute_id); if (!first_span) { return {}; } spans.append(*first_span); for (const int i : IndexRange(1, splines.size() - 1)) { - std::optional<GSpan> span = splines[i]->attributes.get_for_read(attribute_name); + std::optional<GSpan> span = splines[i]->attributes.get_for_read(attribute_id); if (!span) { /* All splines should have the same set of data layers. It would be possible to recover * here and return partial data instead, but that would add a lot of complexity for a @@ -945,7 +945,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { /* This function is almost the same as #try_get_for_read, but without const. */ WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const final + const AttributeIDRef &attribute_id) const final { CurveEval *curve = get_curve_from_component_for_write(component); if (curve == nullptr || curve->splines().size() == 0) { @@ -955,13 +955,13 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { MutableSpan<SplinePtr> splines = curve->splines(); Vector<GMutableSpan> spans; /* GMutableSpan has no default constructor. */ spans.reserve(splines.size()); - std::optional<GMutableSpan> first_span = splines[0]->attributes.get_for_write(attribute_name); + std::optional<GMutableSpan> first_span = splines[0]->attributes.get_for_write(attribute_id); if (!first_span) { return {}; } spans.append(*first_span); for (const int i : IndexRange(1, splines.size() - 1)) { - std::optional<GMutableSpan> span = splines[i]->attributes.get_for_write(attribute_name); + std::optional<GMutableSpan> span = splines[i]->attributes.get_for_write(attribute_id); if (!span) { /* All splines should have the same set of data layers. It would be possible to recover * here and return partial data instead, but that would add a lot of complexity for a @@ -996,7 +996,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { return attribute; } - bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final + bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final { CurveEval *curve = get_curve_from_component_for_write(component); if (curve == nullptr) { @@ -1006,7 +1006,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { /* Reuse the boolean for all splines; we expect all splines to have the same attributes. */ bool layer_freed = false; for (SplinePtr &spline : curve->splines()) { - layer_freed = spline->attributes.remove(attribute_name); + layer_freed = spline->attributes.remove(attribute_id); } return layer_freed; } @@ -1034,7 +1034,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { } bool try_create(GeometryComponent &component, - const StringRef attribute_name, + const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const AttributeInit &initializer) const final @@ -1053,7 +1053,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { /* First check the one case that allows us to avoid copying the input data. */ if (splines.size() == 1 && initializer.type == AttributeInit::Type::MoveArray) { void *source_data = static_cast<const AttributeInitMove &>(initializer).data; - if (!splines[0]->attributes.create_by_move(attribute_name, data_type, source_data)) { + if (!splines[0]->attributes.create_by_move(attribute_id, data_type, source_data)) { MEM_freeN(source_data); return false; } @@ -1062,7 +1062,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { /* Otherwise just create a custom data layer on each of the splines. */ for (const int i : splines.index_range()) { - if (!splines[i]->attributes.create(attribute_name, data_type)) { + if (!splines[i]->attributes.create(attribute_id, data_type)) { /* If attribute creation fails on one of the splines, we cannot leave the custom data * layers in the previous splines around, so delete them before returning. However, * this is not an expected case. */ @@ -1076,7 +1076,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { return true; } - WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_name); + WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_id); /* We just created the attribute, it should exist. */ BLI_assert(write_attribute); diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index ef93a3f9b3f..9a4b8f4eb92 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -818,16 +818,20 @@ class VArray_For_VertexWeights final : public VArray<float> { class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { public: ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final + const AttributeIDRef &attribute_id) const final { BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); + if (!attribute_id.is_named()) { + return {}; + } const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); const Mesh *mesh = mesh_component.get_for_read(); if (mesh == nullptr) { return {}; } + const std::string name = attribute_id.name(); const int vertex_group_index = BLI_findstringindex( - &mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name)); + &mesh->vertex_group_names, name.c_str(), offsetof(bDeformGroup, name)); if (vertex_group_index < 0) { return {}; } @@ -843,17 +847,21 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { } WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const final + const AttributeIDRef &attribute_id) const final { BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); + if (!attribute_id.is_named()) { + return {}; + } MeshComponent &mesh_component = static_cast<MeshComponent &>(component); Mesh *mesh = mesh_component.get_for_write(); if (mesh == nullptr) { return {}; } + const std::string name = attribute_id.name(); const int vertex_group_index = BLI_findstringindex( - &mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name)); + &mesh->vertex_group_names, name.c_str(), offsetof(bDeformGroup, name)); if (vertex_group_index < 0) { return {}; } @@ -872,17 +880,21 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { ATTR_DOMAIN_POINT}; } - bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final + bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final { BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); + if (!attribute_id.is_named()) { + return false; + } MeshComponent &mesh_component = static_cast<MeshComponent &>(component); Mesh *mesh = mesh_component.get_for_write(); if (mesh == nullptr) { return true; } + const std::string name = attribute_id.name(); const int vertex_group_index = BLI_findstringindex( - &mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name)); + &mesh->vertex_group_names, name.c_str(), offsetof(bDeformGroup, name)); if (vertex_group_index < 0) { return false; } diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 376792b9b96..0e19324a3c1 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -331,7 +331,7 @@ void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set, void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> set_groups, Span<GeometryComponentType> component_types, const Set<std::string> &ignored_attributes, - Map<std::string, AttributeKind> &r_attributes) + Map<AttributeIDRef, AttributeKind> &r_attributes) { for (const GeometryInstanceGroup &set_group : set_groups) { const GeometrySet &set = set_group.geometry_set; @@ -341,23 +341,24 @@ void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> se } const GeometryComponent &component = *set.get_component_for_read(component_type); - component.attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { - if (ignored_attributes.contains(name)) { - return true; - } - auto add_info = [&](AttributeKind *attribute_kind) { - attribute_kind->domain = meta_data.domain; - attribute_kind->data_type = meta_data.data_type; - }; - auto modify_info = [&](AttributeKind *attribute_kind) { - attribute_kind->domain = meta_data.domain; /* TODO: Use highest priority domain. */ - attribute_kind->data_type = bke::attribute_data_type_highest_complexity( - {attribute_kind->data_type, meta_data.data_type}); - }; - - r_attributes.add_or_modify(name, add_info, modify_info); - return true; - }); + component.attribute_foreach( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (attribute_id.is_named() && ignored_attributes.contains(attribute_id.name())) { + return true; + } + auto add_info = [&](AttributeKind *attribute_kind) { + attribute_kind->domain = meta_data.domain; + attribute_kind->data_type = meta_data.data_type; + }; + auto modify_info = [&](AttributeKind *attribute_kind) { + attribute_kind->domain = meta_data.domain; /* TODO: Use highest priority domain. */ + attribute_kind->data_type = bke::attribute_data_type_highest_complexity( + {attribute_kind->data_type, meta_data.data_type}); + }; + + r_attributes.add_or_modify(attribute_id, add_info, modify_info); + return true; + }); } } } @@ -512,11 +513,11 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou static void join_attributes(Span<GeometryInstanceGroup> set_groups, Span<GeometryComponentType> component_types, - const Map<std::string, AttributeKind> &attribute_info, + const Map<AttributeIDRef, AttributeKind> &attribute_info, GeometryComponent &result) { - for (Map<std::string, AttributeKind>::Item entry : attribute_info.items()) { - StringRef name = entry.key; + for (Map<AttributeIDRef, AttributeKind>::Item entry : attribute_info.items()) { + const AttributeIDRef attribute_id = entry.key; const AttributeDomain domain_output = entry.value.domain; const CustomDataType data_type_output = entry.value.data_type; const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output); @@ -524,7 +525,7 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups, result.attribute_try_create( entry.key, domain_output, data_type_output, AttributeInitDefault()); - WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(name); + WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(attribute_id); if (!write_attribute || &write_attribute.varray->type() != cpp_type || write_attribute.domain != domain_output) { continue; @@ -543,7 +544,7 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups, continue; /* Domain size is 0, so no need to increment the offset. */ } GVArrayPtr source_attribute = component.attribute_try_get_for_read( - name, domain_output, data_type_output); + attribute_id, domain_output, data_type_output); if (source_attribute) { fn::GVArray_GSpan src_span{*source_attribute}; @@ -653,7 +654,7 @@ static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups, } /* Don't copy attributes that are stored directly in the mesh data structs. */ - Map<std::string, AttributeKind> attributes; + Map<AttributeIDRef, AttributeKind> attributes; geometry_set_gather_instances_attribute_info( set_groups, component_types, @@ -674,7 +675,7 @@ static void join_instance_groups_pointcloud(Span<GeometryInstanceGroup> set_grou PointCloudComponent &dst_component = result.get_component_for_write<PointCloudComponent>(); dst_component.replace(new_pointcloud); - Map<std::string, AttributeKind> attributes; + Map<AttributeIDRef, AttributeKind> attributes; geometry_set_gather_instances_attribute_info( set_groups, {GEO_COMPONENT_TYPE_POINT_CLOUD}, {"position"}, attributes); join_attributes(set_groups, @@ -708,7 +709,7 @@ static void join_instance_groups_curve(Span<GeometryInstanceGroup> set_groups, G CurveComponent &dst_component = result.get_component_for_write<CurveComponent>(); dst_component.replace(curve); - Map<std::string, AttributeKind> attributes; + Map<AttributeIDRef, AttributeKind> attributes; geometry_set_gather_instances_attribute_info( set_groups, {GEO_COMPONENT_TYPE_CURVE}, diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index be4fd7aac4f..950026b9d65 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5140,6 +5140,7 @@ static void registerGeometryNodes() register_node_type_geo_attribute_convert(); register_node_type_geo_attribute_curve_map(); register_node_type_geo_attribute_fill(); + register_node_type_geo_attribute_capture(); register_node_type_geo_attribute_map_range(); register_node_type_geo_attribute_math(); register_node_type_geo_attribute_mix(); @@ -5174,7 +5175,10 @@ static void registerGeometryNodes() register_node_type_geo_curve_trim(); register_node_type_geo_delete_geometry(); register_node_type_geo_edge_split(); + register_node_type_geo_input_index(); register_node_type_geo_input_material(); + register_node_type_geo_input_normal(); + register_node_type_geo_input_position(); register_node_type_geo_is_viewport(); register_node_type_geo_join_geometry(); register_node_type_geo_material_assign(); @@ -5202,6 +5206,7 @@ static void registerGeometryNodes() register_node_type_geo_select_by_handle_type(); register_node_type_geo_select_by_material(); register_node_type_geo_separate_components(); + register_node_type_geo_set_position(); register_node_type_geo_subdivision_surface(); register_node_type_geo_switch(); register_node_type_geo_transform(); |