[Bf-blender-cvs] [ceed37fc5cb] master: Curves: Port tangent and normal calculation to the new data-block

Hans Goudey noreply at git.blender.org
Sat Apr 9 19:46:40 CEST 2022


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 &current_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. @@



More information about the Bf-blender-cvs mailing list