diff options
-rw-r--r-- | source/blender/blenkernel/BKE_geometry_set.hh | 4 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/geometry_component_curve.cc | 183 |
2 files changed, 171 insertions, 16 deletions
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 3b3856f11ab..0ed6eea7954 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -420,6 +420,10 @@ class CurveComponent : public GeometryComponent { CurveEval *get_for_write(); int attribute_domain_size(const AttributeDomain domain) const final; + std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain( + std::unique_ptr<blender::fn::GVArray> varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const final; bool is_empty() const final; diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index d08681da6ec..b9930e2fc50 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -14,11 +14,10 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "BKE_spline.hh" - #include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" #include "BKE_geometry_set.hh" +#include "BKE_spline.hh" #include "attribute_access_intern.hh" @@ -26,6 +25,7 @@ using blender::fn::GMutableSpan; using blender::fn::GSpan; using blender::fn::GVArray_For_GSpan; using blender::fn::GVArray_GSpan; +using blender::fn::GVArrayPtr; using blender::fn::GVMutableArray_For_GMutableSpan; /* -------------------------------------------------------------------- */ @@ -142,6 +142,171 @@ int CurveComponent::attribute_domain_size(const AttributeDomain domain) const return 0; } +namespace blender::bke { + +namespace { +struct PointIndices { + int spline_index; + int point_index; +}; +} // namespace +static PointIndices lookup_point_indices(Span<int> offsets, const int index) +{ + const int spline_index = std::upper_bound(offsets.begin(), offsets.end(), index) - + offsets.begin() - 1; + const int index_in_spline = index - offsets[spline_index]; + return {spline_index, index_in_spline}; +} + +/** + * Mix together all of a spline's control point values. + * + * \note Theoretically this interpolation does not need to compute all values at once. + * However, doing that makes the implementation simpler, and this can be optimized in the future if + * only some values are required. + */ +template<typename T> +static void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve, + const VArray<T> &old_values, + MutableSpan<T> r_values) +{ + const int splines_len = curve.splines().size(); + Array<int> offsets = curve.control_point_offsets(); + BLI_assert(r_values.size() == splines_len); + attribute_math::DefaultMixer<T> mixer(r_values); + + for (const int i_spline : IndexRange(splines_len)) { + const int spline_offset = offsets[i_spline]; + const int spline_point_len = offsets[i_spline + 1] - spline_offset; + for (const int i_point : IndexRange(spline_point_len)) { + const T value = old_values[spline_offset + i_point]; + mixer.mix_in(i_spline, value); + } + } + + mixer.finalize(); +} + +static GVArrayPtr adapt_curve_domain_point_to_spline(const CurveEval &curve, GVArrayPtr varray) +{ + GVArrayPtr new_varray; + attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { + using T = decltype(dummy); + if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { + Array<T> values(curve.splines().size()); + adapt_curve_domain_point_to_spline_impl<T>(curve, varray->typed<T>(), values); + new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); + } + }); + return new_varray; +} + +/** + * A virtual array implementation for the conversion of spline attributes to control point + * attributes. The goal is to avoid copying the spline value for every one of its control points + * unless it is necessary (in that case the materialize functions will be called). + */ +template<typename T> class VArray_For_SplineToPoint final : public VArray<T> { + /* Store existing data materialized if it was not already a span. This is expected + * to be worth it because a single spline's value will likely be accessed many times. */ + VArray_Span<T> original_data_; + Array<int> offsets_; + + public: + VArray_For_SplineToPoint(const VArray<T> &original_varray, Array<int> offsets) + : VArray<T>(offsets.last()), original_data_(original_varray), offsets_(std::move(offsets)) + { + } + + T get_impl(const int64_t index) const final + { + const PointIndices indices = lookup_point_indices(offsets_, index); + return original_data_[indices.spline_index]; + } + + void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final + { + const int total_size = offsets_.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + for (const int spline_index : original_data_.index_range()) { + const int offset = offsets_[spline_index]; + const int next_offset = offsets_[spline_index + 1]; + r_span.slice(offset, next_offset - offset).fill(original_data_[spline_index]); + } + } + else { + int spline_index = 0; + for (const int dst_index : mask) { + while (offsets_[spline_index] < dst_index) { + spline_index++; + } + r_span[dst_index] = original_data_[spline_index]; + } + } + } + + void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final + { + T *dst = r_span.data(); + const int total_size = offsets_.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + for (const int spline_index : original_data_.index_range()) { + const int offset = offsets_[spline_index]; + const int next_offset = offsets_[spline_index + 1]; + uninitialized_fill_n(dst + offset, next_offset - offset, original_data_[spline_index]); + } + } + else { + int spline_index = 0; + for (const int dst_index : mask) { + while (offsets_[spline_index] < dst_index) { + spline_index++; + } + new (dst + dst_index) T(original_data_[spline_index]); + } + } + } +}; + +static GVArrayPtr adapt_curve_domain_spline_to_point(const CurveEval &curve, GVArrayPtr varray) +{ + GVArrayPtr new_varray; + attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { + using T = decltype(dummy); + + Array<int> offsets = curve.control_point_offsets(); + new_varray = std::make_unique<fn::GVArray_For_EmbeddedVArray<T, VArray_For_SplineToPoint<T>>>( + offsets.last(), *varray->typed<T>(), std::move(offsets)); + }); + return new_varray; +} + +} // namespace blender::bke + +GVArrayPtr CurveComponent::attribute_try_adapt_domain(GVArrayPtr varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const +{ + if (!varray) { + return {}; + } + if (varray->size() == 0) { + return {}; + } + if (from_domain == to_domain) { + return varray; + } + + if (from_domain == ATTR_DOMAIN_POINT && to_domain == ATTR_DOMAIN_CURVE) { + return blender::bke::adapt_curve_domain_point_to_spline(*curve_, std::move(varray)); + } + if (from_domain == ATTR_DOMAIN_CURVE && to_domain == ATTR_DOMAIN_POINT) { + return blender::bke::adapt_curve_domain_spline_to_point(*curve_, std::move(varray)); + } + + return {}; +} + static CurveEval *get_curve_from_component_for_write(GeometryComponent &component) { BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); @@ -300,20 +465,6 @@ static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve) * array implementations try to make it workable in common situations. * \{ */ -namespace { -struct PointIndices { - int spline_index; - int point_index; -}; -} // namespace -static PointIndices lookup_point_indices(Span<int> offsets, const int index) -{ - const int spline_index = std::upper_bound(offsets.begin(), offsets.end(), index) - - offsets.begin() - 1; - const int index_in_spline = index - offsets[spline_index]; - return {spline_index, index_in_spline}; -} - template<typename T> static void point_attribute_materialize(Span<Span<T>> data, Span<int> offsets, |