A Rive player and from-scratch vector editor built on the official Rive runtime for parsing, scene and animation — but rendering through our own ground-up GL pipeline that implements every Rive render interface.
The official runtime in external/rive-runtime/ owns the
file format, the object model and animation. Drawing happens through an
abstract interface seam — and we provide a second, independent
implementation of that seam. Same .riv, two renderers.
Everything above the renderer is shared and unmodified.
rive::RiveRenderer over rive::gpu::RenderContextGLImpl (Pixel Local Storage GPU renderer, linked as rive_pls_renderer). Wrapped by gl_backend.cpp. Fast, analytic edges, flattens splines.
GlStencilShapeRenderer + SimpleShapeFactory + the Simple* render objects. A from-scratch GL pipeline: MSAA edges, analytic erf feather, exact spline primitives, in-shader gradients.
Import a .riv with SimpleShapeFactory → the artboard
builds our render objects → artboard->draw(ours) runs our GL
pipeline. The same scene also imports with the official factory for the live A/B compare.
We reimplement rendering ground-up. We do not reimplement parsing, the Core model, or animation — those stay official and call into our objects.
| rive interface (the API) | our implementation | role |
|---|---|---|
| rive::Factory | SimpleShapeFactory | builds the render objects the runtime needs |
| rive::Renderer | GlStencilShapeRenderer | save/clip/drawPath/drawImage — the GL pipeline |
| rive::RenderPath | SimpleRenderPath | geometry: contours, fill rule, exact curves |
| rive::RenderPaint | SimpleRenderPaint | fill/stroke, gradients, feather, blend |
| rive::RenderShader | SimpleRenderShader | linear/radial gradient stops |
| rive::RenderBuffer | SimpleRenderBuffer | vertex/index data for meshes |
| rive::RenderImage | SimpleRenderImage | decoded bitmaps → GL textures |
Code is organised by domain folder; CMake assigns each file to a target. The PlayerApp-free leaves are static libraries; the app layer compiles into the rive_gl_player executable.
Rive integration base: wraps RenderContextGLImpl + RiveRenderer (official), runtime probe, param matcher.
The ours GL stencil-then-cover renderer + Simple* render objects + geometry.
Binary .riv reader/writer + registry fallback; image loading.
Spline evaluation (Bezier/B-spline/Yuksel), contour vectorizer, nested box-tree queries.
Gaussian-splat lab: field math, greedy fitter, gradients, GPU optimizers. (ParamMatcher stays in riv_backend — it is part of that lib's public API.)
Non-photorealistic post-process effect registry + GL executor + aux buffers.
Space-time volume raymarcher (a stack of recent frames; z = time).
Vector beam — an XY oscilloscope trace of the scene's contours.
Loaded-scene introspection: hierarchy, inspector, per-object bounds.
From-scratch shape + curve editor, gradient handles, undo, .rivx export.
Dear ImGui chrome: panels, headers, node graph, auto-layout, theme.
App lifecycle + the PlayerApp state, frame loop, input, feature routing.
The P3 audit found the backend already DRY — the GL shader-compile helper is shared in render/gl_util, .riv (de)serialization in riv_io. The one genuine cross-file duplication (dashed-line draw) was hoisted into ui/ui_common; no separate riv_util lib was warranted.
Targets link upward. The base lib is shared by every module lib; the executable and the GL-free test suites link the libs instead of recompiling sources.
Our editor and effect stacks can express things official .riv cannot
(exact splines, anisotropic splats, a serialized effect/beam/space-time pipeline). Those
persist in our experimental .rivx format, read and written only by the ours path.