Source/Objects/Attributes
< Source
Attributes
Attributes are used to store data that corresponds to geometry elements.
Geometry element are items in one of the domains like points, curves, or faces (eAttrDomain
).
Attributes have generic data types like "Float" or "Integer" (CD_PROP_*
), which means the type itself doesn’t convey how or when the attribute is used. User level documentation about attributes can be found in a dedicated page in the manual.
Attributes are meant to store original data. They aren’t used to cache data that can be recalculated from the geometry like “pointiness” or normals.
The benefits of the attribute system are:
- Generic: Make geometry custom data implementations more generic, more consistent, and easier to use.
- Common Interface: Attributes give a common abstraction for changing data on multiple geometry types, improving consistency and removing boilerplate.
- Data Layout: The attribute system provides access to a high-performance struct-of-arrays layout.
- Abstracted Storage Method: Using the virtual array system gives implementations flexibility on how data is stored, when that is necessary.
- Const Correctness: With a
const
geometry, attributes cannot be modified; thefor_write
versions of the API are not available.
There are two APIs for retrieving attributes in Blender.
BKE_attribute.h
: The C API works on theID
structure. This approach is tied to theCustomData
system commonly used to store attributes.BKE_attribute.hh
: This C++ API is preferred, and is described below. Note thatBMesh
isn't supported yet.
Accessing Attributes
The AttributeAccessor
and MutableAttributeAccessor
are used to read or modify a geometry's attributes.
Attributes are accessed by name. However, they can also be accessed with anonymous attribute IDs (AttributeIDRef
) in geometry nodes to be passed as node sockets.
Functions to access existing attributes start with lookup
. To retrieve write access, the for_write
API functions can be used. To add attributes, lookup_or_add
or add
can be used.
- Domain Interpolation: When retrieving an attribute, a domain (
eAttrDomain
) can be provided. If the attribute is stored on a different domain and conversion is possible, the API will return an attribute with values converted to the specified domain. These conversions are implemented for each geometry type withadapt_domain
methods. - Type Conversion: Attributes can be read with any type. Conversions are lazy; they only happen when the values are actually accessed.
The attribute API uses the virtual array system (BLI_virtual_array.hh
) to make type conversion and domain interpolation more convenient and to provide efficient access to default values. However, if contiguous data is necessary that can be retrieved with VArraySpan/GVArraySpan
(reading) or API methods with span
in the name (for writing).
Attribute Storage
Attributes can be stored in any format, as long as it can be presented as a virtual array when reading and writing data. In practice this usually means contiguous arrays for all geometry elements, but it leaves open the possibility of different formats (i.e. for optimized storage of sparse data). For example, vertex groups can be read and written (though not created yet).
Attributes are often tied to CustomData
. However, since CustomData
was created before attributes,
it combines the legacy style of task-specific attribute types like CD_MLOOPUV
with generic types like "Float".
One benefit of the attribute concept is storing each attribute contiguously, rather than trying to imagine all necessary data and storing it in a specialized structure for each element. For example, here is a structure one might conceive of to store everything necessary for a curve control point:
struct ControlPoint {
float3 position;
float radius;
float tilt;
};
However, there are many problems with this approach:
- To access just all the positions, all other data has to be loaded from memory, which has a large performance cost.
- The positions (or any other attribute) cannot be accessed simply by generic code that is meant to handle any data rather than just control points.
- Even if radius or tilt don't need to be used, they must still be stored.
- If support for user-defined attributes is required, they will have to be stored separately, and handled separately, duplicating all code that deals with the data structure.
In contrast, code that stores each field as a separate named data layer in generic attribute storage like CustomData
is much more flexible, faster, and easier to code.
Naming Convention
Built-in attributes are given singular names, generally with full words. For example, position
is used
instead of positions
and handle_type_left
is used instead of handle_type_l
, etc. Since these names
are visible to users and in the Python API, consistency is important.
Names that start with a period, like .hide_vert
signify internal data that isn't user-accessible in a
procedural context since internal changes might break user expectations. These attributes are hidden from
the UI by default.
Besides built-in attributes, users can choose whatever name they want.