Skip to content

feat: SG-44467: Add support for custom brushes through a shared brush catalogue - #1496

Open
eloisebrosseau wants to merge 13 commits into
AcademySoftwareFoundation:mainfrom
eloisebrosseau:custom-brushes
Open

eloisebrosseau wants to merge 13 commits into
AcademySoftwareFoundation:mainfrom
eloisebrosseau:custom-brushes

Conversation

@eloisebrosseau

@eloisebrosseau eloisebrosseau commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

feat: SG-44467: Add support for custom brushes through a shared brush catalogue

Summarize your change.

  • Update twkpaint-src submodule to get the new shaders and new custom brushes.
  • Update BasicGLProgram and TwkGLF CMake to allow Open RV to compile with the changes made to the shader names. Note that rv_quote_file.cmake is now taking FILENAME instead of STEM to allow differentiating between fragment and vertex shaders that are only different when looking at their extension name (i.e. .vert and .frag).
  • Add a new in_Opacity to the vertex attributes in buildStampQuads that is needed by the new texture shaders from the twkpaint-src submodule. Note that buildStampQuads was cleaned up at the same time for readability purposes, so no other changes were made to its previous behaviour. The cleanup was mostly related to moving it to an anonymous namespace with its StampQuads struct, renaming variables to more descriptive names, and adding named constant variables to magic numbers.
  • Fix an issue with stamp strokes looking smaller than ribbon strokes caused by an unnecessary halving of their width.
  • Add the nlohmann/json header-only library
  • Add a BrushCatalogue class to parse the catalogue.json file from twkpaint-src and give access to its content.
  • Add the new catalogue properties in PaintIPNode, using the values in the catalogue as defaults if unset on the stamp strokes.
  • Add a new brushCatalogue Python RV command to allow the annotate_beta Python package to access its content in a follow-up PR.
  • Fix an issue where the ghosted stamp strokes were changing shape compared to when they were drawn because they were unnecessarily using two-pass isolation.
  • Add a special case to allow single-point strokes to be rendered with the stamp path to prevent applying twice the stamp when a second point is detected.
  • Clean up BrushTextureManager by keeping only what was related to the texture itself and OpenGL calls, so that parsing the catalogue is a separate responsibility of BrushCatalogue instead. Some changes were also needed so that the custom brush tips are decoded as RGBA and their red channel is uploaded as the luminance texture. A refactor of this was possible through the libpng simplified API.

Describe the reason for the change.

We need to support accessing the brushes' default values defined in https://github.com/AcademySoftwareFoundation/OpenRV-annotation to eventually expose the custom brushes feature to the new annotation UI.

Describe what you have tested and on which operating system.

Drawing with custom brushes from the catalogue was tested on macOS 26.3, Rocky Linux 9 and Windows 11. Note that this was done by calling the RV command to access the catalogue's entries and by hardcoding values for the brush properties. The real connection to the UI will be added in another PR.

Add a list of changes, and note any that might need special attention during the review.

Note that two remaining fixes will be added in follow-up PRs since they add new behaviour:

  • Multiply ghostOpacity with the stroke opacity instead of overriding it for ribbon, stamp, text and shape rendering paths. Right now, the ribbon path replaces the stroke alpha with ghostOpacity and the stamp path ignores it completely.
  • Make stamp jitter deterministic with a per-stroke seed so strokes don't change on redraw. This depends on updating the twkpaint-src submodule so Open RV can build its TwkMath instead of the one currently in the repo.

@eloisebrosseau eloisebrosseau changed the title feat: SG-44467: Add support for custom brushes through the shared brush catalogue feat: SG-44467: Add support for custom brushes through a shared brush catalogue Nov 10, 2026
Signed-off-by: Éloïse Brosseau <eloise.brosseau@autodesk.com>
Signed-off-by: Éloïse Brosseau <eloise.brosseau@autodesk.com>
Signed-off-by: Éloïse Brosseau <eloise.brosseau@autodesk.com>
Signed-off-by: Éloïse Brosseau <eloise.brosseau@autodesk.com>
Signed-off-by: Éloïse Brosseau <eloise.brosseau@autodesk.com>
Signed-off-by: Éloïse Brosseau <eloise.brosseau@autodesk.com>
Signed-off-by: Éloïse Brosseau <eloise.brosseau@autodesk.com>
Signed-off-by: Éloïse Brosseau <eloise.brosseau@autodesk.com>
Signed-off-by: Éloïse Brosseau <eloise.brosseau@autodesk.com>
Signed-off-by: Éloïse Brosseau <eloise.brosseau@autodesk.com>
Signed-off-by: Éloïse Brosseau <eloise.brosseau@autodesk.com>
Signed-off-by: Éloïse Brosseau <eloise.brosseau@autodesk.com>
Signed-off-by: Éloïse Brosseau <eloise.brosseau@autodesk.com>
Comment thread cmake/dependencies/CMakeLists.txt Resolved
Comment thread cmake/macros/rv_quote_file.cmake Resolved
rotateToStroke, angle, squish, hardness, flow)

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(BrushEntry, name, tip, params)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to make sure that you really want this is to be strict: NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE requires every field. One entry missing a key (such as flow or squish) discards the whole catalogue.
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT can be used to be more resilient.
This might very well be by design. Just to make sure. Feel free to ignore.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about it but I couldn't see in what case a field would be missing since we are controlling the catalogue content, but you're right that this might be too restrictive. It think it would make sense to use NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT for BrushDefaults but I think the strict macro makes more sense for BrushEntry. Having default values there would mean other parts of the code would need to handle all those edge cases in a not so elegant way. However, I think I could catch the exception instead in parseBrushCatalogue. That way, the problematic entry would be removed from the list of available entries without making the whole thing fail. I'll try to come up with a fix!

Comment thread src/lib/ip/IPCore/PaintCommand.cpp Resolved

@bernie-laberge bernie-laberge left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work @eloisebrosseau !
Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants