User:JoeEagar/SculptAttributes
SculptAttribute
The sculpt attribute API came about as I was porting various brushes and tools to work in DynTopo.
I needed a generic way to handle temporary attributes which for the most part were being
allocated as simple arrays outside of the CustomData
system.
I iterated over the API several times as I ran into issues:
- Adding or removing new attributes from a BMesh invalidated existing stored customdata offsets.
- Multires doesn't support attributes at all.
- I sometimes misspelled attribute names.
After several iterations I ended up with the sculpt attribute API which mostly solved these problems:
- Attribute references are stored in a special
SculptAttribute
structure, which is allocated and tracked by the API. When the attribute layout changes all extantSculptAttribute
instances are updated. - The API has a "simple array" mode that allocates outside of the CustomData system. This is used for temporary multires data (it's also used for BMesh in master).
- There is support for permanent attributes though it's a bit rough.
- For convenience there is a struct of pointers to
StructAttribute
instances in SculptSession,ss->attrs
; e.g.ss->attrs.automasking_factor
. This structure is also used to spellcheck attribute name strings via the SCULPT_ATTRIBUTE_REF macro. - Attributes can be marked to be released when a stroke ends.
There are however issues:
- The API and its implementation were originally written in C.
SculptAttribute
should be templated to encapsulate the attribute type. Some of the most pernicious bugs come from dereferencing an attribute value with the wrong type.SculptAttribute
was originally written in C and thus has no RAII semantics.- Stroke-only attributes are a bit iffy given the high cost of changing the attribute layout of BMeshes, though in master this doesn't matter since
PBVH_BMESH
uses simple arrays.
Stroke ID
Note: this is the final stroke ID design, looks like I never committed it to master. Master just has one stroke ID attribute for automasking that seems to be shared with general automasking factor caching along with cavity mask blurring. It is implemented in temp-sculpt-dyntopo
.
In order to avoid initializing temporary attributes all at once I came up with the concept of stroke IDs. Each stroke (or filter, expand, etc) is assigned a unique id (a short).
In addition there's a 32-bit temp attribute that stores two shorts; the first stores the last stroke ID and the second various use-case flags (e.g. automasking, layer brush, etc). We can use this to initialize temp attributes incrementally with these steps:
- Get current vertex's stroke ID from the stroke id attribute.
- If the stroke id differs from the global stroke id:
- Initialize attribute at vertex.
- Set vertex stroke id to global stroke ID.
- Clear vertex stroke id use-case flag
- Set stroke ID use-case flag for the temp attribute.
- If the stroke id is the same but the use-case flag is not set:
- Initialize attribute at vertex.
- Set stroke ID use-case flag for the temp attribute.
Stroke IDs are used for automasking and, IIRC, cavity masking in master.
temp-sculpt-dyntopo
also uses them for the layer brush.