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 extant SculptAttribute 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.