[Bf-blender-cvs] [00ba51d37bf] master: Geometry Nodes: Port set handle nodes to new data-block

Hans Goudey noreply at git.blender.org
Fri Apr 1 15:12:48 CEST 2022


Commit: 00ba51d37bf5b152176409b393eafbb0ad9333e6
Author: Hans Goudey
Date:   Fri Apr 1 08:11:58 2022 -0500
Branches: master
https://developer.blender.org/rB00ba51d37bf5b152176409b393eafbb0ad9333e6

Geometry Nodes: Port set handle nodes to new data-block

This commit ports the "Set Handle Positions" and "Set Hanle Type"
nodes to use the new curves data-block. The nodes become simpler
and likely much faster too, though they're usually not the bottleneck
anyway.

Most of the code is ported from `BezierSpline` directly. The majority
of the complexity comes from the interaction between different
automatically calculated handle types. In comparison `BezierSpline`,
the calculation of auto handles is done eagerly-- mostly because it's
simpler. Eventually lazy calculation might be good to add.

Differential Revision: https://developer.blender.org/D14464

===================================================================

M	source/blender/blenkernel/BKE_curves.hh
M	source/blender/blenkernel/intern/curve_bezier.cc
M	source/blender/blenkernel/intern/curves_geometry.cc
M	source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc
M	source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
M	source/blender/nodes/geometry/nodes/node_geo_set_position.cc

===================================================================

diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh
index 67671e46ad4..f097acc497f 100644
--- a/source/blender/blenkernel/BKE_curves.hh
+++ b/source/blender/blenkernel/BKE_curves.hh
@@ -338,6 +338,8 @@ class CurvesGeometry : public ::CurvesGeometry {
   void translate(const float3 &translation);
   void transform(const float4x4 &matrix);
 
+  void calculate_bezier_auto_handles();
+
   void update_customdata_pointers();
 
   void remove_curves(IndexMask curves_to_delete);
@@ -396,10 +398,38 @@ void calculate_evaluated_offsets(Span<int8_t> handle_types_left,
                                  int resolution,
                                  MutableSpan<int> evaluated_offsets);
 
+/**
+ * Recalculate all auto (#BEZIER_HANDLE_AUTO) and vector (#BEZIER_HANDLE_VECTOR) handles with
+ * positions automatically derived from the neighboring control points, and update aligned
+ * (#BEZIER_HANDLE_ALIGN) handles to line up with neighboring non-aligned handles. The choices
+ * made here are relatively arbitrary, but having standardized behavior is essential.
+ */
+void calculate_auto_handles(bool cyclic,
+                            Span<int8_t> types_left,
+                            Span<int8_t> types_right,
+                            Span<float3> positions,
+                            MutableSpan<float3> positions_left,
+                            MutableSpan<float3> positions_right);
+
+/**
+ * Change the handles of a single control point, aligning any aligned (#BEZIER_HANDLE_ALIGN)
+ * handles on the other side of the control point.
+ *
+ * \note This ignores the inputs if the handle types are automatically calculated,
+ * so the types should be updated before-hand to be editable.
+ */
+void set_handle_position(const float3 &position,
+                         HandleType type,
+                         HandleType type_other,
+                         const float3 &new_handle,
+                         float3 &handle,
+                         float3 &handle_other);
+
 /**
  * Evaluate a cubic Bezier segment, using the "forward differencing" method.
- * A generic Bezier curve is made up by four points, but in many cases the first and last points
- * are referred to as the control points, and the middle points are the corresponding handles.
+ * A generic Bezier curve is made up by four points, but in many cases the first and last
+ * points are referred to as the control points, and the middle points are the corresponding
+ * handles.
  */
 void evaluate_segment(const float3 &point_0,
                       const float3 &point_1,
diff --git a/source/blender/blenkernel/intern/curve_bezier.cc b/source/blender/blenkernel/intern/curve_bezier.cc
index 0d3bb2e3a7d..30a5869c976 100644
--- a/source/blender/blenkernel/intern/curve_bezier.cc
+++ b/source/blender/blenkernel/intern/curve_bezier.cc
@@ -58,6 +58,131 @@ void calculate_evaluated_offsets(const Span<int8_t> handle_types_left,
   evaluated_offsets.last() = offset;
 }
 
+static float3 calculate_aligned_handle(const float3 &position,
+                                       const float3 &other_handle,
+                                       const float3 &aligned_handle)
+{
+  /* Keep track of the old length of the opposite handle. */
+  const float length = math::distance(aligned_handle, position);
+  /* Set the other handle to directly opposite from the current handle. */
+  const float3 dir = math::normalize(other_handle - position);
+  return position - dir * length;
+}
+
+static void calculate_point_handles(const HandleType type_left,
+                                    const HandleType type_right,
+                                    const float3 position,
+                                    const float3 prev_position,
+                                    const float3 next_position,
+                                    float3 &left,
+                                    float3 &right)
+{
+  if (ELEM(BEZIER_HANDLE_AUTO, type_left, type_right)) {
+    const float3 prev_diff = position - prev_position;
+    const float3 next_diff = next_position - position;
+    float prev_len = math::length(prev_diff);
+    float next_len = math::length(next_diff);
+    if (prev_len == 0.0f) {
+      prev_len = 1.0f;
+    }
+    if (next_len == 0.0f) {
+      next_len = 1.0f;
+    }
+    const float3 dir = next_diff / next_len + prev_diff / prev_len;
+
+    /* This magic number is unfortunate, but comes from elsewhere in Blender. */
+    const float len = math::length(dir) * 2.5614f;
+    if (len != 0.0f) {
+      if (type_left == BEZIER_HANDLE_AUTO) {
+        const float prev_len_clamped = std::min(prev_len, next_len * 5.0f);
+        left = position + dir * -(prev_len_clamped / len);
+      }
+      if (type_right == BEZIER_HANDLE_AUTO) {
+        const float next_len_clamped = std::min(next_len, prev_len * 5.0f);
+        right = position + dir * (next_len_clamped / len);
+      }
+    }
+  }
+
+  if (type_left == BEZIER_HANDLE_VECTOR) {
+    left = math::interpolate(position, prev_position, 1.0f / 3.0f);
+  }
+
+  if (type_right == BEZIER_HANDLE_VECTOR) {
+    right = math::interpolate(position, next_position, 1.0f / 3.0f);
+  }
+
+  /* When one of the handles is "aligned" handle, it must be aligned with the other, i.e. point in
+   * the opposite direction. Don't handle the case of two aligned handles, because code elsewhere
+   * should keep the pair consistent, and the relative locations aren't affected by other points
+   * anyway. */
+  if (type_left == BEZIER_HANDLE_ALIGN && type_right != BEZIER_HANDLE_ALIGN) {
+    left = calculate_aligned_handle(position, right, left);
+  }
+  else if (type_left != BEZIER_HANDLE_ALIGN && type_right == BEZIER_HANDLE_ALIGN) {
+    right = calculate_aligned_handle(position, left, right);
+  }
+}
+
+void set_handle_position(const float3 &position,
+                         const HandleType type,
+                         const HandleType type_other,
+                         const float3 &new_handle,
+                         float3 &handle,
+                         float3 &handle_other)
+{
+  /* Don't bother when the handle positions are calculated automatically anyway. */
+  if (ELEM(type, BEZIER_HANDLE_AUTO, BEZIER_HANDLE_VECTOR)) {
+    return;
+  }
+
+  handle = new_handle;
+  if (type_other == BEZIER_HANDLE_ALIGN) {
+    handle_other = calculate_aligned_handle(position, handle, handle_other);
+  }
+}
+
+void calculate_auto_handles(const bool cyclic,
+                            const Span<int8_t> types_left,
+                            const Span<int8_t> types_right,
+                            const Span<float3> positions,
+                            MutableSpan<float3> positions_left,
+                            MutableSpan<float3> positions_right)
+{
+  const int points_num = positions.size();
+  if (points_num == 1) {
+    return;
+  }
+
+  calculate_point_handles(HandleType(types_left.first()),
+                          HandleType(types_right.first()),
+                          positions.first(),
+                          cyclic ? positions.last() : 2.0f * positions.first() - positions[1],
+                          positions[1],
+                          positions_left.first(),
+                          positions_right.first());
+
+  threading::parallel_for(IndexRange(1, points_num - 2), 1024, [&](IndexRange range) {
+    for (const int i : range) {
+      calculate_point_handles(HandleType(types_left[i]),
+                              HandleType(types_right[i]),
+                              positions[i],
+                              positions[i - 1],
+                              positions[i + 1],
+                              positions_left[i],
+                              positions_right[i]);
+    }
+  });
+
+  calculate_point_handles(HandleType(types_left.last()),
+                          HandleType(types_right.last()),
+                          positions.last(),
+                          positions.last(1),
+                          cyclic ? positions.first() : 2.0f * positions.last() - positions.last(1),
+                          positions_left.last(),
+                          positions_right.last());
+}
+
 void evaluate_segment(const float3 &point_0,
                       const float3 &point_1,
                       const float3 &point_2,
diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc
index 94402f0e548..66088714e63 100644
--- a/source/blender/blenkernel/intern/curves_geometry.cc
+++ b/source/blender/blenkernel/intern/curves_geometry.cc
@@ -851,6 +851,34 @@ static void transform_positions(MutableSpan<float3> positions, const float4x4 &m
   });
 }
 
+void CurvesGeometry::calculate_bezier_auto_handles()
+{
+  const VArray<int8_t> types = std::as_const(*this).curve_types();
+  if (types.is_single() && types.get_internal_single() != CURVE_TYPE_BEZIER) {
+    return;
+  }
+  const VArray<bool> cyclic = std::as_const(*this).cyclic();
+  const Span<int8_t> types_left = this->handle_types_left();
+  const Span<int8_t> types_right = this->handle_types_right();
+  const Span<float3> positions = this->positions();
+  MutableSpan<float3> positions_left = this->handle_positions_left();
+  MutableSpan<float3> positions_right = this->handle_positions_right();
+
+  threading::parallel_for(this->curves_range(), 128, [&](IndexRange range) {
+    for (const int i_curve : range) {
+      if (types[i_curve] == CURVE_TYPE_BEZIER) {
+        const IndexRange points = this->points_for_curve(i_curve);
+        curves::bezier::calculate_auto_handles(cyclic[i_curve],
+                                               types_left.slice(points),
+                                               types_right.slice(points),
+                                               positions.slice(points),
+                                               positions_left.slice(points),
+                                               positions_right.slice(points));
+      }
+    }
+  });
+}
+
 void CurvesGeometry::translate(const float3 &translation)
 {
   /* Use `as_const` because the non-const functions can add the handle attributes. */
diff --git a/source/blender/nod

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list