[Bf-blender-cvs] [b876ce2a4a4] master: Geometry Nodes: new geometry attribute API

Jacques Lucke noreply at git.blender.org
Fri Jul 8 16:17:54 CEST 2022


Commit: b876ce2a4a4638142439a7cf265a0780491ae4cc
Author: Jacques Lucke
Date:   Fri Jul 8 16:16:56 2022 +0200
Branches: master
https://developer.blender.org/rBb876ce2a4a4638142439a7cf265a0780491ae4cc

Geometry Nodes: new geometry attribute API

Currently, there are two attribute API. The first, defined in `BKE_attribute.h` is
accessible from RNA and C code. The second is implemented with `GeometryComponent`
and is only accessible in C++ code. The second is widely used, but only being
accessible through the `GeometrySet` API makes it awkward to use, and even impossible
for types that don't correspond directly to a geometry component like `CurvesGeometry`.

This patch adds a new attribute API, designed to replace the `GeometryComponent`
attribute API now, and to eventually replace or be the basis of the other one.

The basic idea is that there is an `AttributeAccessor` class that allows code to
interact with a set of attributes owned by some geometry. The accessor itself has
no ownership. `AttributeAccessor` is a simple type that can be passed around by
value. That makes it easy to return it from functions and to store it in containers.

For const-correctness, there is also a `MutableAttributeAccessor` that allows
changing individual and can add or remove attributes.

Currently, `AttributeAccessor` is composed of two pointers. The first is a pointer
to the owner of the attribute data. The second is a pointer to a struct with
function pointers, that is similar to a virtual function table. The functions
know how to access attributes on the owner.

The actual attribute access for geometries is still implemented with the `AttributeProvider`
pattern, which makes it easy to support different sources of attributes on a
geometry and simplifies dealing with built-in attributes.

There are different ways to get an attribute accessor for a geometry:
* `GeometryComponent.attributes()`
* `CurvesGeometry.attributes()`
* `bke::mesh_attributes(const Mesh &)`
* `bke::pointcloud_attributes(const PointCloud &)`

All of these also have a `_for_write` variant that returns a `MutabelAttributeAccessor`.

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

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

A	source/blender/blenkernel/BKE_attribute.hh
D	source/blender/blenkernel/BKE_attribute_access.hh
M	source/blender/blenkernel/BKE_curves.hh
M	source/blender/blenkernel/BKE_geometry_set.hh
M	source/blender/blenkernel/BKE_mesh_sample.hh
M	source/blender/blenkernel/BKE_spline.hh
M	source/blender/blenkernel/CMakeLists.txt
M	source/blender/blenkernel/intern/attribute.cc
M	source/blender/blenkernel/intern/attribute_access.cc
M	source/blender/blenkernel/intern/attribute_access_intern.hh
M	source/blender/blenkernel/intern/curve_eval.cc
M	source/blender/blenkernel/intern/curve_legacy_convert.cc
M	source/blender/blenkernel/intern/curve_to_mesh_convert.cc
M	source/blender/blenkernel/intern/geometry_component_curve.cc
M	source/blender/blenkernel/intern/geometry_component_curves.cc
M	source/blender/blenkernel/intern/geometry_component_instances.cc
M	source/blender/blenkernel/intern/geometry_component_mesh.cc
M	source/blender/blenkernel/intern/geometry_component_pointcloud.cc
M	source/blender/blenkernel/intern/geometry_set.cc
M	source/blender/blenkernel/intern/mesh_convert.cc
M	source/blender/blenkernel/intern/mesh_sample.cc
M	source/blender/blenkernel/intern/spline_base.cc
M	source/blender/draw/intern/draw_cache_impl_curves.cc
M	source/blender/draw/intern/draw_curves.cc
M	source/blender/editors/curves/intern/curves_ops.cc
M	source/blender/editors/geometry/geometry_attributes.cc
M	source/blender/editors/object/object_add.cc
M	source/blender/editors/object/object_modifier.cc
M	source/blender/editors/sculpt_paint/curves_sculpt_add.cc
M	source/blender/editors/sculpt_paint/curves_sculpt_density.cc
M	source/blender/editors/sculpt_paint/curves_sculpt_slide.cc
M	source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc
M	source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
M	source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc
M	source/blender/geometry/intern/mesh_primitive_cuboid.cc
M	source/blender/geometry/intern/mesh_to_curve_convert.cc
M	source/blender/geometry/intern/point_merge_by_distance.cc
M	source/blender/geometry/intern/realize_instances.cc
M	source/blender/geometry/intern/resample_curves.cc
M	source/blender/geometry/intern/set_curve_type.cc
M	source/blender/geometry/intern/subdivide_curves.cc
M	source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
M	source/blender/modifiers/intern/MOD_nodes.cc
M	source/blender/nodes/NOD_geometry_exec.hh
M	source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc
M	source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
M	source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc
M	source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
M	source/blender/nodes/geometry/nodes/node_geo_boolean.cc
M	source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
M	source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
M	source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc
M	source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
M	source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc
M	source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
M	source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
M	source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc
M	source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
M	source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
M	source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
M	source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
M	source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
M	source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
M	source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc
M	source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc
M	source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
M	source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
M	source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc
M	source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc
M	source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc
M	source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc
M	source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc
M	source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc
M	source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
M	source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
M	source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
M	source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
M	source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
M	source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
M	source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
M	source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc
M	source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
M	source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
M	source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
M	source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
M	source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
M	source/blender/nodes/geometry/nodes/node_geo_points.cc
M	source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
M	source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
M	source/blender/nodes/geometry/nodes/node_geo_raycast.cc
M	source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc
M	source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
M	source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc
M	source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc
M	source/blender/nodes/geometry/nodes/node_geo_set_id.cc
M	source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc
M	source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc
M	source/blender/nodes/geometry/nodes/node_geo_set_position.cc
M	source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc
M	source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc
M	source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc
M	source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc
M	source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
M	source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
M	source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
M	source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
M	source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc
M	source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc
M	source/blender/nodes/intern/geometry_nodes_eval_log.cc

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

