[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