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:
authorHans Goudey <h.goudey@me.com>2021-05-19 20:22:09 +0300
committerHans Goudey <h.goudey@me.com>2021-05-19 20:22:09 +0300
commit627f3571271e5f1a416314fb73f8e3c613271db3 (patch)
tree913e453295db56a9099262e6e3b30b8fdb90335c /source/blender/blenkernel
parent192a3f1a05d00f1f10f32861c098b66f78cff3e4 (diff)
Geometry Nodes: Support for dynamic attributes on curve splines
With this patch you will be able to add and remove attributes from curve data inside of geometry nodes. The following is currently implemented: * Adding attributes with any data type to splines or spline points. * Support for working with multiple splines at the same time. * Interaction with the three builtin point attributes. * Resampling attributes in the resample node. The following is not implemented in this patch: * Joining attributes when joining splines with the join geometry node. * Domain interpolation between spline and point domains. * More efficient ways to call attribute operations once per spline. Differential Revision: https://developer.blender.org/D11251
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r--source/blender/blenkernel/BKE_attribute_access.hh37
-rw-r--r--source/blender/blenkernel/BKE_spline.hh14
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc100
-rw-r--r--source/blender/blenkernel/intern/curve_eval.cc52
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curve.cc296
-rw-r--r--source/blender/blenkernel/intern/geometry_set_instances.cc11
-rw-r--r--source/blender/blenkernel/intern/spline_bezier.cc4
-rw-r--r--source/blender/blenkernel/intern/spline_nurbs.cc4
-rw-r--r--source/blender/blenkernel/intern/spline_poly.cc4
9 files changed, 511 insertions, 11 deletions
diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh
index 358daa40723..d81d66951cc 100644
--- a/source/blender/blenkernel/BKE_attribute_access.hh
+++ b/source/blender/blenkernel/BKE_attribute_access.hh
@@ -37,6 +37,11 @@
struct AttributeMetaData {
AttributeDomain domain;
CustomDataType data_type;
+
+ constexpr friend bool operator==(AttributeMetaData a, AttributeMetaData b)
+ {
+ return (a.domain == b.domain) && (a.data_type == b.data_type);
+ }
};
/**
@@ -305,4 +310,36 @@ template<typename T> class OutputAttribute_Typed {
}
};
+/**
+ * A basic container around DNA CustomData so that its users
+ * don't have to implement special copy and move constructors.
+ */
+class CustomDataAttributes {
+ /**
+ * #CustomData needs a size to be freed, and unfortunately it isn't stored in the struct
+ * itself, so keep track of the size here so this class can implement its own destructor.
+ * If the implementation of the attribute storage changes, this could be removed.
+ */
+ int size_;
+
+ public:
+ CustomData data;
+
+ CustomDataAttributes();
+ ~CustomDataAttributes();
+ CustomDataAttributes(const CustomDataAttributes &other);
+ CustomDataAttributes(CustomDataAttributes &&other);
+
+ void reallocate(const int size);
+
+ std::optional<blender::fn::GSpan> get_for_read(const blender::StringRef name) const;
+ 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);
+
+ bool foreach_attribute(const AttributeForeachCallback callback,
+ const AttributeDomain domain) const;
+};
+
} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh
index c3386d12b02..9e5552082af 100644
--- a/source/blender/blenkernel/BKE_spline.hh
+++ b/source/blender/blenkernel/BKE_spline.hh
@@ -28,6 +28,7 @@
#include "BLI_float4x4.hh"
#include "BLI_vector.hh"
+#include "BKE_attribute_access.hh"
#include "BKE_attribute_math.hh"
struct Curve;
@@ -74,6 +75,8 @@ class Spline {
/* Only #Zup is supported at the moment. */
NormalCalculationMode normal_mode;
+ blender::bke::CustomDataAttributes attributes;
+
protected:
Type type_;
bool is_cyclic_ = false;
@@ -99,7 +102,10 @@ class Spline {
{
}
Spline(Spline &other)
- : normal_mode(other.normal_mode), type_(other.type_), is_cyclic_(other.is_cyclic_)
+ : normal_mode(other.normal_mode),
+ attributes(other.attributes),
+ type_(other.type_),
+ is_cyclic_(other.is_cyclic_)
{
}
@@ -482,8 +488,10 @@ class CurveEval {
blender::Vector<SplinePtr> splines_;
public:
+ blender::bke::CustomDataAttributes attributes;
+
CurveEval() = default;
- CurveEval(const CurveEval &other)
+ CurveEval(const CurveEval &other) : attributes(other.attributes)
{
for (const SplinePtr &spline : other.splines()) {
this->add_spline(spline->copy());
@@ -502,6 +510,8 @@ class CurveEval {
blender::Array<int> control_point_offsets() const;
blender::Array<int> evaluated_point_offsets() const;
+
+ void assert_valid_point_attributes() const;
};
std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &curve);
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index c24630c5666..62833e10438 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -45,6 +45,7 @@ using blender::Set;
using blender::StringRef;
using blender::StringRefNull;
using blender::fn::GMutableSpan;
+using blender::fn::GSpan;
namespace blender::bke {
@@ -590,6 +591,105 @@ void NamedLegacyCustomDataProvider::foreach_domain(
callback(domain_);
}
+CustomDataAttributes::CustomDataAttributes()
+{
+ CustomData_reset(&data);
+ size_ = 0;
+}
+
+CustomDataAttributes::~CustomDataAttributes()
+{
+ CustomData_free(&data, size_);
+}
+
+CustomDataAttributes::CustomDataAttributes(const CustomDataAttributes &other)
+{
+ size_ = other.size_;
+ CustomData_copy(&other.data, &data, CD_MASK_ALL, CD_DUPLICATE, size_);
+}
+
+CustomDataAttributes::CustomDataAttributes(CustomDataAttributes &&other)
+{
+ size_ = other.size_;
+ data = other.data;
+ CustomData_reset(&other.data);
+}
+
+std::optional<GSpan> CustomDataAttributes::get_for_read(const StringRef name) const
+{
+ BLI_assert(size_ != 0);
+ for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
+ if (layer.name == name) {
+ 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_);
+ }
+ }
+ return {};
+}
+
+std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const StringRef name)
+{
+ BLI_assert(size_ != 0);
+ for (CustomDataLayer &layer : MutableSpan(data.layers, data.totlayer)) {
+ if (layer.name == name) {
+ 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_);
+ }
+ }
+ return {};
+}
+
+bool CustomDataAttributes::create(const StringRef name, 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);
+ return result != nullptr;
+}
+
+bool CustomDataAttributes::create_by_move(const blender::StringRef name,
+ 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);
+ return result != nullptr;
+}
+
+bool CustomDataAttributes::remove(const blender::StringRef name)
+{
+ bool result = false;
+ for (const int i : IndexRange(data.totlayer)) {
+ const CustomDataLayer &layer = data.layers[i];
+ if (layer.name == name) {
+ CustomData_free_layer(&data, layer.type, size_, i);
+ result = true;
+ }
+ }
+ return result;
+}
+
+void CustomDataAttributes::reallocate(const int size)
+{
+ size_ = size;
+ CustomData_realloc(&data, size);
+}
+
+bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback callback,
+ const AttributeDomain domain) const
+{
+ for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
+ AttributeMetaData meta_data{domain, (CustomDataType)layer.type};
+ if (!callback(layer.name, meta_data)) {
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace blender::bke
/* -------------------------------------------------------------------- */
diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc
index 1679f21516a..9cafe1124b1 100644
--- a/source/blender/blenkernel/intern/curve_eval.cc
+++ b/source/blender/blenkernel/intern/curve_eval.cc
@@ -16,7 +16,9 @@
#include "BLI_array.hh"
#include "BLI_listbase.h"
+#include "BLI_map.hh"
#include "BLI_span.hh"
+#include "BLI_string_ref.hh"
#include "DNA_curve_types.h"
@@ -26,7 +28,9 @@
using blender::Array;
using blender::float3;
using blender::float4x4;
+using blender::Map;
using blender::Span;
+using blender::StringRefNull;
blender::Span<SplinePtr> CurveEval::splines() const
{
@@ -38,6 +42,9 @@ blender::MutableSpan<SplinePtr> CurveEval::splines()
return splines_;
}
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all splines.
+ */
void CurveEval::add_spline(SplinePtr spline)
{
splines_.append(std::move(spline));
@@ -178,7 +185,7 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
bezt.radius,
bezt.tilt);
}
-
+ spline->attributes.reallocate(spline->size());
curve->add_spline(std::move(spline));
break;
}
@@ -192,7 +199,7 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
spline->add_point(bp.vec, bp.radius, bp.tilt, bp.vec[3]);
}
-
+ spline->attributes.reallocate(spline->size());
curve->add_spline(std::move(spline));
break;
}
@@ -203,7 +210,7 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
spline->add_point(bp.vec, bp.radius, bp.tilt);
}
-
+ spline->attributes.reallocate(spline->size());
curve->add_spline(std::move(spline));
break;
}
@@ -214,6 +221,9 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
}
}
+ /* Though the curve has no attributes, this is necessary to properly set the custom data size. */
+ curve->attributes.reallocate(curve->splines().size());
+
/* Note: Normal mode is stored separately in each spline to facilitate combining splines
* from multiple curve objects, where the value may be different. */
const Spline::NormalCalculationMode normal_mode = normal_mode_from_dna_curve(
@@ -224,3 +234,39 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
return curve;
}
+
+/**
+ * Check the invariants that curve control point attributes should always uphold, necessary
+ * because attributes are stored on splines rather than in a flat array on the curve:
+ * - The same set of attributes exists on every spline.
+ * - Attributes with the same name have the same type on every spline.
+ */
+void CurveEval::assert_valid_point_attributes() const
+{
+#ifdef DEBUG
+ if (splines_.size() == 0) {
+ return;
+ }
+ const int layer_len = splines_.first()->attributes.data.totlayer;
+ Map<StringRefNull, 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) {
+ map.add_or_modify(
+ name,
+ [&](AttributeMetaData *map_data) {
+ /* All unique attribute names should be added on the first spline. */
+ BLI_assert(spline == splines_.first());
+ *map_data = meta_data;
+ },
+ [&](AttributeMetaData *map_data) {
+ /* Attributes on different splines should all have the same type. */
+ BLI_assert(meta_data == *map_data);
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+ }
+#endif
+} \ No newline at end of file
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
index d6c7cae2727..73c9dae92bc 100644
--- a/source/blender/blenkernel/intern/geometry_component_curve.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curve.cc
@@ -22,6 +22,12 @@
#include "attribute_access_intern.hh"
+using blender::fn::GMutableSpan;
+using blender::fn::GSpan;
+using blender::fn::GVArray_For_GSpan;
+using blender::fn::GVArray_GSpan;
+using blender::fn::GVMutableArray_For_GMutableSpan;
+
/* -------------------------------------------------------------------- */
/** \name Geometry Component Implementation
* \{ */
@@ -445,6 +451,20 @@ template<typename T> class VMutableArray_For_SplinePoints final : public VMutabl
}
};
+template<typename T> GVArrayPtr point_data_gvarray(Array<Span<T>> spans, Array<int> offsets)
+{
+ return std::make_unique<fn::GVArray_For_EmbeddedVArray<T, VArray_For_SplinePoints<T>>>(
+ offsets.last(), std::move(spans), std::move(offsets));
+}
+
+template<typename T>
+GVMutableArrayPtr point_data_gvarray(Array<MutableSpan<T>> spans, Array<int> offsets)
+{
+ return std::make_unique<
+ fn::GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_SplinePoints<T>>>(
+ offsets.last(), std::move(spans), std::move(offsets));
+}
+
/**
* Virtual array implementation specifically for control point positions. This is only needed for
* Bezier splines, where adjusting the position also requires adjusting handle positions depending
@@ -581,8 +601,7 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
spans[i] = get_span_(*splines[i]);
}
- return std::make_unique<fn::GVArray_For_EmbeddedVArray<T, VArray_For_SplinePoints<T>>>(
- offsets.last(), std::move(spans), std::move(offsets));
+ return point_data_gvarray(spans, offsets);
}
GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const override
@@ -607,9 +626,7 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
}
}
- return std::make_unique<
- fn::GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_SplinePoints<T>>>(
- offsets.last(), std::move(spans), std::move(offsets));
+ return point_data_gvarray(spans, offsets);
}
bool try_delete(GeometryComponent &UNUSED(component)) const final
@@ -683,6 +700,256 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Dynamic Control Point Attributes
+ *
+ * The dynamic control point attribute implementation is very similar to the builtin attribute
+ * implementation-- it uses the same virtual array types. In order to work, this code depends on
+ * the fact that all a curve's splines will have the same attributes and they all have the same
+ * type.
+ * \{ */
+
+class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
+ private:
+ static constexpr uint64_t supported_types_mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 |
+ CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 |
+ CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL;
+
+ public:
+ ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr || curve->splines().size() == 0) {
+ return {};
+ }
+
+ 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);
+ 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);
+ 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
+ * situation we don't even expect to encounter. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ if (span->type() != spans.last().type()) {
+ /* Data layer types on separate splines do not match. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ spans.append(*span);
+ }
+
+ /* First check for the simpler situation when we can return a simpler span virtual array. */
+ if (spans.size() == 1) {
+ return {std::make_unique<GVArray_For_GSpan>(spans.first()), ATTR_DOMAIN_POINT};
+ }
+
+ ReadAttributeLookup attribute = {};
+ Array<int> offsets = curve->control_point_offsets();
+ attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ Array<Span<T>> data(splines.size());
+ for (const int i : splines.index_range()) {
+ data[i] = spans[i].typed<T>();
+ BLI_assert(data[i].data() != nullptr);
+ }
+ attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
+ });
+ return attribute;
+ }
+
+ /* 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
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr || curve->splines().size() == 0) {
+ return {};
+ }
+
+ 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);
+ 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);
+ 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
+ * situation we don't even expect to encounter. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ if (span->type() != spans.last().type()) {
+ /* Data layer types on separate splines do not match. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ spans.append(*span);
+ }
+
+ /* First check for the simpler situation when we can return a simpler span virtual array. */
+ if (spans.size() == 1) {
+ return {std::make_unique<GVMutableArray_For_GMutableSpan>(spans.first()), ATTR_DOMAIN_POINT};
+ }
+
+ WriteAttributeLookup attribute = {};
+ Array<int> offsets = curve->control_point_offsets();
+ attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ Array<MutableSpan<T>> data(splines.size());
+ for (const int i : splines.index_range()) {
+ data[i] = spans[i].typed<T>();
+ BLI_assert(data[i].data() != nullptr);
+ }
+ attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
+ });
+ return attribute;
+ }
+
+ bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr) {
+ return false;
+ }
+
+ bool layer_freed = false;
+ for (SplinePtr &spline : curve->splines()) {
+ spline->attributes.remove(attribute_name);
+ }
+ return layer_freed;
+ }
+
+ static GVArrayPtr varray_from_initializer(const AttributeInit &initializer,
+ const CustomDataType data_type,
+ const int total_size)
+ {
+ switch (initializer.type) {
+ case AttributeInit::Type::Default:
+ /* This function shouldn't be called in this case, since there
+ * is no need to copy anything to the new custom data array. */
+ BLI_assert_unreachable();
+ return {};
+ case AttributeInit::Type::VArray:
+ return static_cast<const AttributeInitVArray &>(initializer).varray->shallow_copy();
+ case AttributeInit::Type::MoveArray:
+ return std::make_unique<fn::GVArray_For_GSpan>(
+ GSpan(*bke::custom_data_type_to_cpp_type(data_type),
+ static_cast<const AttributeInitMove &>(initializer).data,
+ total_size));
+ }
+ BLI_assert_unreachable();
+ return {};
+ }
+
+ bool try_create(GeometryComponent &component,
+ const StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const AttributeInit &initializer) const final
+ {
+ BLI_assert(this->type_is_supported(data_type));
+ if (domain != ATTR_DOMAIN_POINT) {
+ return false;
+ }
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr || curve->splines().size() == 0) {
+ return false;
+ }
+
+ MutableSpan<SplinePtr> splines = curve->splines();
+
+ /* 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)) {
+ MEM_freeN(source_data);
+ return false;
+ }
+ return true;
+ }
+
+ /* 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 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. */
+ BLI_assert_unreachable();
+ return false;
+ }
+ }
+
+ /* With a default initializer type, we can keep the values at their initial values. */
+ if (initializer.type == AttributeInit::Type::Default) {
+ return true;
+ }
+
+ WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_name);
+ /* We just created the attribute, it should exist. */
+ BLI_assert(write_attribute);
+
+ const int total_size = curve->control_point_offsets().last();
+ GVArrayPtr source_varray = varray_from_initializer(initializer, data_type, total_size);
+ /* TODO: When we can call a variant of #set_all with a virtual array argument,
+ * this theoretically unnecessary materialize step could be removed. */
+ GVArray_GSpan source_varray_span{*source_varray};
+ write_attribute.varray->set_all(source_varray_span.data());
+
+ if (initializer.type == AttributeInit::Type::MoveArray) {
+ MEM_freeN(static_cast<const AttributeInitMove &>(initializer).data);
+ }
+
+ return true;
+ }
+
+ bool foreach_attribute(const GeometryComponent &component,
+ const AttributeForeachCallback callback) const final
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr || curve->splines().size() == 0) {
+ return false;
+ }
+
+ Span<SplinePtr> splines = curve->splines();
+
+ /* In a debug build, check that all corresponding custom data layers have the same type. */
+ curve->assert_valid_point_attributes();
+
+ /* Use the first spline as a representative for all the others. */
+ splines.first()->attributes.foreach_attribute(callback, ATTR_DOMAIN_POINT);
+
+ return true;
+ }
+
+ void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final
+ {
+ callback(ATTR_DOMAIN_POINT);
+ }
+
+ bool type_is_supported(CustomDataType data_type) const
+ {
+ return ((1ULL << data_type) & supported_types_mask) != 0;
+ }
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Attribute Provider Declaration
* \{ */
@@ -704,6 +971,20 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
make_cyclic_read_attribute,
make_cyclic_write_attribute);
+ static CustomDataAccessInfo spline_custom_data_access = {
+ [](GeometryComponent &component) -> CustomData * {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ return curve ? &curve->attributes.data : nullptr;
+ },
+ [](const GeometryComponent &component) -> const CustomData * {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ return curve ? &curve->attributes.data : nullptr;
+ },
+ nullptr};
+
+ static CustomDataAttributeProvider spline_custom_data(ATTR_DOMAIN_CURVE,
+ spline_custom_data_access);
+
static PositionAttributeProvider position;
static BuiltinPointAttributeProvider<float> radius(
@@ -720,7 +1001,10 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
[](Spline &spline) { return spline.tilts(); },
[](Spline &spline) { spline.mark_cache_invalid(); });
- return ComponentAttributeProviders({&position, &radius, &tilt, &resolution, &cyclic}, {});
+ static DynamicPointAttributeProvider point_custom_data;
+
+ return ComponentAttributeProviders({&position, &radius, &tilt, &resolution, &cyclic},
+ {&spline_custom_data, &point_custom_data});
}
} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc
index 97892075eca..81984321b70 100644
--- a/source/blender/blenkernel/intern/geometry_set_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_set_instances.cc
@@ -563,6 +563,17 @@ static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComp
}
}
+ for (SplinePtr &spline : new_curve->splines()) {
+ /* Spline instances should have no custom attributes, since they always come
+ * from original objects which currenty do not support custom attributes.
+ *
+ * This is only true as long as a GeometrySet cannot be instanced directly. */
+ BLI_assert(spline->attributes.data.totlayer == 0);
+ UNUSED_VARS_NDEBUG(spline);
+ }
+
+ new_curve->attributes.reallocate(new_curve->splines().size());
+
result.replace(new_curve);
}
diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc
index 58a8f46730a..0fd3efce033 100644
--- a/source/blender/blenkernel/intern/spline_bezier.cc
+++ b/source/blender/blenkernel/intern/spline_bezier.cc
@@ -55,6 +55,9 @@ void BezierSpline::set_resolution(const int value)
this->mark_cache_invalid();
}
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all points.
+ */
void BezierSpline::add_point(const float3 position,
const HandleType handle_type_start,
const float3 handle_position_start,
@@ -83,6 +86,7 @@ void BezierSpline::resize(const int size)
radii_.resize(size);
tilts_.resize(size);
this->mark_cache_invalid();
+ attributes.reallocate(size);
}
MutableSpan<float3> BezierSpline::positions()
diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc
index 7816f303e2e..cd3ebe9e680 100644
--- a/source/blender/blenkernel/intern/spline_nurbs.cc
+++ b/source/blender/blenkernel/intern/spline_nurbs.cc
@@ -65,6 +65,9 @@ void NURBSpline::set_order(const uint8_t value)
this->mark_cache_invalid();
}
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all points.
+ */
void NURBSpline::add_point(const float3 position,
const float radius,
const float tilt,
@@ -85,6 +88,7 @@ void NURBSpline::resize(const int size)
tilts_.resize(size);
weights_.resize(size);
this->mark_cache_invalid();
+ attributes.reallocate(size);
}
MutableSpan<float3> NURBSpline::positions()
diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc
index ab6f4704a88..5c33b0052fc 100644
--- a/source/blender/blenkernel/intern/spline_poly.cc
+++ b/source/blender/blenkernel/intern/spline_poly.cc
@@ -36,6 +36,9 @@ int PolySpline::size() const
return size;
}
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all points.
+ */
void PolySpline::add_point(const float3 position, const float radius, const float tilt)
{
positions_.append(position);
@@ -50,6 +53,7 @@ void PolySpline::resize(const int size)
radii_.resize(size);
tilts_.resize(size);
this->mark_cache_invalid();
+ attributes.reallocate(size);
}
MutableSpan<float3> PolySpline::positions()