[Bf-blender-cvs] [627f3571271] master: Geometry Nodes: Support for dynamic attributes on curve splines

Hans Goudey noreply at git.blender.org
Wed May 19 19:22:28 CEST 2021


Commit: 627f3571271e5f1a416314fb73f8e3c613271db3
Author: Hans Goudey
Date:   Wed May 19 13:22:09 2021 -0400
Branches: master
https://developer.blender.org/rB627f3571271e5f1a416314fb73f8e3c613271db3

Geometry Nodes: Support for dynamic attributes on curve splines

With this patch you will be able to add and remove attributes from curve
data inside of geometry nodes. The following is currently implemented:
* Adding attributes with any data type to splines or spline points.
* Support for working with multiple splines at the same time.
* Interaction with the three builtin point attributes.
* Resampling attributes in the resample node.

The following is not implemented in this patch:
* Joining attributes when joining splines with the join geometry node.
* Domain interpolation between spline and point domains.
* More efficient ways to call attribute operations once per spline.

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

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

M	source/blender/blenkernel/BKE_attribute_access.hh
M	source/blender/blenkernel/BKE_spline.hh
M	source/blender/blenkernel/intern/attribute_access.cc
M	source/blender/blenkernel/intern/curve_eval.cc
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/blenkernel/intern/spline_nurbs.cc
M	source/blender/blenkernel/intern/spline_poly.cc
M	source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
M	source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc

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

diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh
index 358daa40723..d81d66951cc 100644
--- a/source/blender/blenkernel/BKE_attribute_access.hh
+++ b/source/blender/blenkernel/BKE_attribute_access.hh
@@ -37,6 +37,11 @@
 struct AttributeMetaData {
   AttributeDomain domain;
   CustomDataType data_type;
+
+  constexpr friend bool operator==(AttributeMetaData a, AttributeMetaData b)
+  {
+    return (a.domain == b.domain) && (a.data_type == b.data_type);
+  }
 };
 
 /**
@@ -305,4 +310,36 @@ template<typename T> class OutputAttribute_Typed {
   }
 };
 
+/**
+ * A basic container around DNA CustomData so that its users
+ * don't have to implement special copy and move constructors.
+ */
+class CustomDataAttributes {
+  /**
+   * #CustomData needs a size to be freed, and unfortunately it isn't stored in the struct
+   * itself, so keep track of the size here so this class can implement its own destructor.
+   * If the implementation of the attribute storage changes, this could be removed.
+   */
+  int size_;
+
+ public:
+  CustomData data;
+
+  CustomDataAttributes();
+  ~CustomDataAttributes();
+  CustomDataAttributes(const CustomDataAttributes &other);
+  CustomDataAttributes(CustomDataAttributes &&other);
+
+  void reallocate(const int size);
+
+  std::optional<blender::fn::GSpan> get_for_read(const blender::StringRef name) const;
+  std::optional<blender::fn::GMutableSpan> get_for_write(const blender::StringRef name);
+  bool create(const blender::StringRef name, const CustomDataType data_type);
+  bool create_by_move(const blender::StringRef name, const CustomDataType data_type, void *buffer);
+  bool remove(const blender::StringRef name);
+
+  bool foreach_attribute(const AttributeForeachCallback callback,
+                         const AttributeDomain domain) const;
+};
+
 }  // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh
index c3386d12b02..9e5552082af 100644
--- a/source/blender/blenkernel/BKE_spline.hh
+++ b/source/blender/blenkernel/BKE_spline.hh
@@ -28,6 +28,7 @@
 #include "BLI_float4x4.hh"
 #include "BLI_vector.hh"
 
+#include "BKE_attribute_access.hh"
 #include "BKE_attribute_math.hh"
 
 struct Curve;
