Curves: Add cyclic curve offsets cache
This will be used to simplify the rendering of cyclic curves and improve the performance. The cache is just a prefix sum of the cyclic attribute. Pull Request: https://projects.blender.org/blender/blender/pulls/144444
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
* \brief Low-level operations for curves.
|
||||
*/
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
#include "BLI_bounds_types.hh"
|
||||
#include "BLI_implicit_sharing_ptr.hh"
|
||||
#include "BLI_index_mask_fwd.hh"
|
||||
@@ -94,6 +95,8 @@ class CurvesGeometryRuntime {
|
||||
};
|
||||
mutable SharedCache<EvaluatedOffsets> evaluated_offsets_cache;
|
||||
|
||||
mutable SharedCache<std::optional<Vector<int>>> cyclic_offsets_cache;
|
||||
|
||||
mutable SharedCache<Vector<curves::nurbs::BasisCache>> nurbs_basis_cache;
|
||||
|
||||
/**
|
||||
@@ -382,6 +385,12 @@ class CurvesGeometry : public ::CurvesGeometry {
|
||||
*/
|
||||
Span<int> bezier_evaluated_offsets_for_curve(int curve_index) const;
|
||||
|
||||
/**
|
||||
* A prefix sum of the cyclic attribute, in other words the number of cyclic curves that precede
|
||||
* each curve. Used for rendering. If there are no cyclic curves, `std::nullopt` is returned.
|
||||
*/
|
||||
std::optional<Span<int>> cyclic_offsets() const;
|
||||
|
||||
Span<float3> evaluated_positions() const;
|
||||
Span<float3> evaluated_tangents() const;
|
||||
Span<float3> evaluated_normals() const;
|
||||
|
||||
@@ -131,6 +131,7 @@ CurvesGeometry::CurvesGeometry(const CurvesGeometry &other)
|
||||
other.runtime->custom_knots_sharing_info,
|
||||
other.runtime->type_counts,
|
||||
other.runtime->evaluated_offsets_cache,
|
||||
other.runtime->cyclic_offsets_cache,
|
||||
other.runtime->nurbs_basis_cache,
|
||||
other.runtime->evaluated_position_cache,
|
||||
other.runtime->bounds_cache,
|
||||
@@ -715,6 +716,51 @@ OffsetIndices<int> CurvesGeometry::evaluated_points_by_curve() const
|
||||
return OffsetIndices<int>(runtime.evaluated_offsets_cache.data().evaluated_offsets);
|
||||
}
|
||||
|
||||
std::optional<Span<int>> CurvesGeometry::cyclic_offsets() const
|
||||
{
|
||||
this->runtime->cyclic_offsets_cache.ensure([&](std::optional<Vector<int>> &r_data) {
|
||||
const VArray<bool> cyclic = this->cyclic();
|
||||
|
||||
const auto ensure_vector = [&]() {
|
||||
if (r_data) {
|
||||
r_data->resize(cyclic.size() + 1);
|
||||
}
|
||||
else {
|
||||
r_data.emplace(cyclic.size() + 1);
|
||||
}
|
||||
return r_data->as_mutable_span();
|
||||
};
|
||||
|
||||
if (const std::optional<bool> single = cyclic.get_if_single()) {
|
||||
if (*single) {
|
||||
array_utils::fill_index_range(ensure_vector());
|
||||
}
|
||||
else {
|
||||
r_data = std::nullopt;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
MutableSpan span = ensure_vector();
|
||||
|
||||
int sum = 0;
|
||||
for (const int i : cyclic.index_range()) {
|
||||
span[i] = sum;
|
||||
if (cyclic[i]) {
|
||||
sum++;
|
||||
}
|
||||
}
|
||||
span.last() = sum;
|
||||
if (sum == 0) {
|
||||
r_data = std::nullopt;
|
||||
}
|
||||
});
|
||||
if (!this->runtime->cyclic_offsets_cache.data()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return this->runtime->cyclic_offsets_cache.data()->as_span();
|
||||
}
|
||||
|
||||
IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type,
|
||||
IndexMaskMemory &memory) const
|
||||
{
|
||||
@@ -1211,6 +1257,7 @@ void CurvesGeometry::tag_topology_changed()
|
||||
this->runtime->custom_knot_offsets_cache.tag_dirty();
|
||||
this->tag_positions_changed();
|
||||
this->runtime->evaluated_offsets_cache.tag_dirty();
|
||||
this->runtime->cyclic_offsets_cache.tag_dirty();
|
||||
this->runtime->nurbs_basis_cache.tag_dirty();
|
||||
this->runtime->max_material_index_cache.tag_dirty();
|
||||
this->runtime->check_type_counts = true;
|
||||
|
||||
@@ -85,6 +85,40 @@ TEST(curves_geometry, TypeCount)
|
||||
EXPECT_EQ(counts[CURVE_TYPE_NURBS], 3);
|
||||
}
|
||||
|
||||
TEST(curves_geometry, CyclicOffsets)
|
||||
{
|
||||
CurvesGeometry curves = create_basic_curves(100, 10);
|
||||
{
|
||||
const std::optional<Span<int>> cyclic_offsets = curves.cyclic_offsets();
|
||||
EXPECT_FALSE(cyclic_offsets.has_value());
|
||||
}
|
||||
{
|
||||
curves.cyclic_for_write().fill(true);
|
||||
curves.tag_topology_changed();
|
||||
const std::optional<Span<int>> cyclic_offsets = curves.cyclic_offsets();
|
||||
EXPECT_TRUE(cyclic_offsets.has_value());
|
||||
EXPECT_EQ_SPAN<int>(*cyclic_offsets, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
|
||||
}
|
||||
{
|
||||
curves.cyclic_for_write().fill(false);
|
||||
curves.tag_topology_changed();
|
||||
const std::optional<Span<int>> cyclic_offsets = curves.cyclic_offsets();
|
||||
EXPECT_FALSE(cyclic_offsets.has_value());
|
||||
}
|
||||
{
|
||||
curves.attributes_for_write().remove("cyclic");
|
||||
const std::optional<Span<int>> cyclic_offsets = curves.cyclic_offsets();
|
||||
EXPECT_FALSE(cyclic_offsets.has_value());
|
||||
}
|
||||
{
|
||||
curves.cyclic_for_write().copy_from(
|
||||
{false, true, false, true, false, false, false, false, true, false});
|
||||
curves.tag_topology_changed();
|
||||
const std::optional<Span<int>> cyclic_offsets = curves.cyclic_offsets();
|
||||
EXPECT_EQ_SPAN<int>(*cyclic_offsets, {0, 0, 1, 1, 2, 2, 2, 2, 2, 3, 3});
|
||||
}
|
||||
}
|
||||
|
||||
TEST(curves_geometry, CatmullRomEvaluation)
|
||||
{
|
||||
CurvesGeometry curves(4, 1);
|
||||
|
||||
Reference in New Issue
Block a user