Grease Pencil 3.0: Final technical design

The data-block

The following code describes the proposed structure of the new grease pencil data-block:

/** * A grease pencil drawing is a set of strokes. The data is stored using * the CurvesGeometry data structure and the custom attributes within it. * It can either own the data or reference it from another GreasePencil * data-block. * * Note: When a drawing references another data-block, it will always * reference all the drawings in that data-block sequentially. */ struct GreasePencilDrawing { /** * The stroke data for this drawing. Can be nullptr in case the drawing * references its data from another data-block. */ CurvesGeometry *geometry; /** * A reference to another GreasePencil data-block. Can be nullptr in * case the drawing owns its data. * * If the data-block has multiple drawings, this drawing references * all of them sequentially. */ GreasePencil *id_reference; /** * Flag. Used to set e.g. the selection status. */ int flag; ... }; /** * Properties for layers and layer groups. */ struct GreasePencilLayerProperties { /** * Name of the layer/group. */ String name; /** * Flag. Used to set e.g. the selection, visibility, ... status. */ int flag; /** * Color tag. */ uchar color[3]; ... }; /** * A grease pencil layer is a collection of drawings. It maps them * to specific scene times on the timeline. */ struct GreasePencilLayer { /** * Settings of this layer. */ GreasePencilLayerProperties settings; /** * This Map maps a scene frame number (key) to an index into * GreasePencil->drawings (value). The frame number indicates * the first frame the drawing is shown. The end time is implicitly * defined by the next greater frame number (key) in the map. * If the value mapped to (index) is -1, no drawing is shown * at this frame. * * Example: * * {0: 0, 5: 1, 10: -1, 12: 2, 16: -1} * * In this example there are three drawings (drawing #0, * drawing #1 and drawing #2). The first drawing starts at frame 0 * and ends at frame 5 (excusive). The second drawing starts at * frame 5 and ends at frame 10. Finally, the third drawing starts * at frame 12 and ends at frame 16. * * | | | | | | | | | | |1|1|1|1|1|1|1| * Time: |0|1|2|3|4|5|6|7|8|9|0|1|2|3|4|5|6|... * Frame: [#0 ][#1 ] [#2 ] * * Note: If a drawing references another data-block, all of the drawings * in that data-block are mapped sequentially to the frames. * If another frame starts, the rest of the mapped drawings * are discarded. */ Map<int, int> frames; }; /** * A grease pencil layer group is a collection of zero or more * grease pencil layers. * It is also an element of a tree-like structure of other layer groups. */ struct GreasePencilLayerGroup { /** * Settings of this layer group. */ GreasePencilLayerProperties settings; /** * Pointer to the parent group and to zero or more children groups. */ GreasePencilLayerGroup *parent; GreasePencilLayerGroup *children; int children_size; /** * An array of GreasePencilLayer's. */ GreasePencilLayer *layers; int layer_size; }; /** * The grease pencil data-block. * * It holds a set of drawings, a tree of layer groups, and a set of layers * whithin each layer group. * * Note: When this data-block is marked as an assets, the layers * and layer groups are ignored. The asset treats the drawings * as a sequence of (one or more) frames with no timing. */ struct GreasePencil { ID id; AnimData adt; /** * An array of GreasePencilDrawing's. */ GreasePencilDrawing *drawings; int drawings_size; /** * The root layer group (is not shown in the UI). * Its parent group is always nullptr. */ GreasePencilLayerGroup *root_group; /** * An array of materials. */ struct Material **mat; int mat_size; /** * Global flag on the data-block. */ int flag; ... };

New Object Type

The new data-block will be ID_GP and the old one will be renamed to ID_GD_LEGACY. Also, there will be a new object type OB_GPENCIL and the old one will be renamed to OB_GPENCIL_LEGACY.

Grease Pencil Operators

In order to not have to redefine all the operator types, the operators will have a switch on the object type. If it's OB_GPENCIL_LEGACY they will run the old behavior, and with OB_GPENCIL they will run the new functions.
The idea is that at some point we can deprecate OB_GPENCIL_LEGACY and remove the switch and all the old functions.

Geometry Nodes Integration

As a first step the plan is to read the current frame of a grease pencil object as a single CurvesGeometry and feed this to geometry nodes as the input. All the curves get a layer_id so that they can be seperated into the layers if needed. The output will also be a single CurvesGeometry.

EDIT: Some concerns have been raised with this approach. Will have to be discussed further.

Dependency Graph

In order to allow for more granular copy-on-write updates, there will be a way to tag a single grease pencil drawing for an update. The exact way of how this will be implemented still has to be decided on. Either we add new flags that are specific to grease pencil, or there is a more general solution.

Select a repo