diff options
author | Hans Goudey <h.goudey@me.com> | 2022-04-09 20:46:30 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2022-04-09 20:46:30 +0300 |
commit | ceed37fc5cbb466a04b4b4f7afba5dcd561fdd6a (patch) | |
tree | 83f515a54846d868d57a10f14854df69cf4c82fd /source/blender/blenkernel/intern/curves_geometry.cc | |
parent | 69a4d113e8dd3f2f267536b2b93af2540f3a0978 (diff) |
Curves: Port tangent and normal calculation to the new data-block
Port the "Normal" and "Curve Tangent" nodes to the new curves data-block
to avoid the conversion to `CurveEval`. This should make them faster by
avoiding all that copying, but otherwise nothing else has changed.
This also includes a fix to move the normal mode as a built-in curve
attribute when converting to and from `CurveEval`. The attribute is
needed because the option is used implicitly in many nodes currently.
Differential Revision: https://developer.blender.org/D14609
Diffstat (limited to 'source/blender/blenkernel/intern/curves_geometry.cc')
-rw-r--r-- | source/blender/blenkernel/intern/curves_geometry.cc | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index cbf2c910771..5c89dfd4df5 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -25,6 +25,7 @@ static const std::string ATTR_RADIUS = "radius"; static const std::string ATTR_CURVE_TYPE = "curve_type"; static const std::string ATTR_CYCLIC = "cyclic"; static const std::string ATTR_RESOLUTION = "resolution"; +static const std::string ATTR_NORMAL_MODE = "normal_mode"; static const std::string ATTR_HANDLE_TYPE_LEFT = "handle_type_left"; static const std::string ATTR_HANDLE_TYPE_RIGHT = "handle_type_right"; static const std::string ATTR_HANDLE_POSITION_LEFT = "handle_left"; @@ -320,6 +321,15 @@ MutableSpan<int> CurvesGeometry::resolution_for_write() return get_mutable_attribute<int>(*this, ATTR_DOMAIN_CURVE, ATTR_RESOLUTION, 12); } +VArray<int8_t> CurvesGeometry::normal_mode() const +{ + return get_varray_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NORMAL_MODE, 0); +} +MutableSpan<int8_t> CurvesGeometry::normal_mode_for_write() +{ + return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NORMAL_MODE); +} + VArray<int8_t> CurvesGeometry::handle_types_left() const { return get_varray_attribute<int8_t>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_TYPE_LEFT, 0); @@ -638,6 +648,113 @@ Span<float3> CurvesGeometry::evaluated_positions() const return this->runtime->evaluated_position_cache; } +Span<float3> CurvesGeometry::evaluated_tangents() const +{ + if (!this->runtime->tangent_cache_dirty) { + return this->runtime->evaluated_tangent_cache; + } + + /* A double checked lock. */ + std::scoped_lock lock{this->runtime->tangent_cache_mutex}; + if (!this->runtime->tangent_cache_dirty) { + return this->runtime->evaluated_tangent_cache; + } + + threading::isolate_task([&]() { + const Span<float3> evaluated_positions = this->evaluated_positions(); + const VArray<bool> cyclic = this->cyclic(); + + this->runtime->evaluated_tangent_cache.resize(this->evaluated_points_num()); + MutableSpan<float3> tangents = this->runtime->evaluated_tangent_cache; + + threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) { + for (const int curve_index : curves_range) { + const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); + if (UNLIKELY(evaluated_points.is_empty())) { + continue; + } + curves::poly::calculate_tangents(evaluated_positions.slice(evaluated_points), + cyclic[curve_index], + tangents.slice(evaluated_points)); + } + }); + + /* Correct the first and last tangents of Bezier curves so that they align with the inner + * handles. This is a separate loop to avoid the cost when Bezier type curves are not used. */ + Vector<int64_t> bezier_indices; + const IndexMask bezier_mask = this->indices_for_curve_type(CURVE_TYPE_BEZIER, bezier_indices); + if (!bezier_mask.is_empty()) { + const Span<float3> positions = this->positions(); + const Span<float3> handles_left = this->handle_positions_left(); + const Span<float3> handles_right = this->handle_positions_right(); + + threading::parallel_for(bezier_mask.index_range(), 1024, [&](IndexRange range) { + for (const int curve_index : bezier_mask.slice(range)) { + const IndexRange points = this->points_for_curve(curve_index); + const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); + + if (handles_right[points.first()] != positions[points.first()]) { + tangents[evaluated_points.first()] = math::normalize(handles_right[points.first()] - + positions[points.first()]); + } + if (handles_left[points.last()] != positions[points.last()]) { + tangents[evaluated_points.last()] = math::normalize(positions[points.last()] - + handles_left[points.last()]); + } + } + }); + } + }); + + this->runtime->tangent_cache_dirty = false; + return this->runtime->evaluated_tangent_cache; +} + +Span<float3> CurvesGeometry::evaluated_normals() const +{ + if (!this->runtime->normal_cache_dirty) { + return this->runtime->evaluated_normal_cache; + } + + /* A double checked lock. */ + std::scoped_lock lock{this->runtime->normal_cache_mutex}; + if (!this->runtime->normal_cache_dirty) { + return this->runtime->evaluated_normal_cache; + } + + threading::isolate_task([&]() { + const Span<float3> evaluated_tangents = this->evaluated_tangents(); + const VArray<bool> cyclic = this->cyclic(); + const VArray<int8_t> normal_mode = this->normal_mode(); + + this->runtime->evaluated_normal_cache.resize(this->evaluated_points_num()); + MutableSpan<float3> evaluated_normals = this->runtime->evaluated_normal_cache; + + threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) { + for (const int curve_index : curves_range) { + const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index); + if (UNLIKELY(evaluated_points.is_empty())) { + continue; + } + switch (normal_mode[curve_index]) { + case NORMAL_MODE_Z_UP: + curves::poly::calculate_normals_z_up(evaluated_tangents.slice(evaluated_points), + evaluated_normals.slice(evaluated_points)); + break; + case NORMAL_MODE_MINIMUM_TWIST: + curves::poly::calculate_normals_minimum(evaluated_tangents.slice(evaluated_points), + cyclic[curve_index], + evaluated_normals.slice(evaluated_points)); + break; + } + } + }); + }); + + this->runtime->normal_cache_dirty = false; + return this->runtime->evaluated_normal_cache; +} + void CurvesGeometry::interpolate_to_evaluated(const int curve_index, const GSpan src, GMutableSpan dst) const |