diff --git a/source/blender/blenkernel/BKE_attribute.hh b/source/blender/blenkernel/BKE_attribute.hh
new file mode 100644
index 00000000000..b92d0d4326b
--- /dev/null
+++ b/source/blender/blenkernel/BKE_attribute.hh
@@ -0,0 +1,814 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_color.hh"
+#include "BLI_function_ref.hh"
+#include "BLI_generic_span.hh"
+#include "BLI_generic_virtual_array.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_set.hh"
+
+#include "BKE_anonymous_attribute.hh"
+#include "BKE_attribute.h"
+
+struct Mesh;
+struct PointCloud;
+
+namespace blender::bke {
+
+/**
+ * Identifies an attribute that is either named or anonymous.
+ * It does not own the identifier, so it is just a reference.
+ */
+class AttributeIDRef {
+ private:
+  StringRef name_;
+  const AnonymousAttributeID *anonymous_id_ = nullptr;
+
+ public:
+  AttributeIDRef();
+  AttributeIDRef(StringRef name);
+  AttributeIDRef(StringRefNull name);
+  AttributeIDRef(const char *name);
+  AttributeIDRef(const std::string &name);
+  AttributeIDRef(const AnonymousAttributeID *anonymous_id);
+
+  operator bool() const;
+  uint64_t hash() const;
+  bool is_named() const;
+  bool is_anonymous() const;
+  StringRef name() const;
+  const AnonymousAttributeID &anonymous_id() const;
+  bool should_be_kept() const;
+
+  friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b);
+  friend std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id);
+};
+
+/**
+ * Contains information about an attribute in a geometry component.
+ * More information can be added in the future. E.g. whether the attribute is builtin and how it is
+ * stored (uv map, vertex group, ...).
+ */
+struct AttributeMetaData {
+  eAttrDomain domain;
+  eCustomDataType data_type;
+
+  constexpr friend bool operator==(AttributeMetaData a, AttributeMetaData b)
+  {
+    return (a.domain == b.domain) && (a.data_type == b.data_type);
+  }
+};
+
+struct AttributeKind {
+  eAttrDomain domain;
+  eCustomDataType data_type;
+};
+
+/**
+ * Base class for the attribute initializer types described below.
+ */
+struct AttributeInit {
+  enum class Type {
+    Default,
+    VArray,
+    MoveArray,
+  };
+  Type type;
+  AttributeInit(const Type type) : type(type)
+  {
+  }
+};
+
+/**
+ * Create an attribute using the default value for the data type.
+ * The default values may depend on the attribute provider implementation.
+ */
+struct AttributeInitDefault : public AttributeInit {
+  AttributeInitDefault() : AttributeInit(Type::Default)
+  {
+  }
+};
+
+/**
+ * Create an attribute by copying data from an existing virtual array. The virtual array
+ * must have the same type as the newly created attribute.
+ *
+ * Note that this can be used to fill the new attribute with the default
+ */
+struct AttributeInitVArray : public AttributeInit {
+  blender::GVArray varray;
+
+  AttributeInitVArray(blender::GVArray varray)
+      : AttributeInit(Type::VArray), varray(std::move(varray))
+  {
+  }
+};
+
+/**
+ * Create an attribute with a by passing ownership of a pre-allocated contiguous array of data.
+ * Sometimes data is created before a geometry component is available. In that case, it's
+ * preferable to move data directly to the created attribute to avoid a new allocation and a copy.
+ *
+ * Note that this will only have a benefit for attributes that are stored directly as contiguous
+ * arrays, so not for some built-in attributes.
+ *
+ * The array must be allocated with MEM_*, since `attribute_try_create` will free the array if it
+ * can't be used directly, and that is generally how Blender expects custom data to be allocated.
+ */
+struct AttributeInitMove : public AttributeInit {
+  void *data = nullptr;
+
+  AttributeInitMove(void *data) : AttributeInit(Type::MoveArray), data(data)
+  {
+  }
+};
+
+/* Returns false when the iteration should be stopped. */
+using AttributeForeachCallback =
+    FunctionRef<bool(const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data)>;
+
+/**
+ * Result when looking up an attribute from some geometry with the intention of only reading from
+ * it.
+ */
+template<typename T> struct AttributeReader {
+  /**
+   * Virtual array that provides access to the attribute data. This may be empty.
+   */
+  VArray<T> varray;
+  /**
+   * Domain where the attribute is stored. This also determines the size of the virtual array.
+   */
+  eAttrDomain domain;
+
+  operator bool() const
+  {
+    return this->varray;
+  }
+};
+
+/**
+ * Result when looking up an attribute from some geometry with read an write access. After writing
+ * to the attribute, the #finish method has to be called. This may invalidate caches based on this
+ * attribute.
+ */
+template<typename T> struct AttributeWriter {
+  /**
+   * Virtual array giving read and write access to the attribute. This may be empty.
+   * Consider using #SpanAttributeWriter when you want to access the virtual array as a span.
+   */
+  VMutableArray<T> varray;
+  /**
+   * Domain where the attribute is stored on the geometry. Also determines the size of the virtual
+   * array.
+   */
+  eAttrDomain domain;
+  /**
+   * A function that has to be called after the attribute has been edited. This may be empty.
+   */
+  std::function<void()> tag_modified_fn;
+
+  operator bool() const
+  {
+    return this->varray;
+  }
+
+  /**
+   * Has to be called after the attribute has been modified.
+   */
+  void finish()
+  {
+    if (this->tag_modified_fn) {
+      this->tag_modified_fn();
+    }
+  }
+};
+
+/**
+ * A version of #AttributeWriter for the common case when the user of the attribute wants to write
+ * to a span instead of a virtual array. Since most attributes are spans internally, this can
+ * result in better performance and also simplifies code.
+ */
+template<typename T> struct SpanAttributeWriter {
+  /**
+   * A span based on the virtual array that contains the attribute data. This may be empty.
+   */
+  MutableVArraySpan<T> span;
+  /**
+   * Domain of the attribute. Also determines the size of the span.
+   */
+  eAttrDomain domain;
+  /**
+   * Has to be called after writing to the span.
+   */
+  std::function<void()> tag_modified_fn;
+
+  SpanAttributeWriter() = default;
+
+  SpanAttributeWriter(AttributeWriter<T> &&other, const bool copy_values_to_span)
+      : span(std::move(other.varray), copy_values_to_span),
+        domain(other.domain),
+        tag_modified_fn(std::move(other.tag_modified_fn))
+  {
+  }
+
+  operator bool() const
+  {
+    return span.varray();
+  }
+
+  /**
+   * Has to be called when done writing to the attribute. This makes sure that the data is copied
+   * to the underlying attribute if it was not stored as an array. Furthermore, this may invalidate
+   * other data depending on the modified attribute.
+   */
+  void finish()
+  {
+    this->span.save();
+    if (this->tag_modified_fn) {
+      this->tag_modified_fn();
+    }
+  }
+};
+
+/**
+ * A generic version of #AttributeReader.
+ */
+struct GAttributeReader {
+  GVArray varray;
+  eAttrDomain domain;
+
+  operator bool() const
+  {
+    return this->varray;
+  }
+
+  template<typename T> AttributeReader<T> typed() const
+  {
+    return {varray.typed<T>(), domain};
+  }
+};
+
+/**
+ * A generic version of #AttributeWriter.
+ */
+struct GAttributeWriter {
+  GVMutableArray varray;
+  eAttrDomain domain;
+  std::function<void()> tag_modified_fn;
+
+  operator bool() const
+  {
+    return this->varray;
+  }
+
+  void finish()
+  {
+    if (this->tag_modified_fn) {
+      this->tag_modified_fn();
+    }
+  }
+
+  template<typename T> AttributeWriter<T> typed() const
+  {
+    return {varray.typed<T>(), domain, tag_modified_fn};
+  }
+};
+
+/**
+ * A generic version of #SpanAttributeWriter.
+ */
+struct GSpanAttributeWriter {
+  GMutableVArraySpan span;
+  eAttrDomain domain;
+  std::function<void()> tag_modified_fn;
+
+  GSpanAttributeWriter() = default;
+
+  GSpanAttributeWriter(GAttributeWriter &&other, const bool copy_values_to_span)
+      : span(std::move(other.varray), copy_values_to_span),
+        domain(other.domain),
+        tag_modified_fn(std::move(other.tag_modified_fn))
+  {
+  }
+
+  operator bool() const
+  {
+    return span.varray();
+  }
+
+  void finish()
+  {
+    this->span.save();
+    if (this->tag_modified_fn) {
+      this->tag_modified_fn();
+    }
+  }
+};
+
+/**
+ * Core functions which make up the attribute API. They should not be called directly, but through
+ * #AttributesAccessor or #MutableAttributesAccessor.
+ *
+ * This is similar to a virtual function table. A struct of function pointers is used instead,
+ * because this way the attribute accessors can be trivial and can be passed around by value. This
+ * makes it easy to return the attribute accessor for a geometry from a function.
+ */
+struct AttributeAccessorFunctions {
+  bool (*contains)(const void *owner, const AttributeIDRef &attribute_id);
+  std::optional<AttributeMetaData> (*lookup_meta_data)(const void *owner,
+                                                       const AttributeIDRef &attribute_id);
+  bool (*domain_supported)(const void *owner, eAttrDomain domain);
+  int (*domain_size)(const void *owner, eAttrDomain domain);
+  bool (*is_builtin)(const void *owner, const AttributeIDRef &attribute_id);
+  GAttributeReader (*lookup)(const void *owner, const AttributeIDRef &attribute_id);
+  GVArray (*adapt_domain)(const void *owner,
+                          const GVArray &varray,
+                          eAttrDomain from_domain,
+                          eAttrDomain to_domain);
+  bool (*for_all)(const void *owner,
+                  FunctionRef<bool(const AttributeIDRef &, const AttributeMetaData &)> fn);
+
+  GAttributeWriter (*lookup_for_write)(void *owner, const AttributeIDRef &attribute_id);
+  bool (*remove)(void *owner, const AttributeIDRef &attribute_id);
+  bool (*add)(void *owner,
+              const AttributeIDRef &attribute_id,
+              eAttrDomain domain,
+              eCustomDataType data_type,
+              const AttributeInit &initializer);
+};
+
+/**
+ * Provides read-only access to the set of attributes on some geometry.
+ *
+ *

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list