[Bf-blender-cvs] [81f552e9ad1] master: Geometry Nodes: Expose Bezier handle positions as an attribute

Hans Goudey noreply at git.blender.org
Wed Sep 29 22:29:37 CEST 2021


Commit: 81f552e9ad1ab5705ef69cf8e7ff7ee67575d45f
Author: Hans Goudey
Date:   Wed Sep 29 15:29:29 2021 -0500
Branches: master
https://developer.blender.org/rB81f552e9ad1ab5705ef69cf8e7ff7ee67575d45f

Geometry Nodes: Expose Bezier handle positions as an attribute

This commit exposes left and right bezier handles as an attribute.
Interaction basically works like edit mode. If you move an aligned
handle, it also moves the opposite handle of the control point.
The difference is that you can't edit "Auto" or "Vector" handles,
you have to first use the "Set Handle Type" node. That gives the handle
types a bit more meaning in the node tree-- changing them in edit mod
is more like a "UI override".

The attributes are named `handle_start` and `handle_end`,
which is the same name used in the curve RNA API.

A new virtual array implementation is added which handles the case of
splines that don't have these attributes, and it also calls two new
functions on `BezierSpline` to set the handle position accounting
for aligned handles.

The virtual arrays and attribute providers will be refactored
(probably templated) in the future, as a next step after the last
built-in curve attribute provider has landed.

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

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

M	source/blender/blenkernel/BKE_spline.hh
M	source/blender/blenkernel/intern/geometry_component_curve.cc
M	source/blender/blenkernel/intern/geometry_set_instances.cc
M	source/blender/blenkernel/intern/spline_bezier.cc
M	source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc

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

diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh
index 541ff19c1cd..97e0d8415a5 100644
--- a/source/blender/blenkernel/BKE_spline.hh
+++ b/source/blender/blenkernel/BKE_spline.hh
@@ -316,6 +316,9 @@ class BezierSpline final : public Spline {
   void translate(const blender::float3 &translation) override;
   void transform(const blender::float4x4 &matrix) override;
 
+  void set_handle_position_right(const int index, const blender::float3 &value);
+  void set_handle_position_left(const int index, const blender::float3 &value);
+
   bool point_is_sharp(const int index) const;
 
   void mark_cache_invalid() final;
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
index 7d0537178ef..73c628d3f0f 100644
--- a/source/blender/blenkernel/intern/geometry_component_curve.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curve.cc
@@ -535,6 +535,9 @@ static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve)
  * array implementations try to make it workable in common situations.
  * \{ */
 
+/**
+ * Individual spans in \a data may be empty if that spline contains no data for the attribute.
+ */
 template<typename T>
 static void point_attribute_materialize(Span<Span<T>> data,
                                         Span<int> offsets,
@@ -546,7 +549,15 @@ static void point_attribute_materialize(Span<Span<T>> data,
     for (const int spline_index : data.index_range()) {
       const int offset = offsets[spline_index];
       const int next_offset = offsets[spline_index + 1];
-      r_span.slice(offset, next_offset - offset).copy_from(data[spline_index]);
+
+      Span<T> src = data[spline_index];
+      MutableSpan<T> dst = r_span.slice(offset, next_offset - offset);
+      if (src.is_empty()) {
+        dst.fill(T());
+      }
+      else {
+        dst.copy_from(src);
+      }
     }
   }
   else {
@@ -557,11 +568,20 @@ static void point_attribute_materialize(Span<Span<T>> data,
       }
 
       const int index_in_spline = dst_index - offsets[spline_index];
-      r_span[dst_index] = data[spline_index][index_in_spline];
+      Span<T> src = data[spline_index];
+      if (src.is_empty()) {
+        r_span[dst_index] = T();
+      }
+      else {
+        r_span[dst_index] = src[index_in_spline];
+      }
     }
   }
 }
 
+/**
+ * Individual spans in \a data may be empty if that spline contains no data for the attribute.
+ */
 template<typename T>
 static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data,
                                                          Span<int> offsets,
@@ -574,7 +594,14 @@ static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data,
     for (const int spline_index : data.index_range()) {
       const int offset = offsets[spline_index];
       const int next_offset = offsets[spline_index + 1];
-      uninitialized_copy_n(data[spline_index].data(), next_offset - offset, dst + offset);
+
+      Span<T> src = data[spline_index];
+      if (src.is_empty()) {
+        uninitialized_fill_n(dst + offset, next_offset - offset, T());
+      }
+      else {
+        uninitialized_copy_n(src.data(), next_offset - offset, dst + offset);
+      }
     }
   }
   else {
@@ -585,7 +612,13 @@ static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data,
       }
 
       const int index_in_spline = dst_index - offsets[spline_index];
-      new (dst + dst_index) T(data[spline_index][index_in_spline]);
+      Span<T> src = data[spline_index];
+      if (src.is_empty()) {
+        new (dst + dst_index) T();
+      }
+      else {
+        new (dst + dst_index) T(src[index_in_spline]);
+      }
     }
   }
 }
@@ -769,6 +802,169 @@ class VMutableArray_For_SplinePosition final : public VMutableArray<float3> {
   }
 };
 
