diff options
Diffstat (limited to 'source/blender/blenkernel/intern/geometry_component_curve.cc')
-rw-r--r-- | source/blender/blenkernel/intern/geometry_component_curve.cc | 427 |
1 files changed, 426 insertions, 1 deletions
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index 7afecb66bcc..216f0930cf9 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -286,6 +286,415 @@ static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Builtin Control Point Attributes + * + * Attributes with a value for every control point. Most of the complexity here is due to the fact + * that we must provide access to the attribute data as if it was a contiguous array when it is + * really stored separately on each spline. That will be inherently rather slow, but these virtual + * array implementations try to make it workable in common situations. + * \{ */ + +static Array<int> control_point_offsets(const CurveEval &curve) +{ + Array<int> offsets(curve.splines.size() + 1); + + int size = 0; + for (const int spline_index : curve.splines.index_range()) { + offsets[spline_index] = size; + size += curve.splines[spline_index]->size(); + } + offsets.last() = size; + + return offsets; +} + +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, + const IndexMask mask, + MutableSpan<T> r_span) +{ + const int total_size = offsets.last(); + if (mask.is_range() && mask.as_range() == IndexRange(total_size)) { + for (const int spline_index : data.index_range()) { + const int offset = offsets[spline_index]; + const int next_offset = offsets[spline_index + 1]; + initialized_copy_n(data[spline_index].data(), next_offset - offset, r_span.data() + offset); + } + } + else { + int spline_index = 0; + for (const int i : r_span.index_range()) { + const int dst_index = mask[i]; + + while (offsets[spline_index] < dst_index) { + spline_index++; + } + + const int index_in_spline = dst_index - offsets[spline_index]; + r_span[dst_index] = data[spline_index][index_in_spline]; + } + } +} + +template<typename T> +static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data, + Span<int> offsets, + const IndexMask mask, + MutableSpan<T> r_span) +{ + 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 : data.index_range()) { + const int offset = offsets[spline_index]; + const int next_offset = offsets[spline_index + 1]; + uninitialized_copy_n(data[spline_index].data(), next_offset - offset, dst + offset); + } + } + else { + int spline_index = 0; + for (const int i : r_span.index_range()) { + const int dst_index = mask[i]; + + while (offsets[spline_index] < dst_index) { + spline_index++; + } + + const int index_in_spline = dst_index - offsets[spline_index]; + new (dst + dst_index) T(data[spline_index][index_in_spline]); + } + } +} + +/** + * Virtual array for any control point data accessed with spans and an offset array. + */ +template<typename T> class VArray_For_SplinePoints : public VArray<T> { + private: + const Array<Span<T>> data_; + Array<int> offsets_; + + public: + VArray_For_SplinePoints(Array<Span<T>> data, Array<int> offsets) + : VArray<T>(offsets.last()), data_(std::move(data)), offsets_(std::move(offsets)) + { + } + + T get_impl(const int64_t index) const final + { + const PointIndices indices = lookup_point_indices(offsets_, index); + return data_[indices.spline_index][indices.point_index]; + } + + void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final + { + point_attribute_materialize(data_.as_span(), offsets_, mask, r_span); + } + + void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final + { + point_attribute_materialize_to_uninitialized(data_.as_span(), offsets_, mask, r_span); + } +}; + +/** + * Mutable virtual array for any control point data accessed with spans and an offset array. + */ +template<typename T> class VMutableArray_For_SplinePoints final : public VMutableArray<T> { + private: + Array<MutableSpan<T>> data_; + Array<int> offsets_; + + public: + VMutableArray_For_SplinePoints(Array<MutableSpan<T>> data, Array<int> offsets) + : VMutableArray<T>(offsets.last()), data_(std::move(data)), offsets_(std::move(offsets)) + { + } + + T get_impl(const int64_t index) const final + { + const PointIndices indices = lookup_point_indices(offsets_, index); + return data_[indices.spline_index][indices.point_index]; + } + + void set_impl(const int64_t index, T value) final + { + const PointIndices indices = lookup_point_indices(offsets_, index); + data_[indices.spline_index][indices.point_index] = value; + } + + void set_all_impl(Span<T> src) final + { + for (const int spline_index : data_.index_range()) { + const int offset = offsets_[spline_index]; + const int next_offsets = offsets_[spline_index + 1]; + data_[spline_index].copy_from(src.slice(offset, next_offsets - offset)); + } + } + + void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final + { + point_attribute_materialize({(Span<T> *)data_.data(), data_.size()}, offsets_, mask, r_span); + } + + void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final + { + point_attribute_materialize_to_uninitialized( + {(Span<T> *)data_.data(), data_.size()}, offsets_, mask, r_span); + } +}; + +/** + * Virtual array implementation specifically for control point positions. This is only needed for + * Bezier splines, where adjusting the position also needs to adjust handle positions depending on + * the handle types. We pay a small price for this when other spline types are mixed with Bezier. + * + * \note There is no need to check the handle type to avoid changing auto handles, since + * retrieving write access to the position data will mark them for recomputation anyway. + */ +class VMutableArray_For_SplinePosition final : public VMutableArray<float3> { + private: + MutableSpan<SplinePtr> splines_; + Array<int> offsets_; + + public: + VMutableArray_For_SplinePosition(MutableSpan<SplinePtr> splines, Array<int> offsets) + : VMutableArray<float3>(offsets.last()), splines_(splines), offsets_(std::move(offsets)) + { + } + + float3 get_impl(const int64_t index) const final + { + const PointIndices indices = lookup_point_indices(offsets_, index); + return splines_[indices.spline_index]->positions()[indices.point_index]; + } + + void set_impl(const int64_t index, float3 value) final + { + const PointIndices indices = lookup_point_indices(offsets_, index); + Spline &spline = *splines_[indices.spline_index]; + if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(&spline)) { + const float3 delta = value - bezier_spline->positions()[indices.point_index]; + bezier_spline->handle_positions_left()[indices.point_index] += delta; + bezier_spline->handle_positions_right()[indices.point_index] += delta; + bezier_spline->positions()[indices.point_index] = value; + } + else { + spline.positions()[indices.point_index] = value; + } + } + + void set_all_impl(Span<float3> src) final + { + for (const int spline_index : splines_.index_range()) { + Spline &spline = *splines_[spline_index]; + const int offset = offsets_[spline_index]; + const int next_offset = offsets_[spline_index + 1]; + if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(&spline)) { + MutableSpan<float3> positions = bezier_spline->positions(); + MutableSpan<float3> handle_positions_left = bezier_spline->handle_positions_left(); + MutableSpan<float3> handle_positions_right = bezier_spline->handle_positions_right(); + for (const int i : IndexRange(next_offset - offset)) { + const float3 delta = src[offset + i] - positions[i]; + handle_positions_left[i] += delta; + handle_positions_right[i] += delta; + positions[i] = src[offset + i]; + } + } + else { + spline.positions().copy_from(src.slice(offset, next_offset - offset)); + } + } + } + + /** Utility so we can pass positions to the materialize functions above. */ + Array<Span<float3>> get_position_spans() const + { + Array<Span<float3>> spans(splines_.size()); + for (const int i : spans.index_range()) { + spans[i] = splines_[i]->positions(); + } + return spans; + } + + void materialize_impl(const IndexMask mask, MutableSpan<float3> r_span) const final + { + Array<Span<float3>> spans = this->get_position_spans(); + point_attribute_materialize(spans.as_span(), offsets_, mask, r_span); + } + + void materialize_to_uninitialized_impl(const IndexMask mask, + MutableSpan<float3> r_span) const final + { + Array<Span<float3>> spans = this->get_position_spans(); + point_attribute_materialize_to_uninitialized(spans.as_span(), offsets_, mask, r_span); + } +}; + +/** + * Provider for any builtin control point attribute that doesn't need + * special handling such as access to other arrays in the spline. + */ +template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttributeProvider { + protected: + using GetSpan = Span<T> (*)(const Spline &spline); + using GetMutableSpan = MutableSpan<T> (*)(Spline &spline); + using UpdateOnWrite = void (*)(Spline &spline); + const GetSpan get_span_; + const GetMutableSpan get_mutable_span_; + const UpdateOnWrite update_on_write_; + + public: + BuiltinPointAttributeProvider(std::string attribute_name, + const WritableEnum writable, + const GetSpan get_span, + const GetMutableSpan get_mutable_span, + const UpdateOnWrite update_on_write) + : BuiltinAttributeProvider(std::move(attribute_name), + ATTR_DOMAIN_POINT, + bke::cpp_type_to_custom_data_type(CPPType::get<T>()), + BuiltinAttributeProvider::NonCreatable, + writable, + BuiltinAttributeProvider::NonDeletable), + get_span_(get_span), + get_mutable_span_(get_mutable_span), + update_on_write_(update_on_write) + { + } + + GVArrayPtr try_get_for_read(const GeometryComponent &component) const + { + const CurveEval *curve = get_curve_from_component_for_read(component); + if (curve == nullptr) { + return {}; + } + + if (curve->splines.size() == 1) { + return std::make_unique<fn::GVArray_For_GSpan>(get_span_(*curve->splines.first())); + } + + Array<int> offsets = control_point_offsets(*curve); + Array<Span<T>> spans(curve->splines.size()); + for (const int i : curve->splines.index_range()) { + spans[i] = get_span_(*curve->splines[i]); + } + + return std::make_unique<fn::GVArray_For_EmbeddedVArray<T, VArray_For_SplinePoints<T>>>( + offsets.last(), std::move(spans), std::move(offsets)); + } + + GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const + { + CurveEval *curve = get_curve_from_component_for_write(component); + if (curve == nullptr) { + return {}; + } + + if (curve->splines.size() == 1) { + return std::make_unique<fn::GVMutableArray_For_GMutableSpan>( + get_mutable_span_(*curve->splines.first())); + } + + Array<int> offsets = control_point_offsets(*curve); + Array<MutableSpan<T>> spans(curve->splines.size()); + for (const int i : curve->splines.index_range()) { + spans[i] = get_mutable_span_(*curve->splines[i]); + if (update_on_write_) { + update_on_write_(*curve->splines[i]); + } + } + + return std::make_unique< + fn::GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_SplinePoints<T>>>( + offsets.last(), std::move(spans), std::move(offsets)); + } + + bool try_delete(GeometryComponent &UNUSED(component)) const final + { + return false; + } + + bool try_create(GeometryComponent &UNUSED(component), + const AttributeInit &UNUSED(initializer)) const final + { + return false; + } + + bool exists(const GeometryComponent &component) const final + { + return component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0; + } +}; + +/** + * Special attribute provider for the position attribute. Having this separate means we don't + * need to make #BuiltinPointAttributeProvider overly generic, and the special handling for the + * positions is more clear. + */ +class PositionAttributeProvider final : public BuiltinPointAttributeProvider<float3> { + public: + PositionAttributeProvider() + : BuiltinPointAttributeProvider( + "position", + BuiltinAttributeProvider::Writable, + [](const Spline &spline) { return spline.positions(); }, + [](Spline &spline) { return spline.positions(); }, + [](Spline &spline) { spline.mark_cache_invalid(); }) + { + } + + GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final + { + CurveEval *curve = get_curve_from_component_for_write(component); + if (curve == nullptr) { + return {}; + } + + /* Changing the positions requires recalculation of cached evaluated data in many cases. + * This could set more specific flags in the future to avoid unnecessary recomputation. */ + bool curve_has_bezier_spline = false; + for (SplinePtr &spline : curve->splines) { + if (spline->type() == Spline::Type::Bezier) { + curve_has_bezier_spline = true; + break; + } + } + + /* Use the regular position virtual array there are any bezier splines to potentially avoid + * using the special position virtual array when there are no Bezier splines anyway. */ + if (!curve_has_bezier_spline) { + return BuiltinPointAttributeProvider<float3>::try_get_for_write(component); + } + + for (SplinePtr &spline : curve->splines) { + spline->mark_cache_invalid(); + } + + Array<int> offsets = control_point_offsets(*curve); + return std::make_unique< + fn::GVMutableArray_For_EmbeddedVMutableArray<float3, VMutableArray_For_SplinePosition>>( + offsets.last(), curve->splines, std::move(offsets)); + } +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Attribute Provider Declaration * \{ */ @@ -307,7 +716,23 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() make_cyclic_read_attribute, make_cyclic_write_attribute); - return ComponentAttributeProviders({&resolution, &cyclic}, {}); + static PositionAttributeProvider position; + + static BuiltinPointAttributeProvider<float> radius( + "radius", + BuiltinAttributeProvider::Writable, + [](const Spline &spline) { return spline.radii(); }, + [](Spline &spline) { return spline.radii(); }, + nullptr); + + static BuiltinPointAttributeProvider<float> tilt( + "tilt", + BuiltinAttributeProvider::Writable, + [](const Spline &spline) { return spline.tilts(); }, + [](Spline &spline) { return spline.tilts(); }, + [](Spline &spline) { spline.mark_cache_invalid(); }); + + return ComponentAttributeProviders({&position, &radius, &tilt, &resolution, &cyclic}, {}); } } // namespace blender::bke |