@@ -74,6 +75,8 @@ class Spline {
   /* Only #Zup is supported at the moment. */
   NormalCalculationMode normal_mode;
 
+  blender::bke::CustomDataAttributes attributes;
+
  protected:
   Type type_;
   bool is_cyclic_ = false;
@@ -99,7 +102,10 @@ class Spline {
   {
   }
   Spline(Spline &other)
-      : normal_mode(other.normal_mode), type_(other.type_), is_cyclic_(other.is_cyclic_)
+      : normal_mode(other.normal_mode),
+        attributes(other.attributes),
+        type_(other.type_),
+        is_cyclic_(other.is_cyclic_)
   {
   }
 
@@ -482,8 +488,10 @@ class CurveEval {
   blender::Vector<SplinePtr> splines_;
 
  public:
+  blender::bke::CustomDataAttributes attributes;
+
   CurveEval() = default;
-  CurveEval(const CurveEval &other)
+  CurveEval(const CurveEval &other) : attributes(other.attributes)
   {
     for (const SplinePtr &spline : other.splines()) {
       this->add_spline(spline->copy());
@@ -502,6 +510,8 @@ class CurveEval {
 
   blender::Array<int> control_point_offsets() const;
   blender::Array<int> evaluated_point_offsets() const;
+
+  void assert_valid_point_attributes() const;
 };
 
 std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &curve);
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index c24630c5666..62833e10438 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -45,6 +45,7 @@ using blender::Set;
 using blender::StringRef;
 using blender::StringRefNull;
 using blender::fn::GMutableSpan;
+using blender::fn::GSpan;
 
 namespace blender::bke {
 
@@ -590,6 +591,105 @@ void NamedLegacyCustomDataProvider::foreach_domain(
   callback(domain_);
 }
 
+CustomDataAttributes::CustomDataAttributes()
+{
+  CustomData_reset(&data);
+  size_ = 0;
+}
+
+CustomDataAttributes::~CustomDataAttributes()
+{
+  CustomData_free(&data, size_);
+}
+
+CustomDataAttributes::CustomDataAttributes(const CustomDataAttributes &other)
+{
+  size_ = other.size_;
+  CustomData_copy(&other.data, &data, CD_MASK_ALL, CD_DUPLICATE, size_);
+}
+
+CustomDataAttributes::CustomDataAttributes(CustomDataAttributes &&other)
+{
+  size_ = other.size_;
+  data = other.data;
+  CustomData_reset(&other.data);
+}
+
+std::optional<GSpan> CustomDataAttributes::get_for_read(const StringRef name) const
+{
+  BLI_assert(size_ != 0);
+  for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
+    if (layer.name == name) {
+      const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
+      BLI_assert(cpp_type != nullptr);
+      return GSpan(*cpp_type, layer.data, size_);
+    }
+  }
+  return {};
+}
+
+std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const StringRef name)
+{
+  BLI_assert(size_ != 0);
+  for (CustomDataLayer &layer : MutableSpan(data.layers, data.totlayer)) {
+    if (layer.name == name) {
+      const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
+      BLI_assert(cpp_type != nullptr);
+      return GMutableSpan(*cpp_type, layer.data, size_);
+    }
+  }
+  return {};
+}
+
+bool CustomDataAttributes::create(const StringRef name, const CustomDataType data_type)
+{
+  char name_c[MAX_NAME];
+  name.copy(name_c);
+  void *result = CustomData_add_layer_named(&data, data_type, CD_DEFAULT, nullptr, size_, name_c);
+  return result != nullptr;
+}
+
+bool CustomDataAttributes::create_by_move(const blender::StringRef name,
+                                          const CustomDataType data_type,
+                                          void *buffer)
+{
+  char name_c[MAX_NAME];
+  name.copy(name_c);
+  void *result = CustomData_add_layer_named(&data, data_type, CD_ASSIGN, buffer, size_, name_c);
+  return result != nullptr;
+}
+
+bool CustomDataAttributes::remove(const blender::StringRef name)
+{
+  bool result = false;
+  for (const int i : IndexRange(data.totlayer)) {
+    const CustomDataLayer &layer = data.layers[i];
+    if (layer.name == name) {
+      CustomData_free_layer(&data, layer.type, size_, i);
+      result = true;
+    }
+  }
+  return result;
+}
+
+void CustomDataAttributes::reallocate(const int size)
+{
+  size_ = size;
+  CustomData_realloc(&data, size);
+}
+
+bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback callback,
+                                             const AttributeDomain domain) const
+{
+  for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
+    AttributeMetaData meta_data{domain, (CustomDataType)layer.type};
+    if (!callback(layer.name, meta_data)) {
+      return false;
+    }
+  }
+  return true;
+}
+
 }  // namespace blender::bke
 
 /* -------------------------------------------------------------------- */
diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc
index 1679f21516a..9cafe1124b1 100644
--- a/source/blender/blenkernel/intern/curve_eval.cc
+++ b/source/blender/blenkernel/intern/curve_eval.cc
@@ -16,7 +16,9 @@
 
 #include "BLI_array.hh"
 #include "BLI_listbase.h"
+#include "BLI_map.hh"
 #include "BLI_span.hh"
+#include "BLI_string_ref.hh"
 
 #include "DNA_curve_types.h"
 
@@ -26,7 +28,9 @@
 using blender::Array;
 using blender::float3;
 using blender::float4x4;
+using blender::Map;
 using blender::Span;
+using blender::StringRefNull;
 
 blender::Span<SplinePtr> CurveEval::splines() const
 {
@@ -38,6 +42,9 @@ blender::MutableSpan<SplinePtr> CurveEval::splines()
   return splines_;
 }
 
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all splines.
+ */
 void CurveEval::add_spline(SplinePtr spline)
 {
   splines_.append(std::move(spline));
@@ -178,7 +185,7 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
                             bezt.radius,
                             bezt.tilt);
         }
-
+        spline->attributes.reallocate(spline->size());
         curve->add_spline(std::move(spline));
         break;
       }
@@ -192,7 +199,7 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
         for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
           spline->add_point(bp.vec, bp.radius, bp.tilt, bp.vec[3]);
         }
-
+        spline->attributes.reallocate(spline->size());
         curve->add_spline(std::move(spline));
         break;
       }
@@ -203,7 +210,7 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
         for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
           spline->add_point(bp.vec, bp.radius, bp.tilt);
         }
-
+        spline->attributes.reallocate(spline->size());
         curve->add_spline(std::move(spline));
         break;
       }
@@ -214,6 +221,9 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
     }
   }
 
+  /* Though the curve has no attributes, this is necessary to properly set the custom data size. */
+  curve->attributes.reallocate(curve->splines().size());
+
   /* Note: Normal mode is stored separately in each spline to facilitate combining splines
    * from multiple curve objects, where the value may be different. */
   const Spline::NormalCalculationMode normal_mode = normal_mode_from_dna_curve(
@@ -224,3 +234,39 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
 
   return curve;
 }
+
+/**
+ * Check the invariants that curve control point attributes should always uphold, necessary
+ * because attributes are stored on splines rather than in a flat array on the curve:
+ *  - The same set of attributes exists on every spline.
+ *  - Attributes with the same name have the same type on every spline.
+ */
+void CurveEval::assert_valid_point_attributes() const
+{
+#ifdef DEBUG
+  if (splines_.size() == 0) {
+    return;
+  }
+  const int layer_len = splines_.first()->attributes.data.to

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list