+class VArray_For_BezierHandle final : public VArray<float3> {
+ private:
+  Span<SplinePtr> splines_;
+  Array<int> offsets_;
+  bool is_right_;
+
+ public:
+  VArray_For_BezierHandle(Span<SplinePtr> splines, Array<int> offsets, const bool is_right)
+      : VArray<float3>(offsets.last()),
+        splines_(std::move(splines)),
+        offsets_(std::move(offsets)),
+        is_right_(is_right)
+  {
+  }
+
+  static float3 get_internal(const int64_t index,
+                             Span<SplinePtr> splines,
+                             Span<int> offsets,
+                             const bool is_right)
+  {
+    const PointIndices indices = lookup_point_indices(offsets, index);
+    const Spline &spline = *splines[indices.spline_index];
+    if (spline.type() == Spline::Type::Bezier) {
+      const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(spline);
+      return is_right ? bezier_spline.handle_positions_right()[indices.point_index] :
+                        bezier_spline.handle_positions_left()[indices.point_index];
+    }
+    return float3(0);
+  }
+
+  float3 get_impl(const int64_t index) const final
+  {
+    return get_internal(index, splines_, offsets_, is_right_);
+  }
+
+  /**
+   * Utility so we can pass handle positions to the materialize functions above.
+   *
+   * \note This relies on the ability of the materialize implementations to
+   * handle empty spans, since only Bezier splines have handles.
+   */
+  static Array<Span<float3>> get_handle_spans(Span<SplinePtr> splines, const bool is_right)
+  {
+    Array<Span<float3>> spans(splines.size());
+    for (const int i : spans.index_range()) {
+      if (splines[i]->type() == Spline::Type::Bezier) {
+        BezierSpline &bezier_spline = static_cast<BezierSpline &>(*splines[i]);
+        spans[i] = is_right ? bezier_spline.handle_positions_right() :
+                              bezier_spline.handle_positions_left();
+      }
+      else {
+        spans[i] = {};
+      }
+    }
+    return spans;
+  }
+
+  static void materialize_internal(const IndexMask mask,
+                                   Span<SplinePtr> splines,
+                                   Span<int> offsets,
+                                   const bool is_right,
+                                   MutableSpan<float3> r_span)
+  {
+    Array<Span<float3>> spans = get_handle_spans(splines, is_right);
+    point_attribute_materialize(spans.as_span(), offsets, mask, r_span);
+  }
+
+  static void materialize_to_uninitialized_internal(const IndexMask mask,
+                                                    Span<SplinePtr> splines,
+                                                    Span<int> offsets,
+                                                    const bool is_right,
+                                                    MutableSpan<float3> r_span)
+  {
+    Array<Span<float3>> spans = get_handle_spans(splines, is_right);
+    point_attribute_materialize_to_uninitialized(spans.as_span(), offsets, mask, r_span);
+  }
+
+  void materialize_impl(const IndexMask mask, MutableSpan<float3> r_span) const final
+  {
+    materialize_internal(mask, splines_, offsets_, is_right_, r_span);
+  }
+
+  void materialize_to_uninitialized_impl(const IndexMask mask,
+                                         MutableSpan<float3> r_span) const final
+  {
+    materialize_to_uninitialized_internal(mask, splines_, offsets_, is_right_, r_span);
+  }
+};
+
+class VMutableArray_For_BezierHandles final : public VMutableArray<float3> {
+ private:
+  MutableSpan<SplinePtr> splines_;
+  Array<int> offsets_;
+  bool is_right_;
+
+ public:
+  VMutableArray_For_BezierHandles(MutableSpan<SplinePtr> splines,
+                                  Array<int> offsets,
+                                  const bool is_right)
+      : VMutableArray<float3>(offsets.last()),
+        splines_(splines),
+        offsets_(std::move(offsets)),
+        is_right_(is_right)
+  {
+  }
+
+  float3 get_impl(const int64_t index) const final
+  {
+    return VArray_For_BezierHandle::get_internal(index, splines_, offsets_, is_right_);
+  }
+
+  void set_impl(const int64_t index, float3 value) final
+  {
+    const PointIndices indices = lookup_point_indices(offsets_, index);
+    Spline &spline = *splines_[indices.spline_index];
+    if (spline.type() == Spline::Type::Bezier) {
+      BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline);
+      if (is_right_) {
+        bezier_spline.set_handle_position_right(indices.point_index, value);
+      }
+      else {
+        bezier_spline.set_handle_position_left(indices.point_index, value);
+      }
+      bezier_spline.mark_cache_invalid();
+    }
+  }
+
+  void set_all_impl(Span<float3> src) final
+  {
+    for (const int spline_index : splines_.index_range()) {
+      Spline &spline = *splines_[spline_index];
+      if (spline.type() == Spline::Type::Bezier) {
+        const int offset = offsets_[spline_index];
+
+        BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline);
+        if (is_right_) {
+          for (const int i : IndexRange(bezier_spline.size())) {
+            bezier_spline.set_handle_position_right(i, src[offset + i]);
+          }
+        }
+        else {
+          for (const int i : IndexRange(bezier_spline.size())) {
+            bezier_spline.set_handle_position_left(i, src[offset + i]);
+          }
+        }
+        bezier_spline.mark_cache_invalid();
+      }
+    }
+  }
+
+  void materialize_impl(const IndexMask mask, MutableSpan<float3> r_span) const final
+  {
+    VArray_For_BezierHandle::materialize_internal(mask, splines_, offsets_, is_right_, r_span);
+  }
+
+  void materialize_to_uninitialized_impl(const IndexMask mask,
+                                         MutableSpan<float3> r_span) const final
+  {
+    VArray_For_BezierHandle::materialize_to_uninitialized_internal(
+        mask, splines_, offsets_, is_right_, r_span);
+  }
+};
+
 /**
  * Provider for any builtin control point attribute that doesn't need
  * special handling like access to other arrays in the spline.
@@ -906,6 +1102,78 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo
   }
 };
 
+class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
+ private:
+  bool is_right_;
+
+ public:
+  BezierHan

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list