Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r--source/blender/blenkernel/BKE_anonymous_attribute.h43
-rw-r--r--source/blender/blenkernel/BKE_anonymous_attribute.hh169
-rw-r--r--source/blender/blenkernel/BKE_attribute_access.hh98
-rw-r--r--source/blender/blenkernel/BKE_customdata.h12
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh114
-rw-r--r--source/blender/blenkernel/BKE_geometry_set_instances.hh9
-rw-r--r--source/blender/blenkernel/BKE_node.h5
-rw-r--r--source/blender/blenkernel/CMakeLists.txt3
-rw-r--r--source/blender/blenkernel/intern/anonymous_attribute.cc118
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc422
-rw-r--r--source/blender/blenkernel/intern/attribute_access_intern.hh23
-rw-r--r--source/blender/blenkernel/intern/curve_eval.cc8
-rw-r--r--source/blender/blenkernel/intern/customdata.c53
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curve.cc24
-rw-r--r--source/blender/blenkernel/intern/geometry_component_mesh.cc24
-rw-r--r--source/blender/blenkernel/intern/geometry_set_instances.cc53
-rw-r--r--source/blender/blenkernel/intern/node.cc5
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 &current_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();