Commit: ceed37fc5cbb466a04b4b4f7afba5dcd561fdd6a Author: Hans Goudey Date: Sat Apr 9 12:46:30 2022 -0500 Branches: master https://developer.blender.org/rBceed37fc5cbb466a04b4b4f7afba5dcd561fdd6a
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 =================================================================== M source/blender/blenkernel/BKE_curves.hh M source/blender/blenkernel/CMakeLists.txt M source/blender/blenkernel/intern/curve_eval.cc A source/blender/blenkernel/intern/curve_poly.cc M source/blender/blenkernel/intern/curves_geometry.cc M source/blender/blenkernel/intern/geometry_component_curves.cc M source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc =================================================================== diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index c4edeae99a4..9fd023edcf2 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -158,6 +158,9 @@ class CurvesGeometry : public ::CurvesGeometry { /** Return the number of curves with each type. */ std::array<int, CURVE_TYPES_NUM> count_curve_types() const; + /** Return true if all of the curves have the provided type. */ + bool is_single_type(CurveType type) const; + Span<float3> positions() const; MutableSpan<float3> positions_for_write(); @@ -174,6 +177,13 @@ class CurvesGeometry : public ::CurvesGeometry { /** Mutable access to curve resolution. Call #tag_topology_changed after changes. */ MutableSpan<int> resolution_for_write(); + /** + * Which method to use for calculating the normals of evaluated points (#NormalMode). + * Call #tag_normals_changed after changes. + */ + VArray<int8_t> normal_mode() const; + MutableSpan<int8_t> normal_mode_for_write(); + /** * Handle types for Bezier control points. Call #tag_topology_changed after changes. */ @@ -280,6 +290,8 @@ class CurvesGeometry : public ::CurvesGeometry { Span<int> bezier_evaluated_offsets_for_curve(int curve_index) const; Span<float3> evaluated_positions() const; + Span<float3> evaluated_tangents() const; + Span<float3> evaluated_normals() const; /** * Return a cache of accumulated lengths along the curve. Each item is the length of the @@ -379,6 +391,31 @@ inline float3 decode_surface_bary_coord(const float2 &v) return {v.x, v.y, 1.0f - v.x - v.y}; } +namespace poly { + +/** + * Calculate the direction at every point, defined as the normalized average of the two neighboring + * segments (and if non-cyclic, the direction of the first and last segments). This is different + * than evaluating the derivative of the basis functions for curve types like NURBS, Bezier, or + * Catmull Rom, though the results may be similar. + */ +void calculate_tangents(Span<float3> positions, bool is_cyclic, MutableSpan<float3> tangents); + +/** + * Calculate directions perpendicular to the tangent at every point by rotating an arbitrary + * starting vector by the same rotation of each tangent. If the curve is cylic, propagate a + * correction through the entire to make sure the first and last normal align. + */ +void calculate_normals_minimum(Span<float3> tangents, bool cyclic, MutableSpan<float3> normals); + +/** + * Calculate a vector perpendicular to every tangent on the X-Y plane (unless the tangent is + * vertical, in that case use the X direction). + */ +void calculate_normals_z_up(Span<float3> tangents, MutableSpan<float3> normals); + +} // namespace poly + namespace bezier { /** @@ -586,6 +623,11 @@ inline IndexRange CurvesGeometry::curves_range() const return IndexRange(this->curves_num()); } +inline bool CurvesGeometry::is_single_type(const CurveType type) const +{ + return this->count_curve_types()[type] == this->curves_num(); +} + inline IndexRange CurvesGeometry::points_for_curve(const int index) const { /* Offsets are not allocated when there are no curves. */ diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 61131cff06d..aca8cdf916e 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -112,6 +112,7 @@ set(SRC intern/curve_deform.c intern/curve_eval.cc intern/curve_nurbs.cc + intern/curve_poly.cc intern/curve_to_mesh_convert.cc intern/curveprofile.cc intern/curves.cc diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 9b1fd510fa8..6e09d1e8f10 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -381,6 +381,7 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves) curves.geometry); VArray<int> resolution = geometry.resolution(); + VArray<int8_t> normal_mode = geometry.normal_mode(); VArray_Span<float> nurbs_weights{ src_component.attribute_get_for_read<float>("nurbs_weight", ATTR_DOMAIN_POINT, 0.0f)}; @@ -436,6 +437,7 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves) spline->positions().fill(float3(0)); spline->tilts().fill(0.0f); spline->radii().fill(1.0f); + spline->normal_mode = static_cast<NormalMode>(normal_mode[curve_index]); curve_eval->add_spline(std::move(spline)); } @@ -448,6 +450,7 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves) dst_component, {"curve_type", "resolution", + "normal_mode", "nurbs_weight", "nurbs_order", "knots_mode", @@ -468,6 +471,8 @@ Curves *curve_eval_to_curves(const CurveEval &curve_eval) geometry.offsets_for_write().copy_from(curve_eval.control_point_offsets()); MutableSpan<int8_t> curve_types = geometry.curve_types_for_write(); + OutputAttribute_Typed<int8_t> normal_mode = + dst_component.attribute_try_get_for_output_only<int8_t>("normal_mode", ATTR_DOMAIN_CURVE); OutputAttribute_Typed<float> nurbs_weight; OutputAttribute_Typed<int> nurbs_order; OutputAttribute_Typed<int8_t> nurbs_knots_mode; @@ -491,7 +496,7 @@ Curves *curve_eval_to_curves(const CurveEval &curve_eval) for (const int curve_index : curve_eval.splines().index_range()) { const Spline &spline = *curve_eval.splines()[curve_index]; curve_types[curve_index] = curve_eval.splines()[curve_index]->type(); - + normal_mode.as_span()[curve_index] = curve_eval.splines()[curve_index]->normal_mode; const IndexRange point_range = geometry.points_for_curve(curve_index); switch (spline.type()) { @@ -517,6 +522,7 @@ Curves *curve_eval_to_curves(const CurveEval &curve_eval) } } + normal_mode.save(); nurbs_weight.save(); nurbs_order.save(); nurbs_knots_mode.save(); diff --git a/source/blender/blenkernel/intern/curve_poly.cc b/source/blender/blenkernel/intern/curve_poly.cc new file mode 100644 index 00000000000..b0ed62d38dd --- /dev/null +++ b/source/blender/blenkernel/intern/curve_poly.cc @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include <algorithm> + +#include "BLI_math_vector.h" +#include "BLI_math_vector.hh" + +#include "BKE_curves.hh" + +namespace blender::bke::curves::poly { + +static float3 direction_bisect(const float3 &prev, const float3 &middle, const float3 &next) +{ + const float3 dir_prev = math::normalize(middle - prev); + const float3 dir_next = math::normalize(next - middle); + + const float3 result = math::normalize(dir_prev + dir_next); + if (UNLIKELY(math::is_zero(result))) { + return float3(0.0f, 0.0f, 1.0f); + } + return result; +} + +void calculate_tangents(const Span<float3> positions, + const bool is_cyclic, + MutableSpan<float3> tangents) +{ + BLI_assert(positions.size() == tangents.size()); + + if (positions.size() == 1) { + tangents.first() = float3(0.0f, 0.0f, 1.0f); + return; + } + + for (const int i : IndexRange(1, positions.size() - 2)) { + tangents[i] = direction_bisect(positions[i - 1], positions[i], positions[i + 1]); + } + + if (is_cyclic) { + const float3 &second_to_last = positions[positions.size() - 2]; + const float3 &last = positions.last(); + const float3 &first = positions.first(); + const float3 &second = positions[1]; + tangents.first() = direction_bisect(last, first, second); + tangents.last() = direction_bisect(second_to_last, last, first); + } + else { + tangents.first() = math::normalize(positions[1] - positions.first()); + tangents.last() = math::normalize(positions.last() - positions[positions.size() - 2]); + } +} + +static float3 rotate_direction_around_axis(const float3 &direction, + const float3 &axis, + const float angle) +{ + BLI_ASSERT_UNIT_V3(direction); + BLI_ASSERT_UNIT_V3(axis); + + const float3 axis_scaled = axis * math::dot(direction, axis); + const float3 diff = direction - axis_scaled; + const float3 cross = math::cross(axis, diff); + + return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle); +} + +void calculate_normals_z_up(const Span<float3> tangents, MutableSpan<float3> normals) +{ + BLI_assert(normals.size() == tangents.size()); + + /* Same as in `vec_to_quat`. */ + const float epsilon = 1e-4f; + for (const int i : normals.index_range()) { + const float3 &tangent = tangents[i]; + if (std::abs(tangent.x) + std::abs(tangent.y) < epsilon) { + normals[i] = {1.0f, 0.0f, 0.0f}; + } + else { + normals[i] = math::normalize(float3(tangent.y, -tangent.x, 0.0f)); + } + } +} + +/** + * Rotate the last normal in the same way the tangent has been rotated. + */ +static float3 calculate_next_normal(const float3 &last_normal, + const float3 &last_tangent, + const float3 ¤t_tangent) +{ + if (math::is_zero(last_tangent) || math::is_zero(current_tangent)) { + return last_normal; + } + const float angle = angle_normalized_v3v3(last_tangent, current_tangent); + if (angle != 0.0) { + const float3 axis = math::normalize(math::cross(last_tangent, current_tangent)); + return rotate_direction_around_axis(last_normal, axis, angle); + } + return last_normal; +} + +void calculate_normals_minimum(const Span<float3> tangents, + const bool cyclic, + MutableSpan<float3> normals) +{ + BLI_assert(normals.size() == tangents.size()); + + if (normals.is_empty()) { + return; + } + + const float epsilon = 1e-4f; + + /* Set initial normal. */ + const float3 &first_tangent = tangents.first(); + if (fabs(first_tangent.x @@ Diff output truncated at 10240 characters. @@ _______________________________________________ Bf-blender-cvs mailing list [email protected] List details, subscription details or unsubscribe: https://lists.blender.org/mailman/listinfo/bf-blender-cvs
