Commit: 233d38bac9d7868b417f1dae5039973cd4ab078a Author: Jacques Lucke Date: Fri Jul 8 16:16:56 2022 +0200 Branches: gpencil-new-data-proposal https://developer.blender.org/rB233d38bac9d7868b417f1dae5039973cd4ab078a
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. @@ _______________________________________________ Bf-blender-cvs mailing list [email protected] List details, subscription details or unsubscribe: https://lists.blender.org/mailman/listinfo/bf-blender-cvs
