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

_______________________________________________
Bf-blender-cvs mailing list
[email protected]
https://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to