diff options
11 files changed, 549 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() diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index d7d31a4ef92..1c42b9341a0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -26,6 +26,7 @@ #include "node_geometry_util.hh" +using blender::fn::GVArray_For_GSpan; using blender::fn::GVArray_For_Span; using blender::fn::GVArray_Typed; @@ -131,6 +132,35 @@ static SplinePtr resample_spline(const Spline &input_spline, const int count) input_spline, uniform_samples, interpolated_data_typed, output_spline->tilts()); } + output_spline->attributes.reallocate(count); + input_spline.attributes.foreach_attribute( + [&](StringRefNull name, const AttributeMetaData &meta_data) { + std::optional<GSpan> input_attribute = input_spline.attributes.get_for_read(name); + BLI_assert(input_attribute); + if (!output_spline->attributes.create(name, meta_data.data_type)) { + BLI_assert_unreachable(); + return false; + } + std::optional<GMutableSpan> output_attribute = output_spline->attributes.get_for_write( + name); + if (!output_attribute) { + BLI_assert_unreachable(); + return false; + } + GVArrayPtr interpolated_attribute = input_spline.interpolate_to_evaluated_points( + GVArray_For_GSpan(*input_attribute)); + attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + GVArray_Typed<T> interpolated_attribute_typed{*interpolated_attribute}; + sample_span_to_output_spline<T>(input_spline, + uniform_samples, + interpolated_attribute_typed, + (*output_attribute).typed<T>()); + }); + return true; + }, + ATTR_DOMAIN_POINT); + return output_spline; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 915ebc1e1f2..adfd924f185 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -337,6 +337,14 @@ static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, Ge } } + /* For now, remove all custom attributes, since they might have different types, + * or an attribute might not exist on all splines. */ + dst_curve->attributes.reallocate(dst_curve->splines().size()); + CustomData_reset(&dst_curve->attributes.data); + for (SplinePtr &spline : dst_curve->splines()) { + CustomData_reset(&spline->attributes.data); + } + dst_component.replace(dst_curve); } |