[Bf-blender-cvs] [1892b131edc] master: Geometry Nodes Curves: Expose first builtin point attributes

Hans Goudey noreply at git.blender.org
Wed May 12 17:22:35 CEST 2021


Commit: 1892b131edc76abb4439ca6fcc69b95a208ab7d7
Author: Hans Goudey
Date:   Wed May 12 10:21:12 2021 -0500
Branches: master
https://developer.blender.org/rB1892b131edc76abb4439ca6fcc69b95a208ab7d7

Geometry Nodes Curves: Expose first builtin point attributes

This commit exposes the first spline control point attributes. The
implementation incorporates the attributes into the virtual array
system, providing efficient methods to flatten the data into a
contiguous array and to apply changes from a flattened array. This
is only part of the eventual goal, which includes changes to run
attribute nodes separately for each spline to completely avoid copying.

So far `tilt` and `radius`, the two generic attributes common to
all spline types, are implemented. The more complex `position`
attribute is also added. It requires some special handling for Bezier
splines, where the control point handles need to be moved along with
the control points. To make that work I also added automatic handle
recalculation to the Bezier spline.

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

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

M	source/blender/blenkernel/BKE_spline.hh
M	source/blender/blenkernel/intern/geometry_component_curve.cc
M	source/blender/blenkernel/intern/spline_bezier.cc

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

diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh
index 48b5dfb1623..35bbc23b21a 100644
--- a/source/blender/blenkernel/BKE_spline.hh
+++ b/source/blender/blenkernel/BKE_spline.hh
@@ -206,8 +206,12 @@ class BezierSpline final : public Spline {
   blender::Vector<HandleType> handle_types_left_;
   blender::Vector<HandleType> handle_types_right_;
 
-  blender::Vector<blender::float3> handle_positions_left_;
-  blender::Vector<blender::float3> handle_positions_right_;
+  /* These are mutable to allow lazy recalculation of #Auto and #Vector handle positions. */
+  mutable blender::Vector<blender::float3> handle_positions_left_;
+  mutable blender::Vector<blender::float3> handle_positions_right_;
+
+  mutable std::mutex auto_handle_mutex_;
+  mutable bool auto_handles_dirty_ = true;
 
   /** Start index in evaluated points array for every control point. */
   mutable blender::Vector<int> offset_cache_;
@@ -296,6 +300,7 @@ class BezierSpline final : public Spline {
       const blender::fn::GVArray &source_data) const override;
 
  private:
+  void ensure_auto_handles() const;
   void correct_end_tangents() const final;
   bool segment_is_vector(const int start_index) const;
   void evaluate_bezier_segment(const int index,
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
index 7afecb66bcc..216f0930cf9 100644
--- a/source/blender/blenkernel/intern/geometry_component_curve.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curve.cc
@@ -285,6 +285,415 @@ static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve)
 
 /** \} */
 
+/* -------------------------------------------------------------------- */
+/** \name Builtin Control Point Attributes
+ *
+ * Attributes with a value for every control point. Most of the complexity here is due to the fact
+ * that we must provide access to the attribute data as if it was a contiguous array when it is
+ * really stored separately on each spline. That will be inherently rather slow, but these virtual
+ * array implementations try to make it workable in common situations.
+ * \{ */
+
+static Array<int> control_point_offsets(const CurveEval &curve)
+{
+  Array<int> offsets(curve.splines.size() + 1);
+
+  int size = 0;
+  for (const int spline_index : curve.splines.index_range()) {
+    offsets[spline_index] = size;
+    size += curve.splines[spline_index]->size();
+  }
+  offsets.last() = size;
+
+  return offsets;
+}
+
+namespace {
+struct PointIndices {
+  int spline_index;
+  int point_index;
+};
+}  // namespace
+static PointIndices lookup_point_indices(Span<int> offsets, const int index)
+{
+  const int spline_index = std::upper_bound(offsets.begin(), offsets.end(), index) -
+                           offsets.begin() - 1;
+  const int index_in_spline = index - offsets[spline_index];
+  return {spline_index, index_in_spline};
+}
+
+template<typename T>
+static void point_attribute_materialize(Span<Span<T>> data,
+                                        Span<int> offsets,
+                                        const IndexMask mask,
+                                        MutableSpan<T> r_span)
+{
+  const int total_size = offsets.last();
+  if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
+    for (const int spline_index : data.index_range()) {
+      const int offset = offsets[spline_index];
+      const int next_offset = offsets[spline_index + 1];
+      initialized_copy_n(data[spline_index].data(), next_offset - offset, r_span.data() + offset);
+    }
+  }
+  else {
+    int spline_index = 0;
+    for (const int i : r_span.index_range()) {
+      const int dst_index = mask[i];
+
+      while (offsets[spline_index] < dst_index) {
+        spline_index++;
+      }
+
+      const int index_in_spline = dst_index - offsets[spline_index];
+      r_span[dst_index] = data[spline_index][index_in_spline];
+    }
+  }
+}
+
+template<typename T>
+static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data,
+                                                         Span<int> offsets,
+                                                         const IndexMask mask,
+                                                         MutableSpan<T> r_span)
+{
+  T *dst = r_span.data();
+  const int total_size = offsets.last();
+  if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
+    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);
+    }
+  }
+  else {
+    int spline_index = 0;
+    for (const int i : r_span.index_range()) {
+      const int dst_index = mask[i];
+
+      while (offsets[spline_index] < dst_index) {
+        spline_index++;
+      }
+
+      const int index_in_spline = dst_index - offsets[spline_index];
+      new (dst + dst_index) T(data[spline_index][index_in_spline]);
+    }
+  }
+}
+
+/**
+ * Virtual array for any control point data accessed with spans and an offset array.
+ */
+template<typename T> class VArray_For_SplinePoints : public VArray<T> {
+ private:
+  const Array<Span<T>> data_;
+  Array<int> offsets_;
+
+ public:
+  VArray_For_SplinePoints(Array<Span<T>> data, Array<int> offsets)
+      : VArray<T>(offsets.last()), data_(std::move(data)), offsets_(std::move(offsets))
+  {
+  }
+
+  T get_impl(const int64_t index) const final
+  {
+    const PointIndices indices = lookup_point_indices(offsets_, index);
+    return data_[indices.spline_index][indices.point_index];
+  }
+
+  void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+  {
+    point_attribute_materialize(data_.as_span(), offsets_, mask, r_span);
+  }
+
+  void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+  {
+    point_attribute_materialize_to_uninitialized(data_.as_span(), offsets_, mask, r_span);
+  }
+};
+
+/**
+ * Mutable virtual array for any control point data accessed with spans and an offset array.
+ */
+template<typename T> class VMutableArray_For_SplinePoints final : public VMutableArray<T> {
+ private:
+  Array<MutableSpan<T>> data_;
+  Array<int> offsets_;
+
+ public:
+  VMutableArray_For_SplinePoints(Array<MutableSpan<T>> data, Array<int> offsets)
+      : VMutableArray<T>(offsets.last()), data_(std::move(data)), offsets_(std::move(offsets))
+  {
+  }
+
+  T get_impl(const int64_t index) const final
+  {
+    const PointIndices indices = lookup_point_indices(offsets_, index);
+    return data_[indices.spline_index][indices.point_index];
+  }
+
+  void set_impl(const int64_t index, T value) final
+  {
+    const PointIndices indices = lookup_point_indices(offsets_, index);
+    data_[indices.spline_index][indices.point_index] = value;
+  }
+
+  void set_all_impl(Span<T> src) final
+  {
+    for (const int spline_index : data_.index_range()) {
+      const int offset = offsets_[spline_index];
+      const int next_offsets = offsets_[spline_index + 1];
+      data_[spline_index].copy_from(src.slice(offset, next_offsets - offset));
+    }
+  }
+
+  void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+  {
+    point_attribute_materialize({(Span<T> *)data_.data(), data_.size()}, offsets_, mask, r_span);
+  }
+
+  void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+  {
+    point_attribute_materialize_to_uninitialized(
+        {(Span<T> *)data_.data(), data_.size()}, offsets_, mask, r_span);
+  }
+};
+
+/**
+ * Virtual array implementation specifically for control point positions. This is only needed for
+ * Bezier splines, where adjusting the position also needs to adjust handle positions depending on
+ * the handle types. We pay a small price for this when other spline types are mixed with Bezier.
+ *
+ * \note There is no need to check the handle type to avoid changing auto handles, since
+ * retrieving write access to the position data will mark them for recomputation anyway.
+ */
+class VMutableArray_For_SplinePosition final : public VMutableArray<float3> {
+ private:
+  MutableSpan<SplinePtr> splines_;
+  Array<int> offsets_;
+
+ public:
+  VMutableArray_For_SplinePosition(MutableSpan<SplinePtr> splines, Array<int> offsets)
+      : VMutableArray<float3>(offsets.last()), splines_(splines), offsets_(std::move(offsets))
+  {
+  }
+
+  float3 get_impl(const int64_t index) const final
+  {
+    const PointIndices indices = lookup_point_indices(offsets_, index);
+    return splines_[indices.spline_index]->positions()[indices.point_index];
+  }
+
+  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 (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(&spline)) {
+      const float3 delta = value - bezier_spline->positions()[indices.point_index];
+      bezier_spline->handle_positions_left()[indices.point_index] += delta;
+      bezier_spline->handle_positions_right()[indices.point_index] += delta;
+      bezier_spline->positions()[indices.point_index] = value;
+    }
+    else {
+      spline.positions()[indices.point_index] = value;
+    }
+  }
+
+  void set_all_impl(Span<float3> src) final
+  {
+    for (const int spline_index : splines_.index_range()) {
+      Spline &spline = *splines_[spline_index];
+      const int offset = offsets_[spline_index];
+      const int next_offset = offsets_[spline_index + 1];
+      if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(&spline)) {
+        MutableSpan<float3> positions = bezier_spline->positions();
+        MutableSpan<float3> handle_positions_left = bezier_spline->handle_positions_left();
+        MutableSpan<float3> handle_positions_right = bezier_spline->handle_positions_right();
+        for (const int i : IndexRange(next_offset - offset)) {
+          const float3 delta = src[offset + i] - positions[i];
+          handle_positions_left[i] += delta;
+          handle_positions_r

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list