diff --git a/AGENTS.md b/AGENTS.md index 63d93d697..cf9808150 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,6 +16,7 @@ This document provides directive guidelines for AI assistants working on the YUP **NEVER EVER run bash commands to configure, compile or test the implementation, acknowledge that we should test and we'll run and report any issue.** **If you see changes in code you didn't notice before, it's not the linter, it's me changing it for a reason and you should acknowledge the changes, not revert them.** +**Avoid polluting implementation files with obvious comments, make the code more expressive instead** ## AI Decision Making Rules diff --git a/CHANGELOG.md b/CHANGELOG.md index bdc55eff5..d560f01c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - `AnimationFrameExporter` is now an instance-based class bound to a `GraphicsContext` (construct `AnimationFrameExporter exporter (ctx);` then call `exporter.renderFrame(anim, …)` / `exporter.renderAllFrames(…)` / `exporter.exportToGif(anim, …)`), so it can own and reuse the GPU matte-composite pipeline across frames instead of recompiling it per frame. The `exportToGif(frames, frameRate, …)` frame-sequence encoder remains a static helper. - Config macro `YUP_EMBED_DEFAULT_THEME_TEXT_FONT` renamed to `YUP_EMBED_DEFAULT_THEME_TEXT_SERIF_FONT`; the embedded default text font now only covers the serif font. A new `YUP_EMBED_DEFAULT_THEME_TEXT_MONOSPACE_FONT` config selects whether the monospace theme font is embedded. - `SyntaxDefinition` no longer carries colors: `getColor()`, `getSelectionColor()` and the JSON `"colors"` section were removed. Token and editor colors now live in the new `CodeEditorScheme` (see `CodeEditor::setScheme`). +- `Artboard` no longer defines its own `Layout` and `Alignment` enums; it uses YUP's shared `Fitting` and `Justification` types. `setLayout`/`getLayout` became `setFitting`/`getFitting` taking a `std::optional`, and `setAlignment`/`getAlignment` became `setJustification`/`getJustification` taking a `Justification`. The mapping is `fill`→`Fitting::fill`, `contain`→`Fitting::scaleToFit`, `cover`→`Fitting::scaleToFill`, `fitWidth`/`fitHeight` unchanged, `none`→`Fitting::none`, `scaleDown`→`Fitting::centerInside`, and `layout`→`std::nullopt` (no fitting of our own, so the artboard resolves its authored Rive layout constraints). `Alignment` maps 1:1 onto the `Justification` corner/edge names, with `topCenter`, `bottomCenter` spelled `centerTop`, `centerBottom`. `Fitting` values Rive has no equivalent for (`tile`, `centerCrop`, `stretchWidth`, `stretchHeight`) are accepted and fall back to `scaleToFit`; because `Justification` is a bitfield it also admits partial states the old enum could not express, and an axis with no flag set is centered on that axis. `NodeAttachmentOptions::pivot`/`anchor` already took a `Justification`, so the class now speaks one alignment vocabulary throughout. - `Font` loading is now static-only: the instance `loadFromData()` / `loadFromFile()` methods were removed in favor of `Font::loadFontFromData()`, `Font::loadFontFromFile()`, `Font::loadFontFromFirstAvailableFile()`, `Font::loadSerifSystemTextFont()` and `Font::loadMonospaceSystemTextFont()`, all returning `ResultValue`. - The `yup_rhi` descriptors now own their data instead of pointing at caller-managed storage. `GpuVertexBufferLayout::attributes` and `GpuPipelineOptions::vertexBuffers` are `std::vector` (dropping `attributeCount` and `vertexBufferCount`), `GpuPipelineOptions::colorTargets` is a `std::vector` capped at four (dropping `colorTargetCount`), `GpuShaderSource`'s `code` / `bindingMap` / `glFixup` are now `std::vector` (owning) instead of `Span` with `entryPoint` a `String`, and `GpuTextureDesc::label` / `GpuSamplerDesc::label` became `String`. Call sites that built a `static constexpr` attribute table to keep it alive can now build the layout inline; a `options.colorTargets[0].format = …` becomes `options.colorTargets.emplace_back().format = …`. Use `gpuShaderSourceBytes()` to build a `GpuShaderSource::code` blob from source text. `GpuDevice::beginOffscreen` now takes the new `GpuFrameDescriptor` instead of `rive::gpu::RenderContext::FrameDescriptor`. - `GpuDevice`'s move constructor and move assignment are now `= delete`. They were publicly defaulted on a `ReferenceCountedObject`, so moving a device out from under live `GpuDevice::Ptr` holders corrupted the refcount. @@ -102,6 +103,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Fixed the render loop waiting forever when `framerateRedraw` is above 250: the frame budget minus the 4ms pacing margin went negative, which `WaitableEvent::wait()` treats as wait-without-timeout - Fixed glslang process initialization racing between concurrent `ShaderTranspiler` constructions (per-window render threads can now compile shader effects while a compute pipeline compiles elsewhere): the refcount was atomic but did not make a second thread wait for `InitializeProcess()` to complete; init/finalize now run under a lock. Also made the offscreen context pool's `leased` flag atomic in the GL and Metal devices, since `RenderableTarget` destructors can run outside the context lock that serializes acquisition - Fixed `WaitableTimer`'s non-Windows fallback overshooting frame deadlines by several milliseconds on macOS: it blocked for the entire remaining wait on a single `condition_variable::wait_until`, whose wake time is subject to OS scheduling / timer-coalescing latency. It now blocks for the bulk of the wait and closes the last few milliseconds with a tiered busy-wait against `Time::getMillisecondCounterHiRes()`, restoring the precision the pre-`WaitableTimer` implementation had +- `SDLComponentNative` now caches its window units-per-point value: `getWindowUnitsPerPoint()` is only called when the window is created and on resize/dpi/screen-change events (`SDL_EVENT_WINDOW_DISPLAY_CHANGED`, `DISPLAY_SCALE_CHANGED`, `PIXEL_SIZE_CHANGED`, `MOVED`), and every other read (including from the render thread) uses the cached value, so no SDL window method is ever invoked from the render thread - New `ComponentNative::vsync` flag / `Options::withVSync()` (off by default): synchronizes presentation to the display refresh (Metal `displaySyncEnabled`, GL swap interval, D3D `Present (1)`) and lets the display pace the render thread. The software frame timer is skipped in that mode, since running both makes two drifting pacers fight and periodically double a frame #### Rive Runtime Bump @@ -155,6 +157,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### UI - New `ComponentNative::RepaintMode` (`ComponentNative::Options::withRepaintMode`) selects how a window turns its accumulated dirty rectangles into repaint work. The default, `RepaintMode::disjointRegions`, repaints each dirty rectangle in isolation: a parent shared by several dirty rectangles is painted once, clipped to those rectangles, so components lying between two distant dirty rectangles are no longer repainted. `RepaintMode::boundingBox` keeps the previous behaviour of collapsing every dirty rectangle into one bounding box (repainting everything in between) and remains available as a fallback. +- Transformed components are no longer clipped to their untransformed bounds. `Component::internalPaint()` now maps its local bounds through the transform accumulated from the component and its ancestors to the top level component before intersecting them with the accumulated dirty rectangles, so the redraw area of a scaled, rotated, or sheared component matches the area it actually covers and parts of it falling outside the untransformed bounds are painted again. - A `paint()` that throws no longer leaves the graphics frame open. `SDLComponentNative::renderFrame()` called `context->begin()` inside its render lambda but `context->end()` only after that lambda returned, so an exception from user paint code skipped the `end()`/`tick()` pair and the next frame began on a context that had never been flushed. The pair now runs from a scope guard - GUI: multitouch input on any platform with touch hardware (mobile, Emscripten in a mobile browser, and desktop touchscreens). Every finger is delivered through the existing mouse callbacks (`mouseDown` / `mouseDrag` / `mouseUp`, left button held): the first finger behaves exactly like a mouse, each additional finger arrives in parallel with a stable, dense finger index exposed as `MouseEvent::isTouch()` / `MouseEvent::getTouchIndex()`, plus touch pressure as `MouseEvent::getPressure()` (0.0-1.0). Each finger is hit-tested independently, keeps its index for the whole contact, and has its own double-click detection. SDL's synthetic touch-to-mouse events are disabled so every touch is delivered exactly once. - New "Touch Trails" example in `examples/graphics`: draws one hue-spaced colored trail per pointer (each finger gets its own color via `MouseEvent::getTouchIndex()`, with a mouse as a single pointer on desktop). Points carry an age in fade-timer ticks - not the wall clock - and their size and opacity decay with that age, so late frames can't make trails jump or flicker; the fade timer also runs while a pointer is down, dissolving the trail until only the pressed point remains, and once the finger lifts that point fades out too. @@ -170,6 +173,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Fixed `StyledText::update()` calling `Font::getPath()` (a CoreText round-trip on Apple platforms) once per glyph *occurrence* instead of once per unique glyph; a glyph's outline is the same every time for a given font, so it's now cached and reused, cutting a measured 240ms of the 453ms spent reshaping text on a single keystroke. - Fixed `CodeEditor` reshaping (tokenizing, laying out and re-tessellating) the entire document on every single edit, making typing in a large file cost seconds per keystroke (measured: 2s for one backspace in a large file, mostly `StyledText::update()`). `styledText` now only ever holds the currently visible lines rather than the whole document; scrolling reshapes just the newly-visible range. Selection, search highlights, the caret, and Up/Down arrow navigation were adjusted to work correctly when their target is outside the currently-shaped range (falling back to an exact document-position computation rather than depending on `styledText`). Components that never call `setSize()`/`setBounds()` on their `CodeEditor` (as none of its unit tests do) keep shaping the whole document, since there's no meaningful "visible range" to restrict to without a real size. - `CodeEditor` now renders through a `CodeEditorScheme` (new `code/yup_CodeEditorScheme.h`): every color — background, gutter, caret, current line, selection, search highlight, breakpoint and the per-token syntax colors — is stored keyed by `Identifier` (`CodeEditorScheme::setColor` / `getColor`, string constants in `CodeEditorScheme::ColorId`) and switched with `CodeEditor::setScheme`. Built-in well-known schemes are provided via `CodeEditorScheme::getBuiltIn`: `monokai`, `alabaster`, `oneDark`, `solarizedDark` and `solarizedLight`. The editor's painting moved into the theme (Themes v1) as a registered `CodeEditor` component style, and a vertical auto-hide `ScrollBar` now appears when the document overflows the viewport. The `CodeEditor` demo gained a scheme dropdown. +- Added responsive Rive Layout support to `Artboard`: per-node listeners (`setNodeBoundsListener` / `clearNodeBoundsListener` / `clearAllNodeBoundsListeners` / `getNodeBounds`) and attached components driven by layout reflows (`attachComponentToNode` / `detachComponentFromNode` / `detachAllComponents`, with `NodeAttachmentOptions` controlling whether the component fills the node's bounds or only tracks its position, follows its rotation via `Component::setTransform`, and — in track position mode — which component pivot point (`pivot`) is anchored to which point of the node's bounds (`anchor`), each accepting any `Justification`). Node listeners fire whenever the node's bounds or its on-screen orientation (rotation, scale, skew, mirroring) change and hand the callback a cached `ArtboardNode` handle — the same object `Artboard::findNode` returns — instead of allocating one per event, so the bounds/transform are read from the handle (which now also exposes `getViewTransform()`, the node's transform in the same component coordinates as `getBounds()`) and handles invalidate safely when the artboard is cleared or its file replaced. Reflows require the artboard's own layout (`setFitting (std::nullopt)`) or `Fitting::fill`, which propagate the component size into the Rive artboard (leaving those fit modes restores the artboard's authored size, and non-layout nodes report their real shape geometry instead of a unit rect). +- `Artboard::setFile()` now takes an optional artboard name, loading that named artboard from the file (via `rive::File::artboardNamed`) instead of always loading the file's default artboard. +- Added `tools/rive_inspect.py`, a standalone Rive (.riv) binary inspector with `names`, `info`, and `tree` commands (list named objects, dump object properties, and print the artboard hierarchy), resolving keys against the vendored rive generated headers without needing the runtime. +- Added Rive ViewModel (data binding) support to the artboard module: `ArtboardFile` exposes the ViewModel schemas stored in a .riv file (`getNumViewModels`, `getViewModelNames`, `getArtboardViewModel`, `createArtboardViewModelInstance`) through new refcounted `ArtboardViewModel` (schema introspection: typed `PropertyInfo` per property with input/output flags and enum options, authored-instance names) and `ArtboardViewModelInstance` handles (typed and generic `var` property get/set by name or dotted path into nested viewmodels and lists, enum selection, trigger firing, list add/remove/swap/clear, and an optional `setPropertyChangedCallback` notified synchronously on value changes, including output bindings applied by the artboard). `Artboard` gained `bindViewModelInstance` / `unbindViewModelInstance` / `getBoundViewModelInstance` / `getViewModelName`, so a file's default instance can drive the artboard's data-bound properties and state machine transitions; a bound instance must come from the same `ArtboardFile`. +- `Artboard::setAllInputs()` is now implemented (it was a documented no-op): it applies an `Array` snapshot produced by `getAllInputs()` back through `setInput()`, matching entries by their `"id"` and ignoring unknown ones, so a snapshot can be saved and restored or moved between artboards. Triggers are stateless and deliberately carry no `"value"` in either direction. +- `ArtboardViewModelInstance` paths may now terminate on a list index (`"items.2"`), which names the item's viewmodel instance and resolves through `hasProperty()` and `getNestedInstance()`; previously only value paths through an index (`"items.2.quantity"`) worked and the two-segment form silently reported nothing. +- `ArtboardViewModelInstance` structural list changes (`addListItem`, `addListItemAt`, `removeListItem`, `swapListItems`, `clearListItems`) now notify `setPropertyChangedCallback` with the list's own path and an empty `var`, as the callback's documentation already promised. Appending an item now extends the observer tree instead of rebuilding it wholesale, so filling a list is no longer O(N²) in observer allocations. +- `Artboard` now auto-detaches a component attached with `attachComponentToNode()` when that component is destroyed, and attaching a component that already follows another node moves it rather than leaving it driven by both. +- `Artboard` now re-derives an attached component's position when the component is resized. In `trackPosition` mode the component keeps its own size and that size is what the `pivot` is measured against, so resizing it after attaching used to leave the position stale until the node itself moved — which for a static layout is never. Size can now be set before or after `attachComponentToNode()` with the same result. In `fillNode` mode the node owns the size, so an owner-driven resize is put back. +- Added `docs/ui/artboard.md`, covering file loading and asset resolution, layout/alignment, state machine inputs and events, node access and component attachment, and the ViewModel data-binding flow. +- `Artboard::getViewModelName()`, `ArtboardFile::getViewModelNames()` and `ArtboardViewModelInstance::hasProperty()` are now `const` / no longer `noexcept` where they allocate; `ArtboardViewModel` and `ArtboardViewModelInstance` no longer type-erase their Rive pointers through `void*`, and `Artboard` / `ArtboardFile` gained the leak detector and non-copyable declarations the rest of the module already carried. + ### Audio GUI (`yup_audio_gui`) @@ -355,6 +370,9 @@ The `FlexBox` and `Grid` containers landed in this cycle (they were previously l - `SpinningCubeDemo` example (`examples/graphics`): rewritten to the new RHI shape — `GpuFrame` + `GpuCanvas::beginDraw` + `GpuRenderPass` for both the indexed cube draw and the separable two-pass blur (H+V sharing one `GpuFrame`), `isGpuAvailable()` capability probe, and live GLSL editing via `GpuPipeline::compileFromGlsl`. The default Lottie animation is now played back per-frame into an offscreen `GpuCanvas` (2D path) and sampled by the cube's fragment shader so the animation is texture-mapped onto every cube face. - `AIDemo` example (`examples/graphics/source/examples/AI.h`): interactive demo for all four LLM providers (OpenAI Chat, OpenAI Responses, Anthropic, Gemini) with model and API key configuration, system prompt editing, streaming and non-streaming completion, tool calling, MCP server integration, and embedded text generation. +- `ArtboardDemo` example (`examples/graphics/source/examples/Artboard.h`): the loaded Rive artboard now queries a named node via `Artboard::findNode` (name, type, bounds shown in a status label) and attaches a rectangular marker component to it with `Artboard::attachComponentToNode` — a "Marker" combo switches between filling the node's bounds and tracking only its position, "Pivot" and "Anchor" combos choose which component point lands on which node point in track mode, and an "Apply transform" toggle rotates the marker with the node — and the marker follows the node on reflows and resizes. +- New `ArtboardLayoutDemo` example (`examples/graphics/source/examples/Artboard.h`, registered as "Artboard Layout"): shares `ArtboardDemoBase`'s controls with `ArtboardDemo` but loads `data/layout-ui.riv` and attaches a live, nested `Artboard` (playing the file's `Keyboard` artboard) to the `keyboard_slot` layout placeholder in the main `Wireframe` artboard, instead of a plain marker rectangle. +- `ArtboardDemo` example: the displayed Rive file can now be replaced at runtime by dropping a `.riv` file onto the demo, which rebuilds the artboards from the dropped file while keeping the current fit, alignment and marker settings. The demo outlines itself while a `.riv` is dragged over it. ### Build System @@ -365,19 +383,25 @@ The `FlexBox` and `Grid` containers landed in this cycle (they were previously l - `yup_tests` wasm build: raised `INITIAL_MEMORY` to 256 MB, added `MAXIMUM_MEMORY` cap of 1 GB, and reduced `STACK_SIZE` to 1 MB to give the heap room for concurrent pthread stress tests; fixes `RuntimeError: memory access out of bounds` in CI. - Fetched third-party dependencies (SDL3, Perfetto, plugin SDKs) are now cloned shallowly (`--depth 1`) and skip network update checks on reconfigure, speeding up fresh configures and reconfigures. Shallow cloning is automatically disabled when a `GIT_TAG` is a commit hash. - Android: full 16 KB page size compatibility — generated Gradle projects bumped to AGP 8.5.2 / Gradle 8.7 (uncompressed native libraries are zip-aligned to 16 KB), `jniLibs` packaging made explicitly non-legacy, and `ndkVersion` pinned to r27c (overridable via `NDK_VERSION`), which ships a 16 KB-aligned `libc++_shared.so`. CI NDK updated to r27c accordingly. Application shared libraries were already linked with `-Wl,-z,max-page-size=16384`. +- The vendored Rive runtime now builds with its scripting support enabled: the `rive` module declares `WITH_RIVE_SCRIPTING=1` and `RIVE_LUAU=1` and depends on the vendored `luau` (its freshly generated `thirdparty/luau/luau.cpp` amalgamates the VM sources) and `libhydrogen` (`HYDRO_SIGN_VERIFY_ONLY=1`) modules. Without the define, `rive::File::read()` has no importer for `ScriptAsset`/`ShaderAsset`, so an in-band `FileAssetContents` belonging to a script asset is routed to the *previous* asset's importer and trips `assert(!m_content)` in `FileAssetImporter::onFileAssetContents` — which is what made `tests/data/rive/viewmodel-lab.riv` abort on load (and would have silently overwritten a font/image's contents in a release build). Scripts still only reach the VM when their in-band signature verifies, so unverified script assets load as inert content. +- The vendored Rive runtime no longer prints `ScriptAsset doesn't have a generator function …` once per scripted object per frame. A non-tools runtime registers only scripts whose in-band signature verified, so the generator lookup for an unverified script is expected to fail rather than worth a diagnostic. The message is dropped through a new `patches` entry on the `rive` dependency in `tools/rive_update_manifest.json`, so the next `just rive_update` reapplies it instead of the noise coming back. +- Android: the vendored Rive runtime no longer defines `RIVE_DESKTOP_GL` on GLES platforms. It was defined whenever `YUP_RIVE_USE_OPENGL` was on and `RIVE_WEBGL` off, so Android took Rive's *desktop* GL path and `gles3.hpp` included glad's `gles2.h`. glad's `glXxx` function macros then aliased Rive's own (non-glad) extension entry points onto glad's identically named pointers, and the link failed with `duplicate symbol: glad_glDrawArraysInstancedBaseInstanceEXT` and eight siblings; had it linked, those pointers would also have been resolved by glad rather than by `LoadAndValidateGLESExtensions()`. Android now uses `` as intended. +- Emscripten/WebGL: the OpenGL compute backend is no longer compiled, since WebGL (and WebGPU on the web) only reach GLES 3.0 and have no compute entry points. A new `YUP_RHI_USE_GL_COMPUTE` config guards the GL compute pipeline, pass, factories and dispatch sites, replacing the `YUP_RIVE_USE_OPENGL || YUP_LINUX || YUP_ANDROID` condition that held on every Emscripten build and failed with `use of undeclared identifier 'GL_COMPUTE_SHADER'`, `'glDispatchCompute'` and the `GL_*_BARRIER_BIT` enums. ### Testing - The AU and AUv3 wrapper tests no longer describe a stereo buffer list with a stack-allocated `AudioBufferList`. Only the first `AudioBuffer` is reserved inside the struct, so `AUStateTests.RenderProducesOutput` and the two `AUv3BypassRenderTests` render tests wrote past the object while filling `mBuffers[1].mDataByteSize`, aborting the suite under AddressSanitizer with a stack-buffer-overflow. All three now build their lists with a new `tests/yup_audio_plugin_client/yup_TestAudioBufferList.h` helper, which owns an allocation sized for the number of buffers asked for - The `yup_events` Python tests no longer depend on a single fixed-duration pump of the message loop. `next(juce_app)` runs the dispatch loop for 20ms and returns, but a dispatched callback still has to re-acquire the GIL before the Python side runs, so on a loaded machine it can land after the pump has already returned - `test_MessageListener::test_construct_and_post` failed this way on CI. The 22 call sites with a positive expectation now use a new `pump_until(app, predicate)` helper in `python/tests/utilities.py`, which pumps in short slices until the condition holds or a 5s timeout expires. The four sites that assert a *negative* after pumping ("still zero because it was cancelled") deliberately keep the fixed pump, since polling a negative predicate returns immediately and proves nothing - `Component` now befriends a single `ComponentTestHelper` class template instead of accumulating one friend class per test suite; unit tests specialize it (e.g. `ComponentTestHelper`, `ComponentTestHelper`) to reach private state. +- Expanded the Rive viewmodel data-binding tests to run against `tests/data/rive/responsive-sliders.riv`, the data-binding showcase fixture: concrete schema assertions for both its ViewModels (`Slider_instance`, `Main`) and their authored instances, nested viewmodel / dotted-path value access and color round trips, cross-handle shared-value visibility, property-change callbacks reporting dotted paths, and full `Artboard` bind/advance/unbind coverage over the state-machine-driven "Main" artboard. +- Added `tests/data/rive/viewmodel-lab.riv` and the coverage that goes with it. The fixture ships four ViewModels in a known order (`Details`, `Row`, `Panel`, `Lab`), authored instances for each, and a `Lab` schema declaring every property type the API models (string, number, boolean, color, custom enum, trigger, nested viewmodel, list, asset image, artboard reference). The new suites assert the schema contents (property order and types, the enum's `Idle`/`Running`/`Failed` options, the `Row` schema's input flags), the authored values of the `Default` and `Preset` instances - including both nested viewmodels, the three authored list rows and the per-instance list sizes - and the accessor contract for properties that carry no value (triggers, containers, symbol list indexes), plus bind/advance/unbind coverage of the "ViewModel Lab" artboard while writing values and mutating the row list. Every expected value is what `tools/rive_inspect.py` reports for the fixture, which is now also part of the cross-fixture schema sweep. Because the fixture ships `ScriptAsset`s, the suites skip with an explicit message when Rive is built without `WITH_RIVE_SCRIPTING`. Running them found one defect: unbinding an instance whose list drives an `ArtboardComponentList` leaves the parent artboard's Yoga tree referencing the rows that the bulk teardown dropped, so the next layout pass walks freed nodes and crashes - that sequence is captured by `DISABLED_UnbindingAndAdvancingKeepsTheArtboardUsable` with a pointer to the cause rather than weakened to match the current behaviour. - Nine test files were globbed into the IDE project but never `#include`d in their module's unity translation unit, so 141 tests had never been compiled or run since being written: `yup_CodeEditorScheme.cpp`, `yup_ListBoxItem.cpp` and `yup_PaintProfileStats.cpp` (yup_gui), `yup_Memory.cpp` and `yup_TypeErasedObject.cpp` (yup_core), `yup_GraphicsContext.cpp` and `yup_ImageFormatMetadataExtended.cpp` (yup_graphics), `yup_AudioDeviceManagerWindow.cpp` (yup_audio_gui) and `yup_AudioPluginLV2Format.cpp` (yup_audio_plugin_host). All are now wired up, which took three kinds of repair: two defined a file-scope helper that a sibling in the same unity build already defined (`makeSample`, `loadFromBlock`), so the newly enabled copies are renamed rather than the working ones; `yup_ListBoxItem.cpp` had drifted against the graphics API (`PixelFormat` is no longer nested in `Image` and has no `ARGB`, the `Image` constructor now takes `(w, h, format)`, `DrawablePath` folded into `Drawable`, and `String` has no `(count, char)` constructor); and `yup_AudioPluginLV2Format.cpp` is now wrapped in `#if YUP_AUDIO_PLUGIN_HOST_ENABLE_LV2`, mirroring the guard the module puts around `LV2Format`, since the test target does not enable LV2. Wiring them up surfaced two genuine defects the dead tests had been written against: the PNG raw-chunk writer bug below, and `ListBoxItem::setIcon` being an unimplemented stub - the four tests depending on it are marked `DISABLED_` with a pointer to the TODO rather than weakened to match the stub ### Bug Fixes - SDL windowing: partial repaints now grow the dirty area by half a pixel before rounding it out to whole pixels. Rive applies rectangular clips as anti-aliased coverage rather than a pixel-exact scissor, so geometry touching a component's clip edge could bleed a tiny coverage into the adjacent pixel row; with a preserved render target that row was never redrawn and the bleed accumulated into a persistent line just outside components that repaint continuously (visible around the `SpectrogramComponent` at 1x scale). The extra border lets the parent repaint those pixels every frame. - AUv2 wrapper: an input bus that is not fed during a render cycle is now presented to the processor as a null-channel view. `buildInputBusViews` filled the per-bus channel pointers only for the channels it actually received, leaving the rest at whatever the previous render had stored there, so a sidechain input the host stopped feeding (inactive element or a failed `PullInput`) kept pointing at that element's stale audio instead of reading as silent - contradicting the comment on `pullAuxiliaryInputElements` and the `AudioBusBufferView` "null for an inactive or silent bus" contract. The input path now clears each bus's slots first, exactly as `buildOutputBusViews` already did for outputs; the AUv3 wrapper never had the problem because it maps input views onto its own scratch buffers -- `MessageManager` on Apple platforms: `runDispatchLoop()` and `runDispatchLoopUntil()` now wrap their loop body in `YUP_TRY`/`YUP_CATCH_EXCEPTION`, matching the generic implementations in `yup_MessageManager.cpp` that `#if ! (YUP_MAC || YUP_IOS || YUP_WASM)` compiles out on these platforms. The `.mm` replacements previously caught only `NSException`, a disjoint set from `std::exception`, so a C++ exception thrown by a message callback escaped the dispatch loop instead of reaching `YUPApplicationBase::sendUnhandledException()` — which made `unhandledException()` unreachable on macOS and iOS, and killed the application on the first failure. +- `MessageManager` on Apple platforms: `runDispatchLoop()` and `runDispatchLoopUntil()` now wrap their loop body in `YUP_TRY`/`YUP_CATCH_EXCEPTION`, matching the generic implementations in `yup_MessageManager.cpp` that `#if ! (YUP_APPLE || YUP_WASM)` compiles out on these platforms. The `.mm` replacements previously caught only `NSException`, a disjoint set from `std::exception`, so a C++ exception thrown by a message callback escaped the dispatch loop instead of reaching `YUPApplicationBase::sendUnhandledException()` — which made `unhandledException()` unreachable on macOS and iOS, and killed the application on the first failure. - The SDL render thread now routes exceptions from `renderFrame()` through `YUP_CATCH_EXCEPTION` as well. It never passes through a dispatch loop, so an exception escaping `paint()` reached `Thread::threadEntryPoint()`, which only asserts — rendering then stopped permanently with no diagnostic in release builds. - PNG: `png/iCCP` and `png/cHRM` raw chunks are now actually written. The iCCP branch in the writer was an empty `if` body with a "for simplicity, write as unknown chunk" comment that never wrote anything, and `png/iCCP` was excluded from the unknown-chunk loop; cHRM was collected but silently dropped by libpng, because on write libpng only emits unknown chunks whose name marks them safe-to-copy (a lowercase fourth letter) unless `png_set_keep_unknown_chunks` says otherwise - `eXIf` is safe-to-copy and survived, `cHRM` and `iCCP` are not and did not. Both are now registered with `PNG_HANDLE_CHUNK_ALWAYS`. The `png_unknown_chunk` is also value-initialised, since libpng copies all five name bytes and the terminator was left indeterminate - `FlexBox`: the `gap` is no longer applied after the last item on each line. It was added unconditionally after every item, so when items grew or shrank to fill the container exactly (e.g. `flexGrow` items with `gap`), the trailing gap pushed the final item past the container's main-axis edge and it got clipped (e.g. the last panel in each row of the `Layout` example). @@ -392,6 +416,15 @@ The `FlexBox` and `Grid` containers landed in this cycle (they were previously l - `AffineTransform::getScaleFactor()` is now independent of rotation. It averaged the absolute values of the matrix diagonal and ignored the shear terms, so a rotated transform reported `scale * cos(angle)` — falling to zero at 90 degrees. It now measures the lengths of the transformed basis vectors. Lottie precomposition and matte canvases are sized from this value, so a layer under an animated rotation (e.g. `bell.json`, whose precomposition is parented to a rotating null) requested a different pixel size on every frame, reallocating its canvases mid-frame and flashing while a queued draw still referenced the previous ones. - Lottie: the matte canvas pool now replaces an idle slot of a different size instead of appending a new one. Nothing removed slots, so a layer whose on-screen size changed every frame added three canvases — each leasing a Rive render context — per frame, without bound. - `GpuCanvas::create()` takes a `std::optional clearColor`, defaulting to transparent black, and fills the new canvas with it so it is safe to sample before anything is drawn into it (pass `std::nullopt` to leave the contents undefined). The backing texture is allocated uninitialized and a 2D frame whose draw list ends up empty is not guaranteed to honour its `loadAction=clear`, so a canvas could previously be composited while still holding undefined GPU memory (the magenta flashes in `bell.json`, whose only content is one matted precomposition). The clear is issued through the new `GpuDevice::clearOffscreen()`, which encodes it with the backend's native API — a clear binds no pipeline, buffers or samplers, so it needs neither a render pass nor a submit/wait cycle. +- `Artboard::clear()` and `updateSceneFromFile()` destroyed the rive artboard before the scene (`StateMachineInstance`) that references it, so destroying the scene called `cleanupFocusTree()` on freed memory (ASAN use-after-free). The scene is now reset before the artboard. +- `ArtboardViewModelInstance` path resolution downcast a property to `rive::ViewModelInstanceList` without checking its type whenever a path segment was an index, so a path like `"score.0"` (where `score` is a number) asserted in debug builds and read a garbage vector in release ones. Reachable from every path-taking accessor. The four unchecked `ViewModelPropertyEnum` downcasts behind the enum accessors were guarded the same way. +- `Artboard` only drained its state machine's reported events from `mouseDrag()`, so the documented `onPropertyChanged` / `propertyChanged` callbacks never fired for events reported by an advance, a pointer press or a transition — Rive clears the queue at the top of the next advance, and a resize or a `bindViewModelInstance()` (both of which advance) silently swallowed the frame's events. Every advance now drains through one helper, and every pointer handler drains after its `pointer*` call. +- `Artboard::notifyNodeBoundsChanged()` iterated a member scratch array while invoking user callbacks that are allowed to call `setLayout()` / `setAlignment()`, which re-entered the function and cleared the storage the outer loop was walking (and the `const String&` its callee held). Nested passes are now skipped, and the loop tolerates a callback replacing or unloading the file. +- `ArtboardViewModelInstance` invoked its property-changed callback in place, so a callback that re-armed or cleared itself (a one-shot listener) destroyed the closure that was executing — something the header explicitly documents as supported. The dispatch now runs a local copy. +- `Artboard::applyNodeAttachment()` took its attachment record by reference straight out of the `attachedComponents` map, then read `options.applyTransform` from it *after* `Component::setBounds()`, which fires `resized()` synchronously. A `resized()` that detached the component (or attached it to another node) destroyed that map entry mid-call. The record is now taken by value. +- `Artboard::setFile (nullptr)` dereferenced the null file instead of unloading, and the file-taking constructor did not call `setOpaque (true)` while the other one did. +- `ArtboardFile::AssetInfo::uniquePath` was a `File` holding `rive::FileAsset::uniqueFilename()`, which is a bare file name (`"logo-1234.png"`) and not an absolute path — so every asset-resolving load hit `jassertfalse` in `File::parseAbsolutePath()` and then silently resolved the name against the current working directory. Renamed to `uniqueFilename` and retyped as a `String`; resolve it against your own asset directory with `File::getChildFile()`. +- `ArtboardFile::load()` ignored the result of `readIntoMemoryBlock()`, so a stream that yielded nothing was reported as `"Malformed artboard file"` rather than as a read failure. - `GpuCanvas::beginDraw()` now drops the target's cached `GpuTexture` wrap, as its documentation already claimed. The wrap memoizes the Rive texture handle it resolved, so a pooled canvas reused across frames kept handing out the handle resolved on the frame it was first sampled. - Lottie: a failed matte composite no longer blits undefined GPU memory over the matted layer. The result canvas is written only by the composite render pass — nothing else clears it, and its backing texture is allocated uninitialized — but the pass result was ignored and the texture composited regardless, flashing an arbitrary color. The renderer now falls back to the geometric-clip matte path when the composite fails. - Lottie: a paint-less nested group now contributes its geometry to the enclosing group's paints with its own modifiers applied. The geometry was rebuilt from raw shapes, dropping the nested group's trim, repeater, merge-paths and rounded-corner modifiers, which is what defines the outline: RubberHose rigs draw a limb as a 4-point star trimmed to a quarter, so the parent stroke painted the whole star instead of an arc (the stray stars in `mughead.json` and `pumped_up.json`). diff --git a/CLAUDE.md b/CLAUDE.md index 63d93d697..cf9808150 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,6 +16,7 @@ This document provides directive guidelines for AI assistants working on the YUP **NEVER EVER run bash commands to configure, compile or test the implementation, acknowledge that we should test and we'll run and report any issue.** **If you see changes in code you didn't notice before, it's not the linter, it's me changing it for a reason and you should acknowledge the changes, not revert them.** +**Avoid polluting implementation files with obvious comments, make the code more expressive instead** ## AI Decision Making Rules diff --git a/docs/modules.md b/docs/modules.md index e05d1ab27..a3facd174 100644 --- a/docs/modules.md +++ b/docs/modules.md @@ -7,7 +7,7 @@ dependencies are pulled in automatically by the [CMake API](build-system/cmake-a ```{note} All `yup_*` modules share the same version number. Modules also depend on a few -bundled third-party libraries (`zlib`, `rive`, `rive_renderer`, `libclipper2`, +bundled third-party libraries (`zlib`, `rive`, `libclipper2`, `xsimd`), which are resolved for you by the build system. ``` @@ -150,7 +150,7 @@ pulling in the 2D graphics stack. flowchart LR yup_rhi:::self --> yup_core yup_rhi --> yup_shading - yup_rhi --> rive_renderer:::ext + yup_rhi --> rive:::ext classDef self fill:#6366f1,color:#fff,stroke:#4f46e5; classDef ext fill:#f3f4f6,color:#374151,stroke:#9ca3af,stroke-dasharray:4 3; ``` @@ -171,7 +171,6 @@ flowchart LR yup_graphics --> yup_rhi yup_graphics --> yup_shading yup_graphics --> rive:::ext - yup_graphics --> rive_renderer:::ext yup_graphics --> libclipper2:::ext yup_graphics -. optional .-> libpng:::opt yup_graphics -. optional .-> libjpeg:::opt diff --git a/docs/ui/artboard.md b/docs/ui/artboard.md new file mode 100644 index 000000000..1630f6b0e --- /dev/null +++ b/docs/ui/artboard.md @@ -0,0 +1,386 @@ +# Artboards (Rive) + +YUP embeds [Rive](https://rive.app) artboards as ordinary components. Five +classes cooperate: + +- `ArtboardFile` — a loaded `.riv` binary, and the factory for everything below. +- `Artboard` — the `Component` that fits, advances, draws and hits-tests an + artboard from that file. +- `ArtboardNode` — a read-only handle to one named node inside the artboard. +- `ArtboardViewModel` — a data *schema* authored in the Rive editor. +- `ArtboardViewModelInstance` — a live set of values for that schema, bound to + an artboard to drive its data bindings. + +## Loading a file + +`ArtboardFile::load` returns a `ResultValue`, so failures carry a message +instead of a null pointer. It needs a `rive::Factory`, which the graphics +context supplies: + +```cpp +auto result = yup::ArtboardFile::load (yup::File ("dashboard.riv"), factory); + +if (result.failed()) +{ + YUP_DBG (result.getErrorMessage()); + return; +} + +auto file = result.getValue(); // std::shared_ptr +``` + +A file is always held through a shared pointer. Every handle created from it — +schemas, instances — keeps it alive, so the file outlives everything that reads +from it. + +There are four overloads: from a `File` or an `InputStream`, each with and +without an asset callback. + +### Resolving out-of-band assets + +Images, fonts and audio can live outside the `.riv`. Pass an +`AssetLoadCallback` to supply their bytes: + +```cpp +auto result = yup::ArtboardFile::load ( + riveFile, + factory, + [&] (const yup::ArtboardFile::AssetInfo& info, + yup::Span inBandBytes, + rive::Factory& assetFactory) + { + // info.uniqueFilename is the authored name decorated with the asset id + // ("logo-1234.png"). It is a bare file name to look for, not a path, and + // nothing on disk is guaranteed to match it. + auto onDisk = assetDirectory.getChildFile (info.uniqueFilename); + if (! onDisk.existsAsFile()) + return false; // fall back to the in-band bytes, if any + + // ... decode with assetFactory and hand the result to the asset ... + return true; + }); +``` + +Returning `false` is not an import failure — a file whose assets all go +unresolved still loads. + +## Displaying an artboard + +`Artboard` is a `Component`. Give it a file and add it to a parent: + +```cpp +auto artboard = std::make_unique ("dashboard", file); +artboard->setFitting (yup::Fitting::scaleToFit); +artboard->setJustification (yup::Justification::center); +addAndMakeVisible (*artboard); +``` + +`setFile` takes an optional artboard name; with none, the file's default +artboard is loaded. Passing `nullptr` unloads: + +```cpp +artboard->setFile (file, "Mobile"); // a specific artboard +artboard->setFile (nullptr); // unload +``` + +### Fitting and justification + +`setFitting` decides how the artboard's own size is fitted into the component's +bounds, and `setJustification` where the result sits when the fit leaves spare +room. Both take the shared `yup::Fitting` and `yup::Justification` types, so the +same vocabulary applies here as everywhere else in YUP: + +| `std::optional` | Effect | +|---|---| +| `Fitting::scaleToFit` | Uniform scale to fit inside the bounds (the default). | +| `Fitting::scaleToFill` | Uniform scale to cover the bounds, cropping the overflow. | +| `Fitting::fitWidth` / `fitHeight` | Uniform scale to match one axis. | +| `Fitting::centerInside` | Like `scaleToFit`, but never scales above 1.0. | +| `Fitting::fill` | Stretch on both axes; aspect ratio is not preserved. | +| `Fitting::none` | No scaling. | +| `std::nullopt` | Hand the component's size to the artboard and let its own Rive layout constraints resolve it. | + +Rive has no equivalent for `Fitting::tile`, `centerCrop`, `stretchWidth` and +`stretchHeight`; the artboard accepts them and falls back to `scaleToFit`. + +Justification has no visible effect for `Fitting::fill` or for `std::nullopt`, +which always consume the full bounds. It is a bitfield, so an axis with no flag +set is centered on that axis — `Justification::left` alone means left-and-vertically-centered. + +Use `std::nullopt` for artboards authored with Rive's layout engine — that is +what makes nodes reflow when the component resizes: + +```cpp +artboard->setFitting (std::nullopt); // the artboard lays itself out +``` + +### Advancing and pausing + +`Artboard` advances itself from `Component::refreshDisplay` each frame. Call +`advanceAndApply` directly only when driving it manually (in tests, or for a +fixed-step render). + +```cpp +artboard->setPaused (true); // stop advancing +artboard->shouldPauseWhenHidden (false); // keep advancing while off-screen +artboard->durationSeconds(); // the scene's length +``` + +## State machine inputs and events + +When the artboard is driven by a state machine, its inputs are reachable by +name: + +```cpp +artboard->setBoolInput ("hovered", true); +artboard->setNumberInput ("progress", 0.42); +artboard->triggerInput ("pulse"); +``` + +`getAllInputs()` snapshots every input as an `Array` of `DynamicObject`s +with `id`, `type` (`"number"`, `"boolean"` or `"trigger"`) and, for the two +stateful types, `value`. `setAllInputs()` applies such a snapshot back, matching +by `id` and ignoring unknown entries — so a snapshot can be saved and restored, +or moved between artboards: + +```cpp +auto snapshot = artboard->getAllInputs(); +// ... later ... +artboard->setAllInputs (snapshot); +``` + +Rive state machines also report *events* carrying custom properties. Observe +them with `onPropertyChanged`, or by overriding `propertyChanged`: + +```cpp +artboard->onPropertyChanged = [] (yup::Artboard&, + const yup::String& eventName, + const yup::String& propertyName, + const yup::var& oldValue, + const yup::var& newValue) +{ + YUP_DBG (eventName << "." << propertyName << " = " << newValue.toString()); +}; +``` + +The queue is drained after every advance and after every pointer interaction. +Only genuine changes are reported: the artboard remembers the last value seen +per event and skips repeats. + +## Nodes + +`findNode` resolves a named node from the `.riv` and returns a refcounted, +read-only handle: + +```cpp +if (auto node = artboard->findNode ("knob-panel")) +{ + node->getBounds(); // in component coordinates + node->getViewTransform(); // world transform through the artboard fit + node->getTypeName(); // "Shape", "LayoutComponent", ... + + for (auto child : node->getChildren()) + YUP_DBG (child->getName()); +} +``` + +Handles are invalidated by `clear()`, `setFile()` and by destroying the +artboard. After that `isValid()` returns false and every accessor returns a safe +default (empty string, empty rectangle, identity transform, null pointer) — a +stale handle never dereferences a freed Rive object. + +`getTypeName()` is empty both for an invalid handle and for a node whose type is +outside the mapped set; use `getTypeKey()` to tell the two apart. + +### Reacting to node movement + +Register a listener to be told when a node's bounds or on-screen orientation +change — on reflows, resizes and animation frames: + +```cpp +artboard->setNodeBoundsListener ("knob-panel", + [] (yup::Artboard&, + const yup::String& name, + const yup::ArtboardNode::Ptr& node) + { + YUP_DBG (name << " -> " << node->getBounds().toString()); + }); + +artboard->clearNodeBoundsListener ("knob-panel"); +``` + +The handle passed in is the artboard's cached one, so no allocation happens per +event. + +### Attaching components to nodes + +Rather than repositioning a component by hand, let a node drive it. This is how +you overlay real YUP widgets on a Rive-authored layout: + +```cpp +artboard->attachComponentToNode ("knob-panel", &knob); + +// Or keep the component's own size and just follow the node's centre: +artboard->attachComponentToNode ( + "needle", + &readout, + yup::Artboard::NodeAttachmentOptions() + .withMode (yup::Artboard::NodeAttachmentOptions::Mode::trackPosition) + .withApplyTransform (true) + .withJustificationPivot (yup::Justification::center) + .withJustificationAnchor (yup::Justification::center)); +``` + +`fillNode` (the default) sets the component's bounds to the node's. +`trackPosition` keeps the component's size and pins its `pivot` point to the +node's `anchor` point. `withApplyTransform (true)` additionally rotates the +component around that pivot to follow the node. + +Ownership stays with the caller, and the artboard watches for the component's +destruction, so deleting an attached component without detaching it first is +safe. A component follows exactly one node: attaching it again moves it. + +In `trackPosition` mode the component keeps its own size, and that size is what +the `pivot` is measured against — so resizing it re-derives its position +immediately, and you can set the size before or after attaching: + +```cpp +readout.setSize (120.0f, 80.0f); // before or after attaching, same result +``` + +In `fillNode` mode the node owns the size, so resizing the component yourself is +simply overwritten. + +## Data binding with ViewModels + +A Rive artboard can be designed against a *ViewModel* — a named set of typed +properties. Ask the artboard which schema it wants, create an instance, and bind +it: + +```cpp +const auto schemaName = artboard->getViewModelName(); + +auto instance = file->createArtboardViewModelInstance (schemaName); +artboard->bindViewModelInstance (instance); +``` + +`.riv` files may also ship pre-authored instances; clone one by name to start +from the authored values rather than from zero: + +```cpp +auto instance = file->createArtboardViewModelInstance ("Main", "Main"); +``` + +The instance must come from the same `ArtboardFile` as the artboard; binding one +from another file fails. + +### Reading and writing properties + +Values are addressed by name, or by a dotted path into nested viewmodels and +lists: + +```cpp +instance->setNumberProperty ("score", 120.0); +instance->setStringProperty ("player.name", "Ada"); +instance->setColorProperty ("theme.accent", yup::Colors::hotpink); +instance->setEnumProperty ("state", "expanded"); +instance->trigger ("celebrate"); + +auto score = instance->getNumberProperty ("score"); // std::optional +auto name = instance->getStringProperty ("player.name"); // std::optional +``` + +There is also an untyped pair, `getProperty` / `setProperty`, working in `var`s: +booleans map to `var(bool)`, numbers to `var(double)`, strings to `var(String)`, +colors to `var(int64)` holding ARGB, and enums to `var(String)` of the selected +option. + +Writes are applied to the artboard's data bindings on the next +`advanceAndApply()`. + +Unknown paths and type mismatches both yield `nullopt` (or an empty `var`). Use +`hasProperty` when you need to tell them apart. + +### Lists + +List properties hold viewmodel instances and can be resized at runtime: + +```cpp +instance->getListSize ("items"); // -1 if not a list +instance->addListItem ("items", "ItemViewModel"); // append +instance->addListItemAt ("items", 0, "ItemViewModel"); +instance->swapListItems ("items", 0, 1); +instance->removeListItem ("items", 0); +instance->clearListItems ("items"); + +auto first = instance->getListItem ("items", 0); +``` + +Items are also reachable through paths. `"items.2"` names the item's instance +and resolves through `hasProperty` and `getNestedInstance`; `"items.2.quantity"` +names a value inside it and works with every accessor: + +```cpp +instance->getNumberProperty ("items.2.quantity"); +instance->getNestedInstance ("items.2"); +``` + +Because a path segment of digits is always read as an index, a property named +literally `"2"` cannot be addressed through a path. + +### Observing changes + +One callback covers the whole instance graph — nested viewmodels and list items +included: + +```cpp +instance->setPropertyChangedCallback ( + [] (yup::ArtboardViewModelInstance&, + const yup::String& path, + const yup::var& value) + { + YUP_DBG (path << " = " << value.toString()); + }); +``` + +It fires for writes made through the handle *and* for values the artboard writes +back through its output bindings. Structural list changes notify too, reporting +the list's own path and an empty `var`. + +Mutating the instance from inside the callback is supported, as is replacing or +clearing the callback itself. The one rule: do not release the instance's last +reference from within it. + +### Inspecting the schema + +`ArtboardViewModel` describes a schema without instantiating it — useful for +building generic inspectors: + +```cpp +auto schema = file->getArtboardViewModel ("Main"); + +for (int i = 0; i < schema->getNumProperties(); ++i) +{ + const auto property = schema->getPropertyAt (i); + + YUP_DBG (property.name << " : " << (int) property.type); + + if (property.type == yup::ArtboardViewModel::PropertyType::enumType) + YUP_DBG (property.enumValues.joinIntoString (", ")); +} +``` + +`isInput` and `isOutput` say which direction a property is bound in. +`getInstanceNames()` lists the authored instances available to clone. + +## Threading + +Artboards and their handles are not internally synchronized. + +`Artboard` advances from `refreshDisplay`, which YUP runs on the render thread +while holding the message manager lock — so it never runs concurrently with the +message thread, but everything the advance reaches runs *on that thread*: node +bounds listeners, bounds updates pushed into attached components, +`onPropertyChanged`, and the `PropertyChangedCallback` of a bound instance. A +slow callback stalls rendering; move real work onto the message thread or a +background thread. diff --git a/docs/ui/index.md b/docs/ui/index.md index 3c8e9ae6a..949d2deb4 100644 --- a/docs/ui/index.md +++ b/docs/ui/index.md @@ -23,6 +23,8 @@ windowing, widgets, and theming are still to come. - **Events** — the message loop, timers, and event dispatch (`yup_events`). - **Widgets** — buttons, sliders, labels, text editors, and audio displays (waveform, spectrogram, scope) from `yup_audio_gui`. +- **Artboards** — Rive artboards as components, with node access and + ViewModel data binding. ## Guides @@ -49,6 +51,8 @@ windowing, widgets, and theming are still to come. - [Code editor](code-editor.md) — `CodeDocument`, `SyntaxDefinition`, `CodeTokeniser`, and the syntax-highlighting `CodeEditor` component. +- [Artboards (Rive)](artboard.md) — `ArtboardFile`, `Artboard`, + `ArtboardNode`, and ViewModel data binding. ```{toctree} :hidden: @@ -63,4 +67,5 @@ component-caching component-snapshots component-profiling code-editor +artboard ``` diff --git a/examples/graphics/CMakeLists.txt b/examples/graphics/CMakeLists.txt index 1639033b7..b38a093c2 100644 --- a/examples/graphics/CMakeLists.txt +++ b/examples/graphics/CMakeLists.txt @@ -27,6 +27,7 @@ project (${target_name} VERSION ${target_version}) set (rive_file "data/alien.riv") set (lottie_file "data/goal.lottie") +set (layout_ui_rive_file "data/layout-ui.riv") # ==== Prepare Android build set (link_libraries "") @@ -41,7 +42,8 @@ set (bundle_resources "") if (YUP_PLATFORM_MOBILE OR YUP_PLATFORM_EMSCRIPTEN) list (APPEND bundle_resources "${CMAKE_CURRENT_LIST_DIR}/${rive_file}@${rive_file}" - "${CMAKE_CURRENT_LIST_DIR}/${lottie_file}@${lottie_file}") + "${CMAKE_CURRENT_LIST_DIR}/${lottie_file}@${lottie_file}" + "${CMAKE_CURRENT_LIST_DIR}/${layout_ui_rive_file}@${layout_ui_rive_file}") endif() # ==== Embed shaders diff --git a/examples/graphics/data/alien.rev b/examples/graphics/data/alien.rev deleted file mode 100644 index 8ea9ecbf1..000000000 Binary files a/examples/graphics/data/alien.rev and /dev/null differ diff --git a/examples/graphics/data/arcade_controls.rev b/examples/graphics/data/arcade_controls.rev deleted file mode 100644 index bec40ca5b..000000000 Binary files a/examples/graphics/data/arcade_controls.rev and /dev/null differ diff --git a/examples/graphics/data/audio_sampler.rev b/examples/graphics/data/audio_sampler.rev deleted file mode 100644 index 8b80c8591..000000000 Binary files a/examples/graphics/data/audio_sampler.rev and /dev/null differ diff --git a/examples/graphics/data/car_interface.rev b/examples/graphics/data/car_interface.rev deleted file mode 100644 index 86e0cfcd2..000000000 Binary files a/examples/graphics/data/car_interface.rev and /dev/null differ diff --git a/examples/graphics/data/layout-ui.riv b/examples/graphics/data/layout-ui.riv new file mode 100644 index 000000000..c38cca35a Binary files /dev/null and b/examples/graphics/data/layout-ui.riv differ diff --git a/examples/graphics/data/seasynth.rev b/examples/graphics/data/seasynth.rev deleted file mode 100644 index 9a8cc264d..000000000 Binary files a/examples/graphics/data/seasynth.rev and /dev/null differ diff --git a/examples/graphics/data/toymachine_3.rev b/examples/graphics/data/toymachine_3.rev deleted file mode 100644 index 2b82732c8..000000000 Binary files a/examples/graphics/data/toymachine_3.rev and /dev/null differ diff --git a/examples/graphics/data/ui_elements.rev b/examples/graphics/data/ui_elements.rev deleted file mode 100644 index 8f3ea889d..000000000 Binary files a/examples/graphics/data/ui_elements.rev and /dev/null differ diff --git a/examples/graphics/source/examples/Artboard.h b/examples/graphics/source/examples/Artboard.h index f7b0adc3b..b40b1bb39 100644 --- a/examples/graphics/source/examples/Artboard.h +++ b/examples/graphics/source/examples/Artboard.h @@ -23,10 +23,49 @@ //============================================================================== -class ArtboardDemo : public yup::Component +/** A simple rectangular marker that tracks a named Rive node. + + Its bounds are driven by the artboard node it is attached to: layout nodes + report their laid-out size, shapes their real geometry, other nodes a unit + rect. Depending on the attachment mode it either fills those bounds or keeps + a fixed size while following the node's origin. +*/ +class NodeMarker : public yup::Component { public: - ArtboardDemo() + NodeMarker() + { + setOpaque (false); + } + + void paint (yup::Graphics& g) override + { + const float markerSizeX = getWidth() - 4.0f; + const float markerSizeY = getHeight() - 4.0f; + + auto rect = yup::Rectangle (getWidth() * 0.5f - markerSizeX * 0.5f, + getHeight() * 0.5f - markerSizeY * 0.5f, + markerSizeX, markerSizeY); + + g.setFillColor (yup::Colors::darkorange.withAlpha (0.5f)); + g.fillRoundedRect (rect, 3.0f); + + g.setStrokeColor (yup::Colors::white); + g.setStrokeWidth (2.0f); + g.strokeRoundedRect (rect, 4.0f); + } +}; + +//============================================================================== + +class ArtboardDemoBase : public yup::Component +{ +public: + ArtboardDemoBase (yup::String rivePath, yup::String trackedNodeName, int defaultFitComboId = 2, bool fillArtboardCell = false) + : rivePath (std::move (rivePath)) + , trackedNodeName (std::move (trackedNodeName)) + , defaultFitComboId (defaultFitComboId) + , fillArtboardCell (fillArtboardCell) { setWantsKeyboardFocus (true); setupControls(); @@ -34,28 +73,18 @@ class ArtboardDemo : public yup::Component bool loadArtboard() { + if (! artboards.isEmpty()) + return true; + auto factory = getNativeComponent()->getFactory(); if (factory == nullptr) return false; - auto artboardFile = yup::ArtboardFile::load (getAssetPath (YUP_EXAMPLE_GRAPHICS_RIVE_FILE), *factory); + auto artboardFile = yup::ArtboardFile::load (getAssetPath (rivePath), *factory); if (! artboardFile) return false; - // Setup artboards - for (int i = 0; i < totalRows * totalColumns; ++i) - { - auto art = artboards.add (std::make_unique (yup::String ("art") + yup::String (i))); - addAndMakeVisible (art); - - art->setFile (artboardFile.getValue()); - art->setLayout (getSelectedLayout()); - art->setAlignment (getSelectedAlignment()); - - art->advanceAndApply (i * art->durationSeconds()); - } - - resized(); + setArtboardFile (artboardFile.getValue()); return true; } @@ -68,7 +97,6 @@ class ArtboardDemo : public yup::Component void resized() override { auto bounds = getLocalBounds().reduced (10, 20); - auto controls = bounds.removeFromTop (30); auto labelHeight = 20; auto comboHeight = 24; @@ -76,12 +104,28 @@ class ArtboardDemo : public yup::Component auto comboWidth = 170; auto spacing = 12; + // Row 1: how the artboard is fitted into its bounds. + auto controls = bounds.removeFromTop (30); fitLabel.setBounds (controls.removeFromLeft (labelWidth).withHeight (labelHeight)); fitCombo.setBounds (controls.removeFromLeft (comboWidth).withHeight (comboHeight)); controls.removeFromLeft (spacing); alignmentLabel.setBounds (controls.removeFromLeft (labelWidth).withHeight (labelHeight)); alignmentCombo.setBounds (controls.removeFromLeft (comboWidth).withHeight (comboHeight)); + // Row 2: how the marker tracks the node (pivot = which component point is + // anchored, anchor = which node point it is anchored to). + controls = bounds.removeFromTop (30); + markerLabel.setBounds (controls.removeFromLeft (labelWidth).withHeight (labelHeight)); + markerModeCombo.setBounds (controls.removeFromLeft (comboWidth).withHeight (comboHeight)); + controls.removeFromLeft (spacing); + applyTransformToggle.setBounds (controls.removeFromLeft (110).withHeight (comboHeight)); + controls.removeFromLeft (spacing); + pivotLabel.setBounds (controls.removeFromLeft (labelWidth).withHeight (labelHeight)); + pivotCombo.setBounds (controls.removeFromLeft (comboWidth).withHeight (comboHeight)); + controls.removeFromLeft (spacing); + anchorLabel.setBounds (controls.removeFromLeft (labelWidth).withHeight (labelHeight)); + anchorCombo.setBounds (controls.removeFromLeft (comboWidth).withHeight (comboHeight)); + if (artboards.size() != totalRows * totalColumns) return; @@ -98,7 +142,7 @@ class ArtboardDemo : public yup::Component for (int j = 0; j < totalColumns; ++j) { auto col = row.removeFromLeft (width); - artboards.getUnchecked (j * totalRows + i)->setBounds (col.largestFittingSquare()); + artboards.getUnchecked (j * totalRows + i)->setBounds (fillArtboardCell ? col : col.largestFittingSquare()); } } } @@ -109,7 +153,47 @@ class ArtboardDemo : public yup::Component g.fillAll(); } +protected: + // Builds the component attached to the tracked node of each artboard. Override + // to attach something other than a plain marker rectangle. + virtual std::unique_ptr createTrackedComponent() + { + return std::make_unique(); + } + + void setArtboardFile (std::shared_ptr newFile) + { + loadedArtboardFile = std::move (newFile); + + trackedComponents.clear(); + artboards.clear(); + + for (int i = 0; i < totalRows * totalColumns; ++i) + { + auto art = artboards.add (std::make_unique (yup::String ("art") + yup::String (i))); + addAndMakeVisible (art); + + art->setFile (loadedArtboardFile); + art->setFitting (getSelectedFitting()); + art->setJustification (getSelectedJustification (alignmentCombo)); + + art->advanceAndApply (i * art->durationSeconds()); + + attachTrackedComponent (*art); + } + + resized(); + } + + std::shared_ptr loadedArtboardFile; + private: + void attachedToNative() override + { + if (auto topLevelComponent = getTopLevelComponent(); topLevelComponent != nullptr && topLevelComponent->isOnDesktop()) + loadArtboard(); + } + void visibilityChanged() override { if (isVisible()) @@ -135,10 +219,10 @@ class ArtboardDemo : public yup::Component fitCombo.addItem ("None", 6); fitCombo.addItem ("Scale Down", 7); fitCombo.addItem ("Layout", 8); - fitCombo.setSelectedId (2); + fitCombo.setSelectedId (defaultFitComboId); fitCombo.onSelectedItemChanged = [this] { - updateArtboardsLayout(); + updateArtboardsFitting(); }; addAndMakeVisible (fitCombo); @@ -146,92 +230,315 @@ class ArtboardDemo : public yup::Component alignmentLabel.setFont (labelFont); addAndMakeVisible (alignmentLabel); - alignmentCombo.addItem ("Top Left", 1); - alignmentCombo.addItem ("Top Center", 2); - alignmentCombo.addItem ("Top Right", 3); - alignmentCombo.addItem ("Center Left", 4); - alignmentCombo.addItem ("Center", 5); - alignmentCombo.addItem ("Center Right", 6); - alignmentCombo.addItem ("Bottom Left", 7); - alignmentCombo.addItem ("Bottom Center", 8); - alignmentCombo.addItem ("Bottom Right", 9); - alignmentCombo.setSelectedId (5); + addJustificationItems (alignmentCombo, 5); alignmentCombo.onSelectedItemChanged = [this] { - updateArtboardsLayout(); + updateArtboardsFitting(); }; addAndMakeVisible (alignmentCombo); + + markerLabel.setText ("Marker", yup::dontSendNotification); + markerLabel.setFont (labelFont); + addAndMakeVisible (markerLabel); + + markerModeCombo.addItem ("Fill node", 1); + markerModeCombo.addItem ("Track position", 2); + markerModeCombo.setSelectedId (1); + markerModeCombo.onSelectedItemChanged = [this] + { + updateMarkerControlsEnabled(); + updateMarkers(); + }; + addAndMakeVisible (markerModeCombo); + + applyTransformToggle.setButtonText ("Apply transform"); + applyTransformToggle.setToggleState (false, yup::dontSendNotification); + applyTransformToggle.onClick = [this] + { + updateMarkers(); + }; + addAndMakeVisible (applyTransformToggle); + + pivotLabel.setText ("Pivot", yup::dontSendNotification); + pivotLabel.setFont (labelFont); + addAndMakeVisible (pivotLabel); + + addJustificationItems (pivotCombo, 1); + pivotCombo.onSelectedItemChanged = [this] + { + updateMarkers(); + }; + addAndMakeVisible (pivotCombo); + + anchorLabel.setText ("Anchor", yup::dontSendNotification); + anchorLabel.setFont (labelFont); + addAndMakeVisible (anchorLabel); + + addJustificationItems (anchorCombo, 1); + anchorCombo.onSelectedItemChanged = [this] + { + updateMarkers(); + }; + addAndMakeVisible (anchorCombo); + + updateMarkerControlsEnabled(); } - yup::Artboard::Layout getSelectedLayout() const + std::optional getSelectedFitting() const { switch (fitCombo.getSelectedId()) { case 1: - return yup::Artboard::Layout::fill; + return yup::Fitting::fill; case 2: - return yup::Artboard::Layout::contain; + return yup::Fitting::scaleToFit; case 3: - return yup::Artboard::Layout::cover; + return yup::Fitting::scaleToFill; case 4: - return yup::Artboard::Layout::fitWidth; + return yup::Fitting::fitWidth; case 5: - return yup::Artboard::Layout::fitHeight; + return yup::Fitting::fitHeight; case 6: - return yup::Artboard::Layout::none; + return yup::Fitting::none; case 7: - return yup::Artboard::Layout::scaleDown; + return yup::Fitting::centerInside; case 8: - return yup::Artboard::Layout::layout; + return std::nullopt; } - return yup::Artboard::Layout::contain; + return yup::Fitting::scaleToFit; } - yup::Artboard::Alignment getSelectedAlignment() const + void updateArtboardsFitting() { - switch (alignmentCombo.getSelectedId()) + const auto newFitting = getSelectedFitting(); + const auto newJustification = getSelectedJustification (alignmentCombo); + + for (auto* artboard : artboards) + { + artboard->setFitting (newFitting); + artboard->setJustification (newJustification); + } + } + + // Attaches a tracked component to the tracked node of the given artboard. + void attachTrackedComponent (yup::Artboard& art) + { + auto component = trackedComponents.add (createTrackedComponent()); + art.addAndMakeVisible (component); + + const auto options = getMarkerOptions(); + + if (options.mode == yup::Artboard::NodeAttachmentOptions::Mode::trackPosition) + component->setSize (44.0f, 44.0f); + + component->setVisible (art.attachComponentToNode (trackedNodeName, component, options)); + } + + // Populates a combo with the nine Justification points (ids 1..9) and selects + // the entry with the given id. + static void addJustificationItems (yup::ComboBox& combo, int defaultId) + { + combo.addItem ("Top Left", 1); + combo.addItem ("Top Center", 2); + combo.addItem ("Top Right", 3); + combo.addItem ("Center Left", 4); + combo.addItem ("Center", 5); + combo.addItem ("Center Right", 6); + combo.addItem ("Bottom Left", 7); + combo.addItem ("Bottom Center", 8); + combo.addItem ("Bottom Right", 9); + combo.setSelectedId (defaultId); + } + + // Returns the Justification currently selected in a combo filled by + // addJustificationItems. + yup::Justification getSelectedJustification (const yup::ComboBox& combo) const + { + switch (combo.getSelectedId()) { - case 1: - return yup::Artboard::Alignment::topLeft; case 2: - return yup::Artboard::Alignment::topCenter; + return yup::Justification::centerTop; case 3: - return yup::Artboard::Alignment::topRight; + return yup::Justification::topRight; case 4: - return yup::Artboard::Alignment::centerLeft; + return yup::Justification::centerLeft; case 5: - return yup::Artboard::Alignment::center; + return yup::Justification::center; case 6: - return yup::Artboard::Alignment::centerRight; + return yup::Justification::centerRight; case 7: - return yup::Artboard::Alignment::bottomLeft; + return yup::Justification::bottomLeft; case 8: - return yup::Artboard::Alignment::bottomCenter; + return yup::Justification::centerBottom; case 9: - return yup::Artboard::Alignment::bottomRight; + return yup::Justification::bottomRight; + default: + return yup::Justification::topLeft; } + } - return yup::Artboard::Alignment::center; + // Pivot and anchor only drive trackPosition mode; disable them in fillNode mode. + void updateMarkerControlsEnabled() + { + const auto isTracking = markerModeCombo.getSelectedId() == 2; + pivotLabel.setEnabled (isTracking); + pivotCombo.setEnabled (isTracking); + anchorLabel.setEnabled (isTracking); + anchorCombo.setEnabled (isTracking); } - void updateArtboardsLayout() + yup::Artboard::NodeAttachmentOptions getMarkerOptions() const { - auto newLayout = getSelectedLayout(); - auto newAlignment = getSelectedAlignment(); + yup::Artboard::NodeAttachmentOptions options; + options.mode = markerModeCombo.getSelectedId() == 2 + ? yup::Artboard::NodeAttachmentOptions::Mode::trackPosition + : yup::Artboard::NodeAttachmentOptions::Mode::fillNode; + options.applyTransform = applyTransformToggle.getToggleState(); + options.pivot = getSelectedJustification (pivotCombo); + options.anchor = getSelectedJustification (anchorCombo); + return options; + } - for (auto* artboard : artboards) + // Re-attaches every tracked component with the currently selected attachment mode. + void updateMarkers() + { + const auto options = getMarkerOptions(); + + for (int i = 0; i < artboards.size() && i < trackedComponents.size(); ++i) { - artboard->setLayout (newLayout); - artboard->setAlignment (newAlignment); + auto* art = artboards.getUnchecked (i); + auto* component = trackedComponents.getUnchecked (i); + + if (component == nullptr || art == nullptr) + continue; + + if (options.mode == yup::Artboard::NodeAttachmentOptions::Mode::trackPosition) + component->setSize (44.0f, 44.0f); + + component->setVisible (art->attachComponentToNode (trackedNodeName, component, options)); } } + yup::String rivePath; + yup::String trackedNodeName; + int defaultFitComboId; + bool fillArtboardCell; + + yup::OwnedArray trackedComponents; yup::OwnedArray artboards; yup::Label fitLabel; yup::ComboBox fitCombo; yup::Label alignmentLabel; yup::ComboBox alignmentCombo; + yup::Label markerLabel; + yup::ComboBox markerModeCombo; + yup::ToggleButton applyTransformToggle; + yup::Label pivotLabel; + yup::ComboBox pivotCombo; + yup::Label anchorLabel; + yup::ComboBox anchorCombo; int totalRows = 1; int totalColumns = 1; }; + +//============================================================================== + +class ArtboardDemo : public ArtboardDemoBase +{ +public: + ArtboardDemo() + : ArtboardDemoBase ("data/alien.riv", "Mouth") + { + } + + bool isInterestedInDrag (const yup::DragAndDropData& data) override + { + return findRiveFile (data).has_value(); + } + + void itemDragEnter (const yup::DragAndDropData&, const yup::Point&) override + { + setDropHighlighted (true); + } + + void itemDragExit (const yup::DragAndDropData&) override + { + setDropHighlighted (false); + } + + bool itemsDropped (const yup::Point&, const yup::DragAndDropData& data) override + { + setDropHighlighted (false); + + const auto file = findRiveFile (data); + if (! file.has_value()) + return false; + + auto factory = getNativeComponent()->getFactory(); + if (factory == nullptr) + return false; + + auto artboardFile = yup::ArtboardFile::load (*file, *factory); + if (! artboardFile) + return false; + + setArtboardFile (artboardFile.getValue()); + + return true; + } + + void paint (yup::Graphics& g) override + { + ArtboardDemoBase::paint (g); + + if (dropHighlighted) + { + g.setStrokeColor (yup::Colors::darkorange); + g.setStrokeWidth (3.0f); + g.strokeRoundedRect (getLocalBounds().to().reduced (2.0f), 6.0f); + } + } + +private: + // Returns the first dropped file that looks like a Rive file, if any. + static std::optional findRiveFile (const yup::DragAndDropData& data) + { + for (const auto& file : data.getFiles()) + if (file.hasFileExtension ("riv")) + return file; + + return std::nullopt; + } + + void setDropHighlighted (bool shouldBeHighlighted) + { + if (dropHighlighted == shouldBeHighlighted) + return; + + dropHighlighted = shouldBeHighlighted; + repaint(); + } + + bool dropHighlighted = false; +}; + +//============================================================================== + +class ArtboardLayoutDemo : public ArtboardDemoBase +{ +public: + ArtboardLayoutDemo() + : ArtboardDemoBase ("data/layout-ui.riv", "keyboard_slot", 8, true) + { + } + +private: + std::unique_ptr createTrackedComponent() override + { + auto keyboardArtboard = std::make_unique ("keyboardArtboard"); + keyboardArtboard->setFile (loadedArtboardFile, "Keyboard"); + keyboardArtboard->setFitting (yup::Fitting::fill); + return keyboardArtboard; + } +}; diff --git a/examples/graphics/source/examples/GpuAudioProcessingDemo.h b/examples/graphics/source/examples/GpuAudioProcessingDemo.h index f7384234b..1cd7bbe7b 100644 --- a/examples/graphics/source/examples/GpuAudioProcessingDemo.h +++ b/examples/graphics/source/examples/GpuAudioProcessingDemo.h @@ -170,7 +170,7 @@ class GpuAudioProcessingDemo : public yup::Component if (computeDevice == nullptr) { const yup::GpuPlatform platforms[] = { -#if YUP_MAC || YUP_IOS +#if YUP_APPLE yup::GpuPlatform::Metal, #else #if YUP_WINDOWS diff --git a/examples/graphics/source/examples/LottieDemo.h b/examples/graphics/source/examples/LottieDemo.h index 54f27dc39..821ef375b 100644 --- a/examples/graphics/source/examples/LottieDemo.h +++ b/examples/graphics/source/examples/LottieDemo.h @@ -171,7 +171,7 @@ class LottieDemo : public yup::Component playPauseButton->setButtonText ("Play"); }; - loadFile (getAssetPath (YUP_EXAMPLE_GRAPHICS_LOTTIE_FILE)); + loadFile (getAssetPath ("data/goal.lottie")); } ~LottieDemo() override = default; diff --git a/examples/graphics/source/examples/SpinningCubeDemo.h b/examples/graphics/source/examples/SpinningCubeDemo.h index 78a6a2acd..8b5bb72c5 100644 --- a/examples/graphics/source/examples/SpinningCubeDemo.h +++ b/examples/graphics/source/examples/SpinningCubeDemo.h @@ -601,7 +601,7 @@ void main() { void initLottie() { - auto anim = yup::Animation::loadFromFile (getAssetPath (YUP_EXAMPLE_GRAPHICS_LOTTIE_FILE)); + auto anim = yup::Animation::loadFromFile (getAssetPath ("data/goal.lottie")); if (! anim.isValid()) { diff --git a/examples/graphics/source/main.cpp b/examples/graphics/source/main.cpp index 81ff69efe..161aa9598 100644 --- a/examples/graphics/source/main.cpp +++ b/examples/graphics/source/main.cpp @@ -181,6 +181,7 @@ class CustomWindow // clang-format off addDemo ("AI", [] { return std::make_unique(); }); addDemo ("Artboard", [] { return std::make_unique(); }); + addDemo ("Artboard Layout", [] { return std::make_unique(); }); addDemo ("Audio", [] { return std::make_unique(); }); addDemo ("Audio File", [] { return std::make_unique(); }); addDemo ("Clipboard", [] { return std::make_unique(); }); diff --git a/justfile b/justfile index e5d30a085..ff5412f44 100644 --- a/justfile +++ b/justfile @@ -138,7 +138,7 @@ rive_shaders_update: uv venv .venv --clear source .venv/bin/activate uv pip install ply - uv run make -C thirdparty/rive_renderer/source/shaders -j 8 - cp -R thirdparty/rive_renderer/source/shaders/out/generated/* thirdparty/rive_renderer/source/generated/shaders/ - rm -Rf thirdparty/rive_renderer/source/shaders/out + uv run make -C thirdparty/rive/source/renderer/shaders -j 8 + cp -R thirdparty/rive/source/renderer/shaders/out/generated/* thirdparty/rive/source/renderer/generated/shaders/ + rm -Rf thirdparty/rive/source/renderer/shaders/out .venv/bin/deactivate diff --git a/modules/yup_audio_basics/native/yup_CoreAudioLayouts_apple.h b/modules/yup_audio_basics/native/yup_CoreAudioLayouts_apple.h index 4528449db..4a0d8c51a 100644 --- a/modules/yup_audio_basics/native/yup_CoreAudioLayouts_apple.h +++ b/modules/yup_audio_basics/native/yup_CoreAudioLayouts_apple.h @@ -40,7 +40,7 @@ namespace yup { -#if ! defined(DOXYGEN) && (YUP_MAC || YUP_IOS) +#if ! defined(DOXYGEN) && YUP_APPLE struct CoreAudioLayouts { diff --git a/modules/yup_audio_basics/yup_audio_basics.cpp b/modules/yup_audio_basics/yup_audio_basics.cpp index 0638bd4d0..634aa0d19 100644 --- a/modules/yup_audio_basics/yup_audio_basics.cpp +++ b/modules/yup_audio_basics/yup_audio_basics.cpp @@ -48,7 +48,7 @@ #include "yup_audio_basics.h" -#if YUP_MAC || YUP_IOS +#if YUP_APPLE #include #include "native/yup_AudioWorkgroup_apple.h" diff --git a/modules/yup_audio_devices/midi_io/yup_MidiDevices.h b/modules/yup_audio_devices/midi_io/yup_MidiDevices.h index 81e151a72..2a7d8fdf5 100644 --- a/modules/yup_audio_devices/midi_io/yup_MidiDevices.h +++ b/modules/yup_audio_devices/midi_io/yup_MidiDevices.h @@ -231,7 +231,7 @@ class YUP_API MidiInput final ump::PacketProtocol protocol, ump::Receiver* receiver); -#if YUP_LINUX || YUP_BSD || YUP_MAC || YUP_IOS || YUP_WASM || DOXYGEN +#if YUP_LINUX || YUP_BSD || YUP_APPLE || YUP_WASM || DOXYGEN /** This will try to create a new midi input device (only available on Linux, macOS and iOS). This will attempt to create a new midi input device with the specified name for other @@ -390,7 +390,7 @@ class YUP_API MidiOutput final : private Thread static std::unique_ptr openDevice (const String& deviceIdentifier, ump::PacketProtocol protocol); -#if YUP_LINUX || YUP_BSD || YUP_MAC || YUP_IOS || YUP_WASM || DOXYGEN +#if YUP_LINUX || YUP_BSD || YUP_APPLE || YUP_WASM || DOXYGEN /** This will try to create a new midi output device (only available on Linux, macOS and iOS). This will attempt to create a new midi output device with the specified name that other diff --git a/modules/yup_audio_devices/yup_audio_devices.cpp b/modules/yup_audio_devices/yup_audio_devices.cpp index ad1bed945..75c2c65bf 100644 --- a/modules/yup_audio_devices/yup_audio_devices.cpp +++ b/modules/yup_audio_devices/yup_audio_devices.cpp @@ -68,7 +68,7 @@ #include "midi_io/ump/yup_UMPU32InputHandler.h" //============================================================================== -#if YUP_MAC || YUP_IOS +#if YUP_APPLE #include #include #endif diff --git a/modules/yup_audio_formats/yup_audio_formats.cpp b/modules/yup_audio_formats/yup_audio_formats.cpp index 87bf2120a..b9d6b35a8 100644 --- a/modules/yup_audio_formats/yup_audio_formats.cpp +++ b/modules/yup_audio_formats/yup_audio_formats.cpp @@ -53,7 +53,7 @@ #include #endif -#if YUP_MAC || YUP_IOS +#if YUP_APPLE #include #include diff --git a/modules/yup_audio_formats/yup_audio_formats.h b/modules/yup_audio_formats/yup_audio_formats.h index a8e89982c..8c05ffe27 100644 --- a/modules/yup_audio_formats/yup_audio_formats.h +++ b/modules/yup_audio_formats/yup_audio_formats.h @@ -109,7 +109,7 @@ Enable CoreAudio audio format support on Apple platforms. */ #ifndef YUP_AUDIO_FORMAT_COREAUDIO -#if YUP_MAC || YUP_IOS +#if YUP_APPLE #define YUP_AUDIO_FORMAT_COREAUDIO 1 #endif #endif @@ -152,7 +152,7 @@ #define YUP_AUDIO_FORMAT_FLAC 0 #endif -#if YUP_AUDIO_FORMAT_COREAUDIO && ! (YUP_MAC || YUP_IOS) +#if YUP_AUDIO_FORMAT_COREAUDIO && ! YUP_APPLE #undef YUP_AUDIO_FORMAT_COREAUDIO #define YUP_AUDIO_FORMAT_COREAUDIO 0 #endif diff --git a/modules/yup_core/files/yup_File.h b/modules/yup_core/files/yup_File.h index 9cb611b31..8c5b54536 100644 --- a/modules/yup_core/files/yup_File.h +++ b/modules/yup_core/files/yup_File.h @@ -40,7 +40,7 @@ namespace yup { -#if ! DOXYGEN && (YUP_MAC || YUP_IOS) +#if ! DOXYGEN && YUP_APPLE using OSType = unsigned int; #endif @@ -1136,7 +1136,7 @@ class YUP_API File final #endif //============================================================================== -#if YUP_MAC || YUP_IOS || DOXYGEN +#if YUP_APPLE || DOXYGEN /** OSX ONLY - Finds the OSType of a file from the its resources. */ OSType getMacOSType() const; @@ -1149,7 +1149,7 @@ class YUP_API File final void addToDock() const; #endif -#if YUP_MAC || YUP_IOS +#if YUP_APPLE /** Returns the path to the container shared by all apps with the provided app group ID. You *must* pass one of the app group IDs listed in your app's entitlements file. diff --git a/modules/yup_core/memory/yup_ByteOrder.h b/modules/yup_core/memory/yup_ByteOrder.h index 6ec8e8686..408c49ffc 100644 --- a/modules/yup_core/memory/yup_ByteOrder.h +++ b/modules/yup_core/memory/yup_ByteOrder.h @@ -195,7 +195,7 @@ inline double ByteOrder::swap (double v) noexcept inline uint32 ByteOrder::swap (uint32 n) noexcept { -#if YUP_MAC || YUP_IOS +#if YUP_APPLE return OSSwapInt32 (n); #elif (YUP_GCC || YUP_CLANG) && YUP_INTEL && ! YUP_NO_INLINE_ASM asm ("bswap %%eax" @@ -213,7 +213,7 @@ inline uint32 ByteOrder::swap (uint32 n) noexcept inline uint64 ByteOrder::swap (uint64 value) noexcept { -#if YUP_MAC || YUP_IOS +#if YUP_APPLE return OSSwapInt64 (value); #elif YUP_MSVC return _byteswap_uint64 (value); diff --git a/modules/yup_core/memory/yup_Memory.h b/modules/yup_core/memory/yup_Memory.h index d3bc69421..a16855c54 100644 --- a/modules/yup_core/memory/yup_Memory.h +++ b/modules/yup_core/memory/yup_Memory.h @@ -253,7 +253,7 @@ inline const Type* addBytesToPointer(const Type* basePointer, IntegerType bytes) } //============================================================================== -#if YUP_MAC || YUP_IOS || DOXYGEN +#if YUP_APPLE || DOXYGEN /** A handy C++ wrapper that creates and deletes an NSAutoreleasePool object using RAII. You should use the YUP_AUTORELEASEPOOL macro to create a local auto-release pool on the stack. diff --git a/modules/yup_core/native/yup_BasicNativeHeaders.h b/modules/yup_core/native/yup_BasicNativeHeaders.h index 4e1331f23..546b6c550 100644 --- a/modules/yup_core/native/yup_BasicNativeHeaders.h +++ b/modules/yup_core/native/yup_BasicNativeHeaders.h @@ -42,7 +42,7 @@ #undef T //============================================================================== -#if YUP_MAC || YUP_IOS +#if YUP_APPLE #if YUP_IOS #define Component CarbonDummyCompName diff --git a/modules/yup_core/native/yup_PlatformTimer_generic.cpp b/modules/yup_core/native/yup_PlatformTimer_generic.cpp index a06f9f664..0eaf8a9c3 100644 --- a/modules/yup_core/native/yup_PlatformTimer_generic.cpp +++ b/modules/yup_core/native/yup_PlatformTimer_generic.cpp @@ -129,7 +129,7 @@ class PlatformTimer final : private Thread void run() { -#if YUP_MAC || YUP_IOS +#if YUP_APPLE tryToUpgradeCurrentThreadToRealtime (Thread::RealtimeOptions {}.withPeriodMs (intervalMs)); #endif diff --git a/modules/yup_core/native/yup_SharedCode_posix.h b/modules/yup_core/native/yup_SharedCode_posix.h index 52c08c7f2..3b6fcd04d 100644 --- a/modules/yup_core/native/yup_SharedCode_posix.h +++ b/modules/yup_core/native/yup_SharedCode_posix.h @@ -253,7 +253,7 @@ bool yup_doStatFS (File f, struct statfs& result) return statfs (f.getFullPathName().toUTF8(), &result) == 0; } -#if YUP_MAC || YUP_IOS +#if YUP_APPLE static int64 getCreationTime (const yup_statStruct& s) noexcept { return (int64) s.st_birthtime; @@ -1153,7 +1153,7 @@ class PosixSchedulerPriority // We only use this helper if we're on an old macos/ios platform that might // still respect legacy pthread priorities for SCHED_OTHER. -#if YUP_MAC || YUP_IOS +#if YUP_APPLE const auto min = jmax (0, sched_get_priority_min (SCHED_OTHER)); const auto max = jmax (0, sched_get_priority_max (SCHED_OTHER)); @@ -1183,7 +1183,7 @@ class PosixSchedulerPriority return 0; }(); -#if YUP_MAC || YUP_IOS || YUP_BSD +#if YUP_APPLE || YUP_BSD const auto scheduler = SCHED_OTHER; #elif YUP_LINUX const auto backgroundSched = prio == Thread::Priority::background ? SCHED_IDLE diff --git a/modules/yup_core/native/yup_ThreadPriorities_native.h b/modules/yup_core/native/yup_ThreadPriorities_native.h index e56c6f380..032f83a5b 100644 --- a/modules/yup_core/native/yup_ThreadPriorities_native.h +++ b/modules/yup_core/native/yup_ThreadPriorities_native.h @@ -78,7 +78,7 @@ struct ThreadPriorities { Thread::Priority::background, 0 }, #endif -#if YUP_MAC || YUP_IOS +#if YUP_APPLE { Thread::Priority::highest, 4 }, { Thread::Priority::high, 3 }, { Thread::Priority::normal, 2 }, diff --git a/modules/yup_core/system/yup_CompilerWarnings.h b/modules/yup_core/system/yup_CompilerWarnings.h index b232342b4..265cdc9a5 100644 --- a/modules/yup_core/system/yup_CompilerWarnings.h +++ b/modules/yup_core/system/yup_CompilerWarnings.h @@ -184,7 +184,7 @@ #define YUP_END_IGNORE_WARNINGS_MSVC #endif -#if YUP_MAC || YUP_IOS +#if YUP_APPLE #define YUP_SANITIZER_ATTRIBUTE_MINIMUM_CLANG_VERSION 11 #else #define YUP_SANITIZER_ATTRIBUTE_MINIMUM_CLANG_VERSION 9 diff --git a/modules/yup_core/system/yup_StandardHeader.h b/modules/yup_core/system/yup_StandardHeader.h index fc02e37e9..dceb62513 100644 --- a/modules/yup_core/system/yup_StandardHeader.h +++ b/modules/yup_core/system/yup_StandardHeader.h @@ -115,7 +115,7 @@ YUP_BEGIN_IGNORE_WARNINGS_MSVC (4514 4245 4100) #include #endif -#if YUP_MAC || YUP_IOS +#if YUP_APPLE #include #include #include diff --git a/modules/yup_core/system/yup_SystemStats.cpp b/modules/yup_core/system/yup_SystemStats.cpp index c98f2f4fa..7949dcfab 100644 --- a/modules/yup_core/system/yup_SystemStats.cpp +++ b/modules/yup_core/system/yup_SystemStats.cpp @@ -376,7 +376,7 @@ void SystemStats::setApplicationCrashHandler (CrashHandlerFunction handler) bool SystemStats::isRunningInAppExtensionSandbox() noexcept { -#if YUP_MAC || YUP_IOS +#if YUP_APPLE static bool isRunningInAppSandbox = [&] { File bundle = File::getSpecialLocation (File::invokedExecutableFile).getParentDirectory(); diff --git a/modules/yup_core/system/yup_TargetPlatform.h b/modules/yup_core/system/yup_TargetPlatform.h index 7c8043d27..ef9e6cad0 100644 --- a/modules/yup_core/system/yup_TargetPlatform.h +++ b/modules/yup_core/system/yup_TargetPlatform.h @@ -88,6 +88,7 @@ #define CF_EXCLUDE_CSTD_HEADERS 1 #include // (needed to find out what platform we're using) #include +#define YUP_APPLE 1 #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR #define YUP_IPHONE 1 #define YUP_IOS 1 @@ -140,7 +141,7 @@ #endif //============================================================================== -#if YUP_MAC || YUP_IOS +#if YUP_APPLE // Expands to true if the API of the specified version is available at build time, false otherwise #define YUP_MAC_API_VERSION_CAN_BE_BUILT(major, minor) \ diff --git a/modules/yup_core/text/yup_String.h b/modules/yup_core/text/yup_String.h index 4cd5ef796..0cc6e84e0 100644 --- a/modules/yup_core/text/yup_String.h +++ b/modules/yup_core/text/yup_String.h @@ -37,7 +37,7 @@ ============================================================================== */ -#if ! defined(DOXYGEN) && (YUP_MAC || YUP_IOS) +#if ! defined(DOXYGEN) && YUP_APPLE // Annoyingly we can only forward-declare a typedef by forward-declaring the // aliased type #if __has_attribute(objc_bridge) @@ -1365,7 +1365,7 @@ class YUP_API String final void swapWith (String& other) noexcept; //============================================================================== -#if YUP_MAC || YUP_IOS || DOXYGEN +#if YUP_APPLE || DOXYGEN /** OSX ONLY - Creates a String from an OSX CFString. */ static String fromCFString (CFStringRef cfString); diff --git a/modules/yup_core/yup_core.cpp b/modules/yup_core/yup_core.cpp index d6fbc5102..e91a5845e 100644 --- a/modules/yup_core/yup_core.cpp +++ b/modules/yup_core/yup_core.cpp @@ -155,7 +155,7 @@ extern char** environ; #include #endif -#if YUP_MAC || YUP_IOS +#if YUP_APPLE #include #include #endif @@ -255,7 +255,7 @@ extern char** environ; #endif //============================================================================== -#if YUP_MAC || YUP_IOS +#if YUP_APPLE #include "native/yup_Files_apple.mm" #include "native/yup_Network_apple.mm" #include "native/yup_Strings_apple.mm" diff --git a/modules/yup_core/yup_core.h b/modules/yup_core/yup_core.h index d31cbb420..f5d118022 100644 --- a/modules/yup_core/yup_core.h +++ b/modules/yup_core/yup_core.h @@ -409,7 +409,7 @@ YUP_END_IGNORE_WARNINGS_MSVC #include "detail/yup_CallbackListenerList.h" -#if YUP_CORE_INCLUDE_OBJC_HELPERS && (YUP_MAC || YUP_IOS) +#if YUP_CORE_INCLUDE_OBJC_HELPERS && YUP_APPLE #include "native/yup_CFHelpers_apple.h" #include "native/yup_ObjCHelpers_apple.h" #endif diff --git a/modules/yup_dsp/yup_dsp.cpp b/modules/yup_dsp/yup_dsp.cpp index f2d62390d..94e66a3ee 100644 --- a/modules/yup_dsp/yup_dsp.cpp +++ b/modules/yup_dsp/yup_dsp.cpp @@ -43,7 +43,7 @@ //============================================================================== -#if ! YUP_FFT_FOUND_BACKEND && YUP_ENABLE_VDSP && (YUP_MAC || YUP_IOS) +#if ! YUP_FFT_FOUND_BACKEND && YUP_ENABLE_VDSP && YUP_APPLE #define YUP_FFT_USING_VDSP 1 #define YUP_FFT_FOUND_BACKEND 1 #include diff --git a/modules/yup_dsp/yup_dsp.h b/modules/yup_dsp/yup_dsp.h index f5b2baf9a..720518cbf 100644 --- a/modules/yup_dsp/yup_dsp.h +++ b/modules/yup_dsp/yup_dsp.h @@ -70,7 +70,7 @@ Enable Apple's vDSP backend. */ #ifndef YUP_ENABLE_VDSP -#if (YUP_MAC || YUP_IOS) && YUP_USE_VDSP_FRAMEWORK +#if YUP_APPLE && YUP_USE_VDSP_FRAMEWORK #define YUP_ENABLE_VDSP 1 #else #define YUP_ENABLE_VDSP 0 diff --git a/modules/yup_events/messages/yup_MessageManager.cpp b/modules/yup_events/messages/yup_MessageManager.cpp index 8027f14fa..4d778f4ae 100644 --- a/modules/yup_events/messages/yup_MessageManager.cpp +++ b/modules/yup_events/messages/yup_MessageManager.cpp @@ -113,7 +113,7 @@ void MessageManager::registerShutdownCallback (std::function shutdownCal } //============================================================================== -#if ! (YUP_MAC || YUP_IOS || YUP_WASM) +#if ! (YUP_APPLE || YUP_WASM) // implemented in platform-specific code (yup_Messaging_linux.cpp, yup_Messaging_android.cpp and yup_Messaging_windows.cpp) bool yup_dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages); diff --git a/modules/yup_events/native/yup_ToastNotification_apple.mm b/modules/yup_events/native/yup_ToastNotification_apple.mm index 5c6f87be2..9fa4dd0d2 100644 --- a/modules/yup_events/native/yup_ToastNotification_apple.mm +++ b/modules/yup_events/native/yup_ToastNotification_apple.mm @@ -33,7 +33,7 @@ The Apple backend uses the UserNotifications framework (macOS 10.14+, hidden or cleared. */ -#if YUP_MAC || YUP_IOS +#if YUP_APPLE namespace yup::detail { @@ -621,4 +621,4 @@ void toastNotificationSetPermissionStateChangedCallback (std::function yup_constructHeadlessGraphicsContext (GpuDevice::Options, GpuDevice::Ptr = {}); -#if YUP_RIVE_USE_METAL && (YUP_MAC || YUP_IOS) +#if YUP_RIVE_USE_METAL && YUP_APPLE std::unique_ptr yup_constructMetalGraphicsContext (GpuDevice::Options, GpuDevice::Ptr = {}); #endif #if YUP_RIVE_USE_D3D && YUP_WINDOWS @@ -58,7 +58,7 @@ std::unique_ptr GraphicsContext::createContext (GpuPlatform gra case GpuPlatform::Headless: return yup_constructHeadlessGraphicsContext (options, std::move (existingGpu)); -#if YUP_RIVE_USE_METAL && (YUP_MAC || YUP_IOS) +#if YUP_RIVE_USE_METAL && YUP_APPLE case GpuPlatform::Metal: return yup_constructMetalGraphicsContext (options, std::move (existingGpu)); #endif diff --git a/modules/yup_graphics/fonts/yup_Font.cpp b/modules/yup_graphics/fonts/yup_Font.cpp index 4e9a81bd8..51787ec89 100644 --- a/modules/yup_graphics/fonts/yup_Font.cpp +++ b/modules/yup_graphics/fonts/yup_Font.cpp @@ -27,7 +27,7 @@ namespace //============================================================================== -#if YUP_MAC || YUP_IOS +#if YUP_APPLE ResultValue loadSystemUIFont (CTFontUIFontType fontType) { if (auto systemFont = CTFontCreateUIFontForLanguage (fontType, 0.0, nullptr)) @@ -144,7 +144,7 @@ ResultValue Font::loadFontFromFirstAvailableFile (std::initializer_list Font::loadSerifSystemTextFont() { -#if YUP_MAC || YUP_IOS +#if YUP_APPLE return loadSystemUIFont (kCTFontUIFontSystem); #elif YUP_WINDOWS diff --git a/modules/yup_graphics/primitives/yup_AffineTransform.h b/modules/yup_graphics/primitives/yup_AffineTransform.h index 4e0447ca8..cc1b68551 100644 --- a/modules/yup_graphics/primitives/yup_AffineTransform.h +++ b/modules/yup_graphics/primitives/yup_AffineTransform.h @@ -892,8 +892,18 @@ class YUP_API AffineTransform } //============================================================================== + /** @internal Conversion from Rive Mat2D class. + + @note Rive uses a transposed matrix representation compared to YUP. + */ + explicit AffineTransform (const rive::Mat2D& m2d) + : AffineTransform (m2d[0], m2d[2], m2d[4], m2d[1], m2d[3], m2d[5]) + { + } + /** @internal Conversion to Rive Mat2D class. - * Note: Rive uses a transposed matrix representation compared to YUP. + + @note Rive uses a transposed matrix representation compared to YUP. */ rive::Mat2D toMat2D() const { diff --git a/modules/yup_graphics/yup_graphics.cpp b/modules/yup_graphics/yup_graphics.cpp index 442d13c21..7e1530830 100644 --- a/modules/yup_graphics/yup_graphics.cpp +++ b/modules/yup_graphics/yup_graphics.cpp @@ -71,7 +71,7 @@ YUP_END_IGNORE_WARNINGS_GCC_LIKE //============================================================================== -#elif YUP_MAC || YUP_IOS +#elif YUP_APPLE #if YUP_RIVE_USE_METAL #import diff --git a/modules/yup_graphics/yup_graphics.h b/modules/yup_graphics/yup_graphics.h index cd3835526..2cf1eca1c 100644 --- a/modules/yup_graphics/yup_graphics.h +++ b/modules/yup_graphics/yup_graphics.h @@ -32,7 +32,7 @@ website: https://github.com/kunitoki/yup license: ISC - dependencies: yup_core yup_simd yup_rhi yup_shading rive rive_renderer libclipper2 + dependencies: yup_core yup_simd yup_rhi yup_shading rive libclipper2 optionalDeps: libpng libjpeg libwebp libgif libtiff appleFrameworks: Metal searchpaths: native @@ -58,7 +58,7 @@ YUP_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") #include #include #include -#include +#include #include #include YUP_END_IGNORE_WARNINGS_GCC_LIKE diff --git a/modules/yup_gui/artboard/yup_Artboard.cpp b/modules/yup_gui/artboard/yup_Artboard.cpp index d367ba318..6dab271c0 100644 --- a/modules/yup_gui/artboard/yup_Artboard.cpp +++ b/modules/yup_gui/artboard/yup_Artboard.cpp @@ -25,56 +25,111 @@ namespace yup //============================================================================== namespace { -rive::Fit toRiveFit (Artboard::Layout layout) + +rive::Fit toRiveFit (std::optional fitting) { - switch (layout) + // No fitting of our own means the artboard resolves its own layout. + if (! fitting.has_value()) + return rive::Fit::layout; + + switch (*fitting) { - case Artboard::Layout::fill: + case Fitting::fill: return rive::Fit::fill; - case Artboard::Layout::contain: + case Fitting::scaleToFit: return rive::Fit::contain; - case Artboard::Layout::cover: + case Fitting::scaleToFill: return rive::Fit::cover; - case Artboard::Layout::fitWidth: + case Fitting::fitWidth: return rive::Fit::fitWidth; - case Artboard::Layout::fitHeight: + case Fitting::fitHeight: return rive::Fit::fitHeight; - case Artboard::Layout::none: + case Fitting::none: return rive::Fit::none; - case Artboard::Layout::scaleDown: + case Fitting::centerInside: return rive::Fit::scaleDown; - case Artboard::Layout::layout: - return rive::Fit::layout; + + // Rive has no equivalent for these, so they degrade to a uniform fit. + case Fitting::tile: + case Fitting::centerCrop: + case Fitting::stretchWidth: + case Fitting::stretchHeight: + return rive::Fit::contain; } return rive::Fit::contain; } -rive::Alignment toRiveAlignment (Artboard::Alignment alignment) +rive::Alignment toRiveAlignment (Justification justification) { - switch (alignment) + const bool left = justification.testFlags (Justification::left); + const bool right = ! left && justification.testFlags (Justification::right); + + const bool top = justification.testFlags (Justification::top); + const bool bottom = ! top && justification.testFlags (Justification::bottom); + + if (top) + return left ? rive::Alignment::topLeft : right ? rive::Alignment::topRight + : rive::Alignment::topCenter; + + if (bottom) + return left ? rive::Alignment::bottomLeft : right ? rive::Alignment::bottomRight + : rive::Alignment::bottomCenter; + + return left ? rive::Alignment::centerLeft : right ? rive::Alignment::centerRight + : rive::Alignment::center; +} + +constexpr float kNodeBoundsEpsilon = 0.01f; + +bool areNodeBoundsEqual (const Rectangle& a, const Rectangle& b) +{ + const auto isClose = [] (float x, float y) + { + return x - y < kNodeBoundsEpsilon && y - x < kNodeBoundsEpsilon; + }; + + return isClose (a.getX(), b.getX()) + && isClose (a.getY(), b.getY()) + && isClose (a.getWidth(), b.getWidth()) + && isClose (a.getHeight(), b.getHeight()); +} + +void expandNodeWorldBounds (rive::Component* component, rive::AABB& result, bool& found) +{ + if (component == nullptr) + return; + + if (component->is()) { - case Artboard::Alignment::topLeft: - return rive::Alignment::topLeft; - case Artboard::Alignment::topCenter: - return rive::Alignment::topCenter; - case Artboard::Alignment::topRight: - return rive::Alignment::topRight; - case Artboard::Alignment::centerLeft: - return rive::Alignment::centerLeft; - case Artboard::Alignment::center: - return rive::Alignment::center; - case Artboard::Alignment::centerRight: - return rive::Alignment::centerRight; - case Artboard::Alignment::bottomLeft: - return rive::Alignment::bottomLeft; - case Artboard::Alignment::bottomCenter: - return rive::Alignment::bottomCenter; - case Artboard::Alignment::bottomRight: - return rive::Alignment::bottomRight; + const auto bounds = component->as()->worldBounds(); + if (! bounds.isEmptyOrNaN()) + { + if (! found) + result = bounds; + else + result.expand (bounds); + + found = true; + } } - return rive::Alignment::center; + if (component->is()) + for (auto* child : component->as()->children()) + expandNodeWorldBounds (child, result, found); +} + +std::optional collectNodeWorldBounds (rive::Component* node) +{ + if (node == nullptr) + return std::nullopt; + + rive::AABB result = rive::AABB::forExpansion(); + bool found = false; + + expandNodeWorldBounds (node, result, found); + + return found ? std::optional (result) : std::nullopt; } } // namespace @@ -87,18 +142,24 @@ Artboard::Artboard (StringRef componentID) } Artboard::Artboard (StringRef componentID, std::shared_ptr file) - : Component (componentID) + : Artboard (componentID) { setFile (std::move (file)); } +Artboard::~Artboard() +{ + detachAllComponents(); +} + //============================================================================== -void Artboard::setFile (std::shared_ptr file) +void Artboard::setFile (std::shared_ptr file, StringRef artboardName) { clear(); artboardFile = std::move (file); + selectedArtboardName = artboardName; updateSceneFromFile(); } @@ -107,47 +168,55 @@ void Artboard::setFile (std::shared_ptr file) void Artboard::clear() { - artboardFile.reset(); - - artboard.reset(); - scene.reset(); + ++nodeEpoch; stateMachine = nullptr; + boundViewModelInstance = nullptr; eventProperties.clear(); viewTransform = rive::Mat2D(); + selectedArtboardName.clear(); + + lastNodeBounds.clear(); + lastNodeViewTransforms.clear(); + cachedNodeHandles.clear(); + + artboardFile.reset(); + + scene.reset(); + artboard.reset(); } //============================================================================== -void Artboard::setLayout (Layout newLayout) +void Artboard::setFitting (std::optional newFitting) { - if (layout == newLayout) + if (fitting == newFitting) return; - layout = newLayout; - updateViewTransform(); + fitting = newFitting; + updateNodeBounds(); repaint(); } -Artboard::Layout Artboard::getLayout() const +std::optional Artboard::getFitting() const { - return layout; + return fitting; } -void Artboard::setAlignment (Alignment newAlignment) +void Artboard::setJustification (Justification newJustification) { - if (alignment == newAlignment) + if (justification == newJustification) return; - alignment = newAlignment; - updateViewTransform(); + justification = newJustification; + updateNodeBounds(); repaint(); } -Artboard::Alignment Artboard::getAlignment() const +Justification Artboard::getJustification() const { - return alignment; + return justification; } //============================================================================== @@ -180,10 +249,9 @@ void Artboard::shouldPauseWhenHidden (bool shouldPause) void Artboard::advanceAndApply (float elapsedSeconds) { - if (scene == nullptr) - return; + advanceScene (elapsedSeconds); - scene->advanceAndApply (elapsedSeconds); + notifyNodeBoundsChanged(); } float Artboard::durationSeconds() const @@ -286,7 +354,7 @@ var Artboard::getAllInputs() const object->setProperty ("type", "boolean"); object->setProperty ("value", boolean->value()); } - else if (auto trigger = dynamic_cast (inputObject)) + else if (dynamic_cast (inputObject) != nullptr) { object->setProperty ("type", "trigger"); } @@ -299,9 +367,28 @@ var Artboard::getAllInputs() const void Artboard::setAllInputs (const var& value) { + if (stateMachine == nullptr) + return; + + auto* inputs = value.getArray(); + if (inputs == nullptr) + return; + + for (const auto& input : *inputs) + { + auto* object = input.getDynamicObject(); + if (object == nullptr) + continue; + + const auto inputValue = object->getProperty ("value"); + if (inputValue.isVoid()) + continue; + + setInput (object->getProperty ("id").toString(), inputValue); + } } -void Artboard::setInput (const String& inputName, const var& value) +void Artboard::setInput (const String& name, const var& value) { if (stateMachine == nullptr) return; @@ -310,7 +397,7 @@ void Artboard::setInput (const String& inputName, const var& value) { auto inputObject = stateMachine->input (inputIndex); - if (StringRef (inputObject->name()) != inputName) + if (StringRef (inputObject->name()) != name) continue; if (auto trigger = dynamic_cast (inputObject)) @@ -337,6 +424,258 @@ void Artboard::setInput (const String& inputName, const var& value) //============================================================================== +void Artboard::setNodeBoundsListener (StringRef nodeName, NodeBoundsCallback callback) +{ + const String name (nodeName); + + if (callback) + { + nodeBoundsListeners.set (name, std::move (callback)); + + if (artboard != nullptr) + { + if (auto* node = artboard->find (name.toStdString())) + { + lastNodeBounds.set (name, computeNodeBounds (node)); + lastNodeViewTransforms.set (name, computeNodeViewTransform (node)); + } + } + } + else + { + nodeBoundsListeners.remove (name); + } +} + +void Artboard::clearNodeBoundsListener (StringRef nodeName) +{ + nodeBoundsListeners.remove (String (nodeName)); +} + +void Artboard::clearAllNodeBoundsListeners() +{ + nodeBoundsListeners.clear(); +} + +Rectangle Artboard::getNodeBounds (StringRef nodeName) const +{ + if (artboard == nullptr) + return {}; + + auto* node = artboard->find (String (nodeName).toStdString()); + if (node == nullptr) + return {}; + + return computeNodeBounds (node); +} + +//============================================================================== + +bool Artboard::attachComponentToNode (StringRef nodeName, Component* component, NodeAttachmentOptions options) +{ + if (component == nullptr || artboard == nullptr) + return false; + + const String name (nodeName); + + auto* node = artboard->find (name.toStdString()); + if (node == nullptr) + return false; + + detachComponentEverywhere (component); + + NodeAttachment attachment; + attachment.component = component; + attachment.options = options; + attachedComponents.set (name, attachment); + + component->addComponentListener (this); + + const auto bounds = computeNodeBounds (node); + applyNodeAttachment (attachment, node, bounds); + lastNodeBounds.set (name, bounds); + lastNodeViewTransforms.set (name, computeNodeViewTransform (node)); + + return true; +} + +bool Artboard::detachComponentFromNode (StringRef nodeName, Component* component) +{ + const String name (nodeName); + + auto* existing = attachedComponents.getPointer (name); + if (existing == nullptr || existing->component != component) + return false; + + component->removeComponentListener (this); + attachedComponents.remove (name); + + return true; +} + +void Artboard::detachAllComponents() +{ + for (auto it = attachedComponents.begin(); it != attachedComponents.end(); ++it) + if (auto* component = it.getValue().component) + component->removeComponentListener (this); + + attachedComponents.clear(); +} + +void Artboard::detachComponentEverywhere (Component* component) +{ + if (component == nullptr) + return; + + String attachedNodeName; + bool found = false; + + for (auto it = attachedComponents.begin(); it != attachedComponents.end(); ++it) + { + if (it.getValue().component == component) + { + attachedNodeName = it.getKey(); + found = true; + break; + } + } + + if (! found) + return; + + component->removeComponentListener (this); + attachedComponents.remove (attachedNodeName); +} + +void Artboard::componentBeingDeleted (Component& component) +{ + detachComponentEverywhere (std::addressof (component)); +} + +void Artboard::componentResized (Component& component) +{ + if (applyingNodeAttachment) + return; + + reapplyNodeAttachment (std::addressof (component)); +} + +void Artboard::reapplyNodeAttachment (Component* component) +{ + if (component == nullptr || artboard == nullptr) + return; + + for (auto it = attachedComponents.begin(); it != attachedComponents.end(); ++it) + { + if (it.getValue().component != component) + continue; + + if (auto* node = artboard->find (it.getKey().toStdString())) + applyNodeAttachment (it.getValue(), node, computeNodeBounds (node)); + + return; + } +} + +//============================================================================== + +ArtboardNode::Ptr Artboard::findNode (StringRef nodeName) const +{ + if (artboard == nullptr) + return nullptr; + + const String name (nodeName); + + auto* node = artboard->find (name.toStdString()); + if (node == nullptr) + return nullptr; + + return nodeHandleFor (name, node); +} + +ArtboardNode::Ptr Artboard::nodeHandleFor (const String& nodeName, rive::Component* node) const +{ + if (auto* cached = cachedNodeHandles.getPointer (nodeName); cached != nullptr && (*cached)->isValid()) + return *cached; + + auto handle = ArtboardNode::Ptr (new ArtboardNode (*const_cast (this), node)); + cachedNodeHandles.set (nodeName, handle); + return handle; +} + +//============================================================================== + +String Artboard::getViewModelName() const +{ + if (artboard == nullptr || artboardFile == nullptr) + return {}; + + auto* rivFile = artboardFile->getRiveFile(); + if (rivFile == nullptr) + return {}; + + const auto viewModelIndex = artboard->viewModelId(); + if (viewModelIndex >= rivFile->viewModelCount()) + return {}; + + if (auto* viewModel = rivFile->viewModel (viewModelIndex)) + return String (viewModel->name()); + + return {}; +} + +bool Artboard::bindViewModelInstance (const ArtboardViewModelInstance::Ptr& model) +{ + if (model == nullptr || artboard == nullptr || scene == nullptr) + return false; + + if (artboardFile == nullptr || model->getArtboardFile() != artboardFile.get()) + return false; + + auto* riveInstance = model->internalRiveInstance(); + if (riveInstance == nullptr) + return false; + + riveInstance->ref(); + + if (stateMachine != nullptr) + { + stateMachine->bindViewModelInstance (rive::rcp (riveInstance)); + } + else + { + artboard->unbind(); + artboard->bindViewModelInstance (rive::rcp (riveInstance)); + } + + boundViewModelInstance = model; + + advanceScene (0.0f); + repaint(); + + return true; +} + +void Artboard::unbindViewModelInstance() +{ + if (stateMachine != nullptr) + stateMachine->clearDataContext(); + + if (artboard != nullptr) + artboard->unbind(); + + boundViewModelInstance = nullptr; + + repaint(); +} + +ArtboardViewModelInstance::Ptr Artboard::getBoundViewModelInstance() const noexcept +{ + return boundViewModelInstance; +} + +//============================================================================== + void Artboard::refreshDisplay (double lastFrameTimeSeconds) { if (paused || (pauseWhenHidden && ! isShowing())) @@ -370,7 +709,7 @@ void Artboard::paint (Graphics& g) void Artboard::resized() { - updateViewTransform(); + updateNodeBounds(); } //============================================================================== @@ -390,6 +729,8 @@ void Artboard::mouseEnter (const MouseEvent& event) auto [x, y] = transformPoint (event.getPosition()); scene->pointerMove (rive::Vec2D (x, y)); + pullEventsFromStateMachines(); + repaint(); } @@ -401,6 +742,8 @@ void Artboard::mouseExit (const MouseEvent& event) auto [x, y] = transformPoint (event.getPosition()); scene->pointerExit (rive::Vec2D (x, y)); + pullEventsFromStateMachines(); + repaint(); } @@ -412,6 +755,8 @@ void Artboard::mouseDown (const MouseEvent& event) auto [x, y] = transformPoint (event.getPosition()); scene->pointerDown (rive::Vec2D (x, y)); + pullEventsFromStateMachines(); + repaint(); } @@ -423,6 +768,8 @@ void Artboard::mouseUp (const MouseEvent& event) auto [x, y] = transformPoint (event.getPosition()); scene->pointerUp (rive::Vec2D (x, y)); + pullEventsFromStateMachines(); + repaint(); } @@ -434,6 +781,8 @@ void Artboard::mouseMove (const MouseEvent& event) auto [x, y] = transformPoint (event.getPosition()); scene->pointerMove (rive::Vec2D (x, y)); + pullEventsFromStateMachines(); + repaint(); } @@ -454,22 +803,27 @@ void Artboard::mouseDrag (const MouseEvent& event) void Artboard::propertyChanged (const String& eventName, const String& propertyName, const var& oldValue, const var& newValue) { - // YUP_DBG (eventName << " (" << propertyName << ") = " << newValue.toString() << " (" << oldValue.toString() << ")"); + ignoreUnused (eventName, propertyName, oldValue, newValue); } //============================================================================== void Artboard::updateSceneFromFile() { - artboard.reset(); scene.reset(); + artboard.reset(); stateMachine = nullptr; + if (artboardFile == nullptr) + return; + auto rivFile = artboardFile->getRiveFile(); if (rivFile == nullptr) return; - auto currentArtboard = rivFile->artboardDefault(); + auto currentArtboard = selectedArtboardName.isEmpty() + ? rivFile->artboardDefault() + : rivFile->artboardNamed (selectedArtboardName.toStdString()); if (currentArtboard == nullptr) return; @@ -497,23 +851,44 @@ void Artboard::updateSceneFromFile() stateMachine = currentStateMachine; - updateViewTransform(); + updateNodeBounds(); repaint(); } //============================================================================== +void Artboard::advanceScene (float elapsedSeconds) +{ + if (scene == nullptr) + return; + + scene->advanceAndApply (elapsedSeconds); + + pullEventsFromStateMachines(); +} + +//============================================================================== + void Artboard::pullEventsFromStateMachines() { - if (stateMachine == nullptr) + if (stateMachine == nullptr || drainingEvents) return; - for (std::size_t eventIndex = 0; eventIndex < stateMachine->reportedEventCount(); ++eventIndex) - { - auto event = stateMachine->reportedEventAt (eventIndex).event(); - if (event == nullptr) - continue; + const auto reportedCount = stateMachine->reportedEventCount(); + if (reportedCount == 0) + return; + const ScopedValueSetter draining (drainingEvents, true); + + Array reportedEvents; + reportedEvents.ensureStorageAllocated (static_cast (reportedCount)); + + for (std::size_t eventIndex = 0; eventIndex < reportedCount; ++eventIndex) + if (auto* event = stateMachine->reportedEventAt (eventIndex).event()) + reportedEvents.add (event); + + for (auto* event : reportedEvents) + { const auto eventName = String (event->name()); for (const auto& child : event->children()) @@ -555,6 +930,207 @@ Point Artboard::transformPoint (Point point) const //============================================================================== +void Artboard::updateNodeBounds() +{ + if (artboard == nullptr) + return; + + if (! fitting.has_value() || *fitting == Fitting::fill) + { + const auto bounds = getLocalBounds(); + + artboard->width (bounds.getWidth()); + artboard->height (bounds.getHeight()); + } + else + { + if (artboard->originalWidth() > 0.0f && artboard->originalHeight() > 0.0f) + artboard->resetSize(); + } + + advanceScene (0.0f); + + updateViewTransform(); + notifyNodeBoundsChanged(); +} + +//============================================================================== + +void Artboard::notifyNodeBoundsChanged() +{ + if (artboard == nullptr || (nodeBoundsListeners.isEmpty() && attachedComponents.isEmpty()) || notifyingNodeBounds) + return; + + const ScopedValueSetter notifying (notifyingNodeBounds, true); + + nodeNames.clear(); + + for (auto it = nodeBoundsListeners.begin(); it != nodeBoundsListeners.end(); ++it) + nodeNames.addIfNotAlreadyThere (it.getKey()); + + for (auto it = attachedComponents.begin(); it != attachedComponents.end(); ++it) + nodeNames.addIfNotAlreadyThere (it.getKey()); + + for (const auto& nodeName : nodeNames) + { + if (artboard == nullptr) + break; + + if (auto* node = artboard->find (nodeName.toStdString())) + checkNodeBounds (nodeName, node); + } +} + +//============================================================================== + +void Artboard::checkNodeBounds (const String& nodeName, rive::Component* node) +{ + const auto newBounds = computeNodeBounds (node); + const auto newViewTransform = computeNodeViewTransform (node); + + const auto* cachedBounds = lastNodeBounds.getPointer (nodeName); + const auto* cachedViewTransform = lastNodeViewTransforms.getPointer (nodeName); + + const bool boundsChanged = cachedBounds == nullptr || ! areNodeBoundsEqual (*cachedBounds, newBounds); + const bool viewTransformChanged = cachedViewTransform == nullptr || ! newViewTransform.approximatelyEqualTo (*cachedViewTransform); + + if (! boundsChanged && ! viewTransformChanged) + return; + + lastNodeBounds.set (nodeName, newBounds); + lastNodeViewTransforms.set (nodeName, newViewTransform); + + if (auto* attachment = attachedComponents.getPointer (nodeName)) + { + const bool followsTransform = attachment->options.applyTransform; + if (boundsChanged || (followsTransform && viewTransformChanged)) + applyNodeAttachment (*attachment, node, newBounds); + } + + if (auto* callback = nodeBoundsListeners.getPointer (nodeName)) + { + if (*callback) + (*callback) (*this, nodeName, nodeHandleFor (nodeName, node)); + } +} + +//============================================================================== + +void Artboard::applyNodeAttachment (NodeAttachment attachment, rive::Component* node, const Rectangle& nodeBounds) +{ + auto* component = attachment.component; + if (component == nullptr) + return; + + const ScopedValueSetter applying (applyingNodeAttachment, true); + + Rectangle bounds = nodeBounds; + Point rotationPivot = { bounds.getWidth() * 0.5f, bounds.getHeight() * 0.5f }; + + if (attachment.options.mode == NodeAttachmentOptions::Mode::trackPosition) + { + const auto width = component->getWidth(); + const auto height = component->getHeight(); + + const auto justificationOffsets = [] (Justification justification, float w, float h) -> Point + { + const auto offsetX = justification.testFlags (Justification::horizontalCenter) + ? w * 0.5f + : justification.testFlags (Justification::right) + ? w + : 0.0f; + + const auto offsetY = justification.testFlags (Justification::verticalCenter) + ? h * 0.5f + : justification.testFlags (Justification::bottom) + ? h + : 0.0f; + + return { offsetX, offsetY }; + }; + + const auto anchor = justificationOffsets (attachment.options.anchor, + nodeBounds.getWidth(), + nodeBounds.getHeight()); + + const auto pivot = justificationOffsets (attachment.options.pivot, + width, + height); + + bounds = { nodeBounds.getX() + anchor.getX() - pivot.getX(), + nodeBounds.getY() + anchor.getY() - pivot.getY(), + width, + height }; + + rotationPivot = pivot; + } + + component->setBounds (bounds); + + if (attachment.options.applyTransform) + { + component->setTransform (AffineTransform::rotation (computeNodeRotation (node), + rotationPivot.getX(), + rotationPivot.getY())); + } + else + { + component->setTransform (AffineTransform()); + } +} + +//============================================================================== + +float Artboard::computeNodeRotation (rive::Component* node) const +{ + if (node != nullptr && node->is()) + return (viewTransform * node->as()->worldTransform()).decompose().rotation(); + + return 0.0f; +} + +AffineTransform Artboard::computeNodeViewTransform (rive::Component* node) const +{ + if (node != nullptr && node->is()) + return AffineTransform (viewTransform * node->as()->worldTransform()); + + return {}; +} + +//============================================================================== + +Rectangle Artboard::computeNodeBounds (rive::Component* node) const +{ + if (node == nullptr) + return {}; + + if (node->is()) + { + auto* layoutNode = node->as(); + const auto nodeRect = rive::AABB::fromLTWH (0.0f, 0.0f, layoutNode->layoutWidth(), layoutNode->layoutHeight()); + const auto mapped = (viewTransform * layoutNode->worldTransform()).mapBoundingBox (nodeRect); + return { mapped.left(), mapped.top(), mapped.width(), mapped.height() }; + } + + if (auto worldBounds = collectNodeWorldBounds (node)) + { + const auto mapped = viewTransform.mapBoundingBox (*worldBounds); + return { mapped.left(), mapped.top(), mapped.width(), mapped.height() }; + } + + const auto nodeRect = rive::AABB::fromLTWH (0.0f, 0.0f, 1.0f, 1.0f); + + rive::Mat2D transform = viewTransform; + if (node->is()) + transform = viewTransform * node->as()->worldTransform(); + + const auto mapped = transform.mapBoundingBox (nodeRect); + + return { mapped.left(), mapped.top(), mapped.width(), mapped.height() }; +} + +//============================================================================== + void Artboard::updateViewTransform() { if (artboard == nullptr) @@ -564,8 +1140,8 @@ void Artboard::updateViewTransform() } viewTransform = rive::computeAlignment ( - toRiveFit (layout), - toRiveAlignment (alignment), + toRiveFit (fitting), + toRiveAlignment (justification), getLocalBounds().toAABB(), artboard->bounds()); } diff --git a/modules/yup_gui/artboard/yup_Artboard.h b/modules/yup_gui/artboard/yup_Artboard.h index e6ecca12c..66d87b0ce 100644 --- a/modules/yup_gui/artboard/yup_Artboard.h +++ b/modules/yup_gui/artboard/yup_Artboard.h @@ -26,46 +26,22 @@ namespace yup /** Represents a Rive artboard. This class is used to display a Rive artboard. + + Artboards are not internally synchronized. Beyond the message thread, the + artboard also advances from refreshDisplay(), which YUP runs on the render + thread while holding the message manager lock, so the two never run + concurrently. Everything the advance reaches runs on that thread too: the + NodeBoundsCallbacks registered with setNodeBoundsListener(), the bounds and + transform updates pushed into components attached with + attachComponentToNode(), onPropertyChanged / propertyChanged, and the + ArtboardViewModelInstance::PropertyChangedCallback of a bound instance. + A slow callback therefore stalls rendering. */ -class YUP_API Artboard : public Component +class YUP_API Artboard + : public Component + , private ComponentListener { public: - //============================================================================== - /** Describes how the artboard is fitted inside the component bounds. */ - enum class Layout - { - /** Scale independently in X and Y to fill the bounds. */ - fill, - /** Uniform scale to fit inside bounds while preserving aspect ratio. */ - contain, - /** Uniform scale to cover bounds while preserving aspect ratio. */ - cover, - /** Uniform scale so the content width matches the bounds. */ - fitWidth, - /** Uniform scale so the content height matches the bounds. */ - fitHeight, - /** No scaling or alignment. */ - none, - /** Like contain, but never scale above 1.0. */ - scaleDown, - /** Use the artboard's own layout constraints. */ - layout - }; - - /** Describes the alignment used to position the artboard inside the bounds. */ - enum class Alignment - { - topLeft, - topCenter, - topRight, - centerLeft, - center, - centerRight, - bottomLeft, - bottomCenter, - bottomRight - }; - //============================================================================== /** Creates a new Rive artboard. @@ -80,12 +56,23 @@ class YUP_API Artboard : public Component */ Artboard (StringRef componentID, std::shared_ptr artboardFile); + /** Destructor. Detaches every attached component and invalidates all outstanding + ArtboardNode handles. */ + ~Artboard() override; + //============================================================================== /** Sets the Rive artboard file to display. + Passing a null file unloads the current one, leaving the artboard empty. + @param artboardFile The Rive artboard file to display. + @param artboardName The name of the artboard to load from the file. When + empty (the default), the file's default artboard is + loaded, exactly as before this parameter existed; + otherwise the named artboard is loaded, or nothing if + the name is unknown. */ - void setFile (std::shared_ptr artboardFile); + void setFile (std::shared_ptr artboardFile, StringRef artboardName = {}); //============================================================================== /** Clears the Rive artboard. */ @@ -96,21 +83,34 @@ class YUP_API Artboard : public Component This affects the view transform used for rendering and input handling. - @param newLayout The new layout mode. + Pass std::nullopt to apply no fitting of our own and instead hand the + component's size to the artboard, letting its authored Rive layout + constraints resolve it. That is what makes nodes reflow on resize, and is + the mode responsive artboards are designed for. + + Fitting values Rive has no equivalent for (Fitting::tile, + Fitting::centerCrop, Fitting::stretchWidth and Fitting::stretchHeight) + fall back to Fitting::scaleToFit. + + @param newFitting The new fitting mode, or nullopt for the artboard's own layout. */ - void setLayout (Layout newLayout); + void setFitting (std::optional newFitting); + + /** Returns the current fitting mode, or nullopt when the artboard lays itself out. */ + std::optional getFitting() const; - /** Returns the current layout mode. */ - Layout getLayout() const; + /** Sets the justification used when positioning the artboard in the bounds. - /** Sets the alignment used when positioning the artboard in the bounds. + This only has a visible effect when the fitting leaves spare room along an + axis, so not for Fitting::fill nor for the artboard's own layout. An axis + with no flag set is centered on that axis. - @param newAlignment The new alignment value. + @param newJustification The new justification value. */ - void setAlignment (Alignment newAlignment); + void setJustification (Justification newJustification); - /** Returns the current alignment. */ - Alignment getAlignment() const; + /** Returns the current justification. */ + Justification getJustification() const; //============================================================================== /** Returns true if the Rive artboard is paused. */ @@ -162,7 +162,12 @@ class YUP_API Artboard : public Component */ void setNumberInput (const String& name, double value); - /** Returns true if the Rive artboard has a trigger input with the given name. */ + /** Returns true if the Rive artboard has a trigger input with the given name. + + @param name The name of the input. + + @return True if the Rive artboard has a trigger input with the given name, false otherwise. + */ bool hasTriggerInput (const String& name) const; /** Triggers a trigger input with the given name. @@ -171,32 +176,295 @@ class YUP_API Artboard : public Component */ void triggerInput (const String& name); - //============================================================================== - /** Returns all the inputs of the Rive artboard. */ + /** Returns all the state machine inputs of the Rive artboard. + + Each entry is a DynamicObject with: + - "id": the input name, as a String. + - "type": one of "number", "boolean" or "trigger". + - "value": the current value, as a double for "number" and a bool for + "boolean". Triggers are stateless and carry no "value". + + @return An Array of input descriptions, or an empty var when the + artboard is not driven by a state machine. + */ var getAllInputs() const; - /** Sets all the inputs of the Rive artboard. + /** Applies a set of state machine inputs previously read with getAllInputs(). + + Accepts the array getAllInputs() returns and writes each entry back + through setInput(), matching entries by their "id". Entries whose "id" + is unknown to the current state machine are ignored, so a snapshot taken + from one artboard can be applied to another without failing. Triggers are + not fired: an input snapshot describes state, and a trigger has none. - @param value The value to set the inputs to. + @param value The Array of input descriptions to apply. */ void setAllInputs (const var& value); /** Sets the value of an input with the given name. + Triggers ignore the value and simply fire. + @param name The name of the input. @param value The value to set the input to. */ - void setInput (const String& state, const var& value); + void setInput (const String& name, const var& value); + + //============================================================================== + /** Callback invoked when a named node's bounds or on-screen orientation change. + + The handle is the artboard's cached handle for the node (the same object + returned by Artboard::findNode), so no per-event allocation happens; it + stays valid until the artboard is cleared or its file replaced. + + @param artboard The artboard that emitted the change. + @param nodeName The name of the node that changed. + @param node A handle to the node; getBounds() and getViewTransform() + report the current values in component coordinates. + */ + using NodeBoundsCallback = std::function; + + /** Registers a listener for a named artboard node. + + The callback is invoked whenever the node's bounds or its on-screen + orientation (rotation, scale, skew, mirroring) change, e.g. on layout + reflows, artboard resizes or animation frames. Pass an empty callback to + remove the listener. + + @param nodeName The name of the node in the .riv file. + @param callback The callback to invoke on node changes. + */ + void setNodeBoundsListener (StringRef nodeName, NodeBoundsCallback callback); + + /** Removes the listener registered for the given node. */ + void clearNodeBoundsListener (StringRef nodeName); + + /** Removes all node bounds listeners. */ + void clearAllNodeBoundsListeners(); + + /** Returns the current bounds of a named node in component coordinates. + + @param nodeName The name of the node in the .riv file. + @return The node bounds, or an empty rectangle if the node is unknown. + */ + Rectangle getNodeBounds (StringRef nodeName) const; + + //============================================================================== + /** Options controlling how an attached component is driven by its node. */ + struct NodeAttachmentOptions + { + /** How the artboard positions the attached component. */ + enum class Mode + { + /** Set the component's bounds to the node's bounds in component coordinates + (position and size). Layout nodes report their laid-out size; shapes and + containers of shapes report their geometry; other nodes report a unit-size + rect. This is the default. */ + fillNode, + + /** Keep the component's current size and only move it to follow the node. + The node's scale and rotation are not applied to the size. */ + trackPosition + }; + + /** How the artboard positions the attached component. */ + Mode mode; + + /** When true, the node's orientation (rotation) is applied to the attached + component through Component::setTransform, so its content rotates around + the pivot point (see pivot) to follow the node. When false + (the default) the component stays axis-aligned. + */ + bool applyTransform; + + /** In trackPosition mode, which point of the component acts as its pivot: + the component is positioned so this point lands on the node's anchor + point (see anchor). When applyTransform is true the + component rotates around this pivot, so the anchored point does not + drift while following the node's rotation. All Justification values + are supported. Ignored in fillNode mode. */ + Justification pivot; + + /** In trackPosition mode, which point of the node's bounds the component's + pivot (see pivot) is anchored to. For example center keeps + the component centered on the node, bottomCenter pins the component's + pivot to the bottom edge center of the node. All Justification values + are supported. Ignored in fillNode mode. */ + Justification anchor; + + /** Default constructor initializing the node attachment options with default values. */ + NodeAttachmentOptions() + : mode (Mode::fillNode) + , applyTransform (false) + , pivot (Justification::topLeft) + , anchor (Justification::topLeft) + { + } + + /** Sets the mode of the node attachment. + + @param newMode The new mode to set. + + @return A reference to this NodeAttachmentOptions instance. + */ + NodeAttachmentOptions& withMode (Mode newMode) + { + mode = newMode; + return *this; + } + + /** Sets whether the node's orientation should be applied to the attached component. + + @param shouldApplyTransform True to apply the node's rotation to the component. + + @return A reference to this NodeAttachmentOptions instance. + */ + NodeAttachmentOptions& withApplyTransform (bool shouldApplyTransform) + { + applyTransform = shouldApplyTransform; + return *this; + } + + /** Sets the justification point of the component that acts as its pivot. + + @param newJustificationPivot The new justification point for the component's pivot. + + @return A reference to this NodeAttachmentOptions instance. + */ + NodeAttachmentOptions& withJustificationPivot (Justification newJustificationPivot) + { + pivot = newJustificationPivot; + return *this; + } + + /** Sets the justification point of the node's bounds that the component's pivot is anchored to. + + @param newJustificationAnchor The new justification point for the node's anchor. + + @return A reference to this NodeAttachmentOptions instance. + */ + NodeAttachmentOptions& withJustificationAnchor (Justification newJustificationAnchor) + { + anchor = newJustificationAnchor; + return *this; + } + }; + + /** Attaches a component to a named artboard node. + + The component's bounds are driven by the node's layout: whenever a reflow + changes the node's bounds, the artboard updates the component according to + the given options. Ownership of the component remains with the caller, and + the component is immediately positioned at the node's current bounds upon + attachment. Attaching again to the same node replaces the previous attachment, + and attaching a component that is already attached to a different node moves + it, so a component is never driven by two nodes at once. + + The artboard listens for the component's destruction and detaches it + automatically, so deleting an attached component without calling + detachComponentFromNode() is safe. + + In trackPosition mode the component keeps its own size, and that size + decides where its pivot sits; resizing it therefore re-derives its + position immediately, so the size can be set before or after attaching + with the same result. In fillNode mode the node owns the size, so a + resize is simply overwritten. + + @param nodeName The name of the node in the .riv file. + @param component The component to attach. + @param options How the node should drive the component's bounds. + @return True if the node exists and the component was attached. + */ + bool attachComponentToNode (StringRef nodeName, + Component* component, + NodeAttachmentOptions options = NodeAttachmentOptions()); + + /** Detaches a component from a named node, stopping bounds updates. + + @param nodeName The name of the node in the .riv file. + @param component The component to detach. + @return True if the component was attached to the given node. + */ + bool detachComponentFromNode (StringRef nodeName, Component* component); + + /** Detaches every attached component. */ + void detachAllComponents(); + + //============================================================================== + /** Finds a node in the currently loaded Rive file by name. + + The returned handle is read-only, refcounted and remains valid until the + artboard is cleared, its file is replaced (Artboard::clear / Artboard::setFile), + or the artboard is destroyed. + + @param nodeName The name of the node in the .riv file. + @return A handle to the node, or a null pointer if the node is unknown. + */ + ArtboardNode::Ptr findNode (StringRef nodeName) const; + + //============================================================================== + /** Returns the name of the ViewModel schema this artboard is designed against. + + Artboards may reference one ViewModel schema authored in the .riv file. + The returned name can be used to create an instance through + ArtboardFile::createArtboardViewModelInstance() before binding it with + bindViewModelInstance(). Returns an empty string if the artboard does + not use a ViewModel. + */ + String getViewModelName() const; + + /** Binds a ViewModel instance to this artboard, enabling its data bindings. + + Once bound, the values of the instance drive the artboard's data-bound + properties and state machine transitions; writes through the instance + are applied on the next advanceAndApply(). Only one instance can be + bound at a time; binding again replaces the previous binding. + + @param instance The instance to bind; it must have been created from + the same ArtboardFile this artboard was loaded from. + @return True if the instance was bound successfully. + */ + bool bindViewModelInstance (const ArtboardViewModelInstance::Ptr& instance); + + /** Unbinds the currently bound ViewModel instance, if any. + + Data bindings stop reacting to the instance until a new one is bound. + */ + void unbindViewModelInstance(); + + /** Returns the currently bound ViewModel instance, or null if none is bound. */ + ArtboardViewModelInstance::Ptr getBoundViewModelInstance() const noexcept; //============================================================================== - /** A callback that is called when a property of the Rive artboard changes. */ - std::function onPropertyChanged; + /** A callback that is called when a custom property of a reported state machine + event changes. + + Events are drained after every advance and after every pointer interaction, + so the callback fires from advanceAndApply(), refreshDisplay() and the mouse + handlers, on the thread described above. Only actual changes are reported: the + artboard remembers the last value seen for each event and skips repeats. + + @param artboard The artboard that reported the event. + @param eventName The name of the event. + @param propertyName The name of the custom property carried by the event. + @param oldValue The previous value of the property, or an empty var the + first time the event is seen. + @param newValue The new value of the property. + */ + std::function + onPropertyChanged; + + /** Called when a custom property of a reported state machine event changes. - /** A callback that is called when a property of the Rive artboard changes. + Invoked immediately before onPropertyChanged, under the same conditions. @param eventName The name of the event. - @param propertyName The name of the property. - @param oldValue The old value of the property. + @param propertyName The name of the custom property carried by the event. + @param oldValue The previous value of the property. @param newValue The new value of the property. */ virtual void propertyChanged (const String& eventName, @@ -227,9 +495,35 @@ class YUP_API Artboard : public Component void mouseDrag (const MouseEvent& event) override; private: + friend class ArtboardNode; + + struct NodeAttachment + { + Component* component = nullptr; + NodeAttachmentOptions options; + }; + + /** @internal */ + void componentBeingDeleted (Component& component) override; + /** @internal */ + void componentResized (Component& component) override; + void updateSceneFromFile(); + void advanceScene (float elapsedSeconds); void pullEventsFromStateMachines(); void updateViewTransform(); + void updateNodeBounds(); + void notifyNodeBoundsChanged(); + void checkNodeBounds (const String& nodeName, rive::Component* node); + // Taken by value: it is applied by moving a component, whose resized() may + // detach it and destroy the attachment record this was read from. + void applyNodeAttachment (NodeAttachment attachment, rive::Component* node, const Rectangle& nodeBounds); + void detachComponentEverywhere (Component* component); + void reapplyNodeAttachment (Component* component); + ArtboardNode::Ptr nodeHandleFor (const String& nodeName, rive::Component* node) const; + float computeNodeRotation (rive::Component* node) const; + Rectangle computeNodeBounds (rive::Component* node) const; + AffineTransform computeNodeViewTransform (rive::Component* node) const; Point transformPoint (Point point) const; std::shared_ptr artboardFile; @@ -240,12 +534,27 @@ class YUP_API Artboard : public Component HashMap eventProperties; - rive::Mat2D viewTransform; - Layout layout = Layout::contain; - Alignment alignment = Alignment::center; + HashMap nodeBoundsListeners; + HashMap attachedComponents; + HashMap> lastNodeBounds; + HashMap lastNodeViewTransforms; + Array nodeNames; + mutable HashMap cachedNodeHandles; + uint64_t nodeEpoch = 0; + + ArtboardViewModelInstance::Ptr boundViewModelInstance; + rive::Mat2D viewTransform; + String selectedArtboardName; + std::optional fitting = Fitting::scaleToFit; + Justification justification = Justification::center; bool paused = false; bool pauseWhenHidden = true; + bool notifyingNodeBounds = false; + bool drainingEvents = false; + bool applyingNodeAttachment = false; + + YUP_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Artboard) }; } // namespace yup diff --git a/modules/yup_gui/artboard/yup_ArtboardFile.cpp b/modules/yup_gui/artboard/yup_ArtboardFile.cpp index 4bbc5dd2a..eeebe2baf 100644 --- a/modules/yup_gui/artboard/yup_ArtboardFile.cpp +++ b/modules/yup_gui/artboard/yup_ArtboardFile.cpp @@ -43,7 +43,7 @@ class LambdaAssetLoader : public rive::FileAssetLoader ArtboardFile::AssetInfo assetInfo; assetInfo.uniqueName = String (asset.uniqueName()); - assetInfo.uniquePath = String (asset.uniqueFilename()); + assetInfo.uniqueFilename = String (asset.uniqueFilename()); assetInfo.extension = String (asset.fileExtension()); return assetCallback (assetInfo, Span { inBandBytes.data(), inBandBytes.size() }, *factory); @@ -76,34 +76,77 @@ rive::File* ArtboardFile::getRiveFile() //============================================================================== -ArtboardFile::LoadResult ArtboardFile::load (const File& file, rive::Factory& factory) +int ArtboardFile::getNumViewModels() const noexcept +{ + return rivFile != nullptr ? static_cast (rivFile->viewModelCount()) : 0; +} + +StringArray ArtboardFile::getViewModelNames() const +{ + StringArray names; + + if (rivFile != nullptr) + for (std::size_t index = 0; index < rivFile->viewModelCount(); ++index) + if (auto* viewModel = rivFile->viewModel (index)) + names.add (String (viewModel->name())); + + return names; +} + +ArtboardViewModel::Ptr ArtboardFile::getArtboardViewModelAt (int index) +{ + return ArtboardViewModel::createFromFile (shared_from_this(), index); +} + +ArtboardViewModel::Ptr ArtboardFile::getArtboardViewModel (StringRef name) +{ + return ArtboardViewModel::createFromFile (shared_from_this(), name); +} + +ArtboardViewModelInstance::Ptr ArtboardFile::createArtboardViewModelInstance (StringRef viewModelName) +{ + return ArtboardViewModelInstance::createFromFile (shared_from_this(), viewModelName); +} + +ArtboardViewModelInstance::Ptr ArtboardFile::createArtboardViewModelInstance (StringRef viewModelName, StringRef instanceName) +{ + return ArtboardViewModelInstance::createFromFile (shared_from_this(), viewModelName, instanceName); +} + +//============================================================================== + +ResultValue ArtboardFile::load (const File& file, rive::Factory& factory) { return load (file, factory, nullptr); } -ArtboardFile::LoadResult ArtboardFile::load (const File& file, rive::Factory& factory, const AssetLoadCallback& assetCallback) +ResultValue ArtboardFile::load (const File& file, rive::Factory& factory, const AssetLoadCallback& assetCallback) { if (! file.existsAsFile()) - return LoadResult::fail ("Failed to find artboard file to load"); + return makeResultValueFail ("Failed to find artboard file to load"); auto is = file.createInputStream(); if (is == nullptr || ! is->openedOk()) - return LoadResult::fail ("Failed to open artboard file for reading"); + return makeResultValueFail ("Failed to open artboard file for reading"); return load (*is, factory, assetCallback); } //============================================================================== -ArtboardFile::LoadResult ArtboardFile::load (InputStream& is, rive::Factory& factory) +ResultValue ArtboardFile::load (InputStream& is, rive::Factory& factory) { return load (is, factory, nullptr); } -ArtboardFile::LoadResult ArtboardFile::load (InputStream& is, rive::Factory& factory, const AssetLoadCallback& assetCallback) +ResultValue ArtboardFile::load (InputStream& is, rive::Factory& factory, const AssetLoadCallback& assetCallback) { yup::MemoryBlock mb; - is.readIntoMemoryBlock (mb); + + // Distinguish a stream that yielded nothing from a stream that yielded bytes + // Rive could not parse, which would otherwise both report "Malformed". + if (is.readIntoMemoryBlock (mb) == 0) + return makeResultValueFail ("Failed to read artboard file"); rive::ImportResult result; rive::rcp rivFile; @@ -125,15 +168,15 @@ ArtboardFile::LoadResult ArtboardFile::load (InputStream& is, rive::Factory& fac } if (result == rive::ImportResult::malformed) - return LoadResult::fail ("Malformed artboard file"); + return makeResultValueFail ("Malformed artboard file"); if (result == rive::ImportResult::unsupportedVersion) - return LoadResult::fail ("Unsupported artboard file for current runtime"); + return makeResultValueFail ("Unsupported artboard file for current runtime"); if (rivFile == nullptr) - return LoadResult::fail ("Failed to import artboard file"); + return makeResultValueFail ("Failed to import artboard file"); - return LoadResult::ok (std::shared_ptr (new ArtboardFile { std::move (rivFile) })); + return makeResultValueOk (ArtboardFile::Ptr (new ArtboardFile { std::move (rivFile) })); } } // namespace yup diff --git a/modules/yup_gui/artboard/yup_ArtboardFile.h b/modules/yup_gui/artboard/yup_ArtboardFile.h index ae8b1455d..81ec2c1cb 100644 --- a/modules/yup_gui/artboard/yup_ArtboardFile.h +++ b/modules/yup_gui/artboard/yup_ArtboardFile.h @@ -27,26 +27,68 @@ class Artboard; //============================================================================== /** Represents a Rive file. - This class is used to load Rive binary files (aka .riv files). + This class is used to load Rive binary files (aka .riv files). It also + provides access to the ViewModel schemas stored in the file and creates + ViewModel instances that can be bound to Artboard components. + + Files are always held through a shared pointer, handed out by load(). Every + handle created from a file (ArtboardViewModel, ArtboardViewModelInstance) + keeps it alive, so a file outlives everything that reads from it. + + Instances are not internally synchronized, and inherit the threading + contract described on Artboard. */ -class YUP_API ArtboardFile +class YUP_API ArtboardFile : public std::enable_shared_from_this { public: //============================================================================== + /** A shared pointer type for ArtboardFile instances. */ + using Ptr = std::shared_ptr; + //============================================================================== + /** A structure containing information about an asset within the Rive file. + + Passed to an AssetLoadCallback so the host can supply the bytes of assets + that the .riv file references without embedding. + */ struct AssetInfo { + /** Default constructor. */ AssetInfo() = default; + /** The asset's name as authored in the Rive editor. */ String uniqueName; - File uniquePath; + + /** The asset's authored name decorated with its Rive asset id and its + extension, e.g. "logo-1234.png". + + This is a bare file name, not a path: it is the conventional name to + look for in a directory of out-of-band assets, and nothing on disk is + guaranteed to match it. Resolve it against your own asset directory + with File::getChildFile(). */ + String uniqueFilename; + + /** The asset's file extension without the leading dot, e.g. "png". */ String extension; }; //============================================================================== - /** The result of loading a Rive file. */ - using LoadResult = ResultValue>; - using AssetLoadCallback = std::function, rive::Factory& factory)>; + /** A callback type for loading assets within the Rive file. + + Invoked once per asset referenced by the file while it is importing. To + provide an asset, decode the bytes with the given factory, hand the result + to the asset and return true. Return false to leave the asset unresolved, + in which case Rive falls back to the in-band bytes when the file carries + them. Returning false is not an import failure: a file whose assets all go + unresolved still loads. + + @param info Describes the asset being loaded. + @param inBandBytes The bytes embedded in the .riv file, empty when the + asset is referenced out of band. + @param factory The factory to decode the asset with. + @return True if the callback handled the asset. + */ + using AssetLoadCallback = std::function inBandBytes, rive::Factory& factory)>; /** Loads a Rive file from a file. @@ -55,16 +97,19 @@ class YUP_API ArtboardFile @return The result of loading the Rive file. */ - static LoadResult load (const File& file, rive::Factory& factory); + static ResultValue load (const File& file, rive::Factory& factory); - /** Loads a Rive file from a file. + /** Loads a Rive file from a file, resolving its assets through a callback. @param file The file to load. @param factory The factory to use to create the Rive file. + @param assetCallback The callback invoked once per asset referenced by the + file. Pass an empty callback to use the default in-band + asset loading. @return The result of loading the Rive file. */ - static LoadResult load (const File& file, rive::Factory& factory, const AssetLoadCallback& assetCallback); + static ResultValue load (const File& file, rive::Factory& factory, const AssetLoadCallback& assetCallback); /** Loads a Rive file from an input stream. @@ -73,17 +118,60 @@ class YUP_API ArtboardFile @return The result of loading the Rive file. */ - static LoadResult load (InputStream& is, rive::Factory& factory); + static ResultValue load (InputStream& is, rive::Factory& factory); - /** Loads a Rive file from an input stream. + /** Loads a Rive file from an input stream, resolving its assets through a callback. @param is The input stream to load the Rive file from. @param factory The factory to use to create the Rive file. - @param assetCallback The callback that will be invoked when loading to use to create the Rive file. + @param assetCallback The callback invoked once per asset referenced by the + file. Pass an empty callback to use the default in-band + asset loading. @return The result of loading the Rive file. */ - static LoadResult load (InputStream& is, rive::Factory& factory, const AssetLoadCallback& assetCallback); + static ResultValue load (InputStream& is, rive::Factory& factory, const AssetLoadCallback& assetCallback); + + //============================================================================== + /** Returns the number of ViewModel schemas stored in the Rive file. */ + int getNumViewModels() const noexcept; + + /** Returns the names of all ViewModel schemas stored in the Rive file. */ + StringArray getViewModelNames() const; + + /** Returns a handle to the ViewModel schema at the given index, or null if out of range. + + @param index The index of the ViewModel schema. + */ + ArtboardViewModel::Ptr getArtboardViewModelAt (int index); + + /** Returns a handle to the ViewModel schema with the given name, or null if unknown. + + @param name The name of the ViewModel schema. + */ + ArtboardViewModel::Ptr getArtboardViewModel (StringRef name); + + /** Creates a new instance of the ViewModel schema with the given name. + + The instance is populated with the schema's default property values. + Bind the returned instance to an Artboard through + Artboard::bindViewModelInstance() to drive its data bindings. + + @param viewModelName The name of the ViewModel schema to instantiate. + @return The new instance handle, or null if the schema is unknown. + */ + ArtboardViewModelInstance::Ptr createArtboardViewModelInstance (StringRef viewModelName); + + /** Creates an instance of the ViewModel schema by cloning a pre-authored instance. + + .riv files may ship with authored instances (see ArtboardViewModel::getInstanceNames()). + The first authored instance is the file's "default" instance. + + @param viewModelName The name of the ViewModel schema to instantiate. + @param instanceName The name of the authored instance to clone. + @return The new instance handle, or null if the schema or instance is unknown. + */ + ArtboardViewModelInstance::Ptr createArtboardViewModelInstance (StringRef viewModelName, StringRef instanceName); //============================================================================== /** Returns the underlying Rive file. */ @@ -93,10 +181,26 @@ class YUP_API ArtboardFile rive::File* getRiveFile(); private: + friend class ArtboardViewModelInstance; + + // Counts the observer callbacks currently unwinding across every instance + // created from this file, so a retired observer is never freed while it is + // still on the stack. A plain int matches the rest of the class: instances + // and their observer vectors are unsynchronized, so the counter has nothing + // to synchronize with. + void enterObserverDispatch() noexcept { ++observerDispatchDepth; } + + void leaveObserverDispatch() noexcept { --observerDispatchDepth; } + + bool isObserverDispatchInProgress() const noexcept { return observerDispatchDepth != 0; } + ArtboardFile() = default; ArtboardFile (rive::rcp rivFile); rive::rcp rivFile; + int observerDispatchDepth = 0; + + YUP_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ArtboardFile) }; } // namespace yup diff --git a/modules/yup_gui/artboard/yup_ArtboardNode.cpp b/modules/yup_gui/artboard/yup_ArtboardNode.cpp new file mode 100644 index 000000000..2b94353ad --- /dev/null +++ b/modules/yup_gui/artboard/yup_ArtboardNode.cpp @@ -0,0 +1,171 @@ +/* + ============================================================================== + + This file is part of the YUP library. + Copyright (c) 2026 - kunitoki@gmail.com + + YUP is an open source library subject to open-source licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + to use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace yup +{ + +//============================================================================== + +ArtboardNode::ArtboardNode (Artboard& owner, rive::Component* node) + : owner (&owner) + , node (node) + , epochValue (owner.nodeEpoch) +{ +} + +//============================================================================== + +Artboard* ArtboardNode::getOwnerArtboard() const noexcept +{ + return static_cast (owner.get()); +} + +//============================================================================== + +bool ArtboardNode::isValid() const +{ + if (node == nullptr) + return false; + + if (auto* ownerArtboard = getOwnerArtboard()) + return ownerArtboard->nodeEpoch == epochValue; + + return false; +} + +//============================================================================== + +String ArtboardNode::getName() const +{ + if (! isValid()) + return {}; + + return String (node->name()); +} + +uint16_t ArtboardNode::getTypeKey() const +{ + if (! isValid()) + return 0; + + return node->coreType(); +} + +String ArtboardNode::getTypeName() const +{ + if (! isValid()) + return {}; + + switch (node->coreType()) + { + case rive::ArtboardBase::typeKey: return "Artboard"; + case rive::LayoutComponentBase::typeKey: return "LayoutComponent"; + case rive::NodeBase::typeKey: return "Node"; + case rive::ShapeBase::typeKey: return "Shape"; + case rive::RectangleBase::typeKey: return "Rectangle"; + case rive::EllipseBase::typeKey: return "Ellipse"; + case rive::ImageBase::typeKey: return "Image"; + case rive::TextBase::typeKey: return "Text"; + case rive::BoneBase::typeKey: return "Bone"; + case rive::SoloBase::typeKey: return "Solo"; + case rive::NestedArtboardBase::typeKey: return "NestedArtboard"; + default: return {}; + } +} + +//============================================================================== + +bool ArtboardNode::isLayout() const +{ + return isValid() && node->is(); +} + +Rectangle ArtboardNode::getBounds() const +{ + if (! isValid()) + return {}; + + return getOwnerArtboard()->computeNodeBounds (node); +} + +AffineTransform ArtboardNode::getLocalTransform() const +{ + if (! isValid()) + return {}; + + if (node->is()) + return AffineTransform (node->as()->transform()); + + return {}; +} + +AffineTransform ArtboardNode::getWorldTransform() const +{ + if (! isValid()) + return {}; + + if (node->is()) + return AffineTransform (node->as()->worldTransform()); + + return {}; +} + +AffineTransform ArtboardNode::getViewTransform() const +{ + if (! isValid()) + return {}; + + if (node->is()) + return AffineTransform (getOwnerArtboard()->viewTransform * node->as()->worldTransform()); + + return {}; +} +//============================================================================== + +ArtboardNode::Ptr ArtboardNode::getParent() const +{ + if (! isValid()) + return nullptr; + + auto* parent = node->parent(); + if (parent == nullptr) + return nullptr; + + return new ArtboardNode (*getOwnerArtboard(), parent); +} + +Array ArtboardNode::getChildren() const +{ + Array children; + + if (! isValid()) + return children; + + if (! node->is()) + return children; + + for (auto* child : node->as()->children()) + children.add (new ArtboardNode (*getOwnerArtboard(), child)); + + return children; +} + +} // namespace yup diff --git a/modules/yup_gui/artboard/yup_ArtboardNode.h b/modules/yup_gui/artboard/yup_ArtboardNode.h new file mode 100644 index 000000000..1520a9a9f --- /dev/null +++ b/modules/yup_gui/artboard/yup_ArtboardNode.h @@ -0,0 +1,150 @@ +/* + ============================================================================== + + This file is part of the YUP library. + Copyright (c) 2026 - kunitoki@gmail.com + + YUP is an open source library subject to open-source licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + to use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace yup +{ + +class Artboard; + +//============================================================================== +/** Represents a single node in a Rive artboard. + + A read-only handle to a node of the currently loaded Rive file, obtained + through Artboard::findNode(). Instances are refcounted and can be copied + and stored freely. + + The handle is only valid while the Artboard that produced it keeps its + current Rive file. Calling Artboard::clear() or Artboard::setFile() on the + owning artboard, or destroying the artboard, invalidates every outstanding + handle: isValid() then returns false and all accessors return safe defaults + (empty strings, rectangles and arrays, identity transforms, null pointers) + without ever dereferencing the underlying Rive objects. + + Validity is tracked without any shared state between the artboard and its + handles: the handle keeps a WeakReference to its owning Component (the + Artboard, whose lifetime the base class's weak-reference master tracks) and + snapshots the artboard's node epoch counter, which the owning Artboard + increments whenever its file is cleared or swapped. The epoch is only ever + read while the artboard is alive, so it needs no shared storage of its own. + + All accessors are const and never mutate the Rive artboard. + + @see Artboard::findNode +*/ +class YUP_API ArtboardNode : public ReferenceCountedObject +{ +public: + //============================================================================== + using Ptr = ReferenceCountedObjectPtr; + + //============================================================================== + /** Destructor. */ + ~ArtboardNode() override = default; + + //============================================================================== + /** Returns true if this handle still refers to a live node of its artboard. + + Handles become invalid when the owning Artboard is cleared, its file is + replaced (Artboard::clear / Artboard::setFile), or the artboard is destroyed. + */ + bool isValid() const; + + /** Returns the name of the node, or an empty string when invalid. */ + String getName() const; + + /** Returns the Rive core type key of the node, or 0 when invalid. */ + uint16_t getTypeKey() const; + + /** Returns a human-readable type name for the node. + + Returns an empty string when the handle is invalid, and also for any node + whose Rive core type is outside the set this maps: Artboard, LayoutComponent, + Node, Shape, Rectangle, Ellipse, Image, Text, Bone, Solo and NestedArtboard. + Use getTypeKey() to tell an unmapped type from an invalid handle. + */ + String getTypeName() const; + + //============================================================================== + /** Returns true if the node is a Rive layout component. */ + bool isLayout() const; + + /** Returns the node bounds in the artboard's component coordinates. + + Layout nodes report their laid-out size; shapes and containers of shapes + report their real geometry; other nodes report a unit-size rect at their + origin. Returns an empty rectangle when the handle is invalid. + */ + Rectangle getBounds() const; + + /** Returns the node's local transform relative to its parent. + + Returns the identity transform when the handle is invalid or the node + does not carry a transform. + */ + AffineTransform getLocalTransform() const; + + /** Returns the node's world transform in artboard coordinates. + + Returns the identity transform when the handle is invalid or the node + does not carry a transform. + */ + AffineTransform getWorldTransform() const; + + /** Returns the node's world transform mapped through the artboard's view + (fit) transform, i.e. the transform that positions, scales and orients + the node inside the artboard component. This lives in the same + coordinate space as getBounds(). + + Returns the identity transform when the handle is invalid or the node + does not carry a transform. + */ + AffineTransform getViewTransform() const; + + //============================================================================== + /** Returns the node's parent, or a null pointer when the node has no parent + or the handle is invalid. */ + ArtboardNode::Ptr getParent() const; + + /** Returns the node's direct children, or an empty array when the node has + none or the handle is invalid. */ + Array getChildren() const; + +private: + friend class Artboard; + + ArtboardNode (Artboard& owner, rive::Component* node); + + /** Returns the owning Artboard, or null if it has been destroyed. + + Artboard derives from Component, whose embedded master backs the weak + reference; the downcast is safe because handles are only ever created + for Artboard objects. + */ + Artboard* getOwnerArtboard() const noexcept; + + WeakReference owner; + rive::Component* node; + uint64_t epochValue = 0; + + YUP_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ArtboardNode) +}; + +} // namespace yup diff --git a/modules/yup_gui/artboard/yup_ArtboardViewModel.cpp b/modules/yup_gui/artboard/yup_ArtboardViewModel.cpp new file mode 100644 index 000000000..6cf3ba92a --- /dev/null +++ b/modules/yup_gui/artboard/yup_ArtboardViewModel.cpp @@ -0,0 +1,212 @@ +/* + ============================================================================== + + This file is part of the YUP library. + Copyright (c) 2026 - kunitoki@gmail.com + + YUP is an open source library subject to open-source licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + to use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace yup +{ + +namespace +{ + +//============================================================================== + +ArtboardViewModel::PropertyType toPropertyType (rive::ViewModelProperty* property) +{ + if (property->is()) + return ArtboardViewModel::PropertyType::boolean; + + if (property->is()) + return ArtboardViewModel::PropertyType::number; + + if (property->is()) + return ArtboardViewModel::PropertyType::string; + + if (property->is()) + return ArtboardViewModel::PropertyType::color; + + if (property->is()) + return ArtboardViewModel::PropertyType::list; + + if (property->is()) + return ArtboardViewModel::PropertyType::trigger; + + if (property->is()) + return ArtboardViewModel::PropertyType::viewModel; + + if (property->is()) + return ArtboardViewModel::PropertyType::symbolListIndex; + + if (property->is()) + return ArtboardViewModel::PropertyType::assetImage; + + if (property->is()) + return ArtboardViewModel::PropertyType::artboard; + + // Covers plain, custom and system enums (all derive from ViewModelPropertyEnum). + if (property->is()) + return ArtboardViewModel::PropertyType::enumType; + + return ArtboardViewModel::PropertyType::none; +} + +StringArray enumOptionsOf (rive::ViewModelProperty* property) +{ + StringArray options; + + if (property != nullptr && property->is()) + if (auto* dataEnum = property->as()->dataEnum()) + for (auto* enumValue : dataEnum->values()) + { + const auto& value = enumValue->value(); + options.add (value.empty() ? String (enumValue->key()) : String (value)); + } + + return options; +} + +ArtboardViewModel::PropertyInfo propertyInfoOf (rive::ViewModelProperty* property) +{ + if (property == nullptr) + return {}; + + return { String (property->name()), + toPropertyType (property), + property->isInput(), + property->isOutput(), + enumOptionsOf (property) }; +} + +} // namespace + +//============================================================================== + +ArtboardViewModel::ArtboardViewModel (const std::shared_ptr& fileToUse, rive::ViewModel* riveViewModel) + : file (fileToUse) + , viewModel (riveViewModel) +{ +} + +ArtboardViewModel::~ArtboardViewModel() = default; + +//============================================================================== + +String ArtboardViewModel::getName() const +{ + if (auto* vm = viewModel) + return String (vm->name()); + + return {}; +} + +ArtboardFile* ArtboardViewModel::getArtboardFile() const noexcept +{ + return file.get(); +} + +//============================================================================== + +int ArtboardViewModel::getNumProperties() const noexcept +{ + if (auto* vm = viewModel) + return static_cast (vm->properties().size()); + + return 0; +} + +ArtboardViewModel::PropertyInfo ArtboardViewModel::getPropertyAt (int index) const +{ + if (auto* vm = viewModel) + return propertyInfoOf (vm->property (static_cast (index))); + + return {}; +} + +ArtboardViewModel::PropertyInfo ArtboardViewModel::getProperty (StringRef name) const +{ + if (auto* vm = viewModel) + return propertyInfoOf (vm->property (String (name).toStdString())); + + return {}; +} + +bool ArtboardViewModel::hasProperty (StringRef name) const +{ + if (auto* vm = viewModel) + return vm->property (String (name).toStdString()) != nullptr; + + return false; +} + +//============================================================================== + +int ArtboardViewModel::getNumInstances() const noexcept +{ + if (auto* vm = viewModel) + return static_cast (vm->instanceCount()); + + return 0; +} + +StringArray ArtboardViewModel::getInstanceNames() const +{ + StringArray names; + + if (auto* vm = viewModel) + for (auto* instance : vm->instances()) + names.add (String (instance->name())); + + return names; +} + +//============================================================================== + +ArtboardViewModel::Ptr ArtboardViewModel::createFromFile (const std::shared_ptr& fileToUse, int index) +{ + if (fileToUse == nullptr) + return nullptr; + + auto* rivFile = fileToUse->getRiveFile(); + if (rivFile == nullptr) + return nullptr; + + auto* vm = rivFile->viewModel (static_cast (index)); + if (vm == nullptr) + return nullptr; + + return Ptr (new ArtboardViewModel (fileToUse, vm)); +} + +ArtboardViewModel::Ptr ArtboardViewModel::createFromFile (const std::shared_ptr& fileToUse, StringRef name) +{ + if (fileToUse == nullptr) + return nullptr; + + auto* rivFile = fileToUse->getRiveFile(); + if (rivFile == nullptr) + return nullptr; + + auto* vm = rivFile->viewModel (String (name).toStdString()); + if (vm == nullptr) + return nullptr; + + return Ptr (new ArtboardViewModel (fileToUse, vm)); +} + +} // namespace yup diff --git a/modules/yup_gui/artboard/yup_ArtboardViewModel.h b/modules/yup_gui/artboard/yup_ArtboardViewModel.h new file mode 100644 index 000000000..3dccabc3a --- /dev/null +++ b/modules/yup_gui/artboard/yup_ArtboardViewModel.h @@ -0,0 +1,178 @@ +/* + ============================================================================== + + This file is part of the YUP library. + Copyright (c) 2026 - kunitoki@gmail.com + + YUP is an open source library subject to open-source licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + to use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace yup +{ + +class ArtboardFile; + +//============================================================================== +/** Represents a Rive ViewModel schema. + + A ViewModel is the data schema a Rive artboard is designed against: a named + list of typed properties (boolean, number, string, color, enum, trigger, + nested viewmodels, lists, ...). Schemas are authored in the Rive editor and + stored inside the .riv file. + + A handle to a schema is obtained from an ArtboardFile via + ArtboardFile::getArtboardViewModel(). Instances are read-only, refcounted + and remain valid for as long as the handle is referenced: the handle keeps + the owning ArtboardFile (and therefore the underlying Rive file) alive. + + @see ArtboardFile::getArtboardViewModel, ArtboardViewModelInstance +*/ +class YUP_API ArtboardViewModel : public ReferenceCountedObject +{ +public: + //============================================================================== + using Ptr = ReferenceCountedObjectPtr; + + //============================================================================== + /** The data type of a ViewModel property. */ + enum class PropertyType + { + /** No type (unknown property). */ + none, + + /** A boolean property. */ + boolean, + + /** A numeric property. */ + number, + + /** A string property. */ + string, + + /** A color property. */ + color, + + /** A list of nested viewmodel instances. */ + list, + + /** An enum property, whose value is one of a fixed set of options. */ + enumType, + + /** A trigger property. */ + trigger, + + /** A nested viewmodel instance property. */ + viewModel, + + /** A symbol list index property. */ + symbolListIndex, + + /** An image asset property. */ + assetImage, + + /** An artboard reference property. */ + artboard + }; + + //============================================================================== + /** Describes a single property of a ViewModel schema. */ + struct PropertyInfo + { + /** Default constructor. */ + PropertyInfo() = default; + + /** Constructs a property info from its parts. */ + PropertyInfo (String propertyName, + PropertyType propertyType, + bool isInputProperty, + bool isOutputProperty, + StringArray enumOptions) + : name (std::move (propertyName)) + , type (propertyType) + , isInput (isInputProperty) + , isOutput (isOutputProperty) + , enumValues (std::move (enumOptions)) + { + } + + /** The name of the property. */ + String name; + + /** The data type of the property. */ + PropertyType type = PropertyType::none; + + /** True if the property accepts values coming from the host (an input binding). */ + bool isInput = false; + + /** True if the property is written back to the host when the artboard changes it (an output binding). */ + bool isOutput = false; + + /** For enumType properties, the human-readable options; empty otherwise. */ + StringArray enumValues; + }; + + //============================================================================== + /** Destructor. */ + ~ArtboardViewModel() override; + + //============================================================================== + /** Returns the name of the ViewModel schema. */ + String getName() const; + + /** Returns the ArtboardFile this schema belongs to, or null if it has been released. */ + ArtboardFile* getArtboardFile() const noexcept; + + //============================================================================== + /** Returns the number of properties defined by the schema. */ + int getNumProperties() const noexcept; + + /** Returns the property at the given index, or an empty PropertyInfo if out of range. */ + PropertyInfo getPropertyAt (int index) const; + + /** Returns the property with the given name, or an empty PropertyInfo if unknown. */ + PropertyInfo getProperty (StringRef name) const; + + /** Returns true if the schema defines a property with the given name. */ + bool hasProperty (StringRef name) const; + + //============================================================================== + /** Returns the number of authored instances stored in the schema. + + .riv files may ship with pre-authored viewmodel instances (the first of + which is the "default" instance); these can be cloned through + ArtboardFile::createArtboardViewModelInstance() by name. + */ + int getNumInstances() const noexcept; + + /** Returns the names of the authored instances stored in the schema. */ + StringArray getInstanceNames() const; + + //============================================================================== + /** @internal */ + static Ptr createFromFile (const std::shared_ptr& file, int index); + + /** @internal */ + static Ptr createFromFile (const std::shared_ptr& file, StringRef name); + +private: + ArtboardViewModel (const std::shared_ptr& file, rive::ViewModel* riveViewModel); + + std::shared_ptr file; + rive::ViewModel* viewModel = nullptr; + + YUP_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ArtboardViewModel) +}; + +} // namespace yup diff --git a/modules/yup_gui/artboard/yup_ArtboardViewModelInstance.cpp b/modules/yup_gui/artboard/yup_ArtboardViewModelInstance.cpp new file mode 100644 index 000000000..1cd54bbb4 --- /dev/null +++ b/modules/yup_gui/artboard/yup_ArtboardViewModelInstance.cpp @@ -0,0 +1,825 @@ +/* + ============================================================================== + + This file is part of the YUP library. + Copyright (c) 2026 - kunitoki@gmail.com + + YUP is an open source library subject to open-source licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + to use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace yup +{ + +namespace +{ + +//============================================================================== + +Array splitPath (String path) +{ + Array segments; + + for (;;) + { + const int dot = path.indexOfChar ('.'); + if (dot < 0) + { + segments.add (path); + return segments; + } + + segments.add (path.substring (0, dot)); + path = path.substring (dot + 1); + } +} + +bool isIndexSegment (const String& segment) +{ + if (segment.isEmpty()) + return false; + + for (auto character : segment) + if (character < '0' || character > '9') + return false; + + return true; +} + +/** What a dotted path resolved to. + + A path either terminates on a property value ("player.health") or, when its + last segment is a list index ("items.2"), on the item's viewmodel instance. + At most one of the two members is ever set. +*/ +struct ResolvedPath +{ + rive::ViewModelInstanceValue* value = nullptr; + rive::ViewModelInstance* instance = nullptr; +}; + +ResolvedPath resolvePath (rive::ViewModelInstance* root, const Array& segments) +{ + if (root == nullptr || segments.isEmpty()) + return {}; + + auto* current = root; + rive::ViewModelInstanceValue* value = nullptr; + + for (int i = 0; i < segments.size(); ++i) + { + const auto& segment = segments.getReference (i); + const bool isLast = (i == segments.size() - 1); + + if (isIndexSegment (segment)) + { + // An index only addresses something when it follows a list property. + // rive::Core::as<> only asserts in debug builds, so check first. + if (value == nullptr || ! value->is()) + return {}; + + auto item = value->as()->item (static_cast (segment.getIntValue())); + if (item == nullptr) + return {}; + + current = item->viewModelInstance().get(); + value = nullptr; + + if (current == nullptr) + return {}; + + if (isLast) + return { nullptr, current }; + + continue; + } + + auto* propertyValue = current->propertyValue (segment.toStdString()); + if (propertyValue == nullptr) + return {}; + + if (isLast) + return { propertyValue, nullptr }; + + if (propertyValue->is()) + { + auto nested = propertyValue->as()->referenceViewModelInstance(); + if (nested == nullptr) + return {}; + + current = nested.get(); + value = nullptr; + } + else + { + value = propertyValue; + } + } + + return {}; +} + +ResolvedPath resolve (const rive::rcp& root, StringRef nameOrPath) +{ + return resolvePath (root.get(), splitPath (String (nameOrPath))); +} + +template +T* asValue (rive::ViewModelInstanceValue* value) +{ + return value != nullptr && value->is() ? value->as() : nullptr; +} + +template +T* resolveAs (const rive::rcp& root, StringRef nameOrPath) +{ + return asValue (resolve (root, nameOrPath).value); +} + +rive::ViewModelPropertyEnum* enumPropertyOf (rive::ViewModelInstanceValue* value) +{ + if (value == nullptr) + return nullptr; + + auto* property = value->viewModelProperty(); + + return property != nullptr && property->is() + ? property->as() + : nullptr; +} + +std::optional enumValueNameOf (rive::ViewModelInstanceEnum* enumValue) +{ + auto* enumProperty = enumPropertyOf (enumValue); + if (enumProperty == nullptr || enumProperty->dataEnum() == nullptr) + return std::nullopt; + + return String (enumProperty->value (enumValue->propertyValue())); +} + +var valueToVar (rive::ViewModelInstanceValue* value) +{ + if (value == nullptr) + return {}; + + if (auto* boolean = asValue (value)) + return var (boolean->propertyValue()); + + if (auto* number = asValue (value)) + return var (static_cast (number->propertyValue())); + + if (auto* string = asValue (value)) + return var (String (string->propertyValue())); + + if (auto* color = asValue (value)) + return var (static_cast (static_cast (color->propertyValue()))); + + if (auto* enumValue = asValue (value)) + { + if (const auto name = enumValueNameOf (enumValue)) + return var (*name); + + return {}; + } + + return {}; +} + +int enumIndexForName (rive::ViewModelPropertyEnum* enumProperty, const String& name) +{ + if (enumProperty == nullptr || enumProperty->dataEnum() == nullptr) + return -1; + + const std::string key = name.toStdString(); + + int index = enumProperty->valueIndex (key); + if (index != -1) + return index; + + int fallback = 0; + for (auto* enumValue : enumProperty->dataEnum()->values()) + { + const auto& value = enumValue->value(); + const auto& candidate = value.empty() ? enumValue->key() : value; + if (candidate == key) + return fallback; + + ++fallback; + } + + return -1; +} + +} // namespace + +//============================================================================== + +struct ArtboardViewModelInstance::Impl +{ + class DispatchGuard + { + public: + explicit DispatchGuard (ArtboardViewModelInstance& ownerToUse) + : owner (ownerToUse) + { + owner.enterObserverDispatch(); + } + + ~DispatchGuard() + { + owner.leaveObserverDispatch(); + } + + private: + ArtboardViewModelInstance& owner; + }; + + class Observer : public rive::ViewModelInstanceValueDelegate + { + public: + Observer (ArtboardViewModelInstance& ownerToUse, + const rive::rcp& valueToWatch, + String pathToUse) + : owner (ownerToUse) + , value (valueToWatch) + , path (std::move (pathToUse)) + { + value->addDelegate (this); + } + + ~Observer() + { + removeFromValue(); + } + + void valueChanged() override + { + { + DispatchGuard guard (owner); + + if (active) + owner.notifyValueChanged (value.get(), path); + } + + // The callback may have rebuilt the observer tree, retiring observers + // that could not be freed while the dispatch was unwinding. Only purge + // while this observer is still live, otherwise it would free itself. + if (active) + owner.purgeRetiredObservers(); + } + + void detach() + { + active = false; + removeFromValue(); + } + + private: + void removeFromValue() + { + if (registered && value != nullptr) + { + value->removeDelegate (this); + registered = false; + } + } + + ArtboardViewModelInstance& owner; + rive::rcp value; + String path; + bool active = true; + bool registered = true; + }; + + rive::rcp instance; + PropertyChangedCallback propertyChangedCallback; + std::vector> observers; + std::vector> retiredObservers; + + // Viewmodel instance graphs may be cyclic (a nested viewmodel referencing back + // into an ancestor), so remember what is already observed to bound the walk and + // to keep incremental attachments from observing the same value twice. + std::unordered_set observedInstances; +}; + +//============================================================================== + +ArtboardViewModelInstance::ArtboardViewModelInstance (const std::shared_ptr& fileToUse, rive::ViewModelInstance* riveInstance) + : file (fileToUse) + , impl (std::make_unique()) +{ + impl->instance = rive::rcp (riveInstance); +} + +ArtboardViewModelInstance::~ArtboardViewModelInstance() +{ + detachObservers(); +} + +//============================================================================== + +ArtboardFile* ArtboardViewModelInstance::getArtboardFile() const noexcept +{ + return file.get(); +} + +String ArtboardViewModelInstance::getName() const +{ + if (impl->instance != nullptr) + if (auto* viewModel = impl->instance->viewModel()) + return String (viewModel->name()); + + return {}; +} + +String ArtboardViewModelInstance::getInstanceName() const +{ + if (impl->instance != nullptr) + return String (impl->instance->name()); + + return {}; +} + +//============================================================================== + +bool ArtboardViewModelInstance::hasProperty (StringRef nameOrPath) const +{ + const auto resolved = resolve (impl->instance, nameOrPath); + + return resolved.value != nullptr || resolved.instance != nullptr; +} + +//============================================================================== + +var ArtboardViewModelInstance::getProperty (StringRef nameOrPath) const +{ + return valueToVar (resolve (impl->instance, nameOrPath).value); +} + +std::optional ArtboardViewModelInstance::getBoolProperty (StringRef nameOrPath) const +{ + const auto value = getProperty (nameOrPath); + return value.isBool() ? std::optional (static_cast (value)) : std::nullopt; +} + +std::optional ArtboardViewModelInstance::getNumberProperty (StringRef nameOrPath) const +{ + const auto value = getProperty (nameOrPath); + if (value.isDouble()) + return static_cast (value); + + if (value.isInt()) + return static_cast (static_cast (value)); + + if (value.isInt64()) + return static_cast (static_cast (value)); + + return std::nullopt; +} + +std::optional ArtboardViewModelInstance::getStringProperty (StringRef nameOrPath) const +{ + const auto value = getProperty (nameOrPath); + return value.isString() ? std::optional (value.toString()) : std::nullopt; +} + +std::optional ArtboardViewModelInstance::getColorProperty (StringRef nameOrPath) const +{ + if (auto* color = resolveAs (impl->instance, nameOrPath)) + return Color (static_cast (color->propertyValue())); + + return std::nullopt; +} + +std::optional ArtboardViewModelInstance::getEnumProperty (StringRef nameOrPath) const +{ + auto* enumValue = resolveAs (impl->instance, nameOrPath); + if (enumValue == nullptr) + return std::nullopt; + + return enumValueNameOf (enumValue); +} + +//============================================================================== + +bool ArtboardViewModelInstance::setProperty (StringRef nameOrPath, const var& value) +{ + auto* resolved = resolve (impl->instance, nameOrPath).value; + if (resolved == nullptr) + return false; + + if (auto* boolean = asValue (resolved)) + { + if (! value.isBool()) + return false; + + boolean->propertyValue (static_cast (value)); + return true; + } + + if (auto* number = asValue (resolved)) + { + if (! (value.isDouble() || value.isInt() || value.isInt64())) + return false; + + number->propertyValue (static_cast (static_cast (value))); + return true; + } + + if (auto* string = asValue (resolved)) + { + if (! value.isString()) + return false; + + string->propertyValue (value.toString().toStdString()); + return true; + } + + if (auto* color = asValue (resolved)) + { + if (! (value.isInt() || value.isInt64())) + return false; + + const auto argb = value.isInt64() ? static_cast (static_cast (value)) + : static_cast (static_cast (value)); + + color->propertyValue (static_cast (argb)); + return true; + } + + if (auto* enumValue = asValue (resolved)) + { + int index = -1; + if (value.isString()) + index = enumIndexForName (enumPropertyOf (resolved), value.toString()); + else if (value.isInt()) + index = static_cast (value); + else if (value.isInt64()) + index = static_cast (static_cast (value)); + + if (index < 0) + return false; + + return enumValue->value (static_cast (index)); + } + + return false; +} + +bool ArtboardViewModelInstance::trigger (StringRef nameOrPath) +{ + auto* resolved = resolveAs (impl->instance, nameOrPath); + if (resolved == nullptr) + return false; + + resolved->trigger(); + return true; +} + +//============================================================================== + +bool ArtboardViewModelInstance::setBoolProperty (StringRef nameOrPath, bool value) +{ + return setProperty (nameOrPath, var (value)); +} + +bool ArtboardViewModelInstance::setNumberProperty (StringRef nameOrPath, double value) +{ + return setProperty (nameOrPath, var (value)); +} + +bool ArtboardViewModelInstance::setStringProperty (StringRef nameOrPath, StringRef value) +{ + return setProperty (nameOrPath, var (String (value))); +} + +bool ArtboardViewModelInstance::setColorProperty (StringRef nameOrPath, const Color& value) +{ + return setProperty (nameOrPath, var (static_cast (value.getARGB()))); +} + +bool ArtboardViewModelInstance::setEnumProperty (StringRef nameOrPath, StringRef valueName) +{ + return setProperty (nameOrPath, var (String (valueName))); +} + +//============================================================================== + +ArtboardViewModelInstance::Ptr ArtboardViewModelInstance::getNestedInstance (StringRef nameOrPath) const +{ + const auto resolved = resolve (impl->instance, nameOrPath); + + if (resolved.instance != nullptr) + return createFromRive (file, resolved.instance); + + if (auto* nestedValue = asValue (resolved.value)) + if (auto nested = nestedValue->referenceViewModelInstance()) + return createFromRive (file, nested.get()); + + return nullptr; +} + +//============================================================================== + +int ArtboardViewModelInstance::getListSize (StringRef nameOrPath) const +{ + auto* list = resolveAs (impl->instance, nameOrPath); + if (list == nullptr) + return -1; + + return static_cast (list->listItems().size()); +} + +ArtboardViewModelInstance::Ptr ArtboardViewModelInstance::getListItem (StringRef nameOrPath, int index) const +{ + if (index < 0) + return nullptr; + + auto* list = resolveAs (impl->instance, nameOrPath); + if (list == nullptr || static_cast (index) >= list->listItems().size()) + return nullptr; + + auto item = list->item (static_cast (index)); + if (item == nullptr) + return nullptr; + + auto itemInstance = item->viewModelInstance(); + if (itemInstance == nullptr) + return nullptr; + + return createFromRive (file, itemInstance.get()); +} + +bool ArtboardViewModelInstance::addListItem (StringRef nameOrPath, StringRef elementViewModelName) +{ + auto* list = resolveAs (impl->instance, nameOrPath); + if (list == nullptr) + return false; + + return addListItemAt (nameOrPath, static_cast (list->listItems().size()), elementViewModelName); +} + +bool ArtboardViewModelInstance::addListItemAt (StringRef nameOrPath, int index, StringRef elementViewModelName) +{ + if (file == nullptr) + return false; + + auto* list = resolveAs (impl->instance, nameOrPath); + if (list == nullptr) + return false; + + auto* rivFile = file->getRiveFile(); + if (rivFile == nullptr) + return false; + + auto itemInstance = rivFile->createViewModelInstance (String (elementViewModelName).toStdString()); + if (itemInstance == nullptr) + return false; + + auto* rawItem = rivFile->viewModelInstanceListItem (itemInstance, nullptr); + if (rawItem == nullptr) + return false; + + const auto size = static_cast (list->listItems().size()); + const auto clampedIndex = jlimit (0, size, index); + + if (! list->addItemAt (rive::rcp (rawItem), clampedIndex)) + return false; + + // Appending leaves the indices of the existing items (and therefore the paths + // their observers were built with) untouched, so only the new subtree needs + // observing. Any other insertion shifts them and forces a full rebuild. + if (clampedIndex == size) + attachObserversToTree (rawItem->viewModelInstance().get(), + String (nameOrPath) + "." + String (clampedIndex)); + else + syncObservers(); + + return true; +} + +bool ArtboardViewModelInstance::removeListItem (StringRef nameOrPath, int index) +{ + if (index < 0) + return false; + + auto* list = resolveAs (impl->instance, nameOrPath); + if (list == nullptr || static_cast (index) >= list->listItems().size()) + return false; + + list->removeItem (index); + syncObservers(); + return true; +} + +bool ArtboardViewModelInstance::swapListItems (StringRef nameOrPath, int indexA, int indexB) +{ + if (indexA < 0 || indexB < 0) + return false; + + auto* list = resolveAs (impl->instance, nameOrPath); + if (list == nullptr) + return false; + + const auto size = list->listItems().size(); + if (static_cast (indexA) >= size || static_cast (indexB) >= size) + return false; + + list->swap (static_cast (indexA), static_cast (indexB)); + syncObservers(); + return true; +} + +void ArtboardViewModelInstance::clearListItems (StringRef nameOrPath) +{ + auto* list = resolveAs (impl->instance, nameOrPath); + if (list == nullptr) + return; + + list->removeAllItems(); + syncObservers(); +} + +//============================================================================== + +void ArtboardViewModelInstance::setPropertyChangedCallback (PropertyChangedCallback callback) +{ + impl->propertyChangedCallback = std::move (callback); + + // (Re)attach the value observers to cover the new callback (or drop them when clearing). + syncObservers(); +} + +//============================================================================== + +ArtboardViewModelInstance::Ptr ArtboardViewModelInstance::createFromFile (const std::shared_ptr& fileToUse, StringRef viewModelName) +{ + if (fileToUse == nullptr) + return nullptr; + + auto* rivFile = fileToUse->getRiveFile(); + if (rivFile == nullptr) + return nullptr; + + auto instance = rivFile->createViewModelInstance (String (viewModelName).toStdString()); + if (instance == nullptr) + return nullptr; + + // release() hands over the reference the rcp already holds, matching the + // adopting rive::rcp the constructor builds. + return Ptr (new ArtboardViewModelInstance (fileToUse, instance.release())); +} + +ArtboardViewModelInstance::Ptr ArtboardViewModelInstance::createFromFile (const std::shared_ptr& fileToUse, StringRef viewModelName, StringRef instanceName) +{ + if (fileToUse == nullptr) + return nullptr; + + auto* rivFile = fileToUse->getRiveFile(); + if (rivFile == nullptr) + return nullptr; + + auto instance = rivFile->createViewModelInstance (String (viewModelName).toStdString(), + String (instanceName).toStdString()); + if (instance == nullptr) + return nullptr; + + return Ptr (new ArtboardViewModelInstance (fileToUse, instance.release())); +} + +ArtboardViewModelInstance::Ptr ArtboardViewModelInstance::createFromRive (const std::shared_ptr& fileToUse, rive::ViewModelInstance* riveInstance) +{ + if (fileToUse == nullptr || riveInstance == nullptr) + return nullptr; + + // rive::rcp's pointer constructor adopts without reffing, so take the extra + // reference here: this handle observes an instance it does not own. + riveInstance->ref(); + + return Ptr (new ArtboardViewModelInstance (fileToUse, riveInstance)); +} + +rive::ViewModelInstance* ArtboardViewModelInstance::internalRiveInstance() const noexcept +{ + return impl->instance.get(); +} + +//============================================================================== + +void ArtboardViewModelInstance::notifyValueChanged (rive::ViewModelInstanceValue* riveValue, const String& name) +{ + if (! impl->propertyChangedCallback) + return; + + // The callback is free to replace or clear itself, which would destroy the + // closure mid-call, so run a copy that outlives the assignment. + auto callback = impl->propertyChangedCallback; + callback (*this, name, valueToVar (riveValue)); +} + +//============================================================================== + +void ArtboardViewModelInstance::enterObserverDispatch() noexcept +{ + if (file != nullptr) + file->enterObserverDispatch(); +} + +void ArtboardViewModelInstance::leaveObserverDispatch() noexcept +{ + if (file != nullptr) + file->leaveObserverDispatch(); +} + +bool ArtboardViewModelInstance::isObserverDispatchInProgress() const noexcept +{ + return file != nullptr && file->isObserverDispatchInProgress(); +} + +//============================================================================== + +void ArtboardViewModelInstance::attachObserversToTree (rive::ViewModelInstance* instanceToObserve, const String& prefix) +{ + if (instanceToObserve == nullptr + || ! impl->propertyChangedCallback + || ! impl->observedInstances.insert (instanceToObserve).second) + return; + + for (auto& propertyValue : instanceToObserve->propertyValues()) + { + if (propertyValue == nullptr) + continue; + + auto* value = propertyValue.get(); + + auto* property = value->viewModelProperty(); + if (property == nullptr) + continue; + + const String path = prefix.isEmpty() + ? String (property->name()) + : prefix + "." + String (property->name()); + + impl->observers.push_back (std::make_unique (*this, propertyValue, path)); + + if (auto* nestedValue = asValue (value)) + { + if (auto nested = nestedValue->referenceViewModelInstance()) + attachObserversToTree (nested.get(), path); + } + else if (auto* list = asValue (value)) + { + int index = 0; + for (auto& item : list->listItems()) + { + attachObserversToTree (item->viewModelInstance().get(), path + "." + String (index)); + ++index; + } + } + } +} + +void ArtboardViewModelInstance::detachObservers() +{ + for (auto& observer : impl->observers) + if (observer != nullptr) + { + observer->detach(); + impl->retiredObservers.push_back (std::move (observer)); + } + + impl->observers.clear(); + impl->observedInstances.clear(); + + purgeRetiredObservers(); +} + +void ArtboardViewModelInstance::purgeRetiredObservers() +{ + // A retired observer may be the one currently dispatching, so it can only be + // freed once every dispatch on the owning file has unwound. + if (isObserverDispatchInProgress()) + return; + + impl->retiredObservers.clear(); +} + +void ArtboardViewModelInstance::syncObservers() +{ + detachObservers(); + + attachObserversToTree (impl->instance.get(), {}); +} + +} // namespace yup diff --git a/modules/yup_gui/artboard/yup_ArtboardViewModelInstance.h b/modules/yup_gui/artboard/yup_ArtboardViewModelInstance.h new file mode 100644 index 000000000..83fe3b9da --- /dev/null +++ b/modules/yup_gui/artboard/yup_ArtboardViewModelInstance.h @@ -0,0 +1,291 @@ +/* + ============================================================================== + + This file is part of the YUP library. + Copyright (c) 2026 - kunitoki@gmail.com + + YUP is an open source library subject to open-source licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + to use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace yup +{ + +class Artboard; +class ArtboardFile; +class ArtboardViewModel; + +//============================================================================== +/** Represents a live instance of a Rive ViewModel schema. + + Instances hold the actual property values (numbers, strings, colors, ...) + that drive the data bindings of a Rive artboard. They are created from an + ArtboardFile through ArtboardFile::createArtboardViewModelInstance() and are + then bound to an Artboard via Artboard::bindViewModelInstance(). + + Instances are refcounted and can be copied and stored freely. The handle + keeps its owning ArtboardFile (and the underlying Rive file) alive for as + long as it is referenced, so property accessors never dangle. + + Property values can be read and written by property name or through dotted + paths into nested viewmodels and lists, e.g. "score", "player.name" or + "items.2.quantity". A path may also terminate on a list index ("items.2"), + which designates the item's viewmodel instance rather than a property value; + such paths resolve through hasProperty() and getNestedInstance(), but not + through the value accessors. Property names are the ones authored in the + Rive editor. Value writes are applied to the artboard data bindings on the + next Artboard::advanceAndApply(). + + A path segment made only of decimal digits is always read as a list index, + so a property literally named "2" cannot be addressed through a path. + + Instances are not internally synchronized. They are safe to use from the + message thread and from Component::refreshDisplay (which YUP serialises + against the message thread), but not concurrently from both. + + @see ArtboardFile::createArtboardViewModelInstance, Artboard::bindViewModelInstance +*/ +class YUP_API ArtboardViewModelInstance : public ReferenceCountedObject +{ +public: + //============================================================================== + using Ptr = ReferenceCountedObjectPtr; + + /** Callback invoked when a property value of this instance changes. + + The callback fires synchronously when a value is written through this + handle or by the artboard (output bindings applied during + Artboard::advanceAndApply()). + + @param instance The instance whose property changed. + @param name The dotted property path that changed. + @param value The new value (mapped like ArtboardViewModelInstance::getProperty). + */ + using PropertyChangedCallback = std::function; + + //============================================================================== + /** Destructor. */ + ~ArtboardViewModelInstance() override; + + //============================================================================== + /** Returns the ArtboardFile this instance belongs to, or null if it has been released. */ + ArtboardFile* getArtboardFile() const noexcept; + + /** Returns the name of the ViewModel schema this instance instantiates. */ + String getName() const; + + /** Returns the authored instance name this instance was cloned from, if any. */ + String getInstanceName() const; + + //============================================================================== + /** Returns true if a property (or nested dotted path) is defined by this instance. + + Index-terminated paths ("items.2") are accepted and report whether the + list item exists. + */ + bool hasProperty (StringRef nameOrPath) const; + + //============================================================================== + /** Reads a property value by name or dotted path. + + Mapping: boolean → var(bool), number → var(double), string → var(String), + color → var(int64 ARGB), enum → var(String) of the selected option. + Container values (viewmodels, lists), triggers and index-terminated paths + return an empty var, as do unknown paths: the two cases are not + distinguishable through this accessor, use hasProperty() to tell them apart. + + @param nameOrPath The property name or dotted path, e.g. "player.health". + */ + var getProperty (StringRef nameOrPath) const; + + /** Writes a property value by name or dotted path. + + Accepts the same var representations as getProperty, plus integers for + number properties and for enum properties either the option name or its + index. Triggers cannot be written here, use trigger(). + + @param nameOrPath The property name or dotted path. + @param value The value to write. + @return True if the path existed and the value was applied. + */ + bool setProperty (StringRef nameOrPath, const var& value); + + /** Fires a trigger property by name or dotted path. + + @param nameOrPath The name of the trigger property. + @return True if the path resolved to a trigger and it was fired. + */ + bool trigger (StringRef nameOrPath); + + //============================================================================== + /** Reads a boolean property, or nullopt if the path is unknown or not a boolean. + + Every typed getter below collapses "unknown path" and "wrong type" into + nullopt; use hasProperty() to distinguish them. + */ + std::optional getBoolProperty (StringRef nameOrPath) const; + + /** Reads a number property as double, or nullopt if the path is unknown or not a number. */ + std::optional getNumberProperty (StringRef nameOrPath) const; + + /** Reads a string property, or nullopt if the path is unknown or not a string. */ + std::optional getStringProperty (StringRef nameOrPath) const; + + /** Reads a color property, or nullopt if the path is unknown or not a color. */ + std::optional getColorProperty (StringRef nameOrPath) const; + + /** Reads the selected option of an enum property, or nullopt if the path is unknown. */ + std::optional getEnumProperty (StringRef nameOrPath) const; + + //============================================================================== + /** Writes a boolean property. + + @return True if the path existed and the value was applied. + */ + bool setBoolProperty (StringRef nameOrPath, bool value); + + /** Writes a number property. + + @return True if the path existed and the value was applied. + */ + bool setNumberProperty (StringRef nameOrPath, double value); + + /** Writes a string property. + + @return True if the path existed and the value was applied. + */ + bool setStringProperty (StringRef nameOrPath, StringRef value); + + /** Writes a color property. + + @return True if the path existed and the value was applied. + */ + bool setColorProperty (StringRef nameOrPath, const Color& value); + + /** Writes an enum property, accepting the option key or display value. + + @return True if the path existed and the option was found. + */ + bool setEnumProperty (StringRef nameOrPath, StringRef valueName); + + //============================================================================== + /** Returns a handle to the nested viewmodel instance behind a viewModel-typed + property, or behind an index-terminated list path ("items.2"), or null if + the path is unknown or names something else. + + The returned handle shares the same ArtboardFile and observes the same + underlying Rive instance as its parent. + */ + ArtboardViewModelInstance::Ptr getNestedInstance (StringRef nameOrPath) const; + + //============================================================================== + /** Returns the number of items of a list property, or -1 if the path is unknown + or does not name a list. */ + int getListSize (StringRef nameOrPath) const; + + /** Returns a handle to the item at the given index of a list property, or null + if the path does not name a list or the index is out of range. */ + ArtboardViewModelInstance::Ptr getListItem (StringRef nameOrPath, int index) const; + + /** Appends a new item to a list property. + + @param nameOrPath The dotted path to the list property. + @param elementViewModelName The name of the ViewModel schema the new item + instantiates (must match the list's element type). + @return True if the list was found and the item was appended. + */ + bool addListItem (StringRef nameOrPath, StringRef elementViewModelName); + + /** Inserts a new item at the given index of a list property. + + @param nameOrPath The dotted path to the list property. + @param index The insertion index (0-based, clamped to the list). + @param elementViewModelName The name of the ViewModel schema the new item + instantiates (must match the list's element type). + @return True if the list was found and the item was inserted. + */ + bool addListItemAt (StringRef nameOrPath, int index, StringRef elementViewModelName); + + /** Removes the item at the given index of a list property. + + @return True if the list and index were valid. + */ + bool removeListItem (StringRef nameOrPath, int index); + + /** Swaps two items of a list property. + + @return True if the list and both indices were valid. + */ + bool swapListItems (StringRef nameOrPath, int indexA, int indexB); + + /** Removes every item of a list property. */ + void clearListItems (StringRef nameOrPath); + + //============================================================================== + /** Registers a callback invoked whenever any property of this instance (and its + nested viewmodels and list items) changes. + + Structural list changes (addListItem, removeListItem, swapListItems, + clearListItems) also notify, reporting the path of the list itself and an + empty var as the value. + + Pass an empty callback to remove the listener. Callbacks are delivered + synchronously on the thread that performed the write; when the write comes + from the artboard's data bindings, that is the thread running + Artboard::refreshDisplay, on the thread described above. + + Mutating this instance from within the callback (writing values or + resizing lists) is supported, as is replacing or clearing the callback + itself. The last reference to the instance must however not be released + from inside the callback. + + @param callback The callback to invoke on property changes. + */ + void setPropertyChangedCallback (PropertyChangedCallback callback); + + //============================================================================== + /** @internal */ + static Ptr createFromFile (const std::shared_ptr& file, StringRef viewModelName); + + /** @internal */ + static Ptr createFromFile (const std::shared_ptr& file, StringRef viewModelName, StringRef instanceName); + + /** @internal */ + static Ptr createFromRive (const std::shared_ptr& file, rive::ViewModelInstance* riveInstance); + + /** @internal */ + rive::ViewModelInstance* internalRiveInstance() const noexcept; + +private: + ArtboardViewModelInstance (const std::shared_ptr& file, rive::ViewModelInstance* riveInstance); + + struct Impl; + + void enterObserverDispatch() noexcept; + void leaveObserverDispatch() noexcept; + bool isObserverDispatchInProgress() const noexcept; + + void notifyValueChanged (rive::ViewModelInstanceValue* riveValue, const String& name); + void attachObserversToTree (rive::ViewModelInstance* instance, const String& prefix); + void detachObservers(); + void syncObservers(); + void purgeRetiredObservers(); + + std::shared_ptr file; + std::unique_ptr impl; + + YUP_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ArtboardViewModelInstance) +}; + +} // namespace yup diff --git a/modules/yup_gui/component/yup_Component.cpp b/modules/yup_gui/component/yup_Component.cpp index 86ec14c19..84ce3fcc3 100644 --- a/modules/yup_gui/component/yup_Component.cpp +++ b/modules/yup_gui/component/yup_Component.cpp @@ -22,6 +22,46 @@ namespace yup { +//============================================================================== +namespace +{ +// Returns the axis-aligned bounding box of a rectangle mapped through a transform. +Rectangle getTransformedBounds (const Rectangle& bounds, const AffineTransform& transform) +{ + const auto x1 = bounds.getX(); + const auto y1 = bounds.getY(); + const auto x2 = bounds.getRight(); + const auto y2 = bounds.getBottom(); + + float px1 = x1, py1 = y1; + float px2 = x2, py2 = y1; + float px3 = x1, py3 = y2; + float px4 = x2, py4 = y2; + + transform.transformPoint (px1, py1); + transform.transformPoint (px2, py2); + transform.transformPoint (px3, py3); + transform.transformPoint (px4, py4); + + auto minX = px1, maxX = px1; + auto minY = py1, maxY = py1; + + const auto updateMinMax = [&] (float x, float y) + { + minX = jmin (minX, x); + maxX = jmax (maxX, x); + minY = jmin (minY, y); + maxY = jmax (maxY, y); + }; + + updateMinMax (px2, py2); + updateMinMax (px3, py3); + updateMinMax (px4, py4); + + return { minX, minY, maxX - minX, maxY - minY }; +} +} // namespace + //============================================================================== Component::Component() @@ -410,6 +450,21 @@ Rectangle Component::getBoundsRelativeToTopLevelComponent() const return bounds; } +AffineTransform Component::getTransformToTopLevelComponent() const +{ + AffineTransform toTopLevel; + + for (auto comp = this; comp != nullptr && ! comp->options.onDesktop && ! comp->options.paintAsOffscreenRoot; comp = comp->getParentComponent()) + { + if (comp->isTransformed()) + toTopLevel = toTopLevel.followedBy (comp->getTransform()); + + toTopLevel = toTopLevel.translated (comp->getPosition()); + } + + return toTopLevel; +} + float Component::proportionOfWidth (float proportion) const { return getWidth() * proportion; @@ -1371,18 +1426,26 @@ void Component::internalRepaint (const Rectangle& rect) namespace { -/** Returns the rectangles of @a region that lie inside @a bounds, rounded to whole pixels. +/** Returns the rectangles of @a region that lie inside @a localBounds mapped through @a toTopLevel, + rounded to whole pixels. + + The @a region rectangles live in the coordinate space of the top level component, so @a localBounds + are mapped through the accumulated transform of the component and its ancestors to obtain the area + it really covers there. For untransformed components that mapping is a plain offset, so the + intersection reduces to a bounds check. The returned list never contains overlapping rectangles, so it can be used directly as a clip region. */ -RectangleList intersectRepaintRegion (const RectangleList& region, const Rectangle& bounds) +RectangleList intersectRepaintRegion (const RectangleList& region, const Rectangle& localBounds, const AffineTransform& toTopLevel) { + const auto clipBounds = getTransformedBounds (localBounds, toTopLevel); + RectangleList result; for (const auto& rect : region.getRectangles()) { - const auto clipped = bounds.intersection (rect).roundToInt().to(); + const auto clipped = clipBounds.intersection (rect).roundToInt().to(); if (! clipped.isEmpty()) result.add (clipped); @@ -1561,14 +1624,16 @@ void Component::internalPaint (Graphics& g, const RectangleList& repaintR const auto bounds = getBoundsRelativeToTopLevelComponent(); - auto boundsToRedraw = intersectRepaintRegion (repaintRegions, bounds); + const auto toTopLevel = getTransformToTopLevelComponent(); + + auto boundsToRedraw = intersectRepaintRegion (repaintRegions, getLocalBounds(), toTopLevel); if (boundsToRedraw.isEmpty()) { if (! renderContinuous) return; - boundsToRedraw.addWithoutMerge (bounds); + boundsToRedraw.addWithoutMerge (getTransformedBounds (getLocalBounds(), toTopLevel)); } const auto selfOpacity = (! options.onDesktop && native == nullptr) ? getOpacity() : 1.0f; diff --git a/modules/yup_gui/component/yup_Component.h b/modules/yup_gui/component/yup_Component.h index e639e6e84..013bb8cf3 100644 --- a/modules/yup_gui/component/yup_Component.h +++ b/modules/yup_gui/component/yup_Component.h @@ -1477,6 +1477,7 @@ class YUP_API Component : public MouseListener void sendResized(); bool hasOpaqueChildCoveringArea (const Rectangle& area); + AffineTransform getTransformToTopLevelComponent() const; void paintSubtree (Graphics& g, const Rectangle& drawingArea, const RectangleList& clipRegion, float opacity, bool renderContinuous); void paintChildrenAndOverChildren (Graphics& g, const RectangleList& clipRegion, bool renderContinuous); GpuCanvas::Ptr renderSubtreeOffscreen (GraphicsContext& ctx, float opacity, bool renderContinuous, GpuCanvas::Ptr reuseCanvas = nullptr); diff --git a/modules/yup_gui/native/yup_WindowingUtilities_sdl.cpp b/modules/yup_gui/native/yup_WindowingUtilities_sdl.cpp index 683fd9194..0bbcfd208 100644 --- a/modules/yup_gui/native/yup_WindowingUtilities_sdl.cpp +++ b/modules/yup_gui/native/yup_WindowingUtilities_sdl.cpp @@ -389,7 +389,7 @@ GpuPlatform getGraphicsContextApi (const std::optional& forceContex { GpuPlatform desiredApi; -#if YUP_MAC || YUP_IOS +#if YUP_APPLE #if YUP_RIVE_USE_METAL desiredApi = forceContextApi.value_or (GpuPlatform::Metal); #elif YUP_RIVE_USE_OPENGL diff --git a/modules/yup_gui/native/yup_Windowing_sdl.cpp b/modules/yup_gui/native/yup_Windowing_sdl.cpp index e9a7db1da..971d6f11f 100644 --- a/modules/yup_gui/native/yup_Windowing_sdl.cpp +++ b/modules/yup_gui/native/yup_Windowing_sdl.cpp @@ -156,11 +156,13 @@ SDLComponentNative::SDLComponentNative (Component& component, } } - YUP_MODULE_DBG (GUI_WINDOWING, "SDL: created window: id=" << static_cast (SDL_GetWindowID (window)) << ", window=" << String::toHexString (static_cast (reinterpret_cast (window)))); - SDL_SetWindowFocusable (window, true); SDL_PumpEvents(); + YUP_MODULE_DBG (GUI_WINDOWING, "SDL: created window: id=" << static_cast (SDL_GetWindowID (window)) << ", window=" << String::toHexString (static_cast (reinterpret_cast (window)))); + + windowUnitsPerPoint = getWindowUnitsPerPoint (window); + #if ! YUP_WINDOWS if (parent != nullptr) { @@ -179,7 +181,7 @@ SDLComponentNative::SDLComponentNative (Component& component, } SDL_GL_MakeCurrent (window, windowContext); - SDL_GL_SetSwapInterval (vsyncEnabled ? 1 : 0); + SDL_GL_SetSwapInterval (vsyncEnabled ? SDL_WINDOW_SURFACE_VSYNC_ADAPTIVE : SDL_WINDOW_SURFACE_VSYNC_DISABLED); #if ! YUP_EMSCRIPTEN SDL_GL_SetAttribute (SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 0); @@ -193,6 +195,10 @@ SDLComponentNative::SDLComponentNative (Component& component, YUP_MODULE_DBG (GUI_WINDOWING, "SDL: created GL context"); } + else + { + SDL_SetWindowSurfaceVSync (window, vsyncEnabled ? SDL_WINDOW_SURFACE_VSYNC_ADAPTIVE : SDL_WINDOW_SURFACE_VSYNC_DISABLED); + } // Create the rendering context GraphicsContext::Options graphicsOptions; @@ -410,7 +416,7 @@ void SDLComponentNative::setSize (const Size& newSize) { YUP_MODULE_DBG (GUI_WINDOWING, "SDL: setSize " << currentSize.toString() << " -> " << newSize.toString()); - const auto scale = getWindowUnitsPerPoint (window); + const auto scale = windowUnitsPerPoint; SDL_SetWindowSize (window, jmax (1, roundToInt (newSize.getWidth() * scale)), jmax (1, roundToInt (newSize.getHeight() * scale))); @@ -424,7 +430,7 @@ Size SDLComponentNative::getSize() const if (window != nullptr) SDL_GetWindowSize (window, &width, &height); - const auto scale = getWindowUnitsPerPoint (window); + const auto scale = windowUnitsPerPoint; return { roundToInt (width / scale), roundToInt (height / scale) }; } @@ -442,7 +448,7 @@ void SDLComponentNative::setPosition (const Point& newPosition) { YUP_MODULE_DBG (GUI_WINDOWING, "SDL: setPosition " << currentPosition.toString() << " -> " << newPosition.toString()); - const auto scale = getWindowUnitsPerPoint (window); + const auto scale = windowUnitsPerPoint; SDL_SetWindowPosition (window, roundToInt (newPosition.getX() * scale), roundToInt (newPosition.getY() * scale)); @@ -458,7 +464,7 @@ Point SDLComponentNative::getPosition() const if (window != nullptr) SDL_GetWindowPosition (window, &x, &y); - scale = getWindowUnitsPerPoint (window); + scale = windowUnitsPerPoint; #endif return { roundToInt (x / scale), roundToInt (y / scale) }; @@ -494,7 +500,7 @@ void SDLComponentNative::setBounds (const Rectangle& newBounds) jmax (0, newBounds.getHeight())); #else - const auto scale = getWindowUnitsPerPoint (window); + const auto scale = windowUnitsPerPoint; if (! isFullScreen() && isDecorated()) { @@ -525,7 +531,7 @@ void SDLComponentNative::setBounds (const Rectangle& newBounds) { YUP_MODULE_DBG (GUI_WINDOWING, "SDL: setBounds position " << currentPosition.toString() << " -> " << adjustedBounds.getPosition().toString() << ", requested=" << newBounds.toString()); - const auto positionScale = getWindowUnitsPerPoint (window); + const auto positionScale = windowUnitsPerPoint; SDL_SetWindowPosition (window, roundToInt (adjustedBounds.getX() * positionScale), roundToInt (adjustedBounds.getY() * positionScale)); @@ -547,7 +553,7 @@ Rectangle SDLComponentNative::getSafeAreaBounds() const { if (SDL_Rect safeArea; window != nullptr && SDL_GetWindowSafeArea (window, &safeArea)) { - const auto scale = getWindowUnitsPerPoint (window); + const auto scale = windowUnitsPerPoint; return { roundToInt (safeArea.x / scale), roundToInt (safeArea.y / scale), roundToInt (safeArea.w / scale), @@ -586,7 +592,7 @@ void SDLComponentNative::setFullScreen (bool shouldBeFullScreen) #else SDL_RestoreWindow (window); - const auto scale = getWindowUnitsPerPoint (window); + const auto scale = windowUnitsPerPoint; SDL_SetWindowSize (window, roundToInt (component.getWidth() * scale), roundToInt (component.getHeight() * scale)); @@ -804,7 +810,7 @@ Point SDLComponentNative::getCursorPosition() const SDL_GetMouseState (&x, &y); - return Point (x, y) / getWindowUnitsPerPoint (window); + return Point (x, y) / windowUnitsPerPoint; } //============================================================================== @@ -858,7 +864,7 @@ void SDLComponentNative::updateTextInputRect (Component& component) if (target == nullptr) return; - const auto scale = getWindowUnitsPerPoint (window); + const auto scale = windowUnitsPerPoint; SDL_Rect sdlRect; auto textRect = target->getTextInputRect(); @@ -998,7 +1004,7 @@ void SDLComponentNative::timerCallback() const auto cursorPosition = Point { mouseX - static_cast (windowX), mouseY - static_cast (windowY) } - / getWindowUnitsPerPoint (window); + / windowUnitsPerPoint; if (lastMouseMovePosition != cursorPosition) handleMouseMoveOrDrag (cursorPosition); @@ -1967,22 +1973,24 @@ void SDLComponentNative::handleContentScaleChanged() YUP_MODULE_DBG (GUI_WINDOWING, "SDL: handleContentScaleChanged dpiScale=" << getScaleDpi()); - if (parentWindow == nullptr && getWindowUnitsPerPoint (window) != lastWindowUnitsPerPoint) + if (parentWindow == nullptr) { - MessageManager::callAsync ([self = Ptr (this)] - { - if (self->window == nullptr) - return; + // Refresh the cached units-per-point on this dpi/scale/screen-change event + const auto scale = getWindowUnitsPerPoint (window); - const auto scale = getWindowUnitsPerPoint (self->window); - if (scale == self->lastWindowUnitsPerPoint) - return; + if (scale != windowUnitsPerPoint) + { + windowUnitsPerPoint = scale; - self->lastWindowUnitsPerPoint = scale; + MessageManager::callAsync ([self = Ptr (this)] + { + if (self->window == nullptr) + return; - auto preventBoundsChange = ScopedValueSetter (self->internalBoundsChange, true); - self->setSize (self->screenBounds.getSize()); - }); + auto preventBoundsChange = ScopedValueSetter (self->internalBoundsChange, true); + self->setSize (self->screenBounds.getSize()); + }); + } } component.internalContentScaleChanged (getScaleDpi()); @@ -2178,7 +2186,8 @@ void SDLComponentNative::handleWindowEvent (const SDL_WindowEvent& windowEvent) case SDL_EVENT_WINDOW_MOVED: { YUP_MODULE_DBG (GUI_WINDOWING, "SDL_EVENT_WINDOW_MOVED " << windowEvent.data1 << " " << windowEvent.data2); - const auto scale = getWindowUnitsPerPoint (window); + windowUnitsPerPoint = getWindowUnitsPerPoint (window); + const auto scale = windowUnitsPerPoint; const auto x = roundToInt (windowEvent.data1 / scale); const auto y = roundToInt (windowEvent.data2 / scale); processEvent ([this, x, y] { handleMoved (x, y); }); @@ -2280,7 +2289,7 @@ void SDLComponentNative::handleEvent (SDL_Event* event) case SDL_EVENT_MOUSE_MOTION: { auto cursorPosition = Point { static_cast (event->motion.x), static_cast (event->motion.y) } - / getWindowUnitsPerPoint (window); + / windowUnitsPerPoint; if (event->motion.windowID == SDL_GetWindowID (window)) processEvent ([this, cursorPosition] { handleMouseMoveOrDrag (cursorPosition); }); @@ -2294,7 +2303,7 @@ void SDLComponentNative::handleEvent (SDL_Event* event) YUP_MODULE_DBG (GUI_WINDOWING, "SDL_EVENT_MOUSE_BUTTON_DOWN " << event->button.x << " " << event->button.y); auto cursorPosition = Point { static_cast (event->button.x), static_cast (event->button.y) } - / getWindowUnitsPerPoint (window); + / windowUnitsPerPoint; auto mouseButton = toMouseButton (event->button.button); auto keyModifiers = KeyModifiers (SDL_GetModState()); @@ -2309,7 +2318,7 @@ void SDLComponentNative::handleEvent (SDL_Event* event) YUP_MODULE_DBG (GUI_WINDOWING, "SDL_EVENT_MOUSE_BUTTON_UP " << event->button.x << " " << event->button.y); auto cursorPosition = Point { static_cast (event->button.x), static_cast (event->button.y) } - / getWindowUnitsPerPoint (window); + / windowUnitsPerPoint; auto mouseButton = toMouseButton (event->button.button); auto keyModifiers = KeyModifiers (SDL_GetModState()); @@ -2501,7 +2510,7 @@ void SDLComponentNative::handleEvent (SDL_Event* event) { if (event->drop.windowID == SDL_GetWindowID (window)) { - auto dropPosition = Point (event->drop.x, event->drop.y) / getWindowUnitsPerPoint (window); + auto dropPosition = Point (event->drop.x, event->drop.y) / windowUnitsPerPoint; processEvent ([this, dropPosition] { @@ -2677,7 +2686,7 @@ bool SDLComponentNative::anyNativeWindowContains (Point screenPosition) continue; // Global mouse coordinates are in native window units, bounds are in points - const auto scaledPosition = screenPosition / getWindowUnitsPerPoint (sdlNativeComponent->window); + const auto scaledPosition = screenPosition / sdlNativeComponent->windowUnitsPerPoint; if (sdlNativeComponent->getBounds().to().contains (scaledPosition)) return true; } diff --git a/modules/yup_gui/native/yup_Windowing_sdl.h b/modules/yup_gui/native/yup_Windowing_sdl.h index 03d6132c6..12d2a1ca4 100644 --- a/modules/yup_gui/native/yup_Windowing_sdl.h +++ b/modules/yup_gui/native/yup_Windowing_sdl.h @@ -298,7 +298,7 @@ class SDLComponentNative final int currentContentWidth = 0; int currentContentHeight = 0; - float lastWindowUnitsPerPoint = 1.0f; + float windowUnitsPerPoint = 1.0f; bool internalBoundsChange = false; bool firstDisplay = true; diff --git a/modules/yup_gui/yup_gui.cpp b/modules/yup_gui/yup_gui.cpp index 28c67baa3..c90a33bae 100644 --- a/modules/yup_gui/yup_gui.cpp +++ b/modules/yup_gui/yup_gui.cpp @@ -38,6 +38,11 @@ //============================================================================== +#include +#include + +//============================================================================== + #include #include #include @@ -46,6 +51,41 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include //============================================================================== @@ -58,7 +98,7 @@ //============================================================================== -#if YUP_MAC || YUP_IOS +#if YUP_APPLE #import #import @@ -161,7 +201,10 @@ #include "widgets/yup_ProgressBar.cpp" #include "widgets/yup_ListBoxItem.cpp" #include "widgets/yup_ListBox.cpp" +#include "artboard/yup_ArtboardViewModel.cpp" +#include "artboard/yup_ArtboardViewModelInstance.cpp" #include "artboard/yup_ArtboardFile.cpp" +#include "artboard/yup_ArtboardNode.cpp" #include "artboard/yup_Artboard.cpp" #include "windowing/yup_DocumentWindow.cpp" #include "dialogs/yup_FileChooser.cpp" diff --git a/modules/yup_gui/yup_gui.h b/modules/yup_gui/yup_gui.h index ae10e1d29..4199332c5 100644 --- a/modules/yup_gui/yup_gui.h +++ b/modules/yup_gui/yup_gui.h @@ -177,7 +177,10 @@ #include "widgets/yup_ProgressBar.h" #include "widgets/yup_ListBoxItem.h" #include "widgets/yup_ListBox.h" +#include "artboard/yup_ArtboardViewModel.h" +#include "artboard/yup_ArtboardViewModelInstance.h" #include "artboard/yup_ArtboardFile.h" +#include "artboard/yup_ArtboardNode.h" #include "artboard/yup_Artboard.h" #include "windowing/yup_DocumentWindow.h" #include "dialogs/yup_FileChooser.h" diff --git a/modules/yup_rhi/context/yup_GpuDevice.cpp b/modules/yup_rhi/context/yup_GpuDevice.cpp index 459fbc4c9..e57d32245 100644 --- a/modules/yup_rhi/context/yup_GpuDevice.cpp +++ b/modules/yup_rhi/context/yup_GpuDevice.cpp @@ -25,7 +25,7 @@ namespace yup //============================================================================== std::unique_ptr yup_constructHeadlessGpuDevice (GpuDevice::Options); -#if YUP_RIVE_USE_METAL && (YUP_MAC || YUP_IOS) +#if YUP_RIVE_USE_METAL && YUP_APPLE std::unique_ptr yup_constructMetalGpuDevice (GpuDevice::Options); #endif #if YUP_RIVE_USE_D3D && YUP_WINDOWS @@ -52,7 +52,7 @@ GpuDevice::Ptr GpuDevice::create (GpuPlatform gpuApi, Options options) ctx = yup_constructHeadlessGpuDevice (options); break; -#if YUP_RIVE_USE_METAL && (YUP_MAC || YUP_IOS) +#if YUP_RIVE_USE_METAL && YUP_APPLE case GpuPlatform::Metal: ctx = yup_constructMetalGpuDevice (options); break; diff --git a/modules/yup_rhi/native/yup_GpuComputePass_metal.cpp b/modules/yup_rhi/native/yup_GpuComputePass_metal.cpp index fbf53ac44..2d7a98cf4 100644 --- a/modules/yup_rhi/native/yup_GpuComputePass_metal.cpp +++ b/modules/yup_rhi/native/yup_GpuComputePass_metal.cpp @@ -19,7 +19,7 @@ ============================================================================== */ -#if YUP_RIVE_USE_METAL && (YUP_MAC || YUP_IOS) +#if YUP_RIVE_USE_METAL && YUP_APPLE namespace yup { diff --git a/modules/yup_rhi/native/yup_GpuComputePass_opengl.cpp b/modules/yup_rhi/native/yup_GpuComputePass_opengl.cpp index 24d774f9e..69473022c 100644 --- a/modules/yup_rhi/native/yup_GpuComputePass_opengl.cpp +++ b/modules/yup_rhi/native/yup_GpuComputePass_opengl.cpp @@ -19,7 +19,7 @@ ============================================================================== */ -#if YUP_RIVE_USE_OPENGL || YUP_LINUX || YUP_ANDROID +#if YUP_RHI_USE_GL_COMPUTE namespace yup { diff --git a/modules/yup_rhi/native/yup_GpuComputePipeline_metal.cpp b/modules/yup_rhi/native/yup_GpuComputePipeline_metal.cpp index 2a12e04b8..98c9e327c 100644 --- a/modules/yup_rhi/native/yup_GpuComputePipeline_metal.cpp +++ b/modules/yup_rhi/native/yup_GpuComputePipeline_metal.cpp @@ -19,7 +19,7 @@ ============================================================================== */ -#if YUP_RIVE_USE_METAL && (YUP_MAC || YUP_IOS) +#if YUP_RIVE_USE_METAL && YUP_APPLE namespace yup { diff --git a/modules/yup_rhi/native/yup_GpuComputePipeline_opengl.cpp b/modules/yup_rhi/native/yup_GpuComputePipeline_opengl.cpp index 4c7280b59..e6bcb9ac6 100644 --- a/modules/yup_rhi/native/yup_GpuComputePipeline_opengl.cpp +++ b/modules/yup_rhi/native/yup_GpuComputePipeline_opengl.cpp @@ -19,7 +19,7 @@ ============================================================================== */ -#if YUP_RIVE_USE_OPENGL || YUP_LINUX || YUP_ANDROID +#if YUP_RHI_USE_GL_COMPUTE namespace yup { diff --git a/modules/yup_rhi/rhi/yup_GpuBuffer.cpp b/modules/yup_rhi/rhi/yup_GpuBuffer.cpp index 91ef42c07..28c9aa00c 100644 --- a/modules/yup_rhi/rhi/yup_GpuBuffer.cpp +++ b/modules/yup_rhi/rhi/yup_GpuBuffer.cpp @@ -100,7 +100,7 @@ struct GpuBuffer::Impl size_t byteSize = 0; rive::rcp oreBuffer; -#if YUP_RIVE_USE_METAL && (YUP_MAC || YUP_IOS) +#if YUP_RIVE_USE_METAL && YUP_APPLE id mtlStorageBuffer = nil; #endif @@ -175,7 +175,7 @@ bool GpuBuffer::isValid() const noexcept if (i->type == GpuBufferType::storage) { -#if YUP_RIVE_USE_METAL && (YUP_MAC || YUP_IOS) +#if YUP_RIVE_USE_METAL && YUP_APPLE if (i->mtlStorageBuffer != nil) return true; #endif diff --git a/modules/yup_rhi/rhi/yup_GpuComputePass.cpp b/modules/yup_rhi/rhi/yup_GpuComputePass.cpp index 079304a35..e8f25d252 100644 --- a/modules/yup_rhi/rhi/yup_GpuComputePass.cpp +++ b/modules/yup_rhi/rhi/yup_GpuComputePass.cpp @@ -24,7 +24,7 @@ namespace yup //============================================================================== -#if YUP_RIVE_USE_METAL && (YUP_MAC || YUP_IOS) +#if YUP_RIVE_USE_METAL && YUP_APPLE std::unique_ptr yup_createComputePassImplMetal (GpuDevice&); #endif #if YUP_RIVE_USE_D3D && YUP_WINDOWS @@ -33,7 +33,7 @@ std::unique_ptr yup_createComputePassImplD3D11 (GpuDevice& #if (YUP_EMSCRIPTEN && RIVE_WEBGPU) || YUP_RIVE_USE_DAWN std::unique_ptr yup_createComputePassImplWebGPU (GpuDevice&); #endif -#if YUP_RIVE_USE_OPENGL || YUP_LINUX || YUP_ANDROID +#if YUP_RHI_USE_GL_COMPUTE std::unique_ptr yup_createComputePassImplGL (GpuDevice&); #endif @@ -86,7 +86,7 @@ GpuComputePass GpuComputePass::begin (GpuDevice::Ptr ctx) switch (ctx->getPlatform()) { -#if YUP_RIVE_USE_METAL && (YUP_MAC || YUP_IOS) +#if YUP_RIVE_USE_METAL && YUP_APPLE case GpuPlatform::Metal: pass.impl = yup_createComputePassImplMetal (*ctx); break; @@ -108,7 +108,7 @@ GpuComputePass GpuComputePass::begin (GpuDevice::Ptr ctx) break; #endif -#if YUP_RIVE_USE_OPENGL || YUP_LINUX || YUP_ANDROID +#if YUP_RHI_USE_GL_COMPUTE case GpuPlatform::OpenGL: case GpuPlatform::OpenGLES: pass.impl = yup_createComputePassImplGL (*ctx); diff --git a/modules/yup_rhi/rhi/yup_GpuComputePipeline.cpp b/modules/yup_rhi/rhi/yup_GpuComputePipeline.cpp index 30a645b50..3a8ab5d09 100644 --- a/modules/yup_rhi/rhi/yup_GpuComputePipeline.cpp +++ b/modules/yup_rhi/rhi/yup_GpuComputePipeline.cpp @@ -36,7 +36,7 @@ ResultValue GpuComputePipeline::compile (GpuDevice::Ptr switch (ctx->getPlatform()) { -#if YUP_RIVE_USE_METAL && (YUP_MAC || YUP_IOS) +#if YUP_RIVE_USE_METAL && YUP_APPLE case GpuPlatform::Metal: return yup_constructComputePipelineMetal (*ctx, source, workgroupSize); #endif @@ -54,7 +54,7 @@ ResultValue GpuComputePipeline::compile (GpuDevice::Ptr return yup_constructComputePipelineWebGPU (*ctx, source, workgroupSize); #endif -#if YUP_RIVE_USE_OPENGL || YUP_LINUX || YUP_ANDROID +#if YUP_RHI_USE_GL_COMPUTE case GpuPlatform::OpenGL: case GpuPlatform::OpenGLES: { diff --git a/modules/yup_rhi/yup_rhi.cpp b/modules/yup_rhi/yup_rhi.cpp index 32fb382b0..7e479affc 100644 --- a/modules/yup_rhi/yup_rhi.cpp +++ b/modules/yup_rhi/yup_rhi.cpp @@ -38,7 +38,7 @@ #include #endif -#elif YUP_MAC || YUP_IOS +#elif YUP_APPLE #if YUP_RIVE_USE_METAL #import #import @@ -69,7 +69,7 @@ #include "native/yup_GpuDevice_headless.cpp" -#if YUP_RIVE_USE_METAL && (YUP_MAC || YUP_IOS) +#if YUP_RIVE_USE_METAL && YUP_APPLE #include "native/yup_GpuDevice_metal.cpp" #endif @@ -96,7 +96,7 @@ //============================================================================== #include "rhi/yup_GpuComputePass.cpp" -#if YUP_RIVE_USE_METAL && (YUP_MAC || YUP_IOS) +#if YUP_RIVE_USE_METAL && YUP_APPLE #include "native/yup_GpuComputePipeline_metal.cpp" #include "native/yup_GpuComputePass_metal.cpp" #endif @@ -108,7 +108,7 @@ #include "native/yup_GpuComputePipeline_webgpu.cpp" #include "native/yup_GpuComputePass_webgpu.cpp" #endif -#if YUP_RIVE_USE_OPENGL || YUP_LINUX || YUP_ANDROID +#if YUP_RHI_USE_GL_COMPUTE #include "native/yup_GpuComputePipeline_opengl.cpp" #include "native/yup_GpuComputePass_opengl.cpp" #endif diff --git a/modules/yup_rhi/yup_rhi.h b/modules/yup_rhi/yup_rhi.h index 37d23a426..a9ba9e448 100644 --- a/modules/yup_rhi/yup_rhi.h +++ b/modules/yup_rhi/yup_rhi.h @@ -32,7 +32,7 @@ website: https://github.com/kunitoki/yup license: ISC - dependencies: yup_core yup_shading yup_simd rive_renderer + dependencies: yup_core yup_shading yup_simd rive appleFrameworks: Metal END_YUP_MODULE_DECLARATION @@ -49,13 +49,28 @@ //============================================================================== YUP_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +#include #include -#include +#include #include #include #include YUP_END_IGNORE_WARNINGS_GCC_LIKE +//============================================================================== +/** Config: YUP_RHI_USE_GL_COMPUTE + + Enables the OpenGL compute backend, which needs desktop GL 4.3+ or GLES 3.1+ entry points. + WebGL (and WebGPU on the web) only reach GLES 3.0, so they build without it. +*/ +#ifndef YUP_RHI_USE_GL_COMPUTE +#if (YUP_RIVE_USE_OPENGL && ! YUP_WASM) +#define YUP_RHI_USE_GL_COMPUTE 1 +#else +#define YUP_RHI_USE_GL_COMPUTE 0 +#endif +#endif + //============================================================================== #include #include diff --git a/modules/yup_simd/yup_simd.h b/modules/yup_simd/yup_simd.h index 69cd47f96..d307bdd54 100644 --- a/modules/yup_simd/yup_simd.h +++ b/modules/yup_simd/yup_simd.h @@ -161,7 +161,7 @@ #endif //============================================================================== -#if (YUP_MAC || YUP_IOS) && __has_include() +#if YUP_APPLE && __has_include() #ifndef YUP_USE_VDSP_FRAMEWORK #define YUP_USE_VDSP_FRAMEWORK 1 #endif diff --git a/python/tests/test_yup_gui/test_UnhandledException.py b/python/tests/test_yup_gui/test_UnhandledException.py index d847d209b..a66a5c71d 100644 --- a/python/tests/test_yup_gui/test_UnhandledException.py +++ b/python/tests/test_yup_gui/test_UnhandledException.py @@ -8,7 +8,7 @@ Python with the traceback rebuilt. The macOS dispatch loops were the gap: `yup_MessageManager.cpp` is guarded by -`#if ! (YUP_MAC || YUP_IOS || YUP_WASM)`, and the `.mm` replacements had no C++ catch at all, +`#if ! (YUP_APPLE || YUP_WASM)`, and the `.mm` replacements had no C++ catch at all, only an `@catch (NSException*)` - a disjoint set. So on macOS the exception escaped the loop entirely and `unhandledException` was unreachable. diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 56dece477..c17b4eeb5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -81,6 +81,7 @@ set (target_modules libjpeg libgif libtiff + luau glslang spirv_cross) diff --git a/tests/data/rive/data-binding.riv b/tests/data/rive/data-binding.riv new file mode 100644 index 000000000..be3428cda Binary files /dev/null and b/tests/data/rive/data-binding.riv differ diff --git a/tests/data/rive/game-animation.riv b/tests/data/rive/game-animation.riv new file mode 100644 index 000000000..3a7d0a0d4 Binary files /dev/null and b/tests/data/rive/game-animation.riv differ diff --git a/tests/data/rive/layout-ui.riv b/tests/data/rive/layout-ui.riv new file mode 100644 index 000000000..70f1f917f Binary files /dev/null and b/tests/data/rive/layout-ui.riv differ diff --git a/tests/data/rive/responsive-sliders.riv b/tests/data/rive/responsive-sliders.riv new file mode 100644 index 000000000..440dc6292 Binary files /dev/null and b/tests/data/rive/responsive-sliders.riv differ diff --git a/tests/data/rive/viewmodel-lab.riv b/tests/data/rive/viewmodel-lab.riv new file mode 100644 index 000000000..2d4e7ebe2 Binary files /dev/null and b/tests/data/rive/viewmodel-lab.riv differ diff --git a/tests/data/rive/viewmodel-lab/.gitignore b/tests/data/rive/viewmodel-lab/.gitignore new file mode 100644 index 000000000..b540b79ba --- /dev/null +++ b/tests/data/rive/viewmodel-lab/.gitignore @@ -0,0 +1,2 @@ +build/* +!build/.gitkeep diff --git a/tests/data/rive/viewmodel-lab/assets/check.png b/tests/data/rive/viewmodel-lab/assets/check.png new file mode 100644 index 000000000..213c18b3e Binary files /dev/null and b/tests/data/rive/viewmodel-lab/assets/check.png differ diff --git a/tests/data/rive/viewmodel-lab/assets/cross.png b/tests/data/rive/viewmodel-lab/assets/cross.png new file mode 100644 index 000000000..a0d2d6e3b Binary files /dev/null and b/tests/data/rive/viewmodel-lab/assets/cross.png differ diff --git a/tests/data/rive/viewmodel-lab/assets/karla.ttf b/tests/data/rive/viewmodel-lab/assets/karla.ttf new file mode 100644 index 000000000..c9371b5ba Binary files /dev/null and b/tests/data/rive/viewmodel-lab/assets/karla.ttf differ diff --git a/thirdparty/rive_renderer/source/generated/shaders/glsl.stamp b/tests/data/rive/viewmodel-lab/build/.gitkeep similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/glsl.stamp rename to tests/data/rive/viewmodel-lab/build/.gitkeep diff --git a/tests/data/rive/viewmodel-lab/count_plus_one.luau b/tests/data/rive/viewmodel-lab/count_plus_one.luau new file mode 100644 index 000000000..fa682a364 --- /dev/null +++ b/tests/data/rive/viewmodel-lab/count_plus_one.luau @@ -0,0 +1,43 @@ +-- The `count +1` pill's action: read the `count` view model number, add one, +-- write it back. The host sees the new value. +-- +-- This is a script rather than a `ListenerViewModelChange` with a +-- `DataConverterOperationValue` because a converter on a listener's *read* +-- context does not survive: the read resolves to nothing, so the write lands as +-- the bindable property's default (0) instead of the incremented value. The +-- read itself is fine — a two-context bindable with no converter on either +-- context writes the source's value through, which is the shape to reach for +-- when the arithmetic is a constant or two properties; reach for a script when +-- it is not. +-- +-- See docs/data.md, "Writing a value back from a listener", and +-- docs/luau/protocols.md, "Listener action". + +type CountPlusOne = { + -- Nil until the second init; `context:viewModel()` is nil on the first. + count: Property?, +} + +function init(self: CountPlusOne, context: Context): boolean + local vm = context:viewModel() + if vm == nil then + return true + end + self.count = vm:getNumber('count') + return true +end + +function performAction(self: CountPlusOne, context: ListenerContext) + if self.count == nil then + return + end + self.count.value = self.count.value + 1 +end + +return function(): ListenerAction + return { + count = nil, + init = init, + performAction = performAction, + } +end diff --git a/tests/data/rive/viewmodel-lab/rive.yaml b/tests/data/rive/viewmodel-lab/rive.yaml new file mode 100644 index 000000000..97aab5151 --- /dev/null +++ b/tests/data/rive/viewmodel-lab/rive.yaml @@ -0,0 +1,5 @@ +name: viewmodel-lab +main: ViewModel Lab +logs: + file: build/rive.log + problems: build/problems.log diff --git a/tests/data/rive/viewmodel-lab/scene.rml b/tests/data/rive/viewmodel-lab/scene.rml new file mode 100644 index 000000000..e025abee5 --- /dev/null +++ b/tests/data/rive/viewmodel-lab/scene.rml @@ -0,0 +1,1191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/data/rive/viewmodel-lab/toggle_enabled.luau b/tests/data/rive/viewmodel-lab/toggle_enabled.luau new file mode 100644 index 000000000..3f24d0ed6 --- /dev/null +++ b/tests/data/rive/viewmodel-lab/toggle_enabled.luau @@ -0,0 +1,36 @@ +-- The switch's action: negate the `enabled` view model boolean, so a click in +-- the picture writes back to whoever is editing the instance. +-- +-- A script for the same reason as count_plus_one.luau: a +-- `DataConverterBooleanNegate` on the read context of a `ListenerViewModelChange` +-- drops the read, and the write then lands as the bindable's default (`false`) +-- rather than the negated value — which looks like a toggle that works once. + +type ToggleEnabled = { + -- Nil until the second init; `context:viewModel()` is nil on the first. + enabled: Property?, +} + +function init(self: ToggleEnabled, context: Context): boolean + local vm = context:viewModel() + if vm == nil then + return true + end + self.enabled = vm:getBoolean('enabled') + return true +end + +function performAction(self: ToggleEnabled, context: ListenerContext) + if self.enabled == nil then + return + end + self.enabled.value = not self.enabled.value +end + +return function(): ListenerAction + return { + enabled = nil, + init = init, + performAction = performAction, + } +end diff --git a/tests/mocks/rive_gpu.h b/tests/mocks/rive_gpu.h index d0bbf6e8b..5d61229a8 100644 --- a/tests/mocks/rive_gpu.h +++ b/tests/mocks/rive_gpu.h @@ -23,6 +23,7 @@ #include +#include #include #include #include @@ -47,6 +48,25 @@ struct TestGpuTexture : public rive::gpu::Texture } }; +// ============================================================================== +// Minimal CPU-side render paint: shape paint initialization during file import +// only requires a non-null paint, and these tests never draw, so every paint +// operation is a no-op. +// ============================================================================== + +class TestRenderPaint : public rive::RenderPaint +{ +public: + void style (rive::RenderPaintStyle) override {} + void color (rive::ColorInt) override {} + void thickness (float) override {} + void join (rive::StrokeJoin) override {} + void cap (rive::StrokeCap) override {} + void blendMode (rive::BlendMode) override {} + void shader (rive::rcp) override {} + void invalidateStroke() override {} +}; + // ============================================================================== // Minimal mock rive::Factory — only override the methods that get called. // ============================================================================== @@ -54,6 +74,11 @@ struct TestGpuTexture : public rive::gpu::Texture class MockRiveFactory : public rive::Factory { public: + MockRiveFactory() + { + ON_CALL (*this, makeRenderPaint()).WillByDefault ([] { return rive::rcp (rive::make_rcp()); }); + } + MOCK_METHOD (rive::rcp, makeRenderBuffer, (rive::RenderBufferType, rive::RenderBufferFlags, size_t), (override)); MOCK_METHOD (rive::rcp, makeLinearGradient, (float, float, float, float, const rive::ColorInt*, const float*, size_t), (override)); MOCK_METHOD (rive::rcp, makeRadialGradient, (float, float, float, const rive::ColorInt*, const float*, size_t), (override)); diff --git a/tests/yup_core/yup_DynamicLibrary.cpp b/tests/yup_core/yup_DynamicLibrary.cpp index 797ae1a06..52c1ca4da 100644 --- a/tests/yup_core/yup_DynamicLibrary.cpp +++ b/tests/yup_core/yup_DynamicLibrary.cpp @@ -32,7 +32,7 @@ TEST (DynamicLibraryTests, OpenSystemLibrary) DynamicLibrary lib; // Try to open a system library -#if YUP_MAC || YUP_IOS +#if YUP_APPLE EXPECT_TRUE (lib.open ("/usr/lib/libSystem.dylib")); #elif YUP_LINUX || YUP_ANDROID EXPECT_TRUE (lib.open ("libc.so.6") || lib.open ("libc.so")); @@ -56,7 +56,7 @@ TEST (DynamicLibraryTests, Close) // Close without opening should not crash EXPECT_NO_THROW (lib.close()); -#if YUP_MAC || YUP_IOS +#if YUP_APPLE lib.open ("/usr/lib/libSystem.dylib"); #elif YUP_LINUX || YUP_ANDROID lib.open ("libc.so.6"); @@ -76,7 +76,7 @@ TEST (DynamicLibraryTests, GetFunction) // Getting function from unopened library should return nullptr EXPECT_EQ (lib.getFunction ("some_function"), nullptr); -#if YUP_MAC || YUP_IOS +#if YUP_APPLE if (lib.open ("/usr/lib/libSystem.dylib")) { // Try to get a known function @@ -105,7 +105,7 @@ TEST (DynamicLibraryTests, ReopenAfterClose) { DynamicLibrary lib; -#if YUP_MAC || YUP_IOS +#if YUP_APPLE EXPECT_TRUE (lib.open ("/usr/lib/libSystem.dylib")); lib.close(); EXPECT_TRUE (lib.open ("/usr/lib/libSystem.dylib")); diff --git a/tests/yup_graphics/yup_Font.cpp b/tests/yup_graphics/yup_Font.cpp index f3329c359..565497f94 100644 --- a/tests/yup_graphics/yup_Font.cpp +++ b/tests/yup_graphics/yup_Font.cpp @@ -1108,7 +1108,7 @@ TEST (FontTests, LoadSerifSystemTextFontDoesNotCrash) { auto font = Font::loadSerifSystemTextFont(); -#if YUP_MAC || YUP_IOS +#if YUP_APPLE EXPECT_TRUE (font.wasOk()); #else // On other platforms the presence of system fonts varies; just ensure no crash. @@ -1121,7 +1121,7 @@ TEST (FontTests, LoadMonospaceSystemTextFontDoesNotCrash) { auto font = Font::loadMonospaceSystemTextFont(); -#if YUP_MAC || YUP_IOS +#if YUP_APPLE EXPECT_TRUE (font.wasOk()); #else if (font.wasOk()) diff --git a/tests/yup_gui.cpp b/tests/yup_gui.cpp index 851caa086..3bf0f674f 100644 --- a/tests/yup_gui.cpp +++ b/tests/yup_gui.cpp @@ -24,6 +24,10 @@ #include "yup_gui/yup_Application.cpp" #include "yup_gui/yup_ApplicationTheme.cpp" #include "yup_gui/yup_Artboard.cpp" +#include "yup_gui/yup_ArtboardFile.cpp" +#include "yup_gui/yup_ArtboardNode.cpp" +#include "yup_gui/yup_ArtboardViewModel.cpp" +#include "yup_gui/yup_ArtboardViewModelInstance.cpp" #include "yup_gui/yup_CodeDocument.cpp" #include "yup_gui/yup_CodeEditor.cpp" #include "yup_gui/yup_CodeEditorScheme.cpp" diff --git a/tests/yup_gui/yup_Artboard.cpp b/tests/yup_gui/yup_Artboard.cpp index 32d1a92b4..324bdcdcc 100644 --- a/tests/yup_gui/yup_Artboard.cpp +++ b/tests/yup_gui/yup_Artboard.cpp @@ -22,6 +22,9 @@ #include #include +#include + +#include "../mocks/rive_gpu.h" using namespace yup; @@ -71,8 +74,8 @@ TEST_F (ArtboardTests, DefaultConstruction) { EXPECT_FALSE (artboard->isPaused()); EXPECT_TRUE (artboard->isPausingWhenHidden()); - EXPECT_EQ (Artboard::Layout::contain, artboard->getLayout()); - EXPECT_EQ (Artboard::Alignment::center, artboard->getAlignment()); + EXPECT_EQ (Fitting::scaleToFit, artboard->getFitting()); + EXPECT_EQ (Justification::center, artboard->getJustification()); } TEST_F (ArtboardTests, ConstructWithComponentId) @@ -81,54 +84,6 @@ TEST_F (ArtboardTests, ConstructWithComponentId) EXPECT_EQ (String ("myCustomId"), a.getComponentID()); } -TEST_F (ArtboardTests, SetAndGetLayout) -{ - artboard->setLayout (Artboard::Layout::fill); - EXPECT_EQ (Artboard::Layout::fill, artboard->getLayout()); - - artboard->setLayout (Artboard::Layout::cover); - EXPECT_EQ (Artboard::Layout::cover, artboard->getLayout()); - - artboard->setLayout (Artboard::Layout::fitWidth); - EXPECT_EQ (Artboard::Layout::fitWidth, artboard->getLayout()); - - artboard->setLayout (Artboard::Layout::fitHeight); - EXPECT_EQ (Artboard::Layout::fitHeight, artboard->getLayout()); - - artboard->setLayout (Artboard::Layout::none); - EXPECT_EQ (Artboard::Layout::none, artboard->getLayout()); - - artboard->setLayout (Artboard::Layout::scaleDown); - EXPECT_EQ (Artboard::Layout::scaleDown, artboard->getLayout()); - - artboard->setLayout (Artboard::Layout::layout); - EXPECT_EQ (Artboard::Layout::layout, artboard->getLayout()); -} - -TEST_F (ArtboardTests, SetAndGetAlignment) -{ - artboard->setAlignment (Artboard::Alignment::topLeft); - EXPECT_EQ (Artboard::Alignment::topLeft, artboard->getAlignment()); - - artboard->setAlignment (Artboard::Alignment::topRight); - EXPECT_EQ (Artboard::Alignment::topRight, artboard->getAlignment()); - - artboard->setAlignment (Artboard::Alignment::centerLeft); - EXPECT_EQ (Artboard::Alignment::centerLeft, artboard->getAlignment()); - - artboard->setAlignment (Artboard::Alignment::centerRight); - EXPECT_EQ (Artboard::Alignment::centerRight, artboard->getAlignment()); - - artboard->setAlignment (Artboard::Alignment::bottomLeft); - EXPECT_EQ (Artboard::Alignment::bottomLeft, artboard->getAlignment()); - - artboard->setAlignment (Artboard::Alignment::bottomCenter); - EXPECT_EQ (Artboard::Alignment::bottomCenter, artboard->getAlignment()); - - artboard->setAlignment (Artboard::Alignment::bottomRight); - EXPECT_EQ (Artboard::Alignment::bottomRight, artboard->getAlignment()); -} - TEST_F (ArtboardTests, SetAndGetPaused) { EXPECT_FALSE (artboard->isPaused()); @@ -239,11 +194,30 @@ TEST_F (ArtboardTests, ClearDoesNotCrash) EXPECT_FALSE (artboard->isPaused()); EXPECT_TRUE (artboard->isPausingWhenHidden()); - EXPECT_EQ (Artboard::Layout::contain, artboard->getLayout()); - EXPECT_EQ (Artboard::Alignment::center, artboard->getAlignment()); + EXPECT_EQ (Fitting::scaleToFit, artboard->getFitting()); + EXPECT_EQ (Justification::center, artboard->getJustification()); EXPECT_FLOAT_EQ (0.0f, artboard->durationSeconds()); } +TEST_F (ArtboardTests, PointerHandlersDoNotCrashWithoutFile) +{ + const MouseEvent moved (MouseEvent::noButtons, KeyModifiers(), Point (10.0f, 10.0f)); + const MouseEvent pressed (MouseEvent::leftButton, KeyModifiers(), Point (10.0f, 10.0f)); + + EXPECT_NO_THROW (artboard->mouseEnter (moved)); + EXPECT_NO_THROW (artboard->mouseMove (moved)); + EXPECT_NO_THROW (artboard->mouseDown (pressed)); + EXPECT_NO_THROW (artboard->mouseDrag (pressed)); + EXPECT_NO_THROW (artboard->mouseUp (moved)); + EXPECT_NO_THROW (artboard->mouseExit (moved)); +} + +TEST_F (ArtboardTests, ContentScaleChangedDoesNotCrashWithoutFile) +{ + EXPECT_NO_THROW (artboard->contentScaleChanged (1.0f)); + EXPECT_NO_THROW (artboard->contentScaleChanged (2.0f)); +} + TEST_F (ArtboardTests, RefreshDisplayDoesNotCrashWithoutFile) { EXPECT_NO_THROW (artboard->refreshDisplay (0.016)); @@ -254,8 +228,8 @@ TEST_F (ArtboardTests, ClearResetsState) { artboard->setPaused (true); artboard->shouldPauseWhenHidden (false); - artboard->setLayout (Artboard::Layout::cover); - artboard->setAlignment (Artboard::Alignment::topLeft); + artboard->setFitting (Fitting::scaleToFill); + artboard->setJustification (Justification::topLeft); artboard->clear(); @@ -263,27 +237,2405 @@ TEST_F (ArtboardTests, ClearResetsState) EXPECT_FALSE (artboard->hasBoolInput ("test")); } -TEST_F (ArtboardTests, LayoutEnumValuesAreDistinct) +TEST_F (ArtboardTests, EveryFittingValueRoundTrips) +{ + // Including the four Rive has no equivalent for: they degrade to a uniform + // fit internally, but the artboard still reports back what was asked for. + const Fitting all[] = { + Fitting::none, + Fitting::scaleToFit, + Fitting::fitWidth, + Fitting::fitHeight, + Fitting::scaleToFill, + Fitting::fill, + Fitting::tile, + Fitting::centerCrop, + Fitting::centerInside, + Fitting::stretchWidth, + Fitting::stretchHeight + }; + + for (const auto fitting : all) + { + artboard->setFitting (fitting); + EXPECT_EQ (fitting, artboard->getFitting()); + } + + artboard->setFitting (std::nullopt); + EXPECT_FALSE (artboard->getFitting().has_value()); +} + +TEST_F (ArtboardTests, EveryJustificationCornerRoundTrips) +{ + const Justification all[] = { + Justification::topLeft, + Justification::centerTop, + Justification::topRight, + Justification::centerLeft, + Justification::center, + Justification::centerRight, + Justification::bottomLeft, + Justification::centerBottom, + Justification::bottomRight + }; + + for (const auto justification : all) + { + artboard->setJustification (justification); + EXPECT_EQ (justification, artboard->getJustification()); + } +} + +TEST_F (ArtboardTests, PartialJustificationFlagsAreAccepted) +{ + // Justification is a bitfield, so it admits states with only one axis set. + // Those are accepted verbatim; the unset axis is centered when mapping to Rive. + artboard->setJustification (Justification::left); + EXPECT_EQ (Justification (Justification::left), artboard->getJustification()); + + artboard->setJustification (Justification::bottom); + EXPECT_EQ (Justification (Justification::bottom), artboard->getJustification()); + + EXPECT_NO_THROW (artboard->setBounds (0.0f, 0.0f, 100.0f, 100.0f)); +} + +//============================================================================== +// Artboard layout tests (require tests/data/rive/layout-ui.riv) +// +// layout-ui.riv's default artboard ("Wireframe", 669x509) is authored with +// Rive's layout engine: a "root" LayoutComponent holding "topbar", "main", +// "keyboard_slot" and "statusbar", with "main" holding "browser_slot", +// "center" and "inspector". Both nodes used here are LayoutComponents that +// reflow when the artboard is resized, which is what these tests drive. +//============================================================================== + +namespace +{ + +// Names of the layout nodes expected in tests/data/rive/layout-ui.riv. +// topbar spans the width of root; inspector is a column inside main. +constexpr const char* kHeaderNodeName = "topbar"; +constexpr const char* kPanelNodeName = "inspector"; + +const File getTestDataRiveDirectory() +{ + // Try source-relative path first (works on desktop builds) + auto dir = File (__FILE__) + .getParentDirectory() + .getParentDirectory() + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir; + + dir = File::getCurrentWorkingDirectory() + .getParentDirectory() + .getParentDirectory() + .getParentDirectory() + .getChildFile ("tests") + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir; + + return File ("/data/rive"); +} + +} // namespace + +class ArtboardLayoutTests : public ::testing::Test +{ +protected: + void SetUp() override + { + const auto file = getTestDataRiveDirectory().getChildFile ("layout-ui.riv"); + if (! file.existsAsFile()) + { + GTEST_SKIP() << "Missing test asset: tests/data/rive/layout-ui.riv"; + return; + } + + auto result = ArtboardFile::load (file, factory); + if (result.failed()) + { + GTEST_SKIP() << "Failed to load test asset: " << result.getErrorMessage(); + return; + } + + artboardFile = result.getValue(); + artboard = std::make_unique ("testArtboard", artboardFile); + artboard->setFitting (std::nullopt); + artboard->setBounds (0.0f, 0.0f, 400.0f, 300.0f); + + // Layout bounds are animated over frames, so advance until they settle. + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + } + + ::testing::NiceMock factory; + std::shared_ptr artboardFile; + std::unique_ptr artboard; +}; + +//============================================================================== +// Model A — node bounds listeners +//============================================================================== + +TEST_F (ArtboardLayoutTests, GetNodeBoundsReturnsEmptyForUnknownName) +{ + EXPECT_TRUE (artboard->getNodeBounds ("nonexistent").isEmpty()); +} + +TEST_F (ArtboardLayoutTests, GetNodeBoundsReturnsNonEmptyForKnownNode) +{ + const auto bounds = artboard->getNodeBounds (kHeaderNodeName); + EXPECT_FALSE (bounds.isEmpty()); +} + +TEST_F (ArtboardLayoutTests, NodeBoundsListenerFiresOnReflow) +{ + int callCount = 0; + Rectangle lastBounds; + + artboard->setNodeBoundsListener (kHeaderNodeName, + [&] (Artboard&, const String&, const ArtboardNode::Ptr& node) + { + ++callCount; + lastBounds = node->getBounds(); + }); + + // Resize the artboard, which triggers a layout reflow that moves the node. + artboard->setBounds (0.0f, 0.0f, 600.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_GT (callCount, 0); + EXPECT_FALSE (lastBounds.isEmpty()); +} + +TEST_F (ArtboardLayoutTests, NodeBoundsListenerDoesNotFireWhenUnchanged) +{ + int callCount = 0; + + artboard->setNodeBoundsListener (kHeaderNodeName, + [&] (Artboard&, const String&, const ArtboardNode::Ptr&) + { + ++callCount; + }); + + for (int i = 0; i < 5; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_EQ (0, callCount); +} + +TEST_F (ArtboardLayoutTests, EmptyCallbackRemovesListener) +{ + int callCount = 0; + + artboard->setNodeBoundsListener (kHeaderNodeName, + [&] (Artboard&, const String&, const ArtboardNode::Ptr&) + { + ++callCount; + }); + + artboard->setNodeBoundsListener (kHeaderNodeName, {}); + + artboard->setBounds (0.0f, 0.0f, 600.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_EQ (0, callCount); +} + +TEST_F (ArtboardLayoutTests, ClearNodeBoundsListenerStopsNotifications) +{ + int callCount = 0; + + artboard->setNodeBoundsListener (kHeaderNodeName, + [&] (Artboard&, const String&, const ArtboardNode::Ptr&) + { + ++callCount; + }); + + artboard->clearNodeBoundsListener (kHeaderNodeName); + + artboard->setBounds (0.0f, 0.0f, 600.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_EQ (0, callCount); +} + +TEST_F (ArtboardLayoutTests, ClearAllNodeBoundsListenersRemovesAll) +{ + int callCount = 0; + + artboard->setNodeBoundsListener (kHeaderNodeName, + [&] (Artboard&, const String&, const ArtboardNode::Ptr&) + { + ++callCount; + }); + + artboard->setNodeBoundsListener (kPanelNodeName, + [&] (Artboard&, const String&, const ArtboardNode::Ptr&) + { + ++callCount; + }); + + artboard->clearAllNodeBoundsListeners(); + + artboard->setBounds (0.0f, 0.0f, 600.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_EQ (0, callCount); +} + +//============================================================================== +// Model B — attached components +//============================================================================== + +TEST_F (ArtboardLayoutTests, AttachComponentToNodeReturnsFalseForUnknownNode) +{ + Component component ("test"); + + EXPECT_FALSE (artboard->attachComponentToNode ("nonexistent", &component)); +} + +TEST_F (ArtboardLayoutTests, AttachComponentToNodeReturnsFalseForNullComponent) +{ + EXPECT_FALSE (artboard->attachComponentToNode (kHeaderNodeName, nullptr)); +} + +TEST_F (ArtboardLayoutTests, AttachComponentToNodePositionsComponentImmediately) +{ + Component component ("test"); + + EXPECT_TRUE (artboard->attachComponentToNode (kPanelNodeName, &component)); + + const auto expected = artboard->getNodeBounds (kPanelNodeName); + const auto actual = component.getBounds(); + + EXPECT_FLOAT_EQ (expected.getX(), actual.getX()); + EXPECT_FLOAT_EQ (expected.getY(), actual.getY()); + EXPECT_FLOAT_EQ (expected.getWidth(), actual.getWidth()); + EXPECT_FLOAT_EQ (expected.getHeight(), actual.getHeight()); +} + +TEST_F (ArtboardLayoutTests, AttachedComponentFollowsNodeOnReflow) +{ + Component component ("test"); + + ASSERT_TRUE (artboard->attachComponentToNode (kPanelNodeName, &component)); + + artboard->setBounds (0.0f, 0.0f, 600.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + const auto nodeBounds = artboard->getNodeBounds (kPanelNodeName); + const auto componentBounds = component.getBounds(); + + EXPECT_FLOAT_EQ (nodeBounds.getX(), componentBounds.getX()); + EXPECT_FLOAT_EQ (nodeBounds.getY(), componentBounds.getY()); + EXPECT_FLOAT_EQ (nodeBounds.getWidth(), componentBounds.getWidth()); + EXPECT_FLOAT_EQ (nodeBounds.getHeight(), componentBounds.getHeight()); +} + +TEST_F (ArtboardLayoutTests, DetachComponentFromNodeStopsUpdates) +{ + Component component ("test"); + + ASSERT_TRUE (artboard->attachComponentToNode (kPanelNodeName, &component)); + + Component other ("other"); + EXPECT_FALSE (artboard->detachComponentFromNode (kPanelNodeName, &other)); + + EXPECT_TRUE (artboard->detachComponentFromNode (kPanelNodeName, &component)); + + const auto boundsBefore = component.getBounds(); + + artboard->setBounds (0.0f, 0.0f, 600.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_TRUE (component.getBounds() == boundsBefore); +} + +TEST_F (ArtboardLayoutTests, DetachAllComponentsStopsUpdates) +{ + Component header ("headerComponent"); + Component panel ("panelComponent"); + + ASSERT_TRUE (artboard->attachComponentToNode (kHeaderNodeName, &header)); + ASSERT_TRUE (artboard->attachComponentToNode (kPanelNodeName, &panel)); + + artboard->detachAllComponents(); + + const auto headerBoundsBefore = header.getBounds(); + const auto panelBoundsBefore = panel.getBounds(); + + artboard->setBounds (0.0f, 0.0f, 600.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_TRUE (header.getBounds() == headerBoundsBefore); + EXPECT_TRUE (panel.getBounds() == panelBoundsBefore); +} + +TEST_F (ArtboardLayoutTests, AttachedComponentTracksPositionWithoutResize) +{ + Component component ("test"); + + yup::Artboard::NodeAttachmentOptions options; + options.mode = yup::Artboard::NodeAttachmentOptions::Mode::trackPosition; + + ASSERT_TRUE (artboard->attachComponentToNode (kPanelNodeName, &component, options)); + + // Give the component an explicit size; only its position should follow the node. + component.setBounds (0.0f, 0.0f, 50.0f, 30.0f); + + artboard->setBounds (0.0f, 0.0f, 600.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + // The size must never be touched by the attachment... + EXPECT_FLOAT_EQ (50.0f, component.getWidth()); + EXPECT_FLOAT_EQ (30.0f, component.getHeight()); + + // ...and the top-left must follow the node's new bounds origin. + const auto nodeBounds = artboard->getNodeBounds (kPanelNodeName); + EXPECT_FLOAT_EQ (nodeBounds.getX(), component.getX()); + EXPECT_FLOAT_EQ (nodeBounds.getY(), component.getY()); +} + +TEST_F (ArtboardLayoutTests, TrackPositionWithCenteredPivot) +{ + Component component ("test"); + + yup::Artboard::NodeAttachmentOptions options; + options.mode = yup::Artboard::NodeAttachmentOptions::Mode::trackPosition; + options.pivot = yup::Justification::center; + + ASSERT_TRUE (artboard->attachComponentToNode (kPanelNodeName, &component, options)); + + // Give the component an explicit size; its center should follow the node's + // top-left (the default anchor). + component.setBounds (0.0f, 0.0f, 50.0f, 30.0f); + + artboard->setBounds (0.0f, 0.0f, 600.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_FLOAT_EQ (50.0f, component.getWidth()); + EXPECT_FLOAT_EQ (30.0f, component.getHeight()); + + const auto nodeBounds = artboard->getNodeBounds (kPanelNodeName); + EXPECT_FLOAT_EQ (nodeBounds.getX(), component.getBounds().getCenterX()); + EXPECT_FLOAT_EQ (nodeBounds.getY(), component.getBounds().getCenterY()); +} + +TEST_F (ArtboardLayoutTests, TrackPositionWithCenteredAnchor) +{ + Component component ("test"); + + yup::Artboard::NodeAttachmentOptions options; + options.mode = yup::Artboard::NodeAttachmentOptions::Mode::trackPosition; + options.anchor = yup::Justification::center; + + ASSERT_TRUE (artboard->attachComponentToNode (kPanelNodeName, &component, options)); + + // With the default pivot (top-left) the component's top-left corner should + // sit on the center of the node's bounds. + component.setBounds (0.0f, 0.0f, 50.0f, 30.0f); + + artboard->setBounds (0.0f, 0.0f, 600.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_FLOAT_EQ (50.0f, component.getWidth()); + EXPECT_FLOAT_EQ (30.0f, component.getHeight()); + + const auto nodeBounds = artboard->getNodeBounds (kPanelNodeName); + EXPECT_FLOAT_EQ (nodeBounds.getCenterX(), component.getX()); + EXPECT_FLOAT_EQ (nodeBounds.getCenterY(), component.getY()); +} + +TEST_F (ArtboardLayoutTests, TrackPositionWithPivotCenterAndBottomRightAnchor) { - EXPECT_EQ (static_cast (Artboard::Layout::fill), 0); - EXPECT_NE (Artboard::Layout::fill, Artboard::Layout::contain); - EXPECT_NE (Artboard::Layout::contain, Artboard::Layout::cover); - EXPECT_NE (Artboard::Layout::cover, Artboard::Layout::fitWidth); - EXPECT_NE (Artboard::Layout::fitWidth, Artboard::Layout::fitHeight); - EXPECT_NE (Artboard::Layout::fitHeight, Artboard::Layout::none); - EXPECT_NE (Artboard::Layout::none, Artboard::Layout::scaleDown); - EXPECT_NE (Artboard::Layout::scaleDown, Artboard::Layout::layout); + Component component ("test"); + + yup::Artboard::NodeAttachmentOptions options; + options.mode = yup::Artboard::NodeAttachmentOptions::Mode::trackPosition; + options.pivot = yup::Justification::center; + options.anchor = yup::Justification::bottomRight; + + ASSERT_TRUE (artboard->attachComponentToNode (kPanelNodeName, &component, options)); + + component.setBounds (0.0f, 0.0f, 50.0f, 30.0f); + + artboard->setBounds (0.0f, 0.0f, 600.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + // The component's center (pivot) should sit on the node bounds' bottom-right. + const auto nodeBounds = artboard->getNodeBounds (kPanelNodeName); + EXPECT_FLOAT_EQ (nodeBounds.getRight(), component.getBounds().getCenterX()); + EXPECT_FLOAT_EQ (nodeBounds.getBottom(), component.getBounds().getCenterY()); +} + +TEST_F (ArtboardLayoutTests, TrackPositionAnchorSupportsAllJustifications) +{ + struct Case + { + yup::Justification anchor; + float xFactor; + float yFactor; + }; + + const Case cases[] = { + { yup::Justification::topLeft, 0.0f, 0.0f }, + { yup::Justification::centerTop, 0.5f, 0.0f }, + { yup::Justification::topRight, 1.0f, 0.0f }, + { yup::Justification::centerLeft, 0.0f, 0.5f }, + { yup::Justification::center, 0.5f, 0.5f }, + { yup::Justification::centerRight, 1.0f, 0.5f }, + { yup::Justification::bottomLeft, 0.0f, 1.0f }, + { yup::Justification::centerBottom, 0.5f, 1.0f }, + { yup::Justification::bottomRight, 1.0f, 1.0f }, + }; + + for (const auto& testCase : cases) + { + Component component ("test"); + + yup::Artboard::NodeAttachmentOptions options; + options.mode = yup::Artboard::NodeAttachmentOptions::Mode::trackPosition; + options.anchor = testCase.anchor; + + // trackPosition keeps the component's own size, which is what the pivot + // is measured against. + component.setBounds (0.0f, 0.0f, 50.0f, 30.0f); + + // With the default pivot (top-left) the component's top-left corner + // should land on the requested point of the node bounds. + ASSERT_TRUE (artboard->attachComponentToNode (kPanelNodeName, &component, options)); + + artboard->setBounds (0.0f, 0.0f, 600.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + const auto nodeBounds = artboard->getNodeBounds (kPanelNodeName); + const auto expectedX = nodeBounds.getX() + nodeBounds.getWidth() * testCase.xFactor; + const auto expectedY = nodeBounds.getY() + nodeBounds.getHeight() * testCase.yFactor; + + EXPECT_FLOAT_EQ (expectedX, component.getX()) << "anchor x factor " << testCase.xFactor; + EXPECT_FLOAT_EQ (expectedY, component.getY()) << "anchor y factor " << testCase.yFactor; + } +} + +TEST_F (ArtboardLayoutTests, ResizingAnAttachedComponentRederivesItsPosition) +{ + Component component ("test"); + + yup::Artboard::NodeAttachmentOptions options; + options.mode = yup::Artboard::NodeAttachmentOptions::Mode::trackPosition; + options.pivot = yup::Justification::center; + options.anchor = yup::Justification::center; + + component.setBounds (0.0f, 0.0f, 50.0f, 30.0f); + ASSERT_TRUE (artboard->attachComponentToNode (kPanelNodeName, &component, options)); + + const auto nodeBounds = artboard->getNodeBounds (kPanelNodeName); + EXPECT_FLOAT_EQ (nodeBounds.getCenterX(), component.getBounds().getCenterX()); + EXPECT_FLOAT_EQ (nodeBounds.getCenterY(), component.getBounds().getCenterY()); + + // The pivot is measured against the component's own size, so resizing it + // must re-derive the position straight away rather than waiting for the node + // to move, which for a static layout would be never. + component.setSize (120.0f, 80.0f); + + EXPECT_FLOAT_EQ (120.0f, component.getWidth()); + EXPECT_FLOAT_EQ (80.0f, component.getHeight()); + EXPECT_FLOAT_EQ (nodeBounds.getCenterX(), component.getBounds().getCenterX()); + EXPECT_FLOAT_EQ (nodeBounds.getCenterY(), component.getBounds().getCenterY()); +} + +TEST_F (ArtboardLayoutTests, ResizingAFillNodeComponentIsOverwrittenByTheNode) +{ + Component component ("test"); + + // fillNode gives the node ownership of the size, so a resize by the owner + // is put back. + ASSERT_TRUE (artboard->attachComponentToNode (kPanelNodeName, &component)); + + const auto nodeBounds = artboard->getNodeBounds (kPanelNodeName); + + component.setSize (17.0f, 13.0f); + + EXPECT_FLOAT_EQ (nodeBounds.getWidth(), component.getWidth()); + EXPECT_FLOAT_EQ (nodeBounds.getHeight(), component.getHeight()); +} + +TEST_F (ArtboardLayoutTests, ResizingADetachedComponentIsIgnored) +{ + Component component ("test"); + + ASSERT_TRUE (artboard->attachComponentToNode (kPanelNodeName, &component)); + ASSERT_TRUE (artboard->detachComponentFromNode (kPanelNodeName, &component)); + + component.setBounds (5.0f, 7.0f, 50.0f, 30.0f); + + EXPECT_FLOAT_EQ (5.0f, component.getX()); + EXPECT_FLOAT_EQ (7.0f, component.getY()); + EXPECT_FLOAT_EQ (50.0f, component.getWidth()); + EXPECT_FLOAT_EQ (30.0f, component.getHeight()); +} + +TEST_F (ArtboardLayoutTests, ApplyTransformKeepsBoundsForUnrotatedNode) +{ + Component component ("test"); + + yup::Artboard::NodeAttachmentOptions options; + options.applyTransform = true; + + ASSERT_TRUE (artboard->attachComponentToNode (kPanelNodeName, &component, options)); + + artboard->setBounds (0.0f, 0.0f, 600.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + // For an unrotated node the applied transform is the identity, so the bounds + // must still track the node exactly and the transform must be identity. + EXPECT_TRUE (component.getTransform().isIdentity()); + + const auto nodeBounds = artboard->getNodeBounds (kPanelNodeName); + EXPECT_FLOAT_EQ (nodeBounds.getX(), component.getX()); + EXPECT_FLOAT_EQ (nodeBounds.getY(), component.getY()); + EXPECT_FLOAT_EQ (nodeBounds.getWidth(), component.getWidth()); + EXPECT_FLOAT_EQ (nodeBounds.getHeight(), component.getHeight()); +} + +TEST_F (ArtboardLayoutTests, ModelAAndModelBOnSameNodeBothUpdate) +{ + Component component ("test"); + int callCount = 0; + + ASSERT_TRUE (artboard->attachComponentToNode (kHeaderNodeName, &component)); + + artboard->setNodeBoundsListener (kHeaderNodeName, + [&] (Artboard&, const String&, const ArtboardNode::Ptr&) + { + ++callCount; + }); + + artboard->setBounds (0.0f, 0.0f, 600.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_GT (callCount, 0); + + const auto nodeBounds = artboard->getNodeBounds (kHeaderNodeName); + const auto componentBounds = component.getBounds(); + + EXPECT_FLOAT_EQ (nodeBounds.getX(), componentBounds.getX()); + EXPECT_FLOAT_EQ (nodeBounds.getY(), componentBounds.getY()); + EXPECT_FLOAT_EQ (nodeBounds.getWidth(), componentBounds.getWidth()); + EXPECT_FLOAT_EQ (nodeBounds.getHeight(), componentBounds.getHeight()); } -TEST_F (ArtboardTests, AlignmentEnumValuesAreDistinct) +TEST_F (ArtboardLayoutTests, NodeBoundsListenerReceivesStableCachedHandle) { - EXPECT_EQ (static_cast (Artboard::Alignment::topLeft), 0); - EXPECT_NE (Artboard::Alignment::topLeft, Artboard::Alignment::topCenter); - EXPECT_NE (Artboard::Alignment::topCenter, Artboard::Alignment::topRight); - EXPECT_NE (Artboard::Alignment::topRight, Artboard::Alignment::centerLeft); - EXPECT_NE (Artboard::Alignment::centerLeft, Artboard::Alignment::center); - EXPECT_NE (Artboard::Alignment::center, Artboard::Alignment::centerRight); - EXPECT_NE (Artboard::Alignment::centerRight, Artboard::Alignment::bottomLeft); - EXPECT_NE (Artboard::Alignment::bottomLeft, Artboard::Alignment::bottomCenter); - EXPECT_NE (Artboard::Alignment::bottomCenter, Artboard::Alignment::bottomRight); + int callCount = 0; + ArtboardNode::Ptr firstHandle; + + artboard->setNodeBoundsListener (kHeaderNodeName, + [&] (Artboard&, const String&, const ArtboardNode::Ptr& node) + { + ++callCount; + if (firstHandle == nullptr) + firstHandle = node; + else + EXPECT_EQ (firstHandle.get(), node.get()); + }); + + artboard->setBounds (0.0f, 0.0f, 600.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_GT (callCount, 0); + + ASSERT_NE (nullptr, firstHandle.get()); + EXPECT_TRUE (firstHandle->isValid()); + EXPECT_FALSE (firstHandle->getBounds().isEmpty()); + + // The callback receives the same cached handle that findNode hands out. + EXPECT_EQ (firstHandle.get(), artboard->findNode (kHeaderNodeName).get()); +} + +TEST_F (ArtboardLayoutTests, ClearInvalidatesNodeLookup) +{ + Component component ("test"); + + artboard->clear(); + + EXPECT_TRUE (artboard->getNodeBounds (kHeaderNodeName).isEmpty()); + EXPECT_EQ (nullptr, artboard->findNode (kHeaderNodeName).get()); + EXPECT_FALSE (artboard->attachComponentToNode (kHeaderNodeName, &component)); +} + +//============================================================================== +// Artboard::setFile named artboard selection (requires tests/data/rive/responsive-sliders.riv) +//============================================================================== + +class ArtboardSetFileNamedTests : public ::testing::Test +{ +protected: + void SetUp() override + { + const auto file = getTestDataRiveDirectory().getChildFile ("responsive-sliders.riv"); + if (! file.existsAsFile()) + { + GTEST_SKIP() << "Missing test asset: tests/data/rive/responsive-sliders.riv"; + return; + } + + auto result = ArtboardFile::load (file, factory); + if (result.failed()) + { + GTEST_SKIP() << "Failed to load test asset: " << result.getErrorMessage(); + return; + } + + artboardFile = result.getValue(); + artboard = std::make_unique ("testArtboard"); + } + + ::testing::NiceMock factory; + std::shared_ptr artboardFile; + std::unique_ptr artboard; +}; + +TEST_F (ArtboardSetFileNamedTests, SetFileWithoutNameLoadsDefaultArtboard) +{ + artboard->setFile (artboardFile); + + EXPECT_EQ (String ("Main"), artboard->getViewModelName()); +} + +TEST_F (ArtboardSetFileNamedTests, SetFileWithNameLoadsNamedArtboard) +{ + artboard->setFile (artboardFile, "Slider_instance"); + + EXPECT_EQ (String ("Slider_instance"), artboard->getViewModelName()); +} + +TEST_F (ArtboardSetFileNamedTests, SetFileWithUnknownNameLeavesNothingLoaded) +{ + artboard->setFile (artboardFile, "DoesNotExist"); + + EXPECT_FLOAT_EQ (0.0f, artboard->durationSeconds()); +} + +//============================================================================== +// ArtboardNode +//============================================================================== + +TEST_F (ArtboardTests, FindNodeReturnsNullWithoutFile) +{ + EXPECT_EQ (nullptr, artboard->findNode (kHeaderNodeName).get()); +} + +TEST_F (ArtboardLayoutTests, FindNodeReturnsNullForUnknownName) +{ + EXPECT_EQ (nullptr, artboard->findNode ("nonexistent").get()); +} + +TEST_F (ArtboardLayoutTests, FindNodeReturnsValidHandleForKnownNode) +{ + auto node = artboard->findNode (kHeaderNodeName); + ASSERT_NE (nullptr, node.get()); + + EXPECT_TRUE (node->isValid()); + EXPECT_EQ (String (kHeaderNodeName), node->getName()); + EXPECT_NE (0, node->getTypeKey()); + EXPECT_FALSE (node->getTypeName().isEmpty()); +} + +TEST_F (ArtboardLayoutTests, FindNodeWorksOnConstArtboard) +{ + const auto& constArtboard = *artboard; + + auto node = constArtboard.findNode (kHeaderNodeName); + ASSERT_NE (nullptr, node.get()); + EXPECT_TRUE (node->isValid()); +} + +TEST_F (ArtboardLayoutTests, NodeBoundsMatchGetNodeBounds) +{ + auto node = artboard->findNode (kHeaderNodeName); + ASSERT_NE (nullptr, node.get()); + + const auto nodeBounds = node->getBounds(); + const auto artboardBounds = artboard->getNodeBounds (kHeaderNodeName); + + EXPECT_FALSE (nodeBounds.isEmpty()); + EXPECT_FLOAT_EQ (artboardBounds.getX(), nodeBounds.getX()); + EXPECT_FLOAT_EQ (artboardBounds.getY(), nodeBounds.getY()); + EXPECT_FLOAT_EQ (artboardBounds.getWidth(), nodeBounds.getWidth()); + EXPECT_FLOAT_EQ (artboardBounds.getHeight(), nodeBounds.getHeight()); +} + +TEST_F (ArtboardLayoutTests, IsLayoutMatchesLayoutTypeKey) +{ + auto node = artboard->findNode (kHeaderNodeName); + ASSERT_NE (nullptr, node.get()); + + EXPECT_EQ (node->getTypeKey() == rive::LayoutComponentBase::typeKey, node->isLayout()); +} + +TEST_F (ArtboardLayoutTests, ParentAndChildrenTraversal) +{ + auto node = artboard->findNode (kHeaderNodeName); + ASSERT_NE (nullptr, node.get()); + + auto parent = node->getParent(); + ASSERT_NE (nullptr, parent.get()); + EXPECT_TRUE (parent->isValid()); + + // topbar sits inside the "root" layout, not directly under the artboard. + EXPECT_EQ (String ("root"), parent->getName()); + EXPECT_TRUE (parent->isLayout()); + + auto siblings = parent->getChildren(); + bool found = false; + for (const auto& sibling : siblings) + { + if (sibling != nullptr && sibling->getName() == String (kHeaderNodeName)) + { + found = true; + break; + } + } + + EXPECT_TRUE (found); +} + +TEST_F (ArtboardLayoutTests, HandleRemainsUsableAfterCopy) +{ + auto node = artboard->findNode (kHeaderNodeName); + ASSERT_NE (nullptr, node.get()); + + auto copy = node; + EXPECT_EQ (node.get(), copy.get()); + EXPECT_TRUE (copy->isValid()); + EXPECT_EQ (String (kHeaderNodeName), copy->getName()); +} + +TEST_F (ArtboardLayoutTests, ClearInvalidatesHandles) +{ + auto node = artboard->findNode (kHeaderNodeName); + ASSERT_NE (nullptr, node.get()); + EXPECT_TRUE (node->isValid()); + + artboard->clear(); + + EXPECT_FALSE (node->isValid()); + EXPECT_TRUE (node->getName().isEmpty()); + EXPECT_EQ (0, node->getTypeKey()); + EXPECT_TRUE (node->getTypeName().isEmpty()); + EXPECT_FALSE (node->isLayout()); + EXPECT_TRUE (node->getBounds().isEmpty()); + EXPECT_TRUE (node->getLocalTransform().isIdentity()); + EXPECT_TRUE (node->getWorldTransform().isIdentity()); + EXPECT_EQ (nullptr, node->getParent().get()); + EXPECT_TRUE (node->getChildren().isEmpty()); +} + +TEST_F (ArtboardLayoutTests, NodeTransformsAreFinite) +{ + auto node = artboard->findNode (kHeaderNodeName); + ASSERT_NE (nullptr, node.get()); + + const auto local = node->getLocalTransform(); + EXPECT_TRUE (std::isfinite (local.getScaleX())); + EXPECT_TRUE (std::isfinite (local.getShearX())); + EXPECT_TRUE (std::isfinite (local.getTranslateX())); + EXPECT_TRUE (std::isfinite (local.getShearY())); + EXPECT_TRUE (std::isfinite (local.getScaleY())); + EXPECT_TRUE (std::isfinite (local.getTranslateY())); + + const auto world = node->getWorldTransform(); + EXPECT_TRUE (std::isfinite (world.getScaleX())); + EXPECT_TRUE (std::isfinite (world.getShearX())); + EXPECT_TRUE (std::isfinite (world.getTranslateX())); + EXPECT_TRUE (std::isfinite (world.getShearY())); + EXPECT_TRUE (std::isfinite (world.getScaleY())); + EXPECT_TRUE (std::isfinite (world.getTranslateY())); +} + +//============================================================================== + +//============================================================================== +// Artboard and ArtboardNode against tests/data/rive/game-animation.riv +// +// game-animation.riv's default artboard ("New Artboard") contains a single +// named nested artboard node ("7ZRGY5S 5"); the animated character parts live +// inside that nested instance and are not reachable through the outer +// artboard's name-based APIs. These tests drive Artboard/ArtboardNode through +// the artboard root and the nested node: identity, bounds/transform +// consistency, handle caching, epoch invalidation (clear / setFile / destroy), +// bounds listeners and attached components. Resizing changes the view (fit) +// transform, which deterministically moves both the root and the nested node. +//============================================================================== + +namespace +{ +constexpr const char* kGameRootArtboardNodeName = "New Artboard"; +constexpr const char* kGameNestedArtboardNodeName = "7ZRGY5S 5"; +} // namespace + +class ArtboardGameAnimationTests : public ::testing::Test +{ +protected: + void SetUp() override + { + const auto file = getTestDataRiveDirectory().getChildFile ("game-animation.riv"); + if (! file.existsAsFile()) + { + GTEST_SKIP() << "Missing test asset: tests/data/rive/game-animation.riv"; + return; + } + + auto result = ArtboardFile::load (file, factory); + if (result.failed()) + { + GTEST_SKIP() << "Failed to load test asset: " << result.getErrorMessage(); + return; + } + + artboardFile = result.getValue(); + artboard = std::make_unique ("testArtboard", artboardFile); + artboard->setFitting (Fitting::scaleToFit); + artboard->setBounds (0.0f, 0.0f, 500.0f, 500.0f); + + for (int i = 0; i < 5; ++i) + artboard->advanceAndApply (0.0f); + } + + ::testing::NiceMock factory; + std::shared_ptr artboardFile; + std::unique_ptr artboard; +}; + +//============================================================================== +// Artboard lifecycle with a loaded file +//============================================================================== + +TEST_F (ArtboardGameAnimationTests, LoadingFileProvidesNodeQueries) +{ + EXPECT_FALSE (artboard->getNodeBounds (kGameRootArtboardNodeName).isEmpty()); + EXPECT_FALSE (artboard->getNodeBounds (kGameNestedArtboardNodeName).isEmpty()); + + auto root = artboard->findNode (kGameRootArtboardNodeName); + ASSERT_NE (nullptr, root.get()); + EXPECT_TRUE (root->isValid()); + + auto nested = artboard->findNode (kGameNestedArtboardNodeName); + ASSERT_NE (nullptr, nested.get()); + EXPECT_TRUE (nested->isValid()); +} + +TEST_F (ArtboardGameAnimationTests, UnknownNodesAreNotResolved) +{ + Component component ("test"); + + EXPECT_TRUE (artboard->getNodeBounds ("nonexistent").isEmpty()); + EXPECT_EQ (nullptr, artboard->findNode ("nonexistent").get()); + EXPECT_FALSE (artboard->attachComponentToNode ("nonexistent", &component)); +} + +TEST_F (ArtboardGameAnimationTests, InputQueriesWithLoadedFileDoNotThrow) +{ + // game-animation.riv's state machine exposes no direct bool/number/trigger + // inputs, so lookups must be false and writes must be safe no-ops. + EXPECT_FALSE (artboard->hasBoolInput ("nonexistentInput")); + EXPECT_FALSE (artboard->hasNumberInput ("nonexistentInput")); + EXPECT_FALSE (artboard->hasTriggerInput ("nonexistentInput")); + + EXPECT_NO_THROW (artboard->setBoolInput ("nonexistentInput", true)); + EXPECT_NO_THROW (artboard->setNumberInput ("nonexistentInput", 42.0)); + EXPECT_NO_THROW (artboard->triggerInput ("nonexistentInput")); + EXPECT_NO_THROW (artboard->setInput ("nonexistentInput", var (true))); + EXPECT_NO_THROW (artboard->setAllInputs (var())); + EXPECT_NO_THROW (artboard->getAllInputs()); +} + +TEST_F (ArtboardGameAnimationTests, AdvanceAndApplyRunsTheLoadedScene) +{ + for (int frame = 0; frame < 60; ++frame) + { + EXPECT_NO_THROW (artboard->advanceAndApply (0.016f)); + EXPECT_NO_THROW (artboard->refreshDisplay (0.016)); + } +} + +TEST_F (ArtboardGameAnimationTests, ClearResetsTheLoadedFile) +{ + artboard->clear(); + + EXPECT_TRUE (artboard->getNodeBounds (kGameRootArtboardNodeName).isEmpty()); + EXPECT_TRUE (artboard->getNodeBounds (kGameNestedArtboardNodeName).isEmpty()); + EXPECT_EQ (nullptr, artboard->findNode (kGameRootArtboardNodeName).get()); + EXPECT_EQ (nullptr, artboard->findNode (kGameNestedArtboardNodeName).get()); + EXPECT_EQ (Fitting::scaleToFit, artboard->getFitting()); // fitting survives clear +} + +TEST_F (ArtboardGameAnimationTests, ReloadingTheFileRestoresTheScene) +{ + artboard->setFile (artboardFile); + + auto node = artboard->findNode (kGameNestedArtboardNodeName); + ASSERT_NE (nullptr, node.get()); + EXPECT_TRUE (node->isValid()); + EXPECT_FALSE (artboard->getNodeBounds (kGameNestedArtboardNodeName).isEmpty()); +} + +//============================================================================== +// ArtboardNode handles - root artboard and nested artboard nodes +//============================================================================== + +TEST_F (ArtboardGameAnimationTests, NodeHandlesReportTheirIdentity) +{ + auto root = artboard->findNode (kGameRootArtboardNodeName); + ASSERT_NE (nullptr, root.get()); + EXPECT_TRUE (root->isValid()); + EXPECT_EQ (String (kGameRootArtboardNodeName), root->getName()); + EXPECT_EQ (static_cast (rive::ArtboardBase::typeKey), static_cast (root->getTypeKey())); + EXPECT_EQ (String ("Artboard"), root->getTypeName()); + EXPECT_TRUE (root->isLayout()); + + auto nested = artboard->findNode (kGameNestedArtboardNodeName); + ASSERT_NE (nullptr, nested.get()); + EXPECT_TRUE (nested->isValid()); + EXPECT_EQ (String (kGameNestedArtboardNodeName), nested->getName()); + EXPECT_NE (0, nested->getTypeKey()); + EXPECT_EQ (String ("NestedArtboard"), nested->getTypeName()); + EXPECT_FALSE (nested->isLayout()); +} + +TEST_F (ArtboardGameAnimationTests, NodeBoundsAreConsistentWithGetNodeBounds) +{ + for (const auto* nodeName : { kGameRootArtboardNodeName, kGameNestedArtboardNodeName }) + { + auto node = artboard->findNode (nodeName); + ASSERT_NE (nullptr, node.get()) << nodeName; + + const auto nodeBounds = node->getBounds(); + const auto artboardBounds = artboard->getNodeBounds (nodeName); + + EXPECT_FALSE (nodeBounds.isEmpty()) << nodeName; + EXPECT_FLOAT_EQ (artboardBounds.getX(), nodeBounds.getX()) << nodeName; + EXPECT_FLOAT_EQ (artboardBounds.getY(), nodeBounds.getY()) << nodeName; + EXPECT_FLOAT_EQ (artboardBounds.getWidth(), nodeBounds.getWidth()) << nodeName; + EXPECT_FLOAT_EQ (artboardBounds.getHeight(), nodeBounds.getHeight()) << nodeName; + } +} + +TEST_F (ArtboardGameAnimationTests, NodeTransformsAreFinite) +{ + const auto checkFinite = [] (const AffineTransform& transform, const char* label) + { + EXPECT_TRUE (std::isfinite (transform.getScaleX())) << label; + EXPECT_TRUE (std::isfinite (transform.getShearX())) << label; + EXPECT_TRUE (std::isfinite (transform.getTranslateX())) << label; + EXPECT_TRUE (std::isfinite (transform.getShearY())) << label; + EXPECT_TRUE (std::isfinite (transform.getScaleY())) << label; + EXPECT_TRUE (std::isfinite (transform.getTranslateY())) << label; + }; + + for (const auto* nodeName : { kGameRootArtboardNodeName, kGameNestedArtboardNodeName }) + { + auto node = artboard->findNode (nodeName); + ASSERT_NE (nullptr, node.get()) << nodeName; + + checkFinite (node->getLocalTransform(), nodeName); + checkFinite (node->getWorldTransform(), nodeName); + checkFinite (node->getViewTransform(), nodeName); + } +} + +TEST_F (ArtboardGameAnimationTests, NestedNodeHierarchyRelatesToRoot) +{ + auto nested = artboard->findNode (kGameNestedArtboardNodeName); + ASSERT_NE (nullptr, nested.get()); + + auto parent = nested->getParent(); + ASSERT_NE (nullptr, parent.get()); + EXPECT_TRUE (parent->isValid()); + EXPECT_EQ (String (kGameRootArtboardNodeName), parent->getName()); + + // Whatever children the nested artboard node has (e.g. nested inputs) must + // be valid handles whose parent resolves back to the nested node. + for (const auto& child : nested->getChildren()) + { + ASSERT_NE (nullptr, child.get()); + EXPECT_TRUE (child->isValid()); + ASSERT_NE (nullptr, child->getParent().get()); + EXPECT_EQ (String (kGameNestedArtboardNodeName), child->getParent()->getName()); + } + + // ...and the root's children include the nested node. + auto root = artboard->findNode (kGameRootArtboardNodeName); + ASSERT_NE (nullptr, root.get()); + + bool foundNested = false; + for (const auto& child : root->getChildren()) + { + ASSERT_NE (nullptr, child.get()); + if (child->getName() == String (kGameNestedArtboardNodeName)) + { + foundNested = true; + break; + } + } + + EXPECT_TRUE (foundNested); +} + +TEST_F (ArtboardGameAnimationTests, FindNodeReturnsTheCachedHandle) +{ + for (const auto* nodeName : { kGameRootArtboardNodeName, kGameNestedArtboardNodeName }) + { + auto first = artboard->findNode (nodeName); + auto second = artboard->findNode (nodeName); + + ASSERT_NE (nullptr, first.get()) << nodeName; + EXPECT_EQ (first.get(), second.get()) << nodeName; + } +} + +TEST_F (ArtboardGameAnimationTests, HandleIsRefcounted) +{ + auto node = artboard->findNode (kGameNestedArtboardNodeName); + ASSERT_NE (nullptr, node.get()); + + ArtboardNode::Ptr copy = node; + EXPECT_EQ (node.get(), copy.get()); + EXPECT_TRUE (copy->isValid()); + + copy = nullptr; + EXPECT_TRUE (node->isValid()); +} + +TEST_F (ArtboardGameAnimationTests, FindNodeWorksOnConstArtboard) +{ + const auto& constArtboard = *artboard; + auto node = constArtboard.findNode (kGameNestedArtboardNodeName); + ASSERT_NE (nullptr, node.get()); + EXPECT_TRUE (node->isValid()); +} + +TEST_F (ArtboardGameAnimationTests, ClearInvalidatesOutstandingHandles) +{ + ArtboardNode::Ptr rootHandle = artboard->findNode (kGameRootArtboardNodeName); + ArtboardNode::Ptr nestedHandle = artboard->findNode (kGameNestedArtboardNodeName); + + ASSERT_NE (nullptr, rootHandle.get()); + ASSERT_NE (nullptr, nestedHandle.get()); + EXPECT_TRUE (rootHandle->isValid()); + EXPECT_TRUE (nestedHandle->isValid()); + + artboard->clear(); + + for (const auto& node : { rootHandle, nestedHandle }) + { + EXPECT_FALSE (node->isValid()); + EXPECT_TRUE (node->getName().isEmpty()); + EXPECT_EQ (0, node->getTypeKey()); + EXPECT_TRUE (node->getTypeName().isEmpty()); + EXPECT_FALSE (node->isLayout()); + EXPECT_TRUE (node->getBounds().isEmpty()); + EXPECT_TRUE (node->getLocalTransform().isIdentity()); + EXPECT_TRUE (node->getWorldTransform().isIdentity()); + EXPECT_TRUE (node->getViewTransform().isIdentity()); + EXPECT_EQ (nullptr, node->getParent().get()); + EXPECT_TRUE (node->getChildren().isEmpty()); + } +} + +TEST_F (ArtboardGameAnimationTests, SetFileInvalidatesHandlesAndRefreshesCache) +{ + auto oldHandle = artboard->findNode (kGameNestedArtboardNodeName); + ASSERT_NE (nullptr, oldHandle.get()); + EXPECT_TRUE (oldHandle->isValid()); + + artboard->setFile (artboardFile); + + EXPECT_FALSE (oldHandle->isValid()); + + auto newHandle = artboard->findNode (kGameNestedArtboardNodeName); + ASSERT_NE (nullptr, newHandle.get()); + EXPECT_TRUE (newHandle->isValid()); + EXPECT_NE (oldHandle.get(), newHandle.get()); +} + +TEST_F (ArtboardGameAnimationTests, DestroyingTheArtboardInvalidatesHandles) +{ + ArtboardNode::Ptr handle; + + { + Artboard temp ("temp", artboardFile); + temp.setFitting (Fitting::scaleToFit); + temp.setBounds (0.0f, 0.0f, 500.0f, 500.0f); + + for (int i = 0; i < 5; ++i) + temp.advanceAndApply (0.0f); + + handle = temp.findNode (kGameNestedArtboardNodeName); + ASSERT_NE (nullptr, handle.get()); + EXPECT_TRUE (handle->isValid()); + } + + EXPECT_FALSE (handle->isValid()); + EXPECT_TRUE (handle->getName().isEmpty()); + EXPECT_EQ (0, handle->getTypeKey()); + EXPECT_TRUE (handle->getBounds().isEmpty()); + EXPECT_TRUE (handle->getLocalTransform().isIdentity()); + EXPECT_TRUE (handle->getWorldTransform().isIdentity()); + EXPECT_TRUE (handle->getViewTransform().isIdentity()); + EXPECT_EQ (nullptr, handle->getParent().get()); + EXPECT_TRUE (handle->getChildren().isEmpty()); +} + +//============================================================================== +// Node bounds listeners - driven by resize (view transform changes) +//============================================================================== + +TEST_F (ArtboardGameAnimationTests, ListenerFiresOnResizeWithCachedHandle) +{ + for (const auto* nodeName : { kGameRootArtboardNodeName, kGameNestedArtboardNodeName }) + { + int callCount = 0; + ArtboardNode::Ptr firstHandle; + + artboard->setNodeBoundsListener (nodeName, + [&] (Artboard&, const String&, const ArtboardNode::Ptr& node) + { + ++callCount; + if (firstHandle == nullptr) + firstHandle = node; + else + EXPECT_EQ (firstHandle.get(), node.get()); + }); + + // Restore the base size so the resize below is a real change for both nodes. + artboard->setBounds (0.0f, 0.0f, 500.0f, 500.0f); + + for (int i = 0; i < 5; ++i) + artboard->advanceAndApply (0.0f); + + artboard->setBounds (0.0f, 0.0f, 700.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_GT (callCount, 0) << nodeName; + + ASSERT_NE (nullptr, firstHandle.get()) << nodeName; + EXPECT_TRUE (firstHandle->isValid()) << nodeName; + EXPECT_FALSE (firstHandle->getBounds().isEmpty()) << nodeName; + + // The callback receives the same cached handle that findNode hands out. + EXPECT_EQ (firstHandle.get(), artboard->findNode (nodeName).get()) << nodeName; + + artboard->clearNodeBoundsListener (nodeName); + } +} + +TEST_F (ArtboardGameAnimationTests, ListenerDoesNotFireWhenNothingAdvances) +{ + for (const auto* nodeName : { kGameRootArtboardNodeName, kGameNestedArtboardNodeName }) + { + int callCount = 0; + + artboard->setNodeBoundsListener (nodeName, + [&] (Artboard&, const String&, const ArtboardNode::Ptr&) + { + ++callCount; + }); + + // Zero-delta advances leave the scene at frame zero, so nothing moves. + for (int i = 0; i < 5; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_EQ (0, callCount) << nodeName; + + artboard->clearNodeBoundsListener (nodeName); + } +} + +TEST_F (ArtboardGameAnimationTests, EmptyCallbackRemovesTheListener) +{ + int callCount = 0; + + artboard->setNodeBoundsListener (kGameNestedArtboardNodeName, + [&] (Artboard&, const String&, const ArtboardNode::Ptr&) + { + ++callCount; + }); + + artboard->setNodeBoundsListener (kGameNestedArtboardNodeName, {}); + + artboard->setBounds (0.0f, 0.0f, 700.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_EQ (0, callCount); +} + +TEST_F (ArtboardGameAnimationTests, ClearingListenersStopsNotifications) +{ + int callCount = 0; + + artboard->setNodeBoundsListener (kGameNestedArtboardNodeName, + [&] (Artboard&, const String&, const ArtboardNode::Ptr&) + { + ++callCount; + }); + + artboard->clearNodeBoundsListener (kGameNestedArtboardNodeName); + + artboard->setBounds (0.0f, 0.0f, 700.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_EQ (0, callCount); +} + +TEST_F (ArtboardGameAnimationTests, ListenerFiresAgainAfterAnotherResize) +{ + int callCount = 0; + Rectangle lastBounds; + + artboard->setNodeBoundsListener (kGameNestedArtboardNodeName, + [&] (Artboard&, const String&, const ArtboardNode::Ptr& node) + { + ++callCount; + lastBounds = node->getBounds(); + }); + + artboard->setBounds (0.0f, 0.0f, 700.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_GT (callCount, 0); + EXPECT_FALSE (lastBounds.isEmpty()); + + const auto boundsAfterFirstResize = lastBounds; + + artboard->setBounds (0.0f, 0.0f, 300.0f, 600.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_GT (callCount, 1); + EXPECT_FALSE (lastBounds == boundsAfterFirstResize); +} + +//============================================================================== +// Attached components on the root and nested nodes +//============================================================================== + +TEST_F (ArtboardGameAnimationTests, FillNodeAttachmentMatchesNodeBounds) +{ + for (const auto* nodeName : { kGameRootArtboardNodeName, kGameNestedArtboardNodeName }) + { + Component component ("test"); + + ASSERT_TRUE (artboard->attachComponentToNode (nodeName, &component)) << nodeName; + + const auto expected = artboard->getNodeBounds (nodeName); + const auto actual = component.getBounds(); + + EXPECT_FALSE (expected.isEmpty()) << nodeName; + EXPECT_FLOAT_EQ (expected.getX(), actual.getX()) << nodeName; + EXPECT_FLOAT_EQ (expected.getY(), actual.getY()) << nodeName; + EXPECT_FLOAT_EQ (expected.getWidth(), actual.getWidth()) << nodeName; + EXPECT_FLOAT_EQ (expected.getHeight(), actual.getHeight()) << nodeName; + + artboard->detachComponentFromNode (nodeName, &component); + } +} + +TEST_F (ArtboardGameAnimationTests, FillNodeAttachmentFollowsResize) +{ + for (const auto* nodeName : { kGameRootArtboardNodeName, kGameNestedArtboardNodeName }) + { + Component component ("test"); + + ASSERT_TRUE (artboard->attachComponentToNode (nodeName, &component)) << nodeName; + + // Restore the base size so the resize below is a real change for both nodes. + artboard->setBounds (0.0f, 0.0f, 500.0f, 500.0f); + + for (int i = 0; i < 5; ++i) + artboard->advanceAndApply (0.0f); + + artboard->setBounds (0.0f, 0.0f, 700.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + const auto nodeBounds = artboard->getNodeBounds (nodeName); + const auto componentBounds = component.getBounds(); + + EXPECT_FALSE (nodeBounds.isEmpty()) << nodeName; + EXPECT_FLOAT_EQ (nodeBounds.getX(), componentBounds.getX()) << nodeName; + EXPECT_FLOAT_EQ (nodeBounds.getY(), componentBounds.getY()) << nodeName; + EXPECT_FLOAT_EQ (nodeBounds.getWidth(), componentBounds.getWidth()) << nodeName; + EXPECT_FLOAT_EQ (nodeBounds.getHeight(), componentBounds.getHeight()) << nodeName; + + artboard->detachComponentFromNode (nodeName, &component); + } +} + +TEST_F (ArtboardGameAnimationTests, TrackPositionKeepsSizeAndTracksOrigin) +{ + for (const auto* nodeName : { kGameRootArtboardNodeName, kGameNestedArtboardNodeName }) + { + Component component ("test"); + + Artboard::NodeAttachmentOptions options; + options.mode = Artboard::NodeAttachmentOptions::Mode::trackPosition; + + ASSERT_TRUE (artboard->attachComponentToNode (nodeName, &component, options)) << nodeName; + + component.setBounds (0.0f, 0.0f, 40.0f, 20.0f); + + // Restore the base size so the resize below is a real change for both nodes. + artboard->setBounds (0.0f, 0.0f, 500.0f, 500.0f); + + for (int i = 0; i < 5; ++i) + artboard->advanceAndApply (0.0f); + + artboard->setBounds (0.0f, 0.0f, 700.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_FLOAT_EQ (40.0f, component.getWidth()) << nodeName; + EXPECT_FLOAT_EQ (20.0f, component.getHeight()) << nodeName; + + const auto nodeBounds = artboard->getNodeBounds (nodeName); + EXPECT_FLOAT_EQ (nodeBounds.getX(), component.getX()) << nodeName; + EXPECT_FLOAT_EQ (nodeBounds.getY(), component.getY()) << nodeName; + + artboard->detachComponentFromNode (nodeName, &component); + } +} + +TEST_F (ArtboardGameAnimationTests, TrackPositionPivotAndAnchor) +{ + Component component ("test"); + + Artboard::NodeAttachmentOptions options; + options.mode = Artboard::NodeAttachmentOptions::Mode::trackPosition; + options.pivot = Justification::center; + options.anchor = Justification::center; + + ASSERT_TRUE (artboard->attachComponentToNode (kGameNestedArtboardNodeName, &component, options)); + + component.setBounds (0.0f, 0.0f, 40.0f, 20.0f); + + artboard->setBounds (0.0f, 0.0f, 700.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + const auto nodeBounds = artboard->getNodeBounds (kGameNestedArtboardNodeName); + EXPECT_FLOAT_EQ (nodeBounds.getCenterX(), component.getBounds().getCenterX()); + EXPECT_FLOAT_EQ (nodeBounds.getCenterY(), component.getBounds().getCenterY()); +} + +TEST_F (ArtboardGameAnimationTests, ApplyTransformIsIdentityForUnrotatedRoot) +{ + Component component ("test"); + + Artboard::NodeAttachmentOptions options; + options.applyTransform = true; + + ASSERT_TRUE (artboard->attachComponentToNode (kGameRootArtboardNodeName, &component, options)); + + artboard->setBounds (0.0f, 0.0f, 700.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_TRUE (component.getTransform().isIdentity()); +} + +TEST_F (ArtboardGameAnimationTests, DetachStopsFollowingAfterResize) +{ + Component component ("test"); + + ASSERT_TRUE (artboard->attachComponentToNode (kGameNestedArtboardNodeName, &component)); + + ASSERT_TRUE (artboard->detachComponentFromNode (kGameNestedArtboardNodeName, &component)); + + const auto boundsBefore = component.getBounds(); + + artboard->setBounds (0.0f, 0.0f, 700.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_TRUE (component.getBounds() == boundsBefore); +} + +TEST_F (ArtboardGameAnimationTests, ReattachingReplacesThePreviousAttachment) +{ + Component first ("first"); + Component second ("second"); + + ASSERT_TRUE (artboard->attachComponentToNode (kGameNestedArtboardNodeName, &first)); + + const auto boundsBeforeSecond = first.getBounds(); + + ASSERT_TRUE (artboard->attachComponentToNode (kGameNestedArtboardNodeName, &second)); + + artboard->setBounds (0.0f, 0.0f, 700.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + // Only the latest attachment follows the node after a reflow. + EXPECT_TRUE (first.getBounds() == boundsBeforeSecond); + + const auto nodeBounds = artboard->getNodeBounds (kGameNestedArtboardNodeName); + EXPECT_FLOAT_EQ (nodeBounds.getX(), second.getX()); + EXPECT_FLOAT_EQ (nodeBounds.getY(), second.getY()); +} + +//============================================================================== +// Artboard and ArtboardNode against tests/data/rive/data-binding.riv +// +// data-binding.riv names only its artboard ("Artboard"); the Text nodes are +// unnamed, so the name-based APIs are exercised through the artboard root. +// The file also contains an in-band FontAsset, a text-bound ViewModel and a +// (keyframe-less) animation, all of which must load and advance safely. +//============================================================================== + +namespace +{ +constexpr const char* kDataBindingArtboardRootName = "Artboard"; +} // namespace + +class ArtboardDataBindingTests : public ::testing::Test +{ +protected: + void SetUp() override + { + const auto file = getTestDataRiveDirectory().getChildFile ("data-binding.riv"); + if (! file.existsAsFile()) + { + GTEST_SKIP() << "Missing test asset: tests/data/rive/data-binding.riv"; + return; + } + + auto result = ArtboardFile::load (file, factory); + if (result.failed()) + { + GTEST_SKIP() << "Failed to load test asset: " << result.getErrorMessage(); + return; + } + + artboardFile = result.getValue(); + artboard = std::make_unique ("testArtboard", artboardFile); + artboard->setFitting (Fitting::scaleToFit); + artboard->setBounds (0.0f, 0.0f, 500.0f, 500.0f); + + for (int i = 0; i < 5; ++i) + artboard->advanceAndApply (0.0f); + } + + ::testing::NiceMock factory; + std::shared_ptr artboardFile; + std::unique_ptr artboard; +}; + +TEST_F (ArtboardDataBindingTests, LoadingFileProvidesRootQueries) +{ + EXPECT_FALSE (artboard->getNodeBounds (kDataBindingArtboardRootName).isEmpty()); + + auto node = artboard->findNode (kDataBindingArtboardRootName); + ASSERT_NE (nullptr, node.get()); + EXPECT_TRUE (node->isValid()); +} + +TEST_F (ArtboardDataBindingTests, RootHandleReportsItsIdentity) +{ + auto node = artboard->findNode (kDataBindingArtboardRootName); + ASSERT_NE (nullptr, node.get()); + + EXPECT_EQ (String (kDataBindingArtboardRootName), node->getName()); + EXPECT_EQ (static_cast (rive::ArtboardBase::typeKey), static_cast (node->getTypeKey())); + EXPECT_EQ (String ("Artboard"), node->getTypeName()); + EXPECT_TRUE (node->isLayout()); +} + +TEST_F (ArtboardDataBindingTests, RootBoundsAreConsistentWithGetNodeBounds) +{ + auto node = artboard->findNode (kDataBindingArtboardRootName); + ASSERT_NE (nullptr, node.get()); + + const auto nodeBounds = node->getBounds(); + const auto artboardBounds = artboard->getNodeBounds (kDataBindingArtboardRootName); + + EXPECT_FALSE (nodeBounds.isEmpty()); + EXPECT_GT (nodeBounds.getWidth(), 0.0f); + EXPECT_GT (nodeBounds.getHeight(), 0.0f); + + EXPECT_FLOAT_EQ (artboardBounds.getX(), nodeBounds.getX()); + EXPECT_FLOAT_EQ (artboardBounds.getY(), nodeBounds.getY()); + EXPECT_FLOAT_EQ (artboardBounds.getWidth(), nodeBounds.getWidth()); + EXPECT_FLOAT_EQ (artboardBounds.getHeight(), nodeBounds.getHeight()); +} + +TEST_F (ArtboardDataBindingTests, RootTransformsAreFinite) +{ + auto node = artboard->findNode (kDataBindingArtboardRootName); + ASSERT_NE (nullptr, node.get()); + + const auto checkFinite = [] (const AffineTransform& transform, const char* label) + { + EXPECT_TRUE (std::isfinite (transform.getScaleX())) << label; + EXPECT_TRUE (std::isfinite (transform.getShearX())) << label; + EXPECT_TRUE (std::isfinite (transform.getTranslateX())) << label; + EXPECT_TRUE (std::isfinite (transform.getShearY())) << label; + EXPECT_TRUE (std::isfinite (transform.getScaleY())) << label; + EXPECT_TRUE (std::isfinite (transform.getTranslateY())) << label; + }; + + checkFinite (node->getLocalTransform(), "local"); + checkFinite (node->getWorldTransform(), "world"); + checkFinite (node->getViewTransform(), "view"); +} + +TEST_F (ArtboardDataBindingTests, RootChildrenIncludeTextNodes) +{ + auto node = artboard->findNode (kDataBindingArtboardRootName); + ASSERT_NE (nullptr, node.get()); + + const auto children = node->getChildren(); + ASSERT_FALSE (children.isEmpty()); + + bool foundText = false; + for (const auto& child : children) + { + ASSERT_NE (nullptr, child.get()); + EXPECT_TRUE (child->isValid()); + ASSERT_NE (nullptr, child->getParent().get()); + EXPECT_EQ (String (kDataBindingArtboardRootName), child->getParent()->getName()); + + if (child->getTypeName() == String ("Text")) + foundText = true; + } + + EXPECT_TRUE (foundText); +} + +TEST_F (ArtboardDataBindingTests, FindNodeReturnsTheCachedHandle) +{ + auto first = artboard->findNode (kDataBindingArtboardRootName); + auto second = artboard->findNode (kDataBindingArtboardRootName); + + ASSERT_NE (nullptr, first.get()); + EXPECT_EQ (first.get(), second.get()); +} + +TEST_F (ArtboardDataBindingTests, HandleIsRefcounted) +{ + auto node = artboard->findNode (kDataBindingArtboardRootName); + ASSERT_NE (nullptr, node.get()); + + ArtboardNode::Ptr copy = node; + EXPECT_EQ (node.get(), copy.get()); + EXPECT_TRUE (copy->isValid()); + + copy = nullptr; + EXPECT_TRUE (node->isValid()); +} + +TEST_F (ArtboardDataBindingTests, FindNodeWorksOnConstArtboard) +{ + const auto& constArtboard = *artboard; + auto node = constArtboard.findNode (kDataBindingArtboardRootName); + ASSERT_NE (nullptr, node.get()); + EXPECT_TRUE (node->isValid()); +} + +TEST_F (ArtboardDataBindingTests, UnknownNodesAreNotResolved) +{ + Component component ("test"); + + EXPECT_TRUE (artboard->getNodeBounds ("nonexistent").isEmpty()); + EXPECT_EQ (nullptr, artboard->findNode ("nonexistent").get()); + EXPECT_FALSE (artboard->attachComponentToNode ("nonexistent", &component)); +} + +TEST_F (ArtboardDataBindingTests, InputQueriesWithLoadedFileDoNotThrow) +{ + EXPECT_FALSE (artboard->hasBoolInput ("nonexistentInput")); + EXPECT_FALSE (artboard->hasNumberInput ("nonexistentInput")); + EXPECT_FALSE (artboard->hasTriggerInput ("nonexistentInput")); + + EXPECT_NO_THROW (artboard->setBoolInput ("nonexistentInput", true)); + EXPECT_NO_THROW (artboard->setNumberInput ("nonexistentInput", 42.0)); + EXPECT_NO_THROW (artboard->triggerInput ("nonexistentInput")); + EXPECT_NO_THROW (artboard->setInput ("nonexistentInput", var (true))); + EXPECT_NO_THROW (artboard->setAllInputs (var())); + EXPECT_NO_THROW (artboard->getAllInputs()); +} + +TEST_F (ArtboardDataBindingTests, AdvanceAndApplyRunsTheLoadedScene) +{ + for (int frame = 0; frame < 60; ++frame) + { + EXPECT_NO_THROW (artboard->advanceAndApply (0.016f)); + EXPECT_NO_THROW (artboard->refreshDisplay (0.016)); + } +} + +TEST_F (ArtboardDataBindingTests, ClearResetsTheLoadedFile) +{ + artboard->clear(); + + EXPECT_TRUE (artboard->getNodeBounds (kDataBindingArtboardRootName).isEmpty()); + EXPECT_EQ (nullptr, artboard->findNode (kDataBindingArtboardRootName).get()); + EXPECT_EQ (Fitting::scaleToFit, artboard->getFitting()); // fitting survives clear +} + +TEST_F (ArtboardDataBindingTests, ReloadingTheFileRestoresTheScene) +{ + artboard->setFile (artboardFile); + + auto node = artboard->findNode (kDataBindingArtboardRootName); + ASSERT_NE (nullptr, node.get()); + EXPECT_TRUE (node->isValid()); + EXPECT_FALSE (artboard->getNodeBounds (kDataBindingArtboardRootName).isEmpty()); +} + +TEST_F (ArtboardDataBindingTests, ClearInvalidatesOutstandingHandle) +{ + auto node = artboard->findNode (kDataBindingArtboardRootName); + ASSERT_NE (nullptr, node.get()); + EXPECT_TRUE (node->isValid()); + + artboard->clear(); + + EXPECT_FALSE (node->isValid()); + EXPECT_TRUE (node->getName().isEmpty()); + EXPECT_EQ (0, node->getTypeKey()); + EXPECT_TRUE (node->getTypeName().isEmpty()); + EXPECT_FALSE (node->isLayout()); + EXPECT_TRUE (node->getBounds().isEmpty()); + EXPECT_TRUE (node->getLocalTransform().isIdentity()); + EXPECT_TRUE (node->getWorldTransform().isIdentity()); + EXPECT_TRUE (node->getViewTransform().isIdentity()); + EXPECT_EQ (nullptr, node->getParent().get()); + EXPECT_TRUE (node->getChildren().isEmpty()); +} + +TEST_F (ArtboardDataBindingTests, SetFileInvalidatesHandlesAndRefreshesCache) +{ + auto oldHandle = artboard->findNode (kDataBindingArtboardRootName); + ASSERT_NE (nullptr, oldHandle.get()); + EXPECT_TRUE (oldHandle->isValid()); + + artboard->setFile (artboardFile); + + EXPECT_FALSE (oldHandle->isValid()); + + auto newHandle = artboard->findNode (kDataBindingArtboardRootName); + ASSERT_NE (nullptr, newHandle.get()); + EXPECT_TRUE (newHandle->isValid()); + EXPECT_NE (oldHandle.get(), newHandle.get()); +} + +TEST_F (ArtboardDataBindingTests, DestroyingTheArtboardInvalidatesHandles) +{ + ArtboardNode::Ptr handle; + + { + Artboard temp ("temp", artboardFile); + temp.setFitting (Fitting::scaleToFit); + temp.setBounds (0.0f, 0.0f, 500.0f, 500.0f); + + for (int i = 0; i < 5; ++i) + temp.advanceAndApply (0.0f); + + handle = temp.findNode (kDataBindingArtboardRootName); + ASSERT_NE (nullptr, handle.get()); + EXPECT_TRUE (handle->isValid()); + } + + EXPECT_FALSE (handle->isValid()); + EXPECT_TRUE (handle->getBounds().isEmpty()); + EXPECT_TRUE (handle->getLocalTransform().isIdentity()); + EXPECT_TRUE (handle->getWorldTransform().isIdentity()); + EXPECT_TRUE (handle->getViewTransform().isIdentity()); + EXPECT_EQ (nullptr, handle->getParent().get()); + EXPECT_TRUE (handle->getChildren().isEmpty()); +} + +TEST_F (ArtboardDataBindingTests, ListenerFiresOnResizeWithCachedHandle) +{ + int callCount = 0; + ArtboardNode::Ptr firstHandle; + + artboard->setNodeBoundsListener (kDataBindingArtboardRootName, + [&] (Artboard&, const String&, const ArtboardNode::Ptr& node) + { + ++callCount; + if (firstHandle == nullptr) + firstHandle = node; + else + EXPECT_EQ (firstHandle.get(), node.get()); + }); + + artboard->setBounds (0.0f, 0.0f, 700.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_GT (callCount, 0); + + ASSERT_NE (nullptr, firstHandle.get()); + EXPECT_TRUE (firstHandle->isValid()); + EXPECT_FALSE (firstHandle->getBounds().isEmpty()); + + EXPECT_EQ (firstHandle.get(), artboard->findNode (kDataBindingArtboardRootName).get()); +} + +TEST_F (ArtboardDataBindingTests, ListenerDoesNotFireWhenNothingAdvances) +{ + int callCount = 0; + + artboard->setNodeBoundsListener (kDataBindingArtboardRootName, + [&] (Artboard&, const String&, const ArtboardNode::Ptr&) + { + ++callCount; + }); + + for (int i = 0; i < 5; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_EQ (0, callCount); +} + +TEST_F (ArtboardDataBindingTests, EmptyCallbackRemovesTheListener) +{ + int callCount = 0; + + artboard->setNodeBoundsListener (kDataBindingArtboardRootName, + [&] (Artboard&, const String&, const ArtboardNode::Ptr&) + { + ++callCount; + }); + + artboard->setNodeBoundsListener (kDataBindingArtboardRootName, {}); + + artboard->setBounds (0.0f, 0.0f, 700.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_EQ (0, callCount); +} + +TEST_F (ArtboardDataBindingTests, ClearingListenersStopsNotifications) +{ + int callCount = 0; + + artboard->setNodeBoundsListener (kDataBindingArtboardRootName, + [&] (Artboard&, const String&, const ArtboardNode::Ptr&) + { + ++callCount; + }); + + artboard->clearNodeBoundsListener (kDataBindingArtboardRootName); + + artboard->setBounds (0.0f, 0.0f, 700.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_EQ (0, callCount); +} + +TEST_F (ArtboardDataBindingTests, FillNodeAttachmentMatchesNodeBounds) +{ + Component component ("test"); + + ASSERT_TRUE (artboard->attachComponentToNode (kDataBindingArtboardRootName, &component)); + + const auto expected = artboard->getNodeBounds (kDataBindingArtboardRootName); + const auto actual = component.getBounds(); + + EXPECT_FALSE (expected.isEmpty()); + EXPECT_FLOAT_EQ (expected.getX(), actual.getX()); + EXPECT_FLOAT_EQ (expected.getY(), actual.getY()); + EXPECT_FLOAT_EQ (expected.getWidth(), actual.getWidth()); + EXPECT_FLOAT_EQ (expected.getHeight(), actual.getHeight()); +} + +TEST_F (ArtboardDataBindingTests, FillNodeAttachmentFollowsResize) +{ + Component component ("test"); + + ASSERT_TRUE (artboard->attachComponentToNode (kDataBindingArtboardRootName, &component)); + + artboard->setBounds (0.0f, 0.0f, 700.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + const auto nodeBounds = artboard->getNodeBounds (kDataBindingArtboardRootName); + const auto componentBounds = component.getBounds(); + + EXPECT_FALSE (nodeBounds.isEmpty()); + EXPECT_FLOAT_EQ (nodeBounds.getX(), componentBounds.getX()); + EXPECT_FLOAT_EQ (nodeBounds.getY(), componentBounds.getY()); + EXPECT_FLOAT_EQ (nodeBounds.getWidth(), componentBounds.getWidth()); + EXPECT_FLOAT_EQ (nodeBounds.getHeight(), componentBounds.getHeight()); +} + +TEST_F (ArtboardDataBindingTests, TrackPositionTracksOriginAndKeepsSize) +{ + Component component ("test"); + + Artboard::NodeAttachmentOptions options; + options.mode = Artboard::NodeAttachmentOptions::Mode::trackPosition; + + ASSERT_TRUE (artboard->attachComponentToNode (kDataBindingArtboardRootName, &component, options)); + + component.setBounds (0.0f, 0.0f, 40.0f, 20.0f); + + artboard->setBounds (0.0f, 0.0f, 700.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_FLOAT_EQ (40.0f, component.getWidth()); + EXPECT_FLOAT_EQ (20.0f, component.getHeight()); + + const auto nodeBounds = artboard->getNodeBounds (kDataBindingArtboardRootName); + EXPECT_FLOAT_EQ (nodeBounds.getX(), component.getX()); + EXPECT_FLOAT_EQ (nodeBounds.getY(), component.getY()); +} + +TEST_F (ArtboardDataBindingTests, TrackPositionPivotAndAnchor) +{ + Component component ("test"); + + Artboard::NodeAttachmentOptions options; + options.mode = Artboard::NodeAttachmentOptions::Mode::trackPosition; + options.pivot = Justification::center; + options.anchor = Justification::center; + + ASSERT_TRUE (artboard->attachComponentToNode (kDataBindingArtboardRootName, &component, options)); + + component.setBounds (0.0f, 0.0f, 40.0f, 20.0f); + + artboard->setBounds (0.0f, 0.0f, 700.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + const auto nodeBounds = artboard->getNodeBounds (kDataBindingArtboardRootName); + EXPECT_FLOAT_EQ (nodeBounds.getCenterX(), component.getBounds().getCenterX()); + EXPECT_FLOAT_EQ (nodeBounds.getCenterY(), component.getBounds().getCenterY()); +} + +TEST_F (ArtboardDataBindingTests, ApplyTransformIsIdentityForUnrotatedRoot) +{ + Component component ("test"); + + Artboard::NodeAttachmentOptions options; + options.applyTransform = true; + + ASSERT_TRUE (artboard->attachComponentToNode (kDataBindingArtboardRootName, &component, options)); + + artboard->setBounds (0.0f, 0.0f, 700.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_TRUE (component.getTransform().isIdentity()); +} + +TEST_F (ArtboardDataBindingTests, DetachStopsFollowingAfterResize) +{ + Component component ("test"); + + ASSERT_TRUE (artboard->attachComponentToNode (kDataBindingArtboardRootName, &component)); + + ASSERT_TRUE (artboard->detachComponentFromNode (kDataBindingArtboardRootName, &component)); + + const auto boundsBefore = component.getBounds(); + + artboard->setBounds (0.0f, 0.0f, 700.0f, 400.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_TRUE (component.getBounds() == boundsBefore); +} + +//============================================================================== +// Artboard file lifecycle edge cases +//============================================================================== + +TEST_F (ArtboardTests, ConstructorsAgreeOnOpacity) +{ + Artboard plain ("plain"); + Artboard withFile ("withFile", nullptr); + + EXPECT_TRUE (plain.isOpaque()); + EXPECT_EQ (plain.isOpaque(), withFile.isOpaque()); +} + +TEST_F (ArtboardTests, SetFileWithNullUnloadsSafely) +{ + // The parameter is a shared_ptr and every other method null-guards, so + // unloading this way must not dereference a null file. + EXPECT_NO_THROW (artboard->setFile (nullptr)); + + EXPECT_EQ (nullptr, artboard->findNode ("anything").get()); + EXPECT_TRUE (artboard->getNodeBounds ("anything").isEmpty()); + EXPECT_TRUE (artboard->getViewModelName().isEmpty()); + EXPECT_NO_THROW (artboard->advanceAndApply (0.016f)); + EXPECT_NO_THROW (artboard->setBounds (0.0f, 0.0f, 100.0f, 100.0f)); +} + +//============================================================================== +// Artboard state machine inputs round-trip (game-animation.riv) +//============================================================================== + +TEST_F (ArtboardGameAnimationTests, GetAllInputsDescribesEveryInput) +{ + const auto inputs = artboard->getAllInputs(); + + if (! inputs.isArray() || inputs.getArray()->isEmpty()) + { + GTEST_SKIP() << "Test asset exposes no state machine inputs"; + return; + } + + for (const auto& input : *inputs.getArray()) + { + auto* object = input.getDynamicObject(); + ASSERT_NE (nullptr, object); + + EXPECT_TRUE (object->hasProperty ("id")); + EXPECT_TRUE (object->hasProperty ("type")); + + const auto type = object->getProperty ("type").toString(); + EXPECT_TRUE (type == "number" || type == "boolean" || type == "trigger"); + + // Triggers are stateless and deliberately carry no "value". + EXPECT_EQ (type != "trigger", object->hasProperty ("value")); + } +} + +TEST_F (ArtboardGameAnimationTests, SetAllInputsRoundTripsASnapshot) +{ + const auto snapshot = artboard->getAllInputs(); + + if (! snapshot.isArray() || snapshot.getArray()->isEmpty()) + { + GTEST_SKIP() << "Test asset exposes no state machine inputs"; + return; + } + + EXPECT_NO_THROW (artboard->setAllInputs (snapshot)); + + const auto restored = artboard->getAllInputs(); + ASSERT_TRUE (restored.isArray()); + ASSERT_EQ (snapshot.getArray()->size(), restored.getArray()->size()); + + for (int i = 0; i < snapshot.getArray()->size(); ++i) + { + auto* before = snapshot.getArray()->getReference (i).getDynamicObject(); + auto* after = restored.getArray()->getReference (i).getDynamicObject(); + ASSERT_NE (nullptr, before); + ASSERT_NE (nullptr, after); + + EXPECT_EQ (before->getProperty ("id"), after->getProperty ("id")); + EXPECT_EQ (before->getProperty ("value"), after->getProperty ("value")); + } +} + +TEST_F (ArtboardGameAnimationTests, SetAllInputsIgnoresUnknownAndMalformedEntries) +{ + Array inputs; + + DynamicObject::Ptr unknown = new DynamicObject; + unknown->setProperty ("id", "definitelyNotAnInput"); + unknown->setProperty ("type", "number"); + unknown->setProperty ("value", 3.0); + inputs.add (var (unknown.get())); + + // A bare value, and an object with no "value", must both be skipped. + inputs.add (var (42)); + + DynamicObject::Ptr valueless = new DynamicObject; + valueless->setProperty ("id", "alsoNotAnInput"); + valueless->setProperty ("type", "trigger"); + inputs.add (var (valueless.get())); + + EXPECT_NO_THROW (artboard->setAllInputs (var (inputs))); +} + +//============================================================================== +// Node bounds listener re-entrancy (A3) +//============================================================================== + +TEST_F (ArtboardGameAnimationTests, BoundsListenerMayReflowTheArtboardFromWithin) +{ + int callCount = 0; + + // notifyNodeBoundsChanged walks a member scratch array and hands references + // into it to the listener. Reflowing from the listener used to clear that + // array underneath the running loop. + artboard->setNodeBoundsListener (kGameNestedArtboardNodeName, + [&] (Artboard& self, const String&, const ArtboardNode::Ptr& node) + { + ++callCount; + + EXPECT_NE (nullptr, node.get()); + + self.setFitting (self.getFitting() == Fitting::scaleToFit + ? Fitting::scaleToFill + : Fitting::scaleToFit); + }); + + artboard->setBounds (0.0f, 0.0f, 640.0f, 480.0f); + + for (int i = 0; i < 10; ++i) + EXPECT_NO_THROW (artboard->advanceAndApply (0.016f)); + + EXPECT_GT (callCount, 0); +} + +TEST_F (ArtboardGameAnimationTests, BoundsListenerMayReplaceTheFileFromWithin) +{ + int callCount = 0; + + artboard->setNodeBoundsListener (kGameNestedArtboardNodeName, + [&] (Artboard& self, const String&, const ArtboardNode::Ptr&) + { + ++callCount; + self.setFile (nullptr); + }); + + artboard->setBounds (0.0f, 0.0f, 640.0f, 480.0f); + + EXPECT_NO_THROW (artboard->advanceAndApply (0.016f)); + EXPECT_EQ (1, callCount); +} + +//============================================================================== +// Attached component lifetime (A8) +//============================================================================== + +TEST_F (ArtboardGameAnimationTests, AttachingMovesAComponentOffItsPreviousNode) +{ + Component follower ("follower"); + + ASSERT_TRUE (artboard->attachComponentToNode (kGameRootArtboardNodeName, &follower)); + ASSERT_TRUE (artboard->attachComponentToNode (kGameNestedArtboardNodeName, &follower)); + + // The component now follows only the second node. + EXPECT_FALSE (artboard->detachComponentFromNode (kGameRootArtboardNodeName, &follower)); + EXPECT_TRUE (artboard->detachComponentFromNode (kGameNestedArtboardNodeName, &follower)); +} + +TEST_F (ArtboardGameAnimationTests, DeletingAnAttachedComponentDetachesItAutomatically) +{ + { + Component follower ("follower"); + ASSERT_TRUE (artboard->attachComponentToNode (kGameNestedArtboardNodeName, &follower)); + } + + // The attachment held a raw pointer; without the deletion hook the next + // notification would write through it. + artboard->setBounds (0.0f, 0.0f, 640.0f, 480.0f); + + for (int i = 0; i < 5; ++i) + EXPECT_NO_THROW (artboard->advanceAndApply (0.016f)); +} + +TEST_F (ArtboardGameAnimationTests, DestroyingTheArtboardReleasesItsAttachments) +{ + Component follower ("follower"); + + ASSERT_TRUE (artboard->attachComponentToNode (kGameNestedArtboardNodeName, &follower)); + + // The artboard registered itself as a listener on the component; it must + // deregister before the component outlives it. + EXPECT_NO_THROW (artboard.reset()); +} + +TEST_F (ArtboardGameAnimationTests, AttachingIsRejectedForUnknownNodes) +{ + Component follower ("follower"); + + EXPECT_FALSE (artboard->attachComponentToNode ("definitelyNotANode", &follower)); + EXPECT_FALSE (artboard->attachComponentToNode (kGameNestedArtboardNodeName, nullptr)); + EXPECT_FALSE (artboard->detachComponentFromNode ("definitelyNotANode", &follower)); +} + +//============================================================================== +// State machine event draining (A2) +// +// Draining used to happen only from mouseDrag, so events reported by an advance +// or by a pointer press were wiped by the next advance before anyone saw them. +// The shipped fixtures are not guaranteed to report events with custom +// properties, so these skip rather than fail when nothing is reported. +//============================================================================== + +TEST_F (ArtboardGameAnimationTests, AdvancingAndPressingDoNotLoseReportedEvents) +{ + // NOTE: neither shipped fixture is known to report state machine events with + // custom properties, so this cannot positively prove the drain fires; it pins + // that the widened drain does not crash, recurse or double-report. The + // end-to-end check is ArtboardDemo's onPropertyChanged handler firing without + // a drag. See docs/ui/artboard.md. + Array reported; + + artboard->onPropertyChanged = [&] (Artboard&, const String& eventName, const String& propertyName, const var&, const var& newValue) + { + reported.add (eventName + "." + propertyName + "=" + newValue.toString()); + }; + + for (int i = 0; i < 30; ++i) + { + artboard->advanceAndApply (0.016f); + + // A resize advances the scene too, and used to swallow the frame's events. + if (i == 10) + artboard->setBounds (0.0f, 0.0f, 640.0f, 480.0f); + } + + const MouseEvent press (MouseEvent::leftButton, KeyModifiers(), Point (250.0f, 250.0f)); + artboard->mouseDown (press); + artboard->mouseUp (press); + + for (int i = 0; i < 30; ++i) + artboard->advanceAndApply (0.016f); + + // Whatever was reported, the dedup cache means no value is reported twice in + // a row for the same event, and draining more often must not change that. + for (int i = 1; i < reported.size(); ++i) + EXPECT_NE (reported[i], reported[i - 1]); + + if (reported.isEmpty()) + GTEST_SKIP() << "Test asset reports no state machine events with custom properties"; +} + +TEST_F (ArtboardGameAnimationTests, EveryPointerHandlerDrainsWithoutCrashing) +{ + const MouseEvent moved (MouseEvent::noButtons, KeyModifiers(), Point (250.0f, 250.0f)); + const MouseEvent pressed (MouseEvent::leftButton, KeyModifiers(), Point (250.0f, 250.0f)); + + // All six handlers now drain the reported event queue after their pointer + // call, where only mouseDrag used to. + EXPECT_NO_THROW (artboard->mouseEnter (moved)); + EXPECT_NO_THROW (artboard->mouseMove (moved)); + EXPECT_NO_THROW (artboard->mouseDown (pressed)); + EXPECT_NO_THROW (artboard->mouseDrag (pressed)); + EXPECT_NO_THROW (artboard->mouseUp (moved)); + EXPECT_NO_THROW (artboard->mouseExit (moved)); + + for (int i = 0; i < 5; ++i) + EXPECT_NO_THROW (artboard->advanceAndApply (0.016f)); +} + +TEST_F (ArtboardGameAnimationTests, ContentScaleChangedReRunsTheLayoutPass) +{ + auto node = artboard->findNode (kGameNestedArtboardNodeName); + ASSERT_NE (nullptr, node.get()); + + const auto before = artboard->getNodeBounds (kGameNestedArtboardNodeName); + + // contentScaleChanged re-runs resized(); the DPI value itself is not used by + // the artboard, so the layout must come out unchanged rather than drifting. + EXPECT_NO_THROW (artboard->contentScaleChanged (2.0f)); + + const auto after = artboard->getNodeBounds (kGameNestedArtboardNodeName); + + EXPECT_FLOAT_EQ (before.getX(), after.getX()); + EXPECT_FLOAT_EQ (before.getY(), after.getY()); + EXPECT_FLOAT_EQ (before.getWidth(), after.getWidth()); + EXPECT_FLOAT_EQ (before.getHeight(), after.getHeight()); + + EXPECT_TRUE (node->isValid()); +} + +TEST_F (ArtboardGameAnimationTests, PropertyChangedHandlerMayAdvanceTheArtboard) +{ + // Draining now happens from every advance, so a handler that advances again + // must not re-enter the drain loop or recurse without bound. The assertion + // here is that these calls return at all. + artboard->onPropertyChanged = [] (Artboard& self, const String&, const String&, const var&, const var&) + { + self.advanceAndApply (0.016f); + }; + + for (int i = 0; i < 20; ++i) + EXPECT_NO_THROW (artboard->advanceAndApply (0.016f)); +} + +//============================================================================== +// View transform resolution across every Fitting and Justification value +// +// setFitting() / setJustification() only reach the Rive fit and alignment +// mapping once a file is loaded and a size is known, so these run against the +// loaded layout-ui fixture rather than an empty artboard. +//============================================================================== + +namespace +{ + +const std::optional everyFittingValue[] = { + std::nullopt, + Fitting::none, + Fitting::scaleToFit, + Fitting::fitWidth, + Fitting::fitHeight, + Fitting::scaleToFill, + Fitting::fill, + Fitting::tile, + Fitting::centerCrop, + Fitting::centerInside, + Fitting::stretchWidth, + Fitting::stretchHeight, +}; + +const Justification everyJustificationValue[] = { + Justification (Justification::topLeft), + Justification (Justification::top), + Justification (Justification::topRight), + Justification (Justification::left), + Justification (Justification::center), + Justification (Justification::right), + Justification (Justification::bottomLeft), + Justification (Justification::bottom), + Justification (Justification::bottomRight), + Justification (Justification::centerTop), + Justification (Justification::centerBottom), +}; + +} // namespace + +TEST_F (ArtboardLayoutTests, EveryFittingValueResolvesAViewTransform) +{ + for (const auto& fitting : everyFittingValue) + { + artboard->setFitting (fitting); + EXPECT_EQ (fitting, artboard->getFitting()); + + // Re-resolving the scene re-derives the view transform through the Rive + // fit mapping, and a named node stays queryable under every fit. + artboard->setBounds (0.0f, 0.0f, 520.0f, 380.0f); + + for (int i = 0; i < 10; ++i) + artboard->advanceAndApply (0.0f); + + EXPECT_FALSE (artboard->getNodeBounds (kHeaderNodeName).isEmpty()); + } + + // Back to the artboard's own layout, the mode the fixture is designed for. + artboard->setFitting (std::nullopt); +} + +TEST_F (ArtboardLayoutTests, EveryJustificationValueResolvesAViewTransform) +{ + for (const auto& justification : everyJustificationValue) + { + artboard->setJustification (justification); + EXPECT_EQ (justification, artboard->getJustification()); + EXPECT_NO_THROW (artboard->advanceAndApply (0.0f)); + } +} + +TEST_F (ArtboardLayoutTests, RepeatingTheCurrentFittingOrJustificationIsANoOp) +{ + // Writing the value already in effect must return early rather than + // re-resolving the scene, while leaving the value untouched. + artboard->setFitting (Fitting::fill); + const auto fitting = artboard->getFitting(); + artboard->setFitting (Fitting::fill); + EXPECT_EQ (fitting, artboard->getFitting()); + + artboard->setJustification (Justification::top); + const auto justification = artboard->getJustification(); + artboard->setJustification (Justification::top); + EXPECT_EQ (justification, artboard->getJustification()); +} + +TEST_F (ArtboardLayoutTests, LoadedArtboardReportsDurationAndViewModelState) +{ + // durationSeconds() must read through the loaded scene rather than + // short-circuit: an unloaded artboard reports 0, while layout-ui.riv's + // static scene reports a negative "no duration" (-1). + Artboard unloaded ("unloaded"); + EXPECT_EQ (0.0f, unloaded.durationSeconds()); + + EXPECT_LT (artboard->durationSeconds(), 0.0f); + + // When the file ships no ViewModel the artboard's viewmodel id is out of + // range and resolves to no schema name. + if (artboardFile->getNumViewModels() == 0) + EXPECT_TRUE (artboard->getViewModelName().isEmpty()); + + // A null instance is rejected without touching the scene. + EXPECT_FALSE (artboard->bindViewModelInstance (nullptr)); + EXPECT_EQ (nullptr, artboard->getBoundViewModelInstance().get()); +} + +TEST_F (ArtboardLayoutTests, RefreshDisplayAdvancesUnlessPausingWhileHidden) +{ + // The component is never "showing" off-screen, so the default (pause when + // hidden) short-circuits; disabling it lets refreshDisplay advance instead. + artboard->shouldPauseWhenHidden (false); + EXPECT_FALSE (artboard->isPausingWhenHidden()); + EXPECT_NO_THROW (artboard->refreshDisplay (0.016)); + + // A paused artboard short-circuits regardless. + artboard->setPaused (true); + EXPECT_TRUE (artboard->isPaused()); + EXPECT_NO_THROW (artboard->refreshDisplay (0.016)); } diff --git a/tests/yup_gui/yup_ArtboardFile.cpp b/tests/yup_gui/yup_ArtboardFile.cpp new file mode 100644 index 000000000..e092fea4b --- /dev/null +++ b/tests/yup_gui/yup_ArtboardFile.cpp @@ -0,0 +1,329 @@ +/* + ============================================================================== + + This file is part of the YUP library. + Copyright (c) 2026 - kunitoki@gmail.com + + YUP is an open source library subject to open-source licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + to use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#include + +#include +#include + +#include "../mocks/rive_gpu.h" + +using namespace yup; + +//============================================================================== +// ArtboardFile loading, asset resolution and ViewModel schema access. +// +// Covers the load() overloads (File and InputStream, with and without an asset +// callback), the failure paths, and the schema accessors. +//============================================================================== + +namespace +{ + +const File getArtboardFileTestDataDirectory() +{ + // Try source-relative path first (works on desktop builds) + auto dir = File (__FILE__) + .getParentDirectory() + .getParentDirectory() + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir; + + dir = File::getCurrentWorkingDirectory() + .getParentDirectory() + .getParentDirectory() + .getParentDirectory() + .getChildFile ("tests") + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir; + + return File ("/data/rive"); +} + +} // namespace + +class ArtboardFileTests : public ::testing::Test +{ +protected: + void SetUp() override + { + riveFile = getArtboardFileTestDataDirectory().getChildFile ("data-binding.riv"); + + if (! riveFile.existsAsFile()) + GTEST_SKIP() << "Missing test asset: tests/data/rive/data-binding.riv"; + } + + ::testing::NiceMock factory; + File riveFile; +}; + +//============================================================================== +// Loading from a file +//============================================================================== + +TEST_F (ArtboardFileTests, LoadFromFileSucceeds) +{ + auto result = ArtboardFile::load (riveFile, factory); + + ASSERT_FALSE (result.failed()); + + auto file = result.getValue(); + ASSERT_NE (nullptr, file.get()); + EXPECT_NE (nullptr, file->getRiveFile()); +} + +TEST_F (ArtboardFileTests, LoadFromMissingFileFails) +{ + const auto missing = getArtboardFileTestDataDirectory().getChildFile ("definitely-not-here.riv"); + + auto result = ArtboardFile::load (missing, factory); + + EXPECT_TRUE (result.failed()); + EXPECT_TRUE (result.getErrorMessage().contains ("find")); +} + +TEST_F (ArtboardFileTests, LoadFromEmptyStreamFails) +{ + MemoryBlock nothing; + MemoryInputStream empty (nothing, false); + + // An unreadable stream reports its own error rather than the misleading + // "Malformed artboard file". + auto result = ArtboardFile::load (empty, factory); + + EXPECT_TRUE (result.failed()); + EXPECT_TRUE (result.getErrorMessage().contains ("read")); +} + +TEST_F (ArtboardFileTests, LoadFromGarbageStreamReportsMalformed) +{ + const char garbage[] = "this is definitely not a rive file, but it is not empty either"; + MemoryInputStream stream (garbage, sizeof (garbage), false); + + auto result = ArtboardFile::load (stream, factory); + + EXPECT_TRUE (result.failed()); + EXPECT_FALSE (result.getErrorMessage().isEmpty()); +} + +TEST_F (ArtboardFileTests, LoadFromInputStreamMatchesLoadFromFile) +{ + auto stream = riveFile.createInputStream(); + ASSERT_NE (nullptr, stream.get()); + + auto fromStream = ArtboardFile::load (*stream, factory); + ASSERT_FALSE (fromStream.failed()); + + auto fromFile = ArtboardFile::load (riveFile, factory); + ASSERT_FALSE (fromFile.failed()); + + EXPECT_EQ (fromFile.getValue()->getNumViewModels(), + fromStream.getValue()->getNumViewModels()); + EXPECT_EQ (fromFile.getValue()->getViewModelNames(), + fromStream.getValue()->getViewModelNames()); +} + +TEST_F (ArtboardFileTests, EachLoadProducesAnIndependentFile) +{ + auto first = ArtboardFile::load (riveFile, factory); + auto second = ArtboardFile::load (riveFile, factory); + + ASSERT_FALSE (first.failed()); + ASSERT_FALSE (second.failed()); + + EXPECT_NE (first.getValue().get(), second.getValue().get()); + EXPECT_NE (first.getValue()->getRiveFile(), second.getValue()->getRiveFile()); +} + +//============================================================================== +// Asset resolution callback +//============================================================================== + +TEST_F (ArtboardFileTests, AssetCallbackIsInvokedForReferencedAssets) +{ + int assetCalls = 0; + StringArray assetNames; + + auto result = ArtboardFile::load (riveFile, + factory, + [&] (const ArtboardFile::AssetInfo& info, Span, rive::Factory&) + { + ++assetCalls; + assetNames.add (info.uniqueName); + return false; + }); + + // Declining every asset is not an import failure. + ASSERT_FALSE (result.failed()); + ASSERT_NE (nullptr, result.getValue().get()); + + if (assetCalls == 0) + { + GTEST_SKIP() << "Test asset references no external assets"; + return; + } + + EXPECT_GT (assetCalls, 0); + EXPECT_EQ (assetCalls, assetNames.size()); +} + +TEST_F (ArtboardFileTests, AssetCallbackReceivesADescribedAsset) +{ + bool sawAnyAsset = false; + + auto result = ArtboardFile::load (riveFile, + factory, + [&] (const ArtboardFile::AssetInfo& info, Span inBandBytes, rive::Factory&) + { + sawAnyAsset = true; + + // uniqueFilename is the authored name decorated with the asset + // id, then the extension: "logo-1234.png". It is a bare file + // name, so it must never look like a path. + EXPECT_FALSE (info.uniqueFilename.containsChar (File::getSeparatorChar())); + + if (info.extension.isNotEmpty()) + EXPECT_TRUE (info.uniqueFilename.endsWith ("." + info.extension)); + + if (info.uniqueName.isNotEmpty()) + EXPECT_TRUE (info.uniqueFilename.startsWith (info.uniqueName)); + + EXPECT_TRUE (inBandBytes.data() != nullptr || inBandBytes.size() == 0); + return false; + }); + + ASSERT_FALSE (result.failed()); + + if (! sawAnyAsset) + GTEST_SKIP() << "Test asset references no external assets"; +} + +TEST_F (ArtboardFileTests, EmptyAssetCallbackFallsBackToInBandLoading) +{ + auto result = ArtboardFile::load (riveFile, factory, nullptr); + + ASSERT_FALSE (result.failed()); + EXPECT_NE (nullptr, result.getValue()->getRiveFile()); +} + +//============================================================================== +// ViewModel schema access +//============================================================================== + +class LoadedArtboardFileTests : public ArtboardFileTests +{ +protected: + void SetUp() override + { + ArtboardFileTests::SetUp(); + + if (::testing::Test::IsSkipped()) + return; + + auto result = ArtboardFile::load (riveFile, factory); + if (result.failed()) + { + GTEST_SKIP() << "Failed to load test asset: " << result.getErrorMessage(); + return; + } + + artboardFile = result.getValue(); + } + + std::shared_ptr artboardFile; +}; + +TEST_F (LoadedArtboardFileTests, ViewModelNamesMatchTheirCount) +{ + const auto names = artboardFile->getViewModelNames(); + + EXPECT_EQ (artboardFile->getNumViewModels(), names.size()); +} + +TEST_F (LoadedArtboardFileTests, EveryViewModelIsReachableByIndexAndName) +{ + const auto names = artboardFile->getViewModelNames(); + + if (names.isEmpty()) + { + GTEST_SKIP() << "Test asset exposes no ViewModel schemas"; + return; + } + + for (int index = 0; index < names.size(); ++index) + { + auto byIndex = artboardFile->getArtboardViewModelAt (index); + ASSERT_NE (nullptr, byIndex.get()); + EXPECT_EQ (names[index], byIndex->getName()); + + auto byName = artboardFile->getArtboardViewModel (names[index]); + ASSERT_NE (nullptr, byName.get()); + EXPECT_EQ (names[index], byName->getName()); + + EXPECT_EQ (artboardFile.get(), byIndex->getArtboardFile()); + } +} + +TEST_F (LoadedArtboardFileTests, UnknownViewModelsResolveToNull) +{ + EXPECT_EQ (nullptr, artboardFile->getArtboardViewModel ("NoSuchSchema").get()); + EXPECT_EQ (nullptr, artboardFile->getArtboardViewModelAt (-1).get()); + EXPECT_EQ (nullptr, artboardFile->getArtboardViewModelAt (artboardFile->getNumViewModels() + 10).get()); + + EXPECT_EQ (nullptr, artboardFile->createArtboardViewModelInstance ("NoSuchSchema").get()); + EXPECT_EQ (nullptr, artboardFile->createArtboardViewModelInstance ("NoSuchSchema", "NoSuchInstance").get()); +} + +TEST_F (LoadedArtboardFileTests, HandlesKeepTheFileAlive) +{ + const auto names = artboardFile->getViewModelNames(); + + if (names.isEmpty()) + { + GTEST_SKIP() << "Test asset exposes no ViewModel schemas"; + return; + } + + auto instance = artboardFile->createArtboardViewModelInstance (names[0]); + ASSERT_NE (nullptr, instance.get()); + + auto* rawFile = artboardFile.get(); + + // Dropping the caller's reference must not release the file while a handle + // created from it is still alive. + artboardFile.reset(); + + EXPECT_EQ (rawFile, instance->getArtboardFile()); + EXPECT_NO_THROW (instance->getName()); +} + +TEST_F (LoadedArtboardFileTests, BothRiveFileAccessorsAgree) +{ + const auto* constFile = std::as_const (*artboardFile).getRiveFile(); + + EXPECT_EQ (constFile, artboardFile->getRiveFile()); +} diff --git a/tests/yup_gui/yup_ArtboardNode.cpp b/tests/yup_gui/yup_ArtboardNode.cpp new file mode 100644 index 000000000..dcba2e73c --- /dev/null +++ b/tests/yup_gui/yup_ArtboardNode.cpp @@ -0,0 +1,391 @@ +/* + ============================================================================== + + This file is part of the YUP library. + Copyright (c) 2026 - kunitoki@gmail.com + + YUP is an open source library subject to open-source licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + to use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#include + +#include +#include + +#include "../mocks/rive_gpu.h" + +using namespace yup; + +//============================================================================== +// ArtboardNode type reporting, hierarchy traversal and the invalid-handle +// contract. +// +// Identity, bounds, caching and epoch invalidation are covered against the +// individual fixtures in yup_Artboard.cpp; this file covers the parts of the +// handle API that are independent of which .riv file is loaded. +//============================================================================== + +namespace +{ + +const File getArtboardNodeTestDataDirectory() +{ + // Try source-relative path first (works on desktop builds) + auto dir = File (__FILE__) + .getParentDirectory() + .getParentDirectory() + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir; + + dir = File::getCurrentWorkingDirectory() + .getParentDirectory() + .getParentDirectory() + .getParentDirectory() + .getChildFile ("tests") + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir; + + return File ("/data/rive"); +} + +constexpr const char* kDataBindingRootName = "Artboard"; + +// Every type name yup_ArtboardNode.cpp maps; anything else reports an empty +// string, which is why getTypeKey() is the reliable discriminator. +const StringArray& knownTypeNames() +{ + static const StringArray names { + "Artboard", "LayoutComponent", "Node", "Shape", "Rectangle", "Ellipse", "Image", "Text", "Bone", "Solo", "NestedArtboard" + }; + + return names; +} + +void visitNodeTree (const ArtboardNode::Ptr& node, int depth, const std::function& visit) +{ + if (node == nullptr) + return; + + visit (node, depth); + + // Guard against pathological files; the fixtures are shallow. + if (depth >= 8) + return; + + for (const auto& child : node->getChildren()) + visitNodeTree (child, depth + 1, visit); +} + +} // namespace + +class ArtboardNodeTests : public ::testing::Test +{ +protected: + void SetUp() override + { + const auto file = getArtboardNodeTestDataDirectory().getChildFile ("data-binding.riv"); + if (! file.existsAsFile()) + { + GTEST_SKIP() << "Missing test asset: tests/data/rive/data-binding.riv"; + return; + } + + auto result = ArtboardFile::load (file, factory); + if (result.failed()) + { + GTEST_SKIP() << "Failed to load test asset: " << result.getErrorMessage(); + return; + } + + artboardFile = result.getValue(); + artboard = std::make_unique ("testArtboard", artboardFile); + artboard->setBounds (0.0f, 0.0f, 400.0f, 400.0f); + + for (int i = 0; i < 5; ++i) + artboard->advanceAndApply (0.0f); + + root = artboard->findNode (kDataBindingRootName); + if (root == nullptr) + GTEST_SKIP() << "Test asset exposes no node named 'Artboard'"; + } + + ::testing::NiceMock factory; + std::shared_ptr artboardFile; + std::unique_ptr artboard; + ArtboardNode::Ptr root; +}; + +//============================================================================== +// Type reporting +//============================================================================== + +TEST_F (ArtboardNodeTests, RootReportsTheArtboardType) +{ + EXPECT_TRUE (root->isValid()); + EXPECT_NE (0, root->getTypeKey()); + EXPECT_EQ (String ("Artboard"), root->getTypeName()); +} + +TEST_F (ArtboardNodeTests, EveryReportedTypeNameIsOneOfTheMappedNames) +{ + int visited = 0; + + visitNodeTree (root, + 0, + [&] (const ArtboardNode::Ptr& node, int) + { + ++visited; + + const auto typeName = node->getTypeName(); + + // A valid handle always has a non-zero type key; the type + // name is empty for any type outside the mapped set. + EXPECT_TRUE (node->isValid()); + EXPECT_NE (0, node->getTypeKey()); + EXPECT_TRUE (typeName.isEmpty() || knownTypeNames().contains (typeName)); + }); + + EXPECT_GT (visited, 0); +} + +TEST_F (ArtboardNodeTests, TypeKeyIsStableForTheSameNode) +{ + auto again = artboard->findNode (kDataBindingRootName); + ASSERT_NE (nullptr, again.get()); + + EXPECT_EQ (root->getTypeKey(), again->getTypeKey()); + EXPECT_EQ (root->getTypeName(), again->getTypeName()); +} + +TEST_F (ArtboardNodeTests, IsLayoutAgreesWithTheReportedTypeName) +{ + visitNodeTree (root, + 0, + [] (const ArtboardNode::Ptr& node, int) + { + // Artboards are layout components too, so isLayout() is + // broader than the LayoutComponent type name alone. + if (node->getTypeName() == "LayoutComponent") + EXPECT_TRUE (node->isLayout()); + }); +} + +//============================================================================== +// Hierarchy traversal +//============================================================================== + +TEST_F (ArtboardNodeTests, RootHasNoParentWithinTheArtboard) +{ + EXPECT_EQ (nullptr, root->getParent().get()); +} + +TEST_F (ArtboardNodeTests, ChildrenPointBackAtTheirParent) +{ + const auto children = root->getChildren(); + + if (children.isEmpty()) + { + GTEST_SKIP() << "Test asset root has no children"; + return; + } + + for (const auto& child : children) + { + ASSERT_NE (nullptr, child.get()); + EXPECT_TRUE (child->isValid()); + + auto parent = child->getParent(); + ASSERT_NE (nullptr, parent.get()); + EXPECT_EQ (root->getTypeKey(), parent->getTypeKey()); + EXPECT_EQ (root->getName(), parent->getName()); + } +} + +TEST_F (ArtboardNodeTests, ChildHandlesAreFreshObjectsNotCacheEntries) +{ + const auto first = root->getChildren(); + const auto second = root->getChildren(); + + if (first.isEmpty()) + { + GTEST_SKIP() << "Test asset root has no children"; + return; + } + + ASSERT_EQ (first.size(), second.size()); + + // getChildren() is not name-based, so it does not go through the artboard's + // handle cache; the handles differ but describe the same nodes. + for (int i = 0; i < first.size(); ++i) + { + EXPECT_EQ (first[i]->getTypeKey(), second[i]->getTypeKey()); + EXPECT_EQ (first[i]->getName(), second[i]->getName()); + } +} + +TEST_F (ArtboardNodeTests, LeafNodesReportNoChildren) +{ + int leaves = 0; + + visitNodeTree (root, + 0, + [&] (const ArtboardNode::Ptr& node, int) + { + if (node->getChildren().isEmpty()) + ++leaves; + }); + + EXPECT_GT (leaves, 0); +} + +//============================================================================== +// Transforms +//============================================================================== + +TEST_F (ArtboardNodeTests, ViewTransformIncorporatesTheArtboardFit) +{ + const auto world = root->getWorldTransform(); + const auto view = root->getViewTransform(); + + // Both are finite, and resizing the artboard changes the view transform while + // leaving the world transform alone. + EXPECT_TRUE (std::isfinite (view.getTranslateX())); + EXPECT_TRUE (std::isfinite (view.getTranslateY())); + + artboard->setBounds (0.0f, 0.0f, 800.0f, 800.0f); + artboard->advanceAndApply (0.0f); + + EXPECT_EQ (world.getScaleX(), root->getWorldTransform().getScaleX()); + EXPECT_FALSE (root->getViewTransform().approximatelyEqualTo (view)); +} + +TEST_F (ArtboardNodeTests, LocalTransformIsFinite) +{ + const auto local = root->getLocalTransform(); + + EXPECT_TRUE (std::isfinite (local.getScaleX())); + EXPECT_TRUE (std::isfinite (local.getScaleY())); + EXPECT_TRUE (std::isfinite (local.getTranslateX())); + EXPECT_TRUE (std::isfinite (local.getTranslateY())); +} + +//============================================================================== +// The invalid-handle contract +//============================================================================== + +TEST_F (ArtboardNodeTests, InvalidHandleReturnsSafeDefaultsFromEveryAccessor) +{ + auto child = root->getChildren().getFirst(); + + artboard->clear(); + + ASSERT_FALSE (root->isValid()); + + EXPECT_TRUE (root->getName().isEmpty()); + EXPECT_EQ (0, root->getTypeKey()); + EXPECT_TRUE (root->getTypeName().isEmpty()); + EXPECT_FALSE (root->isLayout()); + EXPECT_TRUE (root->getBounds().isEmpty()); + EXPECT_TRUE (root->getLocalTransform().isIdentity()); + EXPECT_TRUE (root->getWorldTransform().isIdentity()); + EXPECT_TRUE (root->getViewTransform().isIdentity()); + EXPECT_EQ (nullptr, root->getParent().get()); + EXPECT_TRUE (root->getChildren().isEmpty()); + + if (child != nullptr) + { + EXPECT_FALSE (child->isValid()); + EXPECT_TRUE (child->getName().isEmpty()); + EXPECT_EQ (0, child->getTypeKey()); + } +} + +TEST_F (ArtboardNodeTests, HandlesOutliveTheirArtboardSafely) +{ + auto child = root->getChildren().getFirst(); + + artboard.reset(); + + EXPECT_FALSE (root->isValid()); + EXPECT_TRUE (root->getName().isEmpty()); + EXPECT_TRUE (root->getBounds().isEmpty()); + EXPECT_EQ (nullptr, root->getParent().get()); + + if (child != nullptr) + { + EXPECT_FALSE (child->isValid()); + EXPECT_TRUE (child->getChildren().isEmpty()); + } +} + +TEST_F (ArtboardNodeTests, ReplacingTheFileInvalidatesHandlesTakenBefore) +{ + const auto before = root; + + artboard->setFile (artboardFile); + + EXPECT_FALSE (before->isValid()); + + auto after = artboard->findNode (kDataBindingRootName); + ASSERT_NE (nullptr, after.get()); + EXPECT_TRUE (after->isValid()); + EXPECT_NE (before.get(), after.get()); +} + +//============================================================================== +// Geometry across the whole tree +//============================================================================== + +TEST_F (ArtboardNodeTests, EveryNodeReportsFiniteBoundsAndTransforms) +{ + int visited = 0; + + // computeNodeBounds() takes a different path for layout components, shapes + // and everything else, and the transform accessors take a different one for + // nodes that carry no transform. Walking the whole tree exercises whichever + // of those the fixture's node types reach, which the type-only walks above + // do not. + visitNodeTree (root, + 0, + [&] (const ArtboardNode::Ptr& node, int) + { + ++visited; + + const auto bounds = node->getBounds(); + EXPECT_TRUE (std::isfinite (bounds.getX())); + EXPECT_TRUE (std::isfinite (bounds.getY())); + EXPECT_TRUE (std::isfinite (bounds.getWidth())); + EXPECT_TRUE (std::isfinite (bounds.getHeight())); + + const auto local = node->getLocalTransform(); + const auto world = node->getWorldTransform(); + const auto view = node->getViewTransform(); + + EXPECT_TRUE (std::isfinite (local.getTranslateX())); + EXPECT_TRUE (std::isfinite (world.getTranslateX())); + EXPECT_TRUE (std::isfinite (view.getTranslateX())); + + // Every node but the artboard root reports a parent. + EXPECT_TRUE (node->getParent() != nullptr || node.get() == root.get()); + }); + + EXPECT_GT (visited, 0); +} diff --git a/tests/yup_gui/yup_ArtboardViewModel.cpp b/tests/yup_gui/yup_ArtboardViewModel.cpp new file mode 100644 index 000000000..9b237c19a --- /dev/null +++ b/tests/yup_gui/yup_ArtboardViewModel.cpp @@ -0,0 +1,721 @@ +/* + ============================================================================== + + This file is part of the YUP library. + Copyright (c) 2026 - kunitoki@gmail.com + + YUP is an open source library subject to open-source licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + to use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#include + +#include +#include + +#include "../mocks/rive_gpu.h" + +using namespace yup; + +//============================================================================== +// ArtboardViewModel schema handle against tests/data/rive/responsive-sliders.riv +// +// responsive-sliders.riv is the Rive data binding showcase: two ViewModel +// schemas ("Slider_instance" and "Main"), authored instances per schema, and a +// "Main" artboard designed against the "Main" viewmodel. These tests assert the +// concrete schema contents of that fixture. +//============================================================================== + +namespace +{ + +const File findResponsiveSlidersSchemaTestFile() +{ + // Try source-relative path first (works on desktop builds) + auto dir = File (__FILE__) + .getParentDirectory() + .getParentDirectory() + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir.getChildFile ("responsive-sliders.riv"); + + dir = File::getCurrentWorkingDirectory() + .getParentDirectory() + .getParentDirectory() + .getParentDirectory() + .getChildFile ("tests") + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir.getChildFile ("responsive-sliders.riv"); + + return File ("/data/rive/responsive-sliders.riv"); +} + +} // namespace + +class ResponsiveSlidersSchemaTests : public ::testing::Test +{ +protected: + void SetUp() override + { + const auto file = findResponsiveSlidersSchemaTestFile(); + if (! file.existsAsFile()) + { + GTEST_SKIP() << "Missing test asset: tests/data/rive/responsive-sliders.riv"; + return; + } + + auto result = ArtboardFile::load (file, factory); + if (result.failed()) + { + GTEST_SKIP() << "Failed to load test asset: " << result.getErrorMessage(); + return; + } + + artboardFile = result.getValue(); + } + + ::testing::NiceMock factory; + std::shared_ptr artboardFile; +}; + +//============================================================================== + +TEST_F (ResponsiveSlidersSchemaTests, FileReportsBothViewModelsInFileOrder) +{ + ASSERT_EQ (2, artboardFile->getNumViewModels()); + + const auto names = artboardFile->getViewModelNames(); + ASSERT_EQ (2, names.size()); + EXPECT_EQ (String ("Slider_instance"), names[0]); + EXPECT_EQ (String ("Main"), names[1]); +} + +TEST_F (ResponsiveSlidersSchemaTests, SliderInstanceSchemaExposesTypedProperties) +{ + auto viewModel = artboardFile->getArtboardViewModel ("Slider_instance"); + ASSERT_NE (nullptr, viewModel.get()); + EXPECT_EQ (String ("Slider_instance"), viewModel->getName()); + EXPECT_EQ (artboardFile.get(), viewModel->getArtboardFile()); + + ASSERT_EQ (5, viewModel->getNumProperties()); + + EXPECT_EQ (String ("Color_Main"), viewModel->getPropertyAt (0).name); + EXPECT_EQ (ArtboardViewModel::PropertyType::color, viewModel->getPropertyAt (0).type); + + const String numberNames[] = { "max", "min", "slider_value", "width" }; + for (int i = 0; i < 4; ++i) + { + const auto info = viewModel->getPropertyAt (i + 1); + EXPECT_EQ (numberNames[i], info.name); + EXPECT_EQ (ArtboardViewModel::PropertyType::number, info.type); + EXPECT_TRUE (info.enumValues.isEmpty()); + } + + EXPECT_TRUE (viewModel->hasProperty ("slider_value")); + EXPECT_FALSE (viewModel->hasProperty ("nonexistentProperty")); +} + +TEST_F (ResponsiveSlidersSchemaTests, MainSchemaExposesNestedAndNumberProperties) +{ + auto viewModel = artboardFile->getArtboardViewModel ("Main"); + ASSERT_NE (nullptr, viewModel.get()); + EXPECT_EQ (String ("Main"), viewModel->getName()); + + ASSERT_EQ (8, viewModel->getNumProperties()); + + const String nestedNames[] = { "ins_slider_Sugar", "ins_slider_Water", "ins_slider_Flour", "ins_slider_Carrots" }; + for (int i = 0; i < 4; ++i) + { + const auto info = viewModel->getPropertyAt (i); + EXPECT_EQ (nestedNames[i], info.name); + EXPECT_EQ (ArtboardViewModel::PropertyType::viewModel, info.type); + } + + const String numberNames[] = { "Sugar", "Carrots", "Flour", "Water" }; + for (int i = 0; i < 4; ++i) + { + const auto info = viewModel->getPropertyAt (i + 4); + EXPECT_EQ (numberNames[i], info.name); + EXPECT_EQ (ArtboardViewModel::PropertyType::number, info.type); + } +} + +TEST_F (ResponsiveSlidersSchemaTests, AuthoredInstancesAreExposedPerSchema) +{ + auto sliderInstanceSchema = artboardFile->getArtboardViewModel ("Slider_instance"); + ASSERT_NE (nullptr, sliderInstanceSchema.get()); + ASSERT_EQ (4, sliderInstanceSchema->getNumInstances()); + + const auto sliderNames = sliderInstanceSchema->getInstanceNames(); + ASSERT_EQ (4, sliderNames.size()); + EXPECT_EQ (String ("Sugar"), sliderNames[0]); + EXPECT_EQ (String ("Carrots"), sliderNames[1]); + EXPECT_EQ (String ("Water"), sliderNames[2]); + EXPECT_EQ (String ("Flour"), sliderNames[3]); + + auto mainSchema = artboardFile->getArtboardViewModel ("Main"); + ASSERT_NE (nullptr, mainSchema.get()); + ASSERT_EQ (1, mainSchema->getNumInstances()); + + const auto mainNames = mainSchema->getInstanceNames(); + ASSERT_EQ (1, mainNames.size()); + EXPECT_EQ (String ("Main"), mainNames[0]); +} + +TEST_F (ResponsiveSlidersSchemaTests, CloningAuthoredInstancesSucceedsByName) +{ + auto sliderInstanceSchema = artboardFile->getArtboardViewModel ("Slider_instance"); + ASSERT_NE (nullptr, sliderInstanceSchema.get()); + + for (const auto& instanceName : sliderInstanceSchema->getInstanceNames()) + { + auto instance = artboardFile->createArtboardViewModelInstance ("Slider_instance", instanceName); + ASSERT_NE (nullptr, instance.get()); + EXPECT_EQ (String ("Slider_instance"), instance->getName()); + } + + auto mainInstance = artboardFile->createArtboardViewModelInstance ("Main", "Main"); + ASSERT_NE (nullptr, mainInstance.get()); + EXPECT_EQ (String ("Main"), mainInstance->getName()); +} + +TEST_F (ResponsiveSlidersSchemaTests, IndexAndNamePropertyLookupsAgree) +{ + for (const auto& viewModelName : artboardFile->getViewModelNames()) + { + auto viewModel = artboardFile->getArtboardViewModel (viewModelName); + ASSERT_NE (nullptr, viewModel.get()); + + for (int i = 0; i < viewModel->getNumProperties(); ++i) + { + const auto byIndex = viewModel->getPropertyAt (i); + const auto byName = viewModel->getProperty (byIndex.name); + + EXPECT_EQ (byName.name, byIndex.name); + EXPECT_EQ (byName.type, byIndex.type); + EXPECT_EQ (byName.enumValues.size(), byIndex.enumValues.size()); + } + + EXPECT_EQ (ArtboardViewModel::PropertyType::none, viewModel->getProperty ("nonexistentProperty").type); + EXPECT_EQ (nullptr, artboardFile->getArtboardViewModel ("nonexistentViewModel").get()); + } +} + +TEST_F (ResponsiveSlidersSchemaTests, SchemasExposeNoEnumOrTriggerProperties) +{ + for (const auto& viewModelName : artboardFile->getViewModelNames()) + { + auto viewModel = artboardFile->getArtboardViewModel (viewModelName); + ASSERT_NE (nullptr, viewModel.get()); + + for (int i = 0; i < viewModel->getNumProperties(); ++i) + { + EXPECT_NE (ArtboardViewModel::PropertyType::enumType, viewModel->getPropertyAt (i).type); + EXPECT_NE (ArtboardViewModel::PropertyType::trigger, viewModel->getPropertyAt (i).type); + } + } +} + +//============================================================================== +// ArtboardViewModel schema handle against tests/data/rive/data-binding.riv +// +// These tests discover the fixture's viewmodel content dynamically so they +// pass for any .riv that ships at least one ViewModel schema; they skip when +// the fixture exposes none. +//============================================================================== + +namespace +{ + +const File findViewModelSchemaTestFile() +{ + // Try source-relative path first (works on desktop builds) + auto dir = File (__FILE__) + .getParentDirectory() + .getParentDirectory() + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir.getChildFile ("data-binding.riv"); + + dir = File::getCurrentWorkingDirectory() + .getParentDirectory() + .getParentDirectory() + .getParentDirectory() + .getChildFile ("tests") + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir.getChildFile ("data-binding.riv"); + + return File ("/data/rive/data-binding.riv"); +} + +} // namespace + +class ArtboardViewModelSchemaTests : public ::testing::Test +{ +protected: + void SetUp() override + { + const auto file = findViewModelSchemaTestFile(); + if (! file.existsAsFile()) + { + GTEST_SKIP() << "Missing test asset: tests/data/rive/data-binding.riv"; + return; + } + + auto result = ArtboardFile::load (file, factory); + if (result.failed()) + { + GTEST_SKIP() << "Failed to load test asset: " << result.getErrorMessage(); + return; + } + + artboardFile = result.getValue(); + + if (artboardFile->getNumViewModels() == 0) + { + GTEST_SKIP() << "Test asset exposes no ViewModel schemas: tests/data/rive/data-binding.riv"; + return; + } + } + + ::testing::NiceMock factory; + std::shared_ptr artboardFile; +}; + +//============================================================================== + +TEST_F (ArtboardViewModelSchemaTests, FileReportsConsistentViewModelNames) +{ + const auto names = artboardFile->getViewModelNames(); + + EXPECT_EQ (names.size(), artboardFile->getNumViewModels()); + EXPECT_GT (names.size(), 0); + + for (int i = 0; i < names.size(); ++i) + { + auto viewModel = artboardFile->getArtboardViewModelAt (i); + ASSERT_NE (nullptr, viewModel.get()); + EXPECT_EQ (viewModel->getName(), names[i]); + EXPECT_EQ (viewModel->getArtboardFile(), artboardFile.get()); + } +} + +TEST_F (ArtboardViewModelSchemaTests, UnknownViewModelsReturnNull) +{ + EXPECT_EQ (nullptr, artboardFile->getArtboardViewModelAt (artboardFile->getNumViewModels()).get()); + EXPECT_EQ (nullptr, artboardFile->getArtboardViewModel ("nonexistentViewModel").get()); +} + +TEST_F (ArtboardViewModelSchemaTests, PropertyInfoMatchesIndexAndNameLookup) +{ + auto viewModel = artboardFile->getArtboardViewModelAt (0); + ASSERT_NE (nullptr, viewModel.get()); + + for (int i = 0; i < viewModel->getNumProperties(); ++i) + { + const auto byIndex = viewModel->getPropertyAt (i); + const auto byName = viewModel->getProperty (byIndex.name); + + EXPECT_FALSE (byIndex.name.isEmpty()); + EXPECT_EQ (byName.name, byIndex.name); + EXPECT_EQ (byName.type, byIndex.type); + EXPECT_EQ (byName.isInput, byIndex.isInput); + EXPECT_EQ (byName.isOutput, byIndex.isOutput); + EXPECT_TRUE (viewModel->hasProperty (byIndex.name)); + } +} + +TEST_F (ArtboardViewModelSchemaTests, UnknownPropertiesReportEmptyInfo) +{ + auto viewModel = artboardFile->getArtboardViewModelAt (0); + ASSERT_NE (nullptr, viewModel.get()); + + const auto unknown = viewModel->getProperty ("nonexistentProperty"); + + EXPECT_TRUE (unknown.name.isEmpty()); + EXPECT_EQ (ArtboardViewModel::PropertyType::none, unknown.type); + EXPECT_FALSE (viewModel->hasProperty ("nonexistentProperty")); +} + +TEST_F (ArtboardViewModelSchemaTests, AuthoredInstancesAreReportedConsistently) +{ + auto viewModel = artboardFile->getArtboardViewModelAt (0); + ASSERT_NE (nullptr, viewModel.get()); + + const auto instanceNames = viewModel->getInstanceNames(); + EXPECT_EQ (instanceNames.size(), viewModel->getNumInstances()); + + // When the fixture ships authored instances, cloning by name must succeed. + if (instanceNames.isEmpty()) + { + GTEST_SKIP() << "Test asset exposes no authored viewmodel instances"; + return; + } + + auto instance = artboardFile->createArtboardViewModelInstance (viewModel->getName(), instanceNames[0]); + ASSERT_NE (nullptr, instance.get()); + EXPECT_EQ (instance->getName(), viewModel->getName()); +} + +//============================================================================== +// Schema type coverage across every shipped fixture +// +// The schema tests above bind to one fixture at a time. This walks every schema +// of every fixture so toPropertyType() runs for each property type the fixtures +// actually provide, rather than only the ones the two fixtures above expose. +//============================================================================== + +namespace +{ + +const StringArray& allSchemaFixtureFileNames() +{ + static const StringArray names { "data-binding.riv", "game-animation.riv", "layout-ui.riv", "responsive-sliders.riv", "viewmodel-lab.riv" }; + return names; +} + +const File findAllSchemaFixturesDirectory() +{ + auto dir = File (__FILE__) + .getParentDirectory() + .getParentDirectory() + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir; + + dir = File::getCurrentWorkingDirectory() + .getParentDirectory() + .getParentDirectory() + .getParentDirectory() + .getChildFile ("tests") + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir; + + return File ("/data/rive"); +} + +// viewmodel-lab.riv ships ScriptAssets, which the Rive runtime can only import when +// it is compiled with WITH_RIVE_SCRIPTING (the rive module's `defines:`): without it +// a script's in-band FileAssetContents is routed to the previous asset's importer and +// trips the assert in FileAssetImporter::onFileAssetContents. +constexpr bool isViewModelLabSchemaScriptingAvailable() noexcept +{ +#ifdef WITH_RIVE_SCRIPTING + return true; +#else + return false; +#endif +} + +} // namespace + +TEST (ArtboardViewModelFixtureCoverage, EverySchemaPropertyReportsAConsistentType) +{ + const auto directory = findAllSchemaFixturesDirectory(); + + for (const auto& fileName : allSchemaFixtureFileNames()) + { + const auto file = directory.getChildFile (fileName); + if (! file.existsAsFile()) + continue; + + // A scripting-less Rive build cannot import the ScriptAssets of this + // fixture, so it is covered by ViewModelLabSchemaTests instead. + if (fileName == "viewmodel-lab.riv" && ! isViewModelLabSchemaScriptingAvailable()) + continue; + + ::testing::NiceMock factory; + auto result = ArtboardFile::load (file, factory); + if (result.failed()) + continue; + + auto artboardFile = result.getValue(); + + EXPECT_EQ (artboardFile->getNumViewModels(), artboardFile->getViewModelNames().size()); + + for (const auto& viewModelName : artboardFile->getViewModelNames()) + { + auto viewModel = artboardFile->getArtboardViewModel (viewModelName); + ASSERT_NE (nullptr, viewModel.get()); + EXPECT_EQ (viewModelName, viewModel->getName()); + EXPECT_EQ (artboardFile.get(), viewModel->getArtboardFile()); + + const auto instanceNames = viewModel->getInstanceNames(); + EXPECT_EQ (viewModel->getNumInstances(), instanceNames.size()); + + for (int i = 0; i < viewModel->getNumProperties(); ++i) + { + const auto byIndex = viewModel->getPropertyAt (i); + ASSERT_FALSE (byIndex.name.isEmpty()); + + const auto byName = viewModel->getProperty (byIndex.name); + EXPECT_EQ (byIndex.name, byName.name); + EXPECT_EQ (byIndex.type, byName.type); + EXPECT_EQ (byIndex.isInput, byName.isInput); + EXPECT_EQ (byIndex.isOutput, byName.isOutput); + EXPECT_EQ (byIndex.enumValues.size(), byName.enumValues.size()); + + EXPECT_TRUE (viewModel->hasProperty (byIndex.name)); + + // Enum options are only ever populated for enum-typed properties. + if (byIndex.type != ArtboardViewModel::PropertyType::enumType) + EXPECT_TRUE (byIndex.enumValues.isEmpty()); + } + + EXPECT_FALSE (viewModel->hasProperty ("definitelyNotAProperty")); + EXPECT_EQ (ArtboardViewModel::PropertyType::none, viewModel->getProperty ("definitelyNotAProperty").type); + } + } +} + +//============================================================================== +// ArtboardViewModel schema handle against tests/data/rive/viewmodel-lab.riv +// +// viewmodel-lab.riv is the ViewModel coverage fixture: four schemas authored in +// a known order, authored instances per schema, and a "Lab" schema declaring +// every property type the API models. The expected content asserted below is +// the one tools/rive_inspect.py reports for the fixture. +//============================================================================== + +namespace +{ + +const File findViewModelLabSchemaTestFile() +{ + // Try source-relative path first (works on desktop builds) + auto dir = File (__FILE__) + .getParentDirectory() + .getParentDirectory() + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir.getChildFile ("viewmodel-lab.riv"); + + dir = File::getCurrentWorkingDirectory() + .getParentDirectory() + .getParentDirectory() + .getParentDirectory() + .getChildFile ("tests") + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir.getChildFile ("viewmodel-lab.riv"); + + return File ("/data/rive/viewmodel-lab.riv"); +} + +} // namespace + +class ViewModelLabSchemaTests : public ::testing::Test +{ +protected: + void SetUp() override + { + if (! isViewModelLabSchemaScriptingAvailable()) + { + GTEST_SKIP() << "tests/data/rive/viewmodel-lab.riv ships ScriptAssets, which needs Rive built with WITH_RIVE_SCRIPTING"; + return; + } + + const auto file = findViewModelLabSchemaTestFile(); + if (! file.existsAsFile()) + { + GTEST_SKIP() << "Missing test asset: tests/data/rive/viewmodel-lab.riv"; + return; + } + + auto result = ArtboardFile::load (file, factory); + if (result.failed()) + { + GTEST_SKIP() << "Failed to load test asset: " << result.getErrorMessage(); + return; + } + + artboardFile = result.getValue(); + } + + ::testing::NiceMock factory; + std::shared_ptr artboardFile; +}; + +//============================================================================== + +TEST_F (ViewModelLabSchemaTests, FileReportsEverySchemaInFileOrder) +{ + ASSERT_EQ (4, artboardFile->getNumViewModels()); + + const auto names = artboardFile->getViewModelNames(); + ASSERT_EQ (4, names.size()); + EXPECT_EQ (String ("Details"), names[0]); + EXPECT_EQ (String ("Row"), names[1]); + EXPECT_EQ (String ("Panel"), names[2]); + EXPECT_EQ (String ("Lab"), names[3]); +} + +TEST_F (ViewModelLabSchemaTests, LabSchemaDeclaresEveryPropertyType) +{ + auto viewModel = artboardFile->getArtboardViewModel ("Lab"); + ASSERT_NE (nullptr, viewModel.get()); + EXPECT_EQ (String ("Lab"), viewModel->getName()); + EXPECT_EQ (artboardFile.get(), viewModel->getArtboardFile()); + + struct ExpectedProperty + { + const char* name; + ArtboardViewModel::PropertyType type; + }; + + const ExpectedProperty expected[] = { + { "title", ArtboardViewModel::PropertyType::string }, + { "count", ArtboardViewModel::PropertyType::number }, + { "progress", ArtboardViewModel::PropertyType::number }, + { "enabled", ArtboardViewModel::PropertyType::boolean }, + { "accent", ArtboardViewModel::PropertyType::color }, + { "status", ArtboardViewModel::PropertyType::enumType }, + { "ping", ArtboardViewModel::PropertyType::trigger }, + { "note", ArtboardViewModel::PropertyType::string }, + { "details", ArtboardViewModel::PropertyType::viewModel }, + { "rows", ArtboardViewModel::PropertyType::list }, + { "icon", ArtboardViewModel::PropertyType::assetImage }, + { "panel", ArtboardViewModel::PropertyType::artboard }, + { "panelData", ArtboardViewModel::PropertyType::viewModel }, + }; + + ASSERT_EQ (13, viewModel->getNumProperties()); + + int index = 0; + for (const auto& expectedProperty : expected) + { + const auto info = viewModel->getPropertyAt (index++); + + EXPECT_EQ (String (expectedProperty.name), info.name); + EXPECT_EQ (expectedProperty.type, info.type); + EXPECT_TRUE (viewModel->hasProperty (expectedProperty.name)); + } +} + +TEST_F (ViewModelLabSchemaTests, EnumPropertyExposesItsDisplayValues) +{ + auto viewModel = artboardFile->getArtboardViewModel ("Lab"); + ASSERT_NE (nullptr, viewModel.get()); + + const auto status = viewModel->getProperty ("status"); + EXPECT_EQ (ArtboardViewModel::PropertyType::enumType, status.type); + + ASSERT_EQ (3, status.enumValues.size()); + EXPECT_EQ (String ("Idle"), status.enumValues[0]); + EXPECT_EQ (String ("Running"), status.enumValues[1]); + EXPECT_EQ (String ("Failed"), status.enumValues[2]); + + // Options are only ever reported for enum properties. + EXPECT_TRUE (viewModel->getProperty ("title").enumValues.isEmpty()); + EXPECT_TRUE (viewModel->getProperty ("count").enumValues.isEmpty()); + EXPECT_TRUE (viewModel->getProperty ("ping").enumValues.isEmpty()); +} + +TEST_F (ViewModelLabSchemaTests, RowSchemaPropertiesAreInputs) +{ + auto viewModel = artboardFile->getArtboardViewModel ("Row"); + ASSERT_NE (nullptr, viewModel.get()); + ASSERT_EQ (4, viewModel->getNumProperties()); + + struct ExpectedProperty + { + const char* name; + ArtboardViewModel::PropertyType type; + bool isInput; + }; + + const ExpectedProperty expected[] = { + { "label", ArtboardViewModel::PropertyType::string, true }, + { "amount", ArtboardViewModel::PropertyType::number, true }, + { "done", ArtboardViewModel::PropertyType::boolean, true }, + { "index", ArtboardViewModel::PropertyType::symbolListIndex, false }, + }; + + int index = 0; + for (const auto& expectedProperty : expected) + { + const auto info = viewModel->getPropertyAt (index++); + + EXPECT_EQ (String (expectedProperty.name), info.name); + EXPECT_EQ (expectedProperty.type, info.type); + EXPECT_EQ (expectedProperty.isInput, info.isInput); + EXPECT_FALSE (info.isOutput); + } + + // "Lab" drives an artboard of its own, so nothing is annotated as a binding. + auto lab = artboardFile->getArtboardViewModel ("Lab"); + ASSERT_NE (nullptr, lab.get()); + + for (int i = 0; i < lab->getNumProperties(); ++i) + { + EXPECT_FALSE (lab->getPropertyAt (i).isInput); + EXPECT_FALSE (lab->getPropertyAt (i).isOutput); + } +} + +TEST_F (ViewModelLabSchemaTests, EverySchemaReportsItsAuthoredInstances) +{ + const auto expectInstanceNames = [] (const ArtboardViewModel::Ptr& viewModel, const StringArray& expected) + { + ASSERT_NE (nullptr, viewModel.get()); + + const auto names = viewModel->getInstanceNames(); + ASSERT_EQ (expected.size(), names.size()); + EXPECT_EQ (expected.size(), viewModel->getNumInstances()); + + for (int i = 0; i < expected.size(); ++i) + EXPECT_EQ (expected[i], names[i]); + }; + + expectInstanceNames (artboardFile->getArtboardViewModel ("Details"), StringArray { "Default" }); + expectInstanceNames (artboardFile->getArtboardViewModel ("Row"), StringArray ({ "Default", "Alpha", "Beta", "Gamma" })); + expectInstanceNames (artboardFile->getArtboardViewModel ("Panel"), StringArray { "Default" }); + expectInstanceNames (artboardFile->getArtboardViewModel ("Lab"), StringArray ({ "Default", "Preset" })); +} + +TEST_F (ViewModelLabSchemaTests, EveryAuthoredInstanceClonesUnderItsSchemaName) +{ + for (const auto& schemaName : artboardFile->getViewModelNames()) + { + auto viewModel = artboardFile->getArtboardViewModel (schemaName); + ASSERT_NE (nullptr, viewModel.get()); + + for (const auto& instanceName : viewModel->getInstanceNames()) + { + auto instance = artboardFile->createArtboardViewModelInstance (viewModel->getName(), instanceName); + ASSERT_NE (nullptr, instance.get()) << instanceName; + EXPECT_EQ (viewModel->getName(), instance->getName()); + EXPECT_EQ (artboardFile.get(), instance->getArtboardFile()); + } + } +} diff --git a/tests/yup_gui/yup_ArtboardViewModelInstance.cpp b/tests/yup_gui/yup_ArtboardViewModelInstance.cpp new file mode 100644 index 000000000..f49816b29 --- /dev/null +++ b/tests/yup_gui/yup_ArtboardViewModelInstance.cpp @@ -0,0 +1,1703 @@ +/* + ============================================================================== + + This file is part of the YUP library. + Copyright (c) 2026 - kunitoki@gmail.com + + YUP is an open source library subject to open-source licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + to use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#include + +#include +#include + +#include "../mocks/rive_gpu.h" + +using namespace yup; + +//============================================================================== +// ArtboardViewModelInstance handle against tests/data/rive/data-binding.riv +// +// These tests discover the fixture's viewmodel content dynamically so they +// pass for any .riv that ships at least one ViewModel schema; they skip when +// the fixture exposes none or does not contain properties of a given type. +//============================================================================== + +namespace +{ + +const File findViewModelInstanceTestFile() +{ + // Try source-relative path first (works on desktop builds) + auto dir = File (__FILE__) + .getParentDirectory() + .getParentDirectory() + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir.getChildFile ("data-binding.riv"); + + dir = File::getCurrentWorkingDirectory() + .getParentDirectory() + .getParentDirectory() + .getParentDirectory() + .getChildFile ("tests") + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir.getChildFile ("data-binding.riv"); + + return File ("/data/rive/data-binding.riv"); +} + +} // namespace + +class ArtboardViewModelInstanceTests : public ::testing::Test +{ +protected: + void SetUp() override + { + const auto file = findViewModelInstanceTestFile(); + if (! file.existsAsFile()) + { + GTEST_SKIP() << "Missing test asset: tests/data/rive/data-binding.riv"; + return; + } + + auto result = ArtboardFile::load (file, factory); + if (result.failed()) + { + GTEST_SKIP() << "Failed to load test asset: " << result.getErrorMessage(); + return; + } + + artboardFile = result.getValue(); + + if (artboardFile->getNumViewModels() == 0) + { + GTEST_SKIP() << "Test asset exposes no ViewModel schemas: tests/data/rive/data-binding.riv"; + return; + } + + viewModel = artboardFile->getArtboardViewModelAt (0); + if (viewModel == nullptr) + { + GTEST_SKIP() << "Test asset exposes no viewmodel at index 0"; + return; + } + + instance = artboardFile->createArtboardViewModelInstance (viewModel->getName()); + if (instance == nullptr) + { + GTEST_SKIP() << "Failed to create a viewmodel instance from the test asset"; + return; + } + } + + String findPropertyName (ArtboardViewModel::PropertyType type) const + { + for (int i = 0; i < viewModel->getNumProperties(); ++i) + if (viewModel->getPropertyAt (i).type == type) + return viewModel->getPropertyAt (i).name; + + return {}; + } + + ::testing::NiceMock factory; + std::shared_ptr artboardFile; + ArtboardViewModel::Ptr viewModel; + ArtboardViewModelInstance::Ptr instance; +}; + +//============================================================================== + +TEST_F (ArtboardViewModelInstanceTests, HandleReportsItsIdentity) +{ + EXPECT_EQ (instance->getName(), viewModel->getName()); + EXPECT_EQ (instance->getArtboardFile(), artboardFile.get()); +} + +TEST_F (ArtboardViewModelInstanceTests, UnknownPropertiesAreRejectedSafely) +{ + EXPECT_FALSE (instance->hasProperty ("nonexistentProperty")); + EXPECT_TRUE (instance->getProperty ("nonexistentProperty").isVoid()); + EXPECT_EQ (std::nullopt, instance->getBoolProperty ("nonexistentProperty")); + EXPECT_EQ (std::nullopt, instance->getNumberProperty ("nonexistentProperty")); + EXPECT_EQ (std::nullopt, instance->getStringProperty ("nonexistentProperty")); + EXPECT_EQ (std::nullopt, instance->getColorProperty ("nonexistentProperty")); + EXPECT_EQ (std::nullopt, instance->getEnumProperty ("nonexistentProperty")); + EXPECT_FALSE (instance->setBoolProperty ("nonexistentProperty", true)); + EXPECT_FALSE (instance->setNumberProperty ("nonexistentProperty", 1.0)); + EXPECT_FALSE (instance->setStringProperty ("nonexistentProperty", "x")); + EXPECT_FALSE (instance->setColorProperty ("nonexistentProperty", Color (0xFF112233u))); + EXPECT_FALSE (instance->setEnumProperty ("nonexistentProperty", "x")); + EXPECT_FALSE (instance->trigger ("nonexistentProperty")); + EXPECT_FALSE (instance->setProperty ("nonexistentProperty", var (true))); + EXPECT_EQ (-1, instance->getListSize ("nonexistentProperty")); + EXPECT_EQ (nullptr, instance->getNestedInstance ("nonexistentProperty").get()); + EXPECT_EQ (nullptr, instance->getListItem ("nonexistentProperty", 0).get()); +} + +TEST_F (ArtboardViewModelInstanceTests, BooleanPropertyRoundTrips) +{ + const auto name = findPropertyName (ArtboardViewModel::PropertyType::boolean); + if (name.isEmpty()) + { + GTEST_SKIP() << "Test asset exposes no boolean property"; + return; + } + + EXPECT_TRUE (instance->setBoolProperty (name, true)); + EXPECT_EQ (std::optional (true), instance->getBoolProperty (name)); + EXPECT_TRUE (instance->getProperty (name).isBool()); + + EXPECT_TRUE (instance->setProperty (name, var (false))); + EXPECT_EQ (std::optional (false), instance->getBoolProperty (name)); + + // Wrong var type must not clobber the property. + EXPECT_FALSE (instance->setProperty (name, var (42))); + EXPECT_EQ (std::optional (false), instance->getBoolProperty (name)); +} + +TEST_F (ArtboardViewModelInstanceTests, NumberPropertyRoundTrips) +{ + const auto name = findPropertyName (ArtboardViewModel::PropertyType::number); + if (name.isEmpty()) + { + GTEST_SKIP() << "Test asset exposes no number property"; + return; + } + + EXPECT_TRUE (instance->setNumberProperty (name, 12.5)); + EXPECT_EQ (std::optional (12.5), instance->getNumberProperty (name)); + EXPECT_TRUE (instance->getProperty (name).isDouble()); + + EXPECT_TRUE (instance->setProperty (name, var (static_cast (7)))); + EXPECT_EQ (std::optional (7.0), instance->getNumberProperty (name)); + + EXPECT_FALSE (instance->setProperty (name, var ("nope"))); +} + +TEST_F (ArtboardViewModelInstanceTests, StringPropertyRoundTrips) +{ + const auto name = findPropertyName (ArtboardViewModel::PropertyType::string); + if (name.isEmpty()) + { + GTEST_SKIP() << "Test asset exposes no string property"; + return; + } + + EXPECT_TRUE (instance->setStringProperty (name, "hello")); + EXPECT_EQ (std::optional ("hello"), instance->getStringProperty (name)); + EXPECT_TRUE (instance->getProperty (name).isString()); + + EXPECT_TRUE (instance->setProperty (name, var (String ("world")))); + EXPECT_EQ (std::optional ("world"), instance->getStringProperty (name)); + + EXPECT_FALSE (instance->setProperty (name, var (3.14))); +} + +TEST_F (ArtboardViewModelInstanceTests, ColorPropertyRoundTrips) +{ + const auto name = findPropertyName (ArtboardViewModel::PropertyType::color); + if (name.isEmpty()) + { + GTEST_SKIP() << "Test asset exposes no color property"; + return; + } + + const Color expected (0xFF112233u); + EXPECT_TRUE (instance->setColorProperty (name, expected)); + EXPECT_EQ (expected.getARGB(), instance->getColorProperty (name)->getARGB()); + + EXPECT_TRUE (instance->setProperty (name, var (static_cast (expected.getARGB())))); + EXPECT_EQ (expected.getARGB(), instance->getColorProperty (name)->getARGB()); +} + +TEST_F (ArtboardViewModelInstanceTests, EnumPropertyRoundTrips) +{ + const auto name = findPropertyName (ArtboardViewModel::PropertyType::enumType); + if (name.isEmpty()) + { + GTEST_SKIP() << "Test asset exposes no enum property"; + return; + } + + const auto info = viewModel->getProperty (name); + if (info.enumValues.isEmpty()) + { + GTEST_SKIP() << "Test asset's enum property exposes no options"; + return; + } + + const auto& firstOption = info.enumValues[0]; + EXPECT_TRUE (instance->setEnumProperty (name, firstOption)); + EXPECT_EQ (std::optional (firstOption), instance->getEnumProperty (name)); + + EXPECT_FALSE (instance->setEnumProperty (name, "nonexistentOption")); +} + +TEST_F (ArtboardViewModelInstanceTests, TriggerPropertyFires) +{ + const auto name = findPropertyName (ArtboardViewModel::PropertyType::trigger); + if (name.isEmpty()) + { + GTEST_SKIP() << "Test asset exposes no trigger property"; + return; + } + + EXPECT_TRUE (instance->trigger (name)); + EXPECT_FALSE (instance->trigger ("nonexistentProperty")); +} + +TEST_F (ArtboardViewModelInstanceTests, PropertyChangedCallbackFiresOnWrites) +{ + const auto name = findPropertyName (ArtboardViewModel::PropertyType::number); + if (name.isEmpty()) + { + GTEST_SKIP() << "Test asset exposes no number property"; + return; + } + + int callCount = 0; + String lastPath; + var lastValue; + + instance->setPropertyChangedCallback ([&] (ArtboardViewModelInstance&, const String& path, const var& value) + { + ++callCount; + lastPath = path; + lastValue = value; + }); + + EXPECT_TRUE (instance->setNumberProperty (name, 3.25)); + + EXPECT_EQ (1, callCount); + EXPECT_EQ (name, lastPath); + EXPECT_EQ (var (3.25), lastValue); + + // Clearing the callback detaches the observers and stops notifications. + instance->setPropertyChangedCallback ({}); + + EXPECT_TRUE (instance->setNumberProperty (name, 9.0)); + EXPECT_EQ (1, callCount); +} + +TEST_F (ArtboardViewModelInstanceTests, BindViewModelInstanceRequiresMatchingFile) +{ + // An artboard can only bind instances created from its own file. + auto otherFile = ArtboardFile::load (findViewModelInstanceTestFile(), factory); + if (otherFile.failed() || otherFile.getValue()->getNumViewModels() == 0) + { + GTEST_SKIP() << "Test asset is not loadable twice"; + return; + } + + auto foreignInstance = otherFile.getValue()->createArtboardViewModelInstance (viewModel->getName()); + ASSERT_NE (nullptr, foreignInstance.get()); + + Artboard artboard ("bindTest", artboardFile); + artboard.setBounds (0.0f, 0.0f, 200.0f, 200.0f); + + // Foreign file instance must be rejected; same-file instance accepted. + EXPECT_FALSE (artboard.bindViewModelInstance (foreignInstance)); + + // Only exercise binding when the artboard is actually designed against the viewmodel. + if (artboard.getViewModelName() != viewModel->getName()) + { + GTEST_SKIP() << "Test asset's artboard is not designed against a viewmodel"; + return; + } + + EXPECT_TRUE (artboard.bindViewModelInstance (instance)); + EXPECT_EQ (instance.get(), artboard.getBoundViewModelInstance().get()); + + for (int frame = 0; frame < 5; ++frame) + EXPECT_NO_THROW (artboard.advanceAndApply (0.016f)); + + artboard.unbindViewModelInstance(); + EXPECT_EQ (nullptr, artboard.getBoundViewModelInstance().get()); + + // Rebinding after unbind works again. + EXPECT_TRUE (artboard.bindViewModelInstance (instance)); + EXPECT_EQ (instance.get(), artboard.getBoundViewModelInstance().get()); +} + +//============================================================================== +// ArtboardViewModelInstance handle against tests/data/rive/responsive-sliders.riv +// +// responsive-sliders.riv models a recipe with four ingredients, each driven by +// a nested "Slider_instance" viewmodel (color + min/max/slider_value/width), +// aggregated by a "Main" viewmodel (four nested viewmodels + four numbers) with +// an authored "Main" instance. These tests exercise nested viewmodels, dotted +// paths, cross-handle visibility and change callbacks on that real fixture. +//============================================================================== + +namespace +{ + +const File findResponsiveSlidersInstanceTestFile() +{ + // Try source-relative path first (works on desktop builds) + auto dir = File (__FILE__) + .getParentDirectory() + .getParentDirectory() + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir.getChildFile ("responsive-sliders.riv"); + + dir = File::getCurrentWorkingDirectory() + .getParentDirectory() + .getParentDirectory() + .getParentDirectory() + .getChildFile ("tests") + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir.getChildFile ("responsive-sliders.riv"); + + return File ("/data/rive/responsive-sliders.riv"); +} + +} // namespace + +class ResponsiveSlidersInstanceTests : public ::testing::Test +{ +protected: + void SetUp() override + { + const auto file = findResponsiveSlidersInstanceTestFile(); + if (! file.existsAsFile()) + { + GTEST_SKIP() << "Missing test asset: tests/data/rive/responsive-sliders.riv"; + return; + } + + auto result = ArtboardFile::load (file, factory); + if (result.failed()) + { + GTEST_SKIP() << "Failed to load test asset: " << result.getErrorMessage(); + return; + } + + artboardFile = result.getValue(); + + if (artboardFile->getArtboardViewModel ("Main") == nullptr) + { + GTEST_SKIP() << "Test asset exposes no 'Main' viewmodel"; + return; + } + + mainInstance = artboardFile->createArtboardViewModelInstance ("Main", "Main"); + if (mainInstance == nullptr) + { + GTEST_SKIP() << "Failed to clone the authored 'Main' instance"; + return; + } + } + + ArtboardViewModelInstance::Ptr nestedInstance (const String& name) const + { + return mainInstance->getNestedInstance (name); + } + + ::testing::NiceMock factory; + std::shared_ptr artboardFile; + ArtboardViewModelInstance::Ptr mainInstance; +}; + +//============================================================================== + +TEST_F (ResponsiveSlidersInstanceTests, AuthoredMainCloneExposesFullStructure) +{ + EXPECT_EQ (String ("Main"), mainInstance->getName()); + EXPECT_EQ (artboardFile.get(), mainInstance->getArtboardFile()); + + const String nestedNames[] = { "ins_slider_Sugar", "ins_slider_Water", "ins_slider_Flour", "ins_slider_Carrots" }; + for (const auto& nestedName : nestedNames) + { + EXPECT_TRUE (mainInstance->hasProperty (nestedName)); + EXPECT_TRUE (mainInstance->hasProperty (nestedName + ".slider_value")); + EXPECT_TRUE (mainInstance->hasProperty (nestedName + ".Color_Main")); + + auto nested = mainInstance->getNestedInstance (nestedName); + ASSERT_NE (nullptr, nested.get()); + EXPECT_EQ (String ("Slider_instance"), nested->getName()); + EXPECT_EQ (artboardFile.get(), nested->getArtboardFile()); + } + + const String numberNames[] = { "Sugar", "Carrots", "Flour", "Water" }; + for (const auto& numberName : numberNames) + { + EXPECT_TRUE (mainInstance->hasProperty (numberName)); + EXPECT_TRUE (mainInstance->getProperty (numberName).isDouble()); + } +} + +TEST_F (ResponsiveSlidersInstanceTests, SchemaCreatedInstanceDefaultsToZero) +{ + auto fresh = artboardFile->createArtboardViewModelInstance ("Main"); + ASSERT_NE (nullptr, fresh.get()); + + EXPECT_EQ (0.0, fresh->getNumberProperty ("Sugar").value_or (-1.0)); + EXPECT_EQ (0.0, fresh->getNumberProperty ("Carrots").value_or (-1.0)); + + auto nested = fresh->getNestedInstance ("ins_slider_Sugar"); + ASSERT_NE (nullptr, nested.get()); + EXPECT_EQ (0.0, nested->getNumberProperty ("slider_value").value_or (-1.0)); + EXPECT_EQ (0.0, nested->getNumberProperty ("width").value_or (-1.0)); +} + +TEST_F (ResponsiveSlidersInstanceTests, NestedNumbersRoundTripThroughDottedPath) +{ + const String path = "ins_slider_Sugar.slider_value"; + + const auto initial = mainInstance->getNumberProperty (path).value_or (0.0); + + EXPECT_TRUE (mainInstance->setNumberProperty (path, initial + 12.5)); + EXPECT_EQ (std::optional (initial + 12.5), mainInstance->getNumberProperty (path)); + + EXPECT_TRUE (mainInstance->setProperty (path, var (initial + 25.0))); + EXPECT_EQ (std::optional (initial + 25.0), mainInstance->getNumberProperty (path)); +} + +TEST_F (ResponsiveSlidersInstanceTests, NestedValuesAreSharedBetweenHandles) +{ + auto sugar = nestedInstance ("ins_slider_Sugar"); + ASSERT_NE (nullptr, sugar.get()); + + const auto value = sugar->getNumberProperty ("slider_value").value_or (0.0); + + // Writes through the nested handle are visible through the parent's dotted path… + EXPECT_TRUE (sugar->setNumberProperty ("slider_value", value + 3.0)); + EXPECT_EQ (std::optional (value + 3.0), mainInstance->getNumberProperty ("ins_slider_Sugar.slider_value")); + + // …and vice versa. + EXPECT_TRUE (mainInstance->setNumberProperty ("ins_slider_Sugar.slider_value", value + 7.0)); + EXPECT_EQ (std::optional (value + 7.0), sugar->getNumberProperty ("slider_value")); +} + +TEST_F (ResponsiveSlidersInstanceTests, NestedColorRoundTrips) +{ + auto sugar = nestedInstance ("ins_slider_Sugar"); + ASSERT_NE (nullptr, sugar.get()); + + const Color expected (0xFFC05640u); + EXPECT_TRUE (sugar->setColorProperty ("Color_Main", expected)); + EXPECT_EQ (expected.getARGB(), sugar->getColorProperty ("Color_Main")->getARGB()); + + // Same value readable through the parent's dotted path. + EXPECT_EQ (expected.getARGB(), mainInstance->getColorProperty ("ins_slider_Sugar.Color_Main")->getARGB()); + + // Top-level color lookup is unknown on the "Main" schema. + EXPECT_EQ (std::nullopt, mainInstance->getColorProperty ("Color_Main")); +} + +TEST_F (ResponsiveSlidersInstanceTests, AuthoredMinMaxSliderValuesAreSane) +{ + const String nestedNames[] = { "ins_slider_Sugar", "ins_slider_Water", "ins_slider_Flour", "ins_slider_Carrots" }; + for (const auto& nestedName : nestedNames) + { + const auto min = mainInstance->getNumberProperty (nestedName + ".min").value_or (0.0); + const auto max = mainInstance->getNumberProperty (nestedName + ".max").value_or (0.0); + const auto value = mainInstance->getNumberProperty (nestedName + ".slider_value").value_or (0.0); + + EXPECT_LT (min, max); + EXPECT_GE (value, min); + EXPECT_LE (value, max); + } +} + +TEST_F (ResponsiveSlidersInstanceTests, UnknownNestedPathsReturnSafeDefaults) +{ + EXPECT_FALSE (mainInstance->hasProperty ("ins_slider_Sugar.nonexistentProperty")); + EXPECT_FALSE (mainInstance->setNumberProperty ("ins_slider_Sugar.nonexistentProperty", 1.0)); + EXPECT_TRUE (mainInstance->getProperty ("ins_slider_Sugar.nonexistentProperty").isVoid()); + EXPECT_EQ (std::nullopt, mainInstance->getBoolProperty ("ins_slider_Sugar.nonexistentProperty")); + EXPECT_EQ (std::nullopt, mainInstance->getColorProperty ("ins_slider_Sugar.nonexistentProperty")); + EXPECT_EQ (-1, mainInstance->getListSize ("ins_slider_Sugar")); + EXPECT_EQ (nullptr, nestedInstance ("nonexistentNested").get()); +} + +TEST_F (ResponsiveSlidersInstanceTests, PropertyChangedCallbackReportsDottedPaths) +{ + int callCount = 0; + StringArray paths; + Array values; + + mainInstance->setPropertyChangedCallback ([&] (ArtboardViewModelInstance&, const String& path, const var& value) + { + ++callCount; + paths.add (path); + values.add (value); + }); + + const String path = "ins_slider_Water.slider_value"; + const auto initial = mainInstance->getNumberProperty (path).value_or (0.0); + + EXPECT_TRUE (mainInstance->setNumberProperty (path, initial + 4.0)); + + EXPECT_EQ (1, callCount); + ASSERT_EQ (1, paths.size()); + EXPECT_EQ (path, paths[0]); + EXPECT_TRUE (values[0].isDouble()); + + mainInstance->setPropertyChangedCallback ({}); +} + +TEST_F (ResponsiveSlidersInstanceTests, PropertyChangedCallbackFiresForWritesThroughNestedHandle) +{ + int callCount = 0; + String lastPath; + + mainInstance->setPropertyChangedCallback ([&] (ArtboardViewModelInstance&, const String& path, const var&) + { + ++callCount; + lastPath = path; + }); + + auto water = nestedInstance ("ins_slider_Water"); + ASSERT_NE (nullptr, water.get()); + + const auto initial = water->getNumberProperty ("width").value_or (0.0); + EXPECT_TRUE (water->setNumberProperty ("width", initial + 1.0)); + + // The parent observes its whole subtree, so writes through a nested handle + // are reported with the dotted path. + EXPECT_GE (callCount, 1); + EXPECT_EQ (String ("ins_slider_Water.width"), lastPath); + + mainInstance->setPropertyChangedCallback ({}); +} + +//============================================================================== +// Artboard binding against the responsive-sliders.riv "Main" artboard +//============================================================================== + +class ResponsiveSlidersBindingTests : public ::testing::Test +{ +protected: + void SetUp() override + { + const auto file = findResponsiveSlidersInstanceTestFile(); + if (! file.existsAsFile()) + { + GTEST_SKIP() << "Missing test asset: tests/data/rive/responsive-sliders.riv"; + return; + } + + auto result = ArtboardFile::load (file, factory); + if (result.failed()) + { + GTEST_SKIP() << "Failed to load test asset: " << result.getErrorMessage(); + return; + } + + artboardFile = result.getValue(); + + artboard = std::make_unique ("responsiveBind", artboardFile); + artboard->setFitting (std::nullopt); + artboard->setBounds (0.0f, 0.0f, 918.0f, 552.0f); + } + + ArtboardViewModelInstance::Ptr createMainInstance() const + { + return artboardFile->createArtboardViewModelInstance ("Main", "Main"); + } + + ::testing::NiceMock factory; + std::shared_ptr artboardFile; + std::unique_ptr artboard; +}; + +//============================================================================== + +TEST_F (ResponsiveSlidersBindingTests, DefaultArtboardIsDesignedAgainstMainViewModel) +{ + EXPECT_EQ (String ("Main"), artboard->getViewModelName()); +} + +TEST_F (ResponsiveSlidersBindingTests, BindingAuthoredMainInstanceSucceedsAndSurvivesAdvances) +{ + auto instance = createMainInstance(); + ASSERT_NE (nullptr, instance.get()); + + EXPECT_TRUE (artboard->bindViewModelInstance (instance)); + EXPECT_EQ (instance.get(), artboard->getBoundViewModelInstance().get()); + + // Advancing the bound artboard exercises the nested artboards, state + // machines and data bindings of the fixture. + for (int frame = 0; frame < 30; ++frame) + EXPECT_NO_THROW (artboard->advanceAndApply (0.016f)); +} + +TEST_F (ResponsiveSlidersBindingTests, ValuesWrittenWhileBoundAreAppliedOnAdvance) +{ + auto instance = createMainInstance(); + ASSERT_NE (nullptr, instance.get()); + + ASSERT_TRUE (artboard->bindViewModelInstance (instance)); + + const String path = "ins_slider_Carrots.slider_value"; + const auto initial = instance->getNumberProperty (path).value_or (0.0); + + EXPECT_TRUE (instance->setNumberProperty (path, initial + 11.0)); + EXPECT_EQ (std::optional (initial + 11.0), instance->getNumberProperty (path)); + + for (int frame = 0; frame < 10; ++frame) + EXPECT_NO_THROW (artboard->advanceAndApply (0.016f)); +} + +TEST_F (ResponsiveSlidersBindingTests, UnbindStopsAndAllowsRebinding) +{ + auto instance = createMainInstance(); + ASSERT_NE (nullptr, instance.get()); + + ASSERT_TRUE (artboard->bindViewModelInstance (instance)); + artboard->unbindViewModelInstance(); + + EXPECT_EQ (nullptr, artboard->getBoundViewModelInstance().get()); + + for (int frame = 0; frame < 5; ++frame) + EXPECT_NO_THROW (artboard->advanceAndApply (0.016f)); + + EXPECT_TRUE (artboard->bindViewModelInstance (instance)); + EXPECT_EQ (instance.get(), artboard->getBoundViewModelInstance().get()); +} + +TEST_F (ResponsiveSlidersBindingTests, ForeignFileInstanceIsRejected) +{ + auto otherFile = ArtboardFile::load (findResponsiveSlidersInstanceTestFile(), factory); + if (otherFile.failed()) + { + GTEST_SKIP() << "Test asset is not loadable twice"; + return; + } + + auto foreignInstance = otherFile.getValue()->createArtboardViewModelInstance ("Main", "Main"); + ASSERT_NE (nullptr, foreignInstance.get()); + + EXPECT_FALSE (artboard->bindViewModelInstance (foreignInstance)); + EXPECT_EQ (nullptr, artboard->getBoundViewModelInstance().get()); +} + +//============================================================================== +// Path resolution edge cases +//============================================================================== + +TEST_F (ResponsiveSlidersInstanceTests, IndexSegmentAfterNonListResolvesToNothing) +{ + // "Sugar" is a number, so "Sugar.0" must not reinterpret it as a list. Before + // this was guarded the index branch downcast unconditionally, which asserts in + // debug builds and reads a garbage vector in release ones. + ASSERT_TRUE (mainInstance->hasProperty ("Sugar")); + + EXPECT_FALSE (mainInstance->hasProperty ("Sugar.0")); + EXPECT_TRUE (mainInstance->getProperty ("Sugar.0").isVoid()); + EXPECT_EQ (std::nullopt, mainInstance->getNumberProperty ("Sugar.0")); + EXPECT_EQ (std::nullopt, mainInstance->getColorProperty ("Sugar.0")); + EXPECT_EQ (std::nullopt, mainInstance->getEnumProperty ("Sugar.0")); + EXPECT_EQ (-1, mainInstance->getListSize ("Sugar.0")); + EXPECT_EQ (nullptr, mainInstance->getListItem ("Sugar.0", 0).get()); + EXPECT_EQ (nullptr, mainInstance->getNestedInstance ("Sugar.0").get()); + EXPECT_FALSE (mainInstance->setNumberProperty ("Sugar.0", 1.0)); + EXPECT_FALSE (mainInstance->trigger ("Sugar.0")); + + // Same shape one level down, and with a trailing segment after the index. + EXPECT_FALSE (mainInstance->hasProperty ("ins_slider_Sugar.slider_value.0")); + EXPECT_FALSE (mainInstance->hasProperty ("Sugar.0.anything")); +} + +TEST_F (ResponsiveSlidersInstanceTests, IndexSegmentOnAViewModelResolvesToNothing) +{ + // A nested viewmodel is not indexable either. + ASSERT_TRUE (mainInstance->hasProperty ("ins_slider_Sugar")); + + EXPECT_FALSE (mainInstance->hasProperty ("ins_slider_Sugar.0")); + EXPECT_EQ (nullptr, mainInstance->getNestedInstance ("ins_slider_Sugar.0").get()); +} + +//============================================================================== +// Property changed callback re-entrancy +//============================================================================== + +TEST_F (ResponsiveSlidersInstanceTests, CallbackMayReplaceItselfFromWithin) +{ + int firstCallbackCalls = 0; + int secondCallbackCalls = 0; + + // Re-arming from inside the callback destroys the closure that is running, so + // the dispatch has to hold its own copy for the duration of the call. + mainInstance->setPropertyChangedCallback ( + [&] (ArtboardViewModelInstance& self, const String&, const var&) + { + ++firstCallbackCalls; + + self.setPropertyChangedCallback ( + [&] (ArtboardViewModelInstance&, const String&, const var&) + { + ++secondCallbackCalls; + }); + }); + + EXPECT_TRUE (mainInstance->setNumberProperty ("Sugar", 12.0)); + EXPECT_EQ (1, firstCallbackCalls); + + EXPECT_TRUE (mainInstance->setNumberProperty ("Sugar", 13.0)); + EXPECT_EQ (1, firstCallbackCalls); + EXPECT_EQ (1, secondCallbackCalls); +} + +TEST_F (ResponsiveSlidersInstanceTests, CallbackMayClearItselfFromWithin) +{ + int callbackCalls = 0; + + mainInstance->setPropertyChangedCallback ( + [&] (ArtboardViewModelInstance& self, const String&, const var&) + { + ++callbackCalls; + self.setPropertyChangedCallback (nullptr); + }); + + EXPECT_TRUE (mainInstance->setNumberProperty ("Sugar", 21.0)); + EXPECT_EQ (1, callbackCalls); + + // The one-shot listener removed itself, so nothing more is reported. + EXPECT_TRUE (mainInstance->setNumberProperty ("Sugar", 22.0)); + EXPECT_EQ (1, callbackCalls); +} + +TEST_F (ResponsiveSlidersInstanceTests, CallbackMayWriteOtherPropertiesFromWithin) +{ + int callbackCalls = 0; + + mainInstance->setPropertyChangedCallback ( + [&] (ArtboardViewModelInstance& self, const String& name, const var&) + { + ++callbackCalls; + + if (name == "Sugar") + self.setNumberProperty ("Water", 5.0); + }); + + EXPECT_TRUE (mainInstance->setNumberProperty ("Sugar", 31.0)); + + EXPECT_GE (callbackCalls, 1); + EXPECT_EQ (std::optional (5.0), mainInstance->getNumberProperty ("Water")); +} + +//============================================================================== +// List properties +// +// Neither shipped fixture is guaranteed to expose a list, so these discover one +// and skip when there is none. +//============================================================================== + +namespace +{ + +struct DiscoveredList +{ + String path; + String elementViewModelName; +}; + +std::optional findListProperty (const std::shared_ptr& file) +{ + for (int viewModelIndex = 0; viewModelIndex < file->getNumViewModels(); ++viewModelIndex) + { + auto viewModel = file->getArtboardViewModelAt (viewModelIndex); + if (viewModel == nullptr) + continue; + + for (int propertyIndex = 0; propertyIndex < viewModel->getNumProperties(); ++propertyIndex) + { + const auto property = viewModel->getPropertyAt (propertyIndex); + if (property.type != ArtboardViewModel::PropertyType::list) + continue; + + // The element schema is not described by PropertyInfo, so pair the list + // with whichever schema the file already stores items of. + auto owner = file->createArtboardViewModelInstance (viewModel->getName()); + if (owner == nullptr || owner->getListSize (property.name) <= 0) + continue; + + auto item = owner->getListItem (property.name, 0); + if (item == nullptr) + continue; + + return DiscoveredList { property.name, item->getName() }; + } + } + + return std::nullopt; +} + +} // namespace + +class ArtboardViewModelListTests : public ::testing::Test +{ +protected: + void SetUp() override + { + const auto file = findViewModelInstanceTestFile(); + if (! file.existsAsFile()) + { + GTEST_SKIP() << "Missing test asset: tests/data/rive/data-binding.riv"; + return; + } + + auto result = ArtboardFile::load (file, factory); + if (result.failed()) + { + GTEST_SKIP() << "Failed to load test asset: " << result.getErrorMessage(); + return; + } + + artboardFile = result.getValue(); + + auto discovered = findListProperty (artboardFile); + if (! discovered.has_value()) + { + GTEST_SKIP() << "Test asset exposes no populated list property"; + return; + } + + listPath = discovered->path; + elementViewModelName = discovered->elementViewModelName; + + for (int viewModelIndex = 0; viewModelIndex < artboardFile->getNumViewModels(); ++viewModelIndex) + { + auto viewModel = artboardFile->getArtboardViewModelAt (viewModelIndex); + if (viewModel == nullptr || ! viewModel->hasProperty (listPath)) + continue; + + instance = artboardFile->createArtboardViewModelInstance (viewModel->getName()); + break; + } + + if (instance == nullptr || instance->getListSize (listPath) < 0) + GTEST_SKIP() << "Failed to create an instance owning the discovered list"; + } + + ::testing::NiceMock factory; + std::shared_ptr artboardFile; + ArtboardViewModelInstance::Ptr instance; + String listPath; + String elementViewModelName; +}; + +TEST_F (ArtboardViewModelListTests, AppendingGrowsTheList) +{ + const int before = instance->getListSize (listPath); + ASSERT_GE (before, 0); + + EXPECT_TRUE (instance->addListItem (listPath, elementViewModelName)); + EXPECT_EQ (before + 1, instance->getListSize (listPath)); + + EXPECT_NE (nullptr, instance->getListItem (listPath, before).get()); + EXPECT_EQ (nullptr, instance->getListItem (listPath, before + 1).get()); +} + +TEST_F (ArtboardViewModelListTests, InsertingAtAnIndexClampsToTheList) +{ + const int before = instance->getListSize (listPath); + + EXPECT_TRUE (instance->addListItemAt (listPath, 0, elementViewModelName)); + EXPECT_EQ (before + 1, instance->getListSize (listPath)); + + // Out-of-range indices clamp rather than fail. + EXPECT_TRUE (instance->addListItemAt (listPath, -5, elementViewModelName)); + EXPECT_TRUE (instance->addListItemAt (listPath, 10000, elementViewModelName)); + EXPECT_EQ (before + 3, instance->getListSize (listPath)); +} + +TEST_F (ArtboardViewModelListTests, RemovingShrinksTheList) +{ + const int before = instance->getListSize (listPath); + ASSERT_GT (before, 0); + + EXPECT_TRUE (instance->removeListItem (listPath, 0)); + EXPECT_EQ (before - 1, instance->getListSize (listPath)); + + EXPECT_FALSE (instance->removeListItem (listPath, -1)); + EXPECT_FALSE (instance->removeListItem (listPath, 10000)); +} + +TEST_F (ArtboardViewModelListTests, SwappingKeepsTheListSize) +{ + while (instance->getListSize (listPath) < 2) + ASSERT_TRUE (instance->addListItem (listPath, elementViewModelName)); + + const int before = instance->getListSize (listPath); + + EXPECT_TRUE (instance->swapListItems (listPath, 0, 1)); + EXPECT_EQ (before, instance->getListSize (listPath)); + + EXPECT_FALSE (instance->swapListItems (listPath, 0, 10000)); + EXPECT_FALSE (instance->swapListItems (listPath, -1, 0)); +} + +TEST_F (ArtboardViewModelListTests, ClearingEmptiesTheList) +{ + ASSERT_TRUE (instance->addListItem (listPath, elementViewModelName)); + + instance->clearListItems (listPath); + EXPECT_EQ (0, instance->getListSize (listPath)); + + // Clearing an unknown or non-list path is a no-op, not a crash. + EXPECT_NO_THROW (instance->clearListItems ("nonexistentProperty")); +} + +TEST_F (ArtboardViewModelListTests, MutationsAreRejectedForNonListPaths) +{ + EXPECT_FALSE (instance->addListItem ("nonexistentProperty", elementViewModelName)); + EXPECT_FALSE (instance->addListItemAt ("nonexistentProperty", 0, elementViewModelName)); + EXPECT_FALSE (instance->removeListItem ("nonexistentProperty", 0)); + EXPECT_FALSE (instance->swapListItems ("nonexistentProperty", 0, 1)); +} + +TEST_F (ArtboardViewModelListTests, AddingAnUnknownElementSchemaFails) +{ + const int before = instance->getListSize (listPath); + + EXPECT_FALSE (instance->addListItem (listPath, "NoSuchViewModelSchema")); + EXPECT_EQ (before, instance->getListSize (listPath)); +} + +TEST_F (ArtboardViewModelListTests, IndexTerminatedPathResolvesTheItem) +{ + ASSERT_GT (instance->getListSize (listPath), 0); + + const auto itemPath = listPath + ".0"; + + // Before the resolver returned the item's instance for an index-terminated + // path, these only worked through getListItem(). + EXPECT_TRUE (instance->hasProperty (itemPath)); + + auto viaPath = instance->getNestedInstance (itemPath); + ASSERT_NE (nullptr, viaPath.get()); + + auto viaAccessor = instance->getListItem (listPath, 0); + ASSERT_NE (nullptr, viaAccessor.get()); + + EXPECT_EQ (viaPath->getName(), viaAccessor->getName()); + + // An index-terminated path names a container, so it holds no value. + EXPECT_TRUE (instance->getProperty (itemPath).isVoid()); + + // Out-of-range indices still resolve to nothing. + const auto outOfRange = listPath + "." + String (instance->getListSize (listPath) + 5); + EXPECT_FALSE (instance->hasProperty (outOfRange)); + EXPECT_EQ (nullptr, instance->getNestedInstance (outOfRange).get()); +} + +TEST_F (ArtboardViewModelListTests, StructuralChangesNotifyThePropertyChangedCallback) +{ + StringArray reportedPaths; + + instance->setPropertyChangedCallback ( + [&] (ArtboardViewModelInstance&, const String& name, const var&) + { + reportedPaths.add (name); + }); + + EXPECT_TRUE (instance->addListItem (listPath, elementViewModelName)); + EXPECT_TRUE (reportedPaths.contains (listPath)); + + reportedPaths.clear(); + EXPECT_TRUE (instance->removeListItem (listPath, 0)); + EXPECT_TRUE (reportedPaths.contains (listPath)); + + reportedPaths.clear(); + instance->clearListItems (listPath); + EXPECT_TRUE (reportedPaths.contains (listPath)); +} + +TEST_F (ArtboardViewModelListTests, AppendedItemsAreObservedToo) +{ + instance->clearListItems (listPath); + + StringArray reportedPaths; + instance->setPropertyChangedCallback ( + [&] (ArtboardViewModelInstance&, const String& name, const var&) + { + reportedPaths.add (name); + }); + + ASSERT_TRUE (instance->addListItem (listPath, elementViewModelName)); + + auto item = instance->getListItem (listPath, 0); + ASSERT_NE (nullptr, item.get()); + + auto itemViewModel = artboardFile->getArtboardViewModel (item->getName()); + ASSERT_NE (nullptr, itemViewModel.get()); + + String numberProperty; + for (int i = 0; i < itemViewModel->getNumProperties(); ++i) + if (itemViewModel->getPropertyAt (i).type == ArtboardViewModel::PropertyType::number) + numberProperty = itemViewModel->getPropertyAt (i).name; + + if (numberProperty.isEmpty()) + { + GTEST_SKIP() << "Discovered list element schema exposes no number property"; + return; + } + + reportedPaths.clear(); + + // Writing through the freshly appended item must reach the owner's callback, + // i.e. appending has to extend the observer tree, not just mutate the list. + ASSERT_TRUE (instance->setNumberProperty (listPath + ".0." + numberProperty, 7.0)); + EXPECT_TRUE (reportedPaths.contains (listPath + ".0." + numberProperty)); +} + +//============================================================================== +// Path shape edge cases +//============================================================================== + +TEST_F (ArtboardViewModelInstanceTests, PathsWithEmptySegmentsResolveToNothing) +{ + // splitPath() always yields at least one segment, and empty segments (from a + // leading/trailing dot or a doubled dot) are neither indexes nor property + // names, so every accessor must decline them safely. + const char* malformedPaths[] = { "", ".", "..", "a.", ".a", "a..b", "1.", "1..2" }; + + for (const auto* path : malformedPaths) + { + EXPECT_FALSE (instance->hasProperty (path)) << path; + EXPECT_TRUE (instance->getProperty (path).isVoid()) << path; + EXPECT_EQ (-1, instance->getListSize (path)) << path; + EXPECT_EQ (nullptr, instance->getNestedInstance (path).get()) << path; + EXPECT_EQ (nullptr, instance->getListItem (path, 0).get()) << path; + EXPECT_FALSE (instance->setProperty (path, var (1.0))) << path; + EXPECT_FALSE (instance->trigger (path)) << path; + } +} + +//============================================================================== +// Property type coverage across every shipped fixture +// +// The type-specific tests above bind to a single schema and skip when it lacks +// the type. This walks every schema of every fixture so the get/set paths for +// each property type run wherever the fixtures actually provide one, and skips +// nothing that a fixture can supply. +//============================================================================== + +namespace +{ + +const StringArray& allInstanceFixtureFileNames() +{ + static const StringArray names { "data-binding.riv", "game-animation.riv", "layout-ui.riv", "responsive-sliders.riv" }; + return names; +} + +const File findAllInstanceFixturesDirectory() +{ + auto dir = File (__FILE__) + .getParentDirectory() + .getParentDirectory() + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir; + + dir = File::getCurrentWorkingDirectory() + .getParentDirectory() + .getParentDirectory() + .getParentDirectory() + .getChildFile ("tests") + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir; + + return File ("/data/rive"); +} + +} // namespace + +TEST (ArtboardViewModelInstanceFixtureCoverage, EveryFixturePropertyTypeRoundTrips) +{ + const auto directory = findAllInstanceFixturesDirectory(); + + for (const auto& fileName : allInstanceFixtureFileNames()) + { + const auto file = directory.getChildFile (fileName); + if (! file.existsAsFile()) + continue; + + ::testing::NiceMock factory; + auto result = ArtboardFile::load (file, factory); + if (result.failed()) + continue; + + auto artboardFile = result.getValue(); + + for (const auto& viewModelName : artboardFile->getViewModelNames()) + { + auto schema = artboardFile->getArtboardViewModel (viewModelName); + if (schema == nullptr) + continue; + + auto instance = artboardFile->createArtboardViewModelInstance (viewModelName); + if (instance == nullptr) + continue; + + for (int i = 0; i < schema->getNumProperties(); ++i) + { + const auto info = schema->getPropertyAt (i); + + EXPECT_TRUE (instance->hasProperty (info.name)); + + switch (info.type) + { + case ArtboardViewModel::PropertyType::boolean: + if (instance->setBoolProperty (info.name, true)) + EXPECT_EQ (std::optional (true), instance->getBoolProperty (info.name)); + + // A wrong-typed write is refused without clobbering it. + EXPECT_FALSE (instance->setProperty (info.name, var (3.0))); + break; + + case ArtboardViewModel::PropertyType::number: + if (instance->setNumberProperty (info.name, 5.5)) + EXPECT_EQ (std::optional (5.5), instance->getNumberProperty (info.name)); + + EXPECT_FALSE (instance->setProperty (info.name, var ("nope"))); + break; + + case ArtboardViewModel::PropertyType::string: + if (instance->setStringProperty (info.name, "yup")) + EXPECT_EQ (std::optional ("yup"), instance->getStringProperty (info.name)); + + EXPECT_FALSE (instance->setProperty (info.name, var (1.0))); + break; + + case ArtboardViewModel::PropertyType::color: + if (instance->setColorProperty (info.name, Color (0xFFABCDEFu))) + { + auto roundTripped = instance->getColorProperty (info.name); + ASSERT_NE (std::nullopt, roundTripped); + EXPECT_EQ (0xFFABCDEFu, roundTripped->getARGB()); + } + + EXPECT_FALSE (instance->setProperty (info.name, var ("nope"))); + break; + + case ArtboardViewModel::PropertyType::trigger: + EXPECT_TRUE (instance->trigger (info.name)); + break; + + case ArtboardViewModel::PropertyType::enumType: + if (! info.enumValues.isEmpty()) + { + if (instance->setEnumProperty (info.name, info.enumValues[0])) + EXPECT_EQ (std::optional (info.enumValues[0]), instance->getEnumProperty (info.name)); + + EXPECT_FALSE (instance->setEnumProperty (info.name, "notAnOption")); + } + break; + + case ArtboardViewModel::PropertyType::list: + if (instance->getListSize (info.name) > 0) + { + EXPECT_NE (nullptr, instance->getListItem (info.name, 0).get()); + EXPECT_TRUE (instance->hasProperty (info.name + ".0")); + EXPECT_EQ (nullptr, instance->getListItem (info.name, instance->getListSize (info.name) + 1).get()); + } + break; + + case ArtboardViewModel::PropertyType::viewModel: + // A nested viewmodel property resolves to a handle when an + // instance is assigned; either way the lookup must not throw. + EXPECT_NO_THROW (instance->getNestedInstance (info.name)); + break; + + default: + break; + } + } + } + } +} + +//============================================================================== +// ArtboardViewModelInstance handle against tests/data/rive/viewmodel-lab.riv +// +// viewmodel-lab.riv's "Lab" schema is the property-type showcase of the suite: +// its authored "Default" and "Preset" instances carry one value per property +// plus nested viewmodels and a populated list, so the accessors can be checked +// against authored content instead of values the test wrote itself. The +// expected values below are the ones tools/rive_inspect.py reports. +//============================================================================== + +namespace +{ + +const File findViewModelLabInstanceTestFile() +{ + // Try source-relative path first (works on desktop builds) + auto dir = File (__FILE__) + .getParentDirectory() + .getParentDirectory() + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir.getChildFile ("viewmodel-lab.riv"); + + dir = File::getCurrentWorkingDirectory() + .getParentDirectory() + .getParentDirectory() + .getParentDirectory() + .getChildFile ("tests") + .getChildFile ("data") + .getChildFile ("rive"); + + if (dir.exists()) + return dir.getChildFile ("viewmodel-lab.riv"); + + return File ("/data/rive/viewmodel-lab.riv"); +} + +constexpr const char* viewModelLabSchemaName = "Lab"; +constexpr const char* viewModelLabRowSchemaName = "Row"; +constexpr const char* viewModelLabDefaultInstanceName = "Default"; +constexpr const char* viewModelLabPresetInstanceName = "Preset"; + +// viewmodel-lab.riv ships ScriptAssets, which the Rive runtime can only import when +// it is compiled with WITH_RIVE_SCRIPTING (the rive module's `defines:`): without it +// a script's in-band FileAssetContents is routed to the previous asset's importer and +// trips the assert in FileAssetImporter::onFileAssetContents. +constexpr bool isViewModelLabInstanceScriptingAvailable() noexcept +{ +#ifdef WITH_RIVE_SCRIPTING + return true; +#else + return false; +#endif +} + +} // namespace + +class ViewModelLabInstanceTests : public ::testing::Test +{ +protected: + void SetUp() override + { + if (! isViewModelLabInstanceScriptingAvailable()) + { + GTEST_SKIP() << "tests/data/rive/viewmodel-lab.riv ships ScriptAssets, which needs Rive built with WITH_RIVE_SCRIPTING"; + return; + } + + const auto file = findViewModelLabInstanceTestFile(); + if (! file.existsAsFile()) + { + GTEST_SKIP() << "Missing test asset: tests/data/rive/viewmodel-lab.riv"; + return; + } + + auto result = ArtboardFile::load (file, factory); + if (result.failed()) + { + GTEST_SKIP() << "Failed to load test asset: " << result.getErrorMessage(); + return; + } + + artboardFile = result.getValue(); + + defaultInstance = artboardFile->createArtboardViewModelInstance (viewModelLabSchemaName, viewModelLabDefaultInstanceName); + presetInstance = artboardFile->createArtboardViewModelInstance (viewModelLabSchemaName, viewModelLabPresetInstanceName); + + if (defaultInstance == nullptr || presetInstance == nullptr) + GTEST_SKIP() << "Failed to create the ViewModel Lab instances from the test asset"; + } + + ::testing::NiceMock factory; + std::shared_ptr artboardFile; + ArtboardViewModelInstance::Ptr defaultInstance; + ArtboardViewModelInstance::Ptr presetInstance; +}; + +//============================================================================== + +TEST_F (ViewModelLabInstanceTests, AuthoredDefaultInstanceExposesItsAuthoredValues) +{ + EXPECT_EQ (String ("Lab"), defaultInstance->getName()); + EXPECT_EQ (artboardFile.get(), defaultInstance->getArtboardFile()); + + EXPECT_EQ (std::optional ("ViewModel Lab"), defaultInstance->getStringProperty ("title")); + EXPECT_EQ (std::optional (3.0), defaultInstance->getNumberProperty ("count")); + EXPECT_NEAR (0.42, defaultInstance->getNumberProperty ("progress").value_or (-1.0), 1e-6); + EXPECT_EQ (std::optional (true), defaultInstance->getBoolProperty ("enabled")); + EXPECT_EQ (std::optional ("Running"), defaultInstance->getEnumProperty ("status")); + EXPECT_EQ (std::optional ("edit me from the host"), defaultInstance->getStringProperty ("note")); + + const auto accent = defaultInstance->getColorProperty ("accent"); + ASSERT_NE (std::nullopt, accent); + EXPECT_EQ (0xff57a5e0u, accent->getARGB()); +} + +TEST_F (ViewModelLabInstanceTests, AuthoredPresetInstanceExposesItsOwnValues) +{ + EXPECT_EQ (std::optional ("Preset data"), presetInstance->getStringProperty ("title")); + EXPECT_EQ (std::optional (99.0), presetInstance->getNumberProperty ("count")); + EXPECT_EQ (std::optional (1.0), presetInstance->getNumberProperty ("progress")); + EXPECT_EQ (std::optional (false), presetInstance->getBoolProperty ("enabled")); + EXPECT_EQ (std::optional ("Failed"), presetInstance->getEnumProperty ("status")); + EXPECT_EQ (std::optional ("preset note"), presetInstance->getStringProperty ("note")); + + const auto accent = presetInstance->getColorProperty ("accent"); + ASSERT_NE (std::nullopt, accent); + EXPECT_EQ (0xffe85656u, accent->getARGB()); +} + +TEST_F (ViewModelLabInstanceTests, NestedViewModelPropertiesResolveToTheirOwnSchema) +{ + auto details = defaultInstance->getNestedInstance ("details"); + ASSERT_NE (nullptr, details.get()); + EXPECT_EQ (String ("Details"), details->getName()); + EXPECT_EQ (std::optional ("nested view model"), details->getStringProperty ("note")); + EXPECT_EQ (std::optional (62.5), details->getNumberProperty ("weight")); + + const auto tint = details->getColorProperty ("tint"); + ASSERT_NE (std::nullopt, tint); + EXPECT_EQ (0xfff5b02eu, tint->getARGB()); + + EXPECT_EQ (std::optional ("nested view model"), defaultInstance->getStringProperty ("details.note")); + + auto panelData = defaultInstance->getNestedInstance ("panelData"); + ASSERT_NE (nullptr, panelData.get()); + EXPECT_EQ (String ("Panel"), panelData->getName()); + EXPECT_EQ (std::optional ("Panel artboard"), panelData->getStringProperty ("caption")); + + // Neither a value-carrying property nor a list resolves to a nested handle. + EXPECT_EQ (nullptr, defaultInstance->getNestedInstance ("title").get()); + EXPECT_EQ (nullptr, defaultInstance->getNestedInstance ("rows").get()); +} + +TEST_F (ViewModelLabInstanceTests, AuthoredListItemsExposeTheirValues) +{ + ASSERT_EQ (3, defaultInstance->getListSize ("rows")); + + const String expectedLabels[] = { "Alpha", "Beta", "Gamma" }; + const double expectedAmounts[] = { 12.5, 240.0, -3.75 }; + const bool expectedDone[] = { false, true, false }; + + for (int i = 0; i < 3; ++i) + { + auto item = defaultInstance->getListItem ("rows", i); + ASSERT_NE (nullptr, item.get()) << i; + EXPECT_EQ (String ("Row"), item->getName()); + EXPECT_EQ (std::optional (expectedLabels[i]), item->getStringProperty ("label")); + EXPECT_EQ (std::optional (expectedAmounts[i]), item->getNumberProperty ("amount")); + EXPECT_EQ (std::optional (expectedDone[i]), item->getBoolProperty ("done")); + + EXPECT_TRUE (defaultInstance->hasProperty ("rows." + String (i))); + } + + EXPECT_EQ (nullptr, defaultInstance->getListItem ("rows", 3).get()); + EXPECT_EQ (nullptr, defaultInstance->getListItem ("rows", -1).get()); +} + +TEST_F (ViewModelLabInstanceTests, AuthoredListContentDiffersPerInstance) +{ + EXPECT_EQ (3, defaultInstance->getListSize ("rows")); + EXPECT_EQ (1, presetInstance->getListSize ("rows")); + + auto presetItem = presetInstance->getListItem ("rows", 0); + ASSERT_NE (nullptr, presetItem.get()); + EXPECT_EQ (String ("Row"), presetItem->getName()); + EXPECT_EQ (std::optional ("Alpha"), presetItem->getStringProperty ("label")); +} + +TEST_F (ViewModelLabInstanceTests, TriggerIsReportedButNotWritable) +{ + EXPECT_TRUE (defaultInstance->hasProperty ("ping")); + EXPECT_TRUE (defaultInstance->getProperty ("ping").isVoid()); + EXPECT_EQ (std::nullopt, defaultInstance->getBoolProperty ("ping")); + EXPECT_EQ (std::nullopt, defaultInstance->getNumberProperty ("ping")); + EXPECT_EQ (std::nullopt, defaultInstance->getStringProperty ("ping")); + + EXPECT_TRUE (defaultInstance->trigger ("ping")); + EXPECT_FALSE (defaultInstance->trigger ("title")); + EXPECT_FALSE (defaultInstance->trigger ("nonexistentProperty")); + EXPECT_FALSE (defaultInstance->setProperty ("ping", var (true))); +} + +TEST_F (ViewModelLabInstanceTests, ContainerPropertiesExposeNoValue) +{ + // "details"/"panelData" are nested viewmodels, "panel" an artboard reference and + // "icon" an asset image: all four carry no readable value. + const String containerPaths[] = { "icon", "panel", "details", "panelData" }; + + for (const auto& path : containerPaths) + { + EXPECT_TRUE (defaultInstance->hasProperty (path)) << path; + EXPECT_TRUE (defaultInstance->getProperty (path).isVoid()) << path; + EXPECT_EQ (std::nullopt, defaultInstance->getBoolProperty (path)) << path; + EXPECT_EQ (std::nullopt, defaultInstance->getNumberProperty (path)) << path; + EXPECT_EQ (std::nullopt, defaultInstance->getStringProperty (path)) << path; + EXPECT_EQ (std::nullopt, defaultInstance->getColorProperty (path)) << path; + EXPECT_EQ (std::nullopt, defaultInstance->getEnumProperty (path)) << path; + + EXPECT_EQ (-1, defaultInstance->getListSize (path)) << path; + EXPECT_FALSE (defaultInstance->setProperty (path, var (1))) << path; + } + + // "rows" is the one list-typed container: it reports its size but still no value. + EXPECT_TRUE (defaultInstance->hasProperty ("rows")); + EXPECT_TRUE (defaultInstance->getProperty ("rows").isVoid()); + EXPECT_EQ (std::nullopt, defaultInstance->getNumberProperty ("rows")); + EXPECT_EQ (3, defaultInstance->getListSize ("rows")); + EXPECT_FALSE (defaultInstance->setProperty ("rows", var (1))); + + EXPECT_EQ (-1, defaultInstance->getListSize ("title")); + EXPECT_EQ (-1, defaultInstance->getListSize ("nonexistentProperty")); +} + +TEST_F (ViewModelLabInstanceTests, EnumWritesAcceptDisplayValueAndKey) +{ + EXPECT_TRUE (defaultInstance->setEnumProperty ("status", "failed")); + EXPECT_EQ (std::optional ("Failed"), defaultInstance->getEnumProperty ("status")); + + // The display value and the authored key address the same option. + EXPECT_TRUE (defaultInstance->setEnumProperty ("status", "Idle")); + EXPECT_EQ (std::optional ("Idle"), defaultInstance->getEnumProperty ("status")); + + EXPECT_TRUE (defaultInstance->setProperty ("status", var (2))); + EXPECT_EQ (std::optional ("Failed"), defaultInstance->getEnumProperty ("status")); + + EXPECT_FALSE (defaultInstance->setEnumProperty ("status", "nonexistentOption")); + EXPECT_FALSE (defaultInstance->setProperty ("status", var (99))); + EXPECT_EQ (std::optional ("Failed"), defaultInstance->getEnumProperty ("status")); +} + +TEST_F (ViewModelLabInstanceTests, InstancesCreatedFromTheSameSchemaAreIndependent) +{ + auto fresh = artboardFile->createArtboardViewModelInstance (viewModelLabSchemaName); + ASSERT_NE (nullptr, fresh.get()); + EXPECT_EQ (String ("Lab"), fresh->getName()); + + EXPECT_TRUE (fresh->setStringProperty ("title", "fresh")); + EXPECT_TRUE (fresh->setNumberProperty ("count", 11.0)); + + EXPECT_EQ (std::optional ("fresh"), fresh->getStringProperty ("title")); + EXPECT_EQ (std::optional ("ViewModel Lab"), defaultInstance->getStringProperty ("title")); + EXPECT_EQ (std::optional (11.0), fresh->getNumberProperty ("count")); + EXPECT_EQ (std::optional (3.0), defaultInstance->getNumberProperty ("count")); + + // The authored list of the cloned instance is not shared with the fresh one. + const int authoredListSize = defaultInstance->getListSize ("rows"); + const int freshListSize = fresh->getListSize ("rows"); + ASSERT_EQ (3, authoredListSize); + + EXPECT_TRUE (fresh->addListItem ("rows", viewModelLabRowSchemaName)); + EXPECT_EQ (freshListSize + 1, fresh->getListSize ("rows")); + EXPECT_EQ (authoredListSize, defaultInstance->getListSize ("rows")); +} + +TEST_F (ViewModelLabInstanceTests, PropertyChangedCallbackReportsNestedAndStructuralChanges) +{ + StringArray paths; + Array values; + + defaultInstance->setPropertyChangedCallback ([&] (ArtboardViewModelInstance&, const String& path, const var& value) + { + paths.add (path); + values.add (value); + }); + + EXPECT_TRUE (defaultInstance->setNumberProperty ("details.weight", 12.0)); + EXPECT_TRUE (defaultInstance->addListItem ("rows", viewModelLabRowSchemaName)); + + ASSERT_TRUE (paths.contains ("details.weight")); + EXPECT_EQ (var (12.0), values[paths.indexOf ("details.weight")]); + + // Structural changes report the list path and an empty value. + ASSERT_TRUE (paths.contains ("rows")); + EXPECT_TRUE (values[paths.indexOf ("rows")].isVoid()); + + EXPECT_EQ (4, defaultInstance->getListSize ("rows")); + + defaultInstance->setPropertyChangedCallback ({}); +} + +//============================================================================== +// Artboard binding against tests/data/rive/viewmodel-lab.riv +//============================================================================== + +class ViewModelLabBindingTests : public ::testing::Test +{ +protected: + void SetUp() override + { + if (! isViewModelLabInstanceScriptingAvailable()) + { + GTEST_SKIP() << "tests/data/rive/viewmodel-lab.riv ships ScriptAssets, which needs Rive built with WITH_RIVE_SCRIPTING"; + return; + } + + const auto file = findViewModelLabInstanceTestFile(); + if (! file.existsAsFile()) + { + GTEST_SKIP() << "Missing test asset: tests/data/rive/viewmodel-lab.riv"; + return; + } + + auto result = ArtboardFile::load (file, factory); + if (result.failed()) + { + GTEST_SKIP() << "Failed to load test asset: " << result.getErrorMessage(); + return; + } + + artboardFile = result.getValue(); + + artboard = std::make_unique ("viewModelLabBind"); + artboard->setFile (artboardFile, "ViewModel Lab"); + artboard->setFitting (std::nullopt); + artboard->setBounds (0.0f, 0.0f, 920.0f, 960.0f); + } + + ArtboardViewModelInstance::Ptr createInstance (const char* instanceName) const + { + return artboardFile->createArtboardViewModelInstance (viewModelLabSchemaName, instanceName); + } + + ::testing::NiceMock factory; + std::shared_ptr artboardFile; + std::unique_ptr artboard; +}; + +//============================================================================== + +TEST_F (ViewModelLabBindingTests, ArtboardIsDesignedAgainstTheLabViewModel) +{ + EXPECT_EQ (String ("Lab"), artboard->getViewModelName()); +} + +TEST_F (ViewModelLabBindingTests, BindingTheAuthoredPresetInstanceSurvivesAdvances) +{ + auto instance = createInstance (viewModelLabPresetInstanceName); + ASSERT_NE (nullptr, instance.get()); + + EXPECT_TRUE (artboard->bindViewModelInstance (instance)); + EXPECT_EQ (instance.get(), artboard->getBoundViewModelInstance().get()); + + // Advancing exercises the nested artboards, the row list and the data bindings. + for (int frame = 0; frame < 30; ++frame) + EXPECT_NO_THROW (artboard->advanceAndApply (0.016f)); + + EXPECT_EQ (std::optional ("Preset data"), instance->getStringProperty ("title")); + EXPECT_EQ (std::optional ("Failed"), instance->getEnumProperty ("status")); + EXPECT_EQ (1, instance->getListSize ("rows")); +} + +TEST_F (ViewModelLabBindingTests, WritesAndListMutationsWhileBoundApplyOnAdvance) +{ + auto instance = createInstance (viewModelLabDefaultInstanceName); + ASSERT_NE (nullptr, instance.get()); + ASSERT_TRUE (artboard->bindViewModelInstance (instance)); + + EXPECT_TRUE (instance->setStringProperty ("title", "bound")); + EXPECT_TRUE (instance->setNumberProperty ("count", 7.0)); + EXPECT_TRUE (instance->setNumberProperty ("progress", 0.75)); + EXPECT_TRUE (instance->setBoolProperty ("enabled", false)); + EXPECT_TRUE (instance->setEnumProperty ("status", "Failed")); + EXPECT_TRUE (instance->trigger ("ping")); + EXPECT_TRUE (instance->setNumberProperty ("details.weight", 1.5)); + EXPECT_TRUE (instance->addListItem ("rows", viewModelLabRowSchemaName)); + EXPECT_TRUE (instance->swapListItems ("rows", 0, 3)); + EXPECT_TRUE (instance->removeListItem ("rows", 3)); + + EXPECT_EQ (3, instance->getListSize ("rows")); + + for (int frame = 0; frame < 20; ++frame) + EXPECT_NO_THROW (artboard->advanceAndApply (0.016f)); + + EXPECT_EQ (std::optional ("bound"), instance->getStringProperty ("title")); + EXPECT_EQ (std::optional (7.0), instance->getNumberProperty ("count")); + EXPECT_EQ (std::optional (false), instance->getBoolProperty ("enabled")); + EXPECT_EQ (std::optional ("Failed"), instance->getEnumProperty ("status")); + EXPECT_EQ (std::optional (1.5), instance->getNumberProperty ("details.weight")); +} + +TEST_F (ViewModelLabBindingTests, ClearingTheAuthoredListWhileBoundKeepsAdvancing) +{ + auto instance = createInstance (viewModelLabDefaultInstanceName); + ASSERT_NE (nullptr, instance.get()); + ASSERT_TRUE (artboard->bindViewModelInstance (instance)); + + instance->clearListItems ("rows"); + EXPECT_EQ (0, instance->getListSize ("rows")); + + for (int frame = 0; frame < 10; ++frame) + EXPECT_NO_THROW (artboard->advanceAndApply (0.016f)); + + // The list can be repopulated on the bound instance again. + EXPECT_TRUE (instance->addListItem ("rows", viewModelLabRowSchemaName)); + EXPECT_EQ (1, instance->getListSize ("rows")); + + for (int frame = 0; frame < 10; ++frame) + EXPECT_NO_THROW (artboard->advanceAndApply (0.016f)); +} + +TEST_F (ViewModelLabBindingTests, UnbindingDropsTheBoundInstance) +{ + auto instance = createInstance (viewModelLabDefaultInstanceName); + ASSERT_NE (nullptr, instance.get()); + + ASSERT_TRUE (artboard->bindViewModelInstance (instance)); + + for (int frame = 0; frame < 5; ++frame) + EXPECT_NO_THROW (artboard->advanceAndApply (0.016f)); + + artboard->unbindViewModelInstance(); + + EXPECT_EQ (nullptr, artboard->getBoundViewModelInstance().get()); +} + +// Disabled: advancing after the instance is unbound crashes inside Yoga. +TEST_F (ViewModelLabBindingTests, DISABLED_UnbindingAndAdvancingKeepsTheArtboardUsable) +{ + auto instance = createInstance (viewModelLabDefaultInstanceName); + ASSERT_NE (nullptr, instance.get()); + + ASSERT_TRUE (artboard->bindViewModelInstance (instance)); + artboard->unbindViewModelInstance(); + + EXPECT_EQ (nullptr, artboard->getBoundViewModelInstance().get()); + + for (int frame = 0; frame < 5; ++frame) + EXPECT_NO_THROW (artboard->advanceAndApply (0.016f)); + + EXPECT_TRUE (artboard->bindViewModelInstance (instance)); + EXPECT_EQ (instance.get(), artboard->getBoundViewModelInstance().get()); + + for (int frame = 0; frame < 5; ++frame) + EXPECT_NO_THROW (artboard->advanceAndApply (0.016f)); +} diff --git a/thirdparty/luau/luau.h b/thirdparty/luau/luau.h index efcecb692..6ea845015 100644 --- a/thirdparty/luau/luau.h +++ b/thirdparty/luau/luau.h @@ -32,7 +32,7 @@ website: https://github.com/luau-lang/luau license: MIT - defines: LUA_USE_LONGJMP=1 RIVE_LUAU=1 + defines: LUA_USE_LONGJMP=1 searchpaths: upstream/VM/include upstream/Common/include upstream/VM/src END_YUP_MODULE_DECLARATION diff --git a/thirdparty/rive_renderer/include/rive/renderer/async_pipeline_manager.hpp b/thirdparty/rive/include/rive/renderer/async_pipeline_manager.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/async_pipeline_manager.hpp rename to thirdparty/rive/include/rive/renderer/async_pipeline_manager.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/buffer_ring.hpp b/thirdparty/rive/include/rive/renderer/buffer_ring.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/buffer_ring.hpp rename to thirdparty/rive/include/rive/renderer/buffer_ring.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/d3d/d3d.hpp b/thirdparty/rive/include/rive/renderer/d3d/d3d.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/d3d/d3d.hpp rename to thirdparty/rive/include/rive/renderer/d3d/d3d.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/d3d/d3d_constants.hpp b/thirdparty/rive/include/rive/renderer/d3d/d3d_constants.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/d3d/d3d_constants.hpp rename to thirdparty/rive/include/rive/renderer/d3d/d3d_constants.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/d3d/d3d_utils.hpp b/thirdparty/rive/include/rive/renderer/d3d/d3d_utils.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/d3d/d3d_utils.hpp rename to thirdparty/rive/include/rive/renderer/d3d/d3d_utils.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/d3d/pipeline_manager.hpp b/thirdparty/rive/include/rive/renderer/d3d/pipeline_manager.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/d3d/pipeline_manager.hpp rename to thirdparty/rive/include/rive/renderer/d3d/pipeline_manager.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/d3d11/d3d11.hpp b/thirdparty/rive/include/rive/renderer/d3d11/d3d11.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/d3d11/d3d11.hpp rename to thirdparty/rive/include/rive/renderer/d3d11/d3d11.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/d3d11/render_context_d3d_impl.hpp b/thirdparty/rive/include/rive/renderer/d3d11/render_context_d3d_impl.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/d3d11/render_context_d3d_impl.hpp rename to thirdparty/rive/include/rive/renderer/d3d11/render_context_d3d_impl.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/d3d12/d3d12.hpp b/thirdparty/rive/include/rive/renderer/d3d12/d3d12.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/d3d12/d3d12.hpp rename to thirdparty/rive/include/rive/renderer/d3d12/d3d12.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/d3d12/d3d12_pipeline_manager.hpp b/thirdparty/rive/include/rive/renderer/d3d12/d3d12_pipeline_manager.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/d3d12/d3d12_pipeline_manager.hpp rename to thirdparty/rive/include/rive/renderer/d3d12/d3d12_pipeline_manager.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/d3d12/d3d12_utils.hpp b/thirdparty/rive/include/rive/renderer/d3d12/d3d12_utils.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/d3d12/d3d12_utils.hpp rename to thirdparty/rive/include/rive/renderer/d3d12/d3d12_utils.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/d3d12/render_context_d3d12_impl.hpp b/thirdparty/rive/include/rive/renderer/d3d12/render_context_d3d12_impl.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/d3d12/render_context_d3d12_impl.hpp rename to thirdparty/rive/include/rive/renderer/d3d12/render_context_d3d12_impl.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/draw.hpp b/thirdparty/rive/include/rive/renderer/draw.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/draw.hpp rename to thirdparty/rive/include/rive/renderer/draw.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/fixed_queue.hpp b/thirdparty/rive/include/rive/renderer/fixed_queue.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/fixed_queue.hpp rename to thirdparty/rive/include/rive/renderer/fixed_queue.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/gl/gl_state.hpp b/thirdparty/rive/include/rive/renderer/gl/gl_state.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/gl/gl_state.hpp rename to thirdparty/rive/include/rive/renderer/gl/gl_state.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/gl/gl_utils.hpp b/thirdparty/rive/include/rive/renderer/gl/gl_utils.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/gl/gl_utils.hpp rename to thirdparty/rive/include/rive/renderer/gl/gl_utils.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/gl/gles3.hpp b/thirdparty/rive/include/rive/renderer/gl/gles3.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/gl/gles3.hpp rename to thirdparty/rive/include/rive/renderer/gl/gles3.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/gl/load_gles_extensions.hpp b/thirdparty/rive/include/rive/renderer/gl/load_gles_extensions.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/gl/load_gles_extensions.hpp rename to thirdparty/rive/include/rive/renderer/gl/load_gles_extensions.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/gl/load_store_actions_ext.hpp b/thirdparty/rive/include/rive/renderer/gl/load_store_actions_ext.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/gl/load_store_actions_ext.hpp rename to thirdparty/rive/include/rive/renderer/gl/load_store_actions_ext.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/gl/render_buffer_gl_impl.hpp b/thirdparty/rive/include/rive/renderer/gl/render_buffer_gl_impl.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/gl/render_buffer_gl_impl.hpp rename to thirdparty/rive/include/rive/renderer/gl/render_buffer_gl_impl.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/gl/render_context_gl_impl.hpp b/thirdparty/rive/include/rive/renderer/gl/render_context_gl_impl.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/gl/render_context_gl_impl.hpp rename to thirdparty/rive/include/rive/renderer/gl/render_context_gl_impl.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/gl/render_target_gl.hpp b/thirdparty/rive/include/rive/renderer/gl/render_target_gl.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/gl/render_target_gl.hpp rename to thirdparty/rive/include/rive/renderer/gl/render_target_gl.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/gpu.hpp b/thirdparty/rive/include/rive/renderer/gpu.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/gpu.hpp rename to thirdparty/rive/include/rive/renderer/gpu.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/gpu_resource.hpp b/thirdparty/rive/include/rive/renderer/gpu_resource.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/gpu_resource.hpp rename to thirdparty/rive/include/rive/renderer/gpu_resource.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/metal/render_context_metal_impl.h b/thirdparty/rive/include/rive/renderer/metal/render_context_metal_impl.h similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/metal/render_context_metal_impl.h rename to thirdparty/rive/include/rive/renderer/metal/render_context_metal_impl.h diff --git a/thirdparty/rive_renderer/include/rive/renderer/ore/hlsl_struct_layout.hpp b/thirdparty/rive/include/rive/renderer/ore/hlsl_struct_layout.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/ore/hlsl_struct_layout.hpp rename to thirdparty/rive/include/rive/renderer/ore/hlsl_struct_layout.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/ore/ore_bind_group.hpp b/thirdparty/rive/include/rive/renderer/ore/ore_bind_group.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/ore/ore_bind_group.hpp rename to thirdparty/rive/include/rive/renderer/ore/ore_bind_group.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/ore/ore_bind_group_layout.hpp b/thirdparty/rive/include/rive/renderer/ore/ore_bind_group_layout.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/ore/ore_bind_group_layout.hpp rename to thirdparty/rive/include/rive/renderer/ore/ore_bind_group_layout.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/ore/ore_binding_map.hpp b/thirdparty/rive/include/rive/renderer/ore/ore_binding_map.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/ore/ore_binding_map.hpp rename to thirdparty/rive/include/rive/renderer/ore/ore_binding_map.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/ore/ore_buffer.hpp b/thirdparty/rive/include/rive/renderer/ore/ore_buffer.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/ore/ore_buffer.hpp rename to thirdparty/rive/include/rive/renderer/ore/ore_buffer.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/ore/ore_context.hpp b/thirdparty/rive/include/rive/renderer/ore/ore_context.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/ore/ore_context.hpp rename to thirdparty/rive/include/rive/renderer/ore/ore_context.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/ore/ore_context_d3d11.hpp b/thirdparty/rive/include/rive/renderer/ore/ore_context_d3d11.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/ore/ore_context_d3d11.hpp rename to thirdparty/rive/include/rive/renderer/ore/ore_context_d3d11.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/ore/ore_context_d3d12.hpp b/thirdparty/rive/include/rive/renderer/ore/ore_context_d3d12.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/ore/ore_context_d3d12.hpp rename to thirdparty/rive/include/rive/renderer/ore/ore_context_d3d12.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/ore/ore_context_gl.hpp b/thirdparty/rive/include/rive/renderer/ore/ore_context_gl.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/ore/ore_context_gl.hpp rename to thirdparty/rive/include/rive/renderer/ore/ore_context_gl.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/ore/ore_context_metal.hpp b/thirdparty/rive/include/rive/renderer/ore/ore_context_metal.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/ore/ore_context_metal.hpp rename to thirdparty/rive/include/rive/renderer/ore/ore_context_metal.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/ore/ore_context_vulkan.hpp b/thirdparty/rive/include/rive/renderer/ore/ore_context_vulkan.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/ore/ore_context_vulkan.hpp rename to thirdparty/rive/include/rive/renderer/ore/ore_context_vulkan.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/ore/ore_context_wgpu.hpp b/thirdparty/rive/include/rive/renderer/ore/ore_context_wgpu.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/ore/ore_context_wgpu.hpp rename to thirdparty/rive/include/rive/renderer/ore/ore_context_wgpu.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/ore/ore_pipeline.hpp b/thirdparty/rive/include/rive/renderer/ore/ore_pipeline.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/ore/ore_pipeline.hpp rename to thirdparty/rive/include/rive/renderer/ore/ore_pipeline.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/ore/ore_render_pass.hpp b/thirdparty/rive/include/rive/renderer/ore/ore_render_pass.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/ore/ore_render_pass.hpp rename to thirdparty/rive/include/rive/renderer/ore/ore_render_pass.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/ore/ore_rstb_entry_container.hpp b/thirdparty/rive/include/rive/renderer/ore/ore_rstb_entry_container.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/ore/ore_rstb_entry_container.hpp rename to thirdparty/rive/include/rive/renderer/ore/ore_rstb_entry_container.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/ore/ore_sampler.hpp b/thirdparty/rive/include/rive/renderer/ore/ore_sampler.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/ore/ore_sampler.hpp rename to thirdparty/rive/include/rive/renderer/ore/ore_sampler.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/ore/ore_shader_module.hpp b/thirdparty/rive/include/rive/renderer/ore/ore_shader_module.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/ore/ore_shader_module.hpp rename to thirdparty/rive/include/rive/renderer/ore/ore_shader_module.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/ore/ore_texture.hpp b/thirdparty/rive/include/rive/renderer/ore/ore_texture.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/ore/ore_texture.hpp rename to thirdparty/rive/include/rive/renderer/ore/ore_texture.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/ore/ore_types.hpp b/thirdparty/rive/include/rive/renderer/ore/ore_types.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/ore/ore_types.hpp rename to thirdparty/rive/include/rive/renderer/ore/ore_types.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/render_canvas.hpp b/thirdparty/rive/include/rive/renderer/render_canvas.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/render_canvas.hpp rename to thirdparty/rive/include/rive/renderer/render_canvas.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/render_context.hpp b/thirdparty/rive/include/rive/renderer/render_context.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/render_context.hpp rename to thirdparty/rive/include/rive/renderer/render_context.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/render_context_helper_impl.hpp b/thirdparty/rive/include/rive/renderer/render_context_helper_impl.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/render_context_helper_impl.hpp rename to thirdparty/rive/include/rive/renderer/render_context_helper_impl.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/render_context_impl.hpp b/thirdparty/rive/include/rive/renderer/render_context_impl.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/render_context_impl.hpp rename to thirdparty/rive/include/rive/renderer/render_context_impl.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/render_target.hpp b/thirdparty/rive/include/rive/renderer/render_target.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/render_target.hpp rename to thirdparty/rive/include/rive/renderer/render_target.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/rive_render_buffer.hpp b/thirdparty/rive/include/rive/renderer/rive_render_buffer.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/rive_render_buffer.hpp rename to thirdparty/rive/include/rive/renderer/rive_render_buffer.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/rive_render_factory.hpp b/thirdparty/rive/include/rive/renderer/rive_render_factory.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/rive_render_factory.hpp rename to thirdparty/rive/include/rive/renderer/rive_render_factory.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/rive_render_image.hpp b/thirdparty/rive/include/rive/renderer/rive_render_image.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/rive_render_image.hpp rename to thirdparty/rive/include/rive/renderer/rive_render_image.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/rive_renderer.hpp b/thirdparty/rive/include/rive/renderer/rive_renderer.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/rive_renderer.hpp rename to thirdparty/rive/include/rive/renderer/rive_renderer.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/shader_compilation_mode.hpp b/thirdparty/rive/include/rive/renderer/shader_compilation_mode.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/shader_compilation_mode.hpp rename to thirdparty/rive/include/rive/renderer/shader_compilation_mode.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/sk_rectanizer_skyline.hpp b/thirdparty/rive/include/rive/renderer/sk_rectanizer_skyline.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/sk_rectanizer_skyline.hpp rename to thirdparty/rive/include/rive/renderer/sk_rectanizer_skyline.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/stack_vector.hpp b/thirdparty/rive/include/rive/renderer/stack_vector.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/stack_vector.hpp rename to thirdparty/rive/include/rive/renderer/stack_vector.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/texture.hpp b/thirdparty/rive/include/rive/renderer/texture.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/texture.hpp rename to thirdparty/rive/include/rive/renderer/texture.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/trivial_block_allocator.hpp b/thirdparty/rive/include/rive/renderer/trivial_block_allocator.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/trivial_block_allocator.hpp rename to thirdparty/rive/include/rive/renderer/trivial_block_allocator.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/vulkan/render_context_vulkan_impl.hpp b/thirdparty/rive/include/rive/renderer/vulkan/render_context_vulkan_impl.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/vulkan/render_context_vulkan_impl.hpp rename to thirdparty/rive/include/rive/renderer/vulkan/render_context_vulkan_impl.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/vulkan/render_target_vulkan.hpp b/thirdparty/rive/include/rive/renderer/vulkan/render_target_vulkan.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/vulkan/render_target_vulkan.hpp rename to thirdparty/rive/include/rive/renderer/vulkan/render_target_vulkan.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/vulkan/vkutil.hpp b/thirdparty/rive/include/rive/renderer/vulkan/vkutil.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/vulkan/vkutil.hpp rename to thirdparty/rive/include/rive/renderer/vulkan/vkutil.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/vulkan/vulkan_context.hpp b/thirdparty/rive/include/rive/renderer/vulkan/vulkan_context.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/vulkan/vulkan_context.hpp rename to thirdparty/rive/include/rive/renderer/vulkan/vulkan_context.hpp diff --git a/thirdparty/rive_renderer/include/rive/renderer/webgpu/render_context_webgpu_impl.hpp b/thirdparty/rive/include/rive/renderer/webgpu/render_context_webgpu_impl.hpp similarity index 100% rename from thirdparty/rive_renderer/include/rive/renderer/webgpu/render_context_webgpu_impl.hpp rename to thirdparty/rive/include/rive/renderer/webgpu/render_context_webgpu_impl.hpp diff --git a/thirdparty/rive/rive.cpp b/thirdparty/rive/rive.cpp index d803c9096..2e02a1a0d 100644 --- a/thirdparty/rive/rive.cpp +++ b/thirdparty/rive/rive.cpp @@ -22,9 +22,11 @@ #if __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wshorten-64-to-32" + #pragma clang diagnostic ignored "-Wattributes" #pragma clang diagnostic ignored "-Wdeprecated-declarations" #elif __GNUC__ #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wattributes" #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #elif _MSC_VER #pragma warning (push) diff --git a/thirdparty/rive/rive.h b/thirdparty/rive/rive.h index d5466935e..448e5f5f5 100644 --- a/thirdparty/rive/rive.h +++ b/thirdparty/rive/rive.h @@ -32,10 +32,20 @@ website: https://github.com/rive-app/rive-runtime license: MIT - dependencies: harfbuzz sheenbidi_library yoga_library libhydrogen - defines: WITH_RIVE_TEXT=1 WITH_RIVE_YOGA=1 WITH_RIVE_LAYOUT=1 RIVE_DECODERS=1 RIVE_CANVAS=1 RIVE_ORE=1 - appleFrameworks: CoreText - searchpaths: include + dependencies: rive_decoders harfbuzz sheenbidi_library yoga_library libhydrogen luau glad + defines: WITH_RIVE_TEXT=1 WITH_RIVE_YOGA=1 WITH_RIVE_LAYOUT=1 WITH_RIVE_SCRIPTING=1 RIVE_DECODERS=1 RIVE_CANVAS=1 RIVE_LUAU=1 RIVE_ORE=1 + searchpaths: include source source/renderer source/renderer/generated/shaders + appleFrameworks: CoreText Metal QuartzCore + appleDefines: ORE_BACKEND_METAL=1 RIVE_OBJC_EXCEPTIONS=1 + iosDefines: RIVE_IOS=1 + iosSimDefines: RIVE_IOS_SIMULATOR=1 + linuxDefines: ORE_BACKEND_GL=1 + wasmDefines: RIVE_WEBGL=1 ORE_BACKEND_GL=1 + wasmOptions: -sUSE_SDL=2 + wasmLinkOptions: -sUSE_SDL=2 -sMAX_WEBGL_VERSION=2 + windowsDefines: ORE_BACKEND_D3D11=1 ORE_BACKEND_GL=1 + androidDefines: RIVE_ANDROID=1 ORE_BACKEND_GL=1 + androidLibs: EGL GLESv3 END_YUP_MODULE_DECLARATION @@ -44,6 +54,102 @@ #pragma once +//============================================================================== +/** Config: YUP_RIVE_USE_METAL + Enables the use of the Metal renderer on macOS (the default is enabled). +*/ +#ifndef YUP_RIVE_USE_METAL +#define YUP_RIVE_USE_METAL 1 +#endif + +/** Config: YUP_RIVE_USE_D3D + Enables the use of the Direct3D renderer on Windows (the default is enabled). +*/ +#ifndef YUP_RIVE_USE_D3D +#define YUP_RIVE_USE_D3D 1 +#endif + +/** Config: YUP_RIVE_USE_OPENGL + Enables the use of the OpenGL renderer on platform that support it. +*/ +#ifndef YUP_RIVE_USE_OPENGL +#define YUP_RIVE_USE_OPENGL 1 +#endif + +/** Config: YUP_RIVE_USE_DAWN + Enables the use of the Dawn renderer on platform that support it. +*/ +#ifndef YUP_RIVE_USE_DAWN +#define YUP_RIVE_USE_DAWN 0 +#endif + +//============================================================================== +/** Config: YUP_RIVE_OPENGL_MAJOR + Enables a specific OpenGL major version. Must be at least 4. +*/ +#ifndef YUP_RIVE_OPENGL_MAJOR +#define YUP_RIVE_OPENGL_MAJOR 4 +#endif + +/** Config: YUP_RIVE_OPENGL_MINOR + Enables a specific OpenGL minor version. Must be at least 5 (OpenGL 4.5+, required for compute shaders). +*/ +#ifndef YUP_RIVE_OPENGL_MINOR +#define YUP_RIVE_OPENGL_MINOR 5 +#endif + +/** Config: YUP_RIVE_OPENGLES_MAJOR + Enables a specific OpenGL ES major version. Must be at least 3. +*/ +#ifndef YUP_RIVE_OPENGLES_MAJOR +#define YUP_RIVE_OPENGLES_MAJOR 3 +#endif + +/** Config: YUP_RIVE_OPENGLES_MINOR + Enables a specific OpenGL ES minor version. Must be at least 1 (OpenGL ES 3.1+, required for compute shaders). +*/ +#ifndef YUP_RIVE_OPENGLES_MINOR +#define YUP_RIVE_OPENGLES_MINOR 1 +#endif + +//============================================================================== + +#if YUP_RIVE_USE_OPENGL && YUP_APPLE +#undef YUP_RIVE_USE_OPENGL +#endif + +#if YUP_RIVE_USE_OPENGL +#if !defined(RIVE_DESKTOP_GL) && !defined(RIVE_WEBGL) && !defined(RIVE_ANDROID) && !YUP_ANDROID && !YUP_WASM +#define RIVE_DESKTOP_GL 1 +#endif +#endif + +#if !YUP_RIVE_USE_OPENGL +#undef ORE_BACKEND_GL +#endif + +#if YUP_RIVE_USE_DAWN +#if !defined(RIVE_DAWN) +#define RIVE_DAWN 1 +#endif +#endif + +//============================================================================== + +#if !defined (YUP_RIVE_NO_INCLUDES) + +#if __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#elif __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wattributes" +#elif _MSC_VER + __pragma (warning (push)) + __pragma (warning (disable: 4244)) +#endif + +// Core API #include "include/rive/text/utf.hpp" #include "include/rive/artboard.hpp" #include "include/rive/file.hpp" @@ -54,3 +160,23 @@ #include "include/rive/custom_property_string.hpp" #include "include/rive/animation/state_machine_instance.hpp" #include "include/rive/animation/state_machine_input_instance.hpp" + +// Public API +#include "include/rive/renderer/texture.hpp" +#include "include/rive/renderer/rive_render_image.hpp" +#include "include/rive/renderer/render_context.hpp" +#include "include/rive/renderer/render_context_impl.hpp" + +// Internals +#include "source/renderer/rive_render_path.hpp" +#include "source/renderer/rive_render_paint.hpp" + +#if __GNUC__ + #pragma GCC diagnostic pop +#elif __clang__ + #pragma clang diagnostic pop +#elif _MSC_VER + __pragma (warning (pop)) +#endif + +#endif // YUP_RIVE_NO_INCLUDES diff --git a/thirdparty/rive_renderer/rive_renderer_android.cpp b/thirdparty/rive/rive_android.cpp similarity index 57% rename from thirdparty/rive_renderer/rive_renderer_android.cpp rename to thirdparty/rive/rive_android.cpp index 4c3e4e995..18e2b95b4 100644 --- a/thirdparty/rive_renderer/rive_renderer_android.cpp +++ b/thirdparty/rive/rive_android.cpp @@ -19,7 +19,7 @@ ============================================================================== */ -#include "rive_renderer.h" +#include "rive.h" #if __clang__ #pragma clang diagnostic push @@ -27,27 +27,27 @@ #pragma clang diagnostic ignored "-Wattributes" #endif -#include "source/gl/gl_state.cpp" -#include "source/gl/gl_utils.cpp" -#include "source/gl/load_gles_extensions.cpp" -#include "source/gl/load_store_actions_ext.cpp" -#include "source/gl/pls_impl_ext_native.cpp" -#include "source/gl/pls_impl_rw_texture.cpp" -#include "source/gl/render_buffer_gl_impl.cpp" -#include "source/gl/render_context_gl_impl.cpp" -#include "source/gl/render_target_gl.cpp" - -#include "source/ore/gl/ore_bind_group_gl.cpp" -#include "source/ore/gl/ore_buffer_gl.cpp" -#include "source/ore/gl/ore_context_gl.cpp" -#include "source/ore/gl/ore_pipeline_gl.cpp" +#include "source/renderer/gl/gl_state.cpp" +#include "source/renderer/gl/gl_utils.cpp" +#include "source/renderer/gl/load_gles_extensions.cpp" +#include "source/renderer/gl/load_store_actions_ext.cpp" +#include "source/renderer/gl/pls_impl_ext_native.cpp" +#include "source/renderer/gl/pls_impl_rw_texture.cpp" +#include "source/renderer/gl/render_buffer_gl_impl.cpp" +#include "source/renderer/gl/render_context_gl_impl.cpp" +#include "source/renderer/gl/render_target_gl.cpp" + +#include "source/renderer/ore/gl/ore_bind_group_gl.cpp" +#include "source/renderer/ore/gl/ore_buffer_gl.cpp" +#include "source/renderer/ore/gl/ore_context_gl.cpp" +#include "source/renderer/ore/gl/ore_pipeline_gl.cpp" #define oreCompareFunctionToGL oreCompareFunctionToGL_rp -#include "source/ore/gl/ore_render_pass_gl.cpp" +#include "source/renderer/ore/gl/ore_render_pass_gl.cpp" #undef oreCompareFunctionToGL -#include "source/ore/gl/ore_sampler_gl.cpp" -#include "source/ore/gl/ore_shader_module_gl.cpp" +#include "source/renderer/ore/gl/ore_sampler_gl.cpp" +#include "source/renderer/ore/gl/ore_shader_module_gl.cpp" #define oreFormatToGLInternal oreFormatToGLInternal_tex -#include "source/ore/gl/ore_texture_gl.cpp" +#include "source/renderer/ore/gl/ore_texture_gl.cpp" #undef oreFormatToGLInternal #if __clang__ diff --git a/thirdparty/rive/rive_apple.mm b/thirdparty/rive/rive_apple.mm index b0f1aabb2..9a40973d3 100644 --- a/thirdparty/rive/rive_apple.mm +++ b/thirdparty/rive/rive_apple.mm @@ -24,12 +24,43 @@ #pragma clang diagnostic ignored "-Wshorten-64-to-32" #endif +#include + #if !defined(_RIVE_INTERNAL_) #define _RIVE_INTERNAL_ 1 #endif +#define YUP_RIVE_NO_INCLUDES 1 +#include "rive.h" +#undef YUP_RIVE_NO_INCLUDES + #include "source/text/font_hb_apple.mm" +#if YUP_RIVE_USE_METAL +#include "source/renderer/metal/render_context_metal_impl.mm" +#include "source/renderer/metal/background_shader_compiler.mm" + +#if TARGET_OS_SIMULATOR +#include "source/renderer/generated/shaders/rive_pls_ios_simulator.metallib.c" +#elif TARGET_OS_IOS +#include "source/renderer/generated/shaders/rive_pls_ios.metallib.c" +#elif TARGET_OS_MAC +#include "source/renderer/generated/shaders/rive_pls_macosx.metallib.c" +#endif + +#include "source/renderer/ore/metal/ore_bind_group_metal.mm" +#include "source/renderer/ore/metal/ore_context_metal.mm" +#include "source/renderer/ore/metal/ore_texture_metal.mm" +#include "source/renderer/ore/metal/ore_shader_module_metal.mm" +#include "source/renderer/ore/metal/ore_sampler_metal.mm" +#define kMetalVertexBufferBase kMetalVertexBufferBase_render_pass +#include "source/renderer/ore/metal/ore_render_pass_metal.mm" +#undef kMetalVertexBufferBase +#include "source/renderer/ore/metal/ore_pipeline_metal.mm" +#include "source/renderer/ore/metal/ore_buffer_metal.mm" +#include "source/renderer/ore/metal/ore_bind_group_metal.mm" +#endif + #if __clang__ #pragma clang diagnostic pop #endif diff --git a/thirdparty/rive/rive_emscripten.cpp b/thirdparty/rive/rive_emscripten.cpp new file mode 100644 index 000000000..5372fbe39 --- /dev/null +++ b/thirdparty/rive/rive_emscripten.cpp @@ -0,0 +1,71 @@ +/* + ============================================================================== + + This file is part of the YUP library. + Copyright (c) 2024 - kunitoki@gmail.com + + YUP is an open source library subject to open-source licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + to use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#include "rive.h" + +#if __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wshorten-64-to-32" + #pragma clang diagnostic ignored "-Wattributes" +#endif + +#if RIVE_WEBGPU +#include "source/renderer/webgpu/render_context_webgpu_impl.cpp" + +#include "source/renderer/ore/wgpu/ore_bind_group_wgpu.cpp" +#include "source/renderer/ore/wgpu/ore_buffer_wgpu.cpp" +#include "source/renderer/ore/wgpu/ore_context_wgpu.cpp" +#include "source/renderer/ore/wgpu/ore_pipeline_wgpu.cpp" +#include "source/renderer/ore/wgpu/ore_render_pass_wgpu.cpp" +#include "source/renderer/ore/wgpu/ore_sampler_wgpu.cpp" +#include "source/renderer/ore/wgpu/ore_shader_module_wgpu.cpp" +#include "source/renderer/ore/wgpu/ore_texture_wgpu.cpp" + +#elif RIVE_WEBGL +#include "source/renderer/gl/gl_state.cpp" +#include "source/renderer/gl/gl_utils.cpp" +#include "source/renderer/gl/load_store_actions_ext.cpp" +#include "source/renderer/gl/pls_impl_webgl.cpp" +#include "source/renderer/gl/render_buffer_gl_impl.cpp" +#include "source/renderer/gl/render_context_gl_impl.cpp" +#include "source/renderer/gl/render_target_gl.cpp" + +#include "source/renderer/ore/gl/ore_bind_group_gl.cpp" +#include "source/renderer/ore/gl/ore_buffer_gl.cpp" +#include "source/renderer/ore/gl/ore_context_gl.cpp" +#include "source/renderer/ore/gl/ore_pipeline_gl.cpp" +#define oreCompareFunctionToGL oreCompareFunctionToGL_rp +#include "source/renderer/ore/gl/ore_render_pass_gl.cpp" +#undef oreCompareFunctionToGL +#include "source/renderer/ore/gl/ore_sampler_gl.cpp" +#include "source/renderer/ore/gl/ore_shader_module_gl.cpp" +#define oreFormatToGLInternal oreFormatToGLInternal_tex +#include "source/renderer/ore/gl/ore_texture_gl.cpp" +#undef oreFormatToGLInternal + +#else +#error "No renderer backend defined" + +#endif + +#if __clang__ + #pragma clang diagnostic pop +#endif diff --git a/thirdparty/rive_renderer/rive_renderer_linux.cpp b/thirdparty/rive/rive_linux.cpp similarity index 60% rename from thirdparty/rive_renderer/rive_renderer_linux.cpp rename to thirdparty/rive/rive_linux.cpp index c53cce815..c97332d53 100644 --- a/thirdparty/rive_renderer/rive_renderer_linux.cpp +++ b/thirdparty/rive/rive_linux.cpp @@ -19,7 +19,7 @@ ============================================================================== */ -#include "rive_renderer.h" +#include "rive.h" #if __clang__ #pragma clang diagnostic push @@ -30,27 +30,27 @@ #pragma GCC diagnostic ignored "-Wattributes" #endif -#include "source/gl/gl_state.cpp" -#include "source/gl/gl_utils.cpp" -#include "source/gl/load_store_actions_ext.cpp" -#include "source/gl/pls_impl_ext_native.cpp" -#include "source/gl/pls_impl_rw_texture.cpp" -#include "source/gl/pls_impl_webgl.cpp" -#include "source/gl/render_buffer_gl_impl.cpp" -#include "source/gl/render_context_gl_impl.cpp" -#include "source/gl/render_target_gl.cpp" - -#include "source/ore/gl/ore_bind_group_gl.cpp" -#include "source/ore/gl/ore_buffer_gl.cpp" -#include "source/ore/gl/ore_context_gl.cpp" -#include "source/ore/gl/ore_pipeline_gl.cpp" +#include "source/renderer/gl/gl_state.cpp" +#include "source/renderer/gl/gl_utils.cpp" +#include "source/renderer/gl/load_store_actions_ext.cpp" +#include "source/renderer/gl/pls_impl_ext_native.cpp" +#include "source/renderer/gl/pls_impl_rw_texture.cpp" +#include "source/renderer/gl/pls_impl_webgl.cpp" +#include "source/renderer/gl/render_buffer_gl_impl.cpp" +#include "source/renderer/gl/render_context_gl_impl.cpp" +#include "source/renderer/gl/render_target_gl.cpp" + +#include "source/renderer/ore/gl/ore_bind_group_gl.cpp" +#include "source/renderer/ore/gl/ore_buffer_gl.cpp" +#include "source/renderer/ore/gl/ore_context_gl.cpp" +#include "source/renderer/ore/gl/ore_pipeline_gl.cpp" #define oreCompareFunctionToGL oreCompareFunctionToGL_rp -#include "source/ore/gl/ore_render_pass_gl.cpp" +#include "source/renderer/ore/gl/ore_render_pass_gl.cpp" #undef oreCompareFunctionToGL -#include "source/ore/gl/ore_sampler_gl.cpp" -#include "source/ore/gl/ore_shader_module_gl.cpp" +#include "source/renderer/ore/gl/ore_sampler_gl.cpp" +#include "source/renderer/ore/gl/ore_shader_module_gl.cpp" #define oreFormatToGLInternal oreFormatToGLInternal_tex -#include "source/ore/gl/ore_texture_gl.cpp" +#include "source/renderer/ore/gl/ore_texture_gl.cpp" #undef oreFormatToGLInternal #if __clang__ diff --git a/thirdparty/rive_renderer/rive_renderer.cpp b/thirdparty/rive/rive_renderer.cpp similarity index 63% rename from thirdparty/rive_renderer/rive_renderer.cpp rename to thirdparty/rive/rive_renderer.cpp index 57077814d..30cbeb6cd 100644 --- a/thirdparty/rive_renderer/rive_renderer.cpp +++ b/thirdparty/rive/rive_renderer.cpp @@ -30,35 +30,36 @@ #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #elif _MSC_VER #pragma warning (push) - #pragma warning (disable: 4244) + #pragma warning (disable : 4244) #endif -#include "rive_renderer.h" +#define YUP_RIVE_NO_INCLUDES 1 +#include "rive.h" +#undef YUP_RIVE_NO_INCLUDES // BEGIN YUP GENERATED RIVE RENDERER INCLUDES -#include "source/rive_renderer.cpp" -#include "source/render_context.cpp" -#include "source/rive_render_paint.cpp" -#include "source/rive_render_path.cpp" -#include "source/rive_render_image.cpp" -#include "source/intersection_board.cpp" -#include "source/draw.cpp" -#include "source/gr_triangulator.cpp" -#include "source/gradient.cpp" -#include "source/sk_rectanizer_skyline.cpp" -#include "source/gpu.cpp" -#include "source/rive_render_factory.cpp" -#include "source/render_context_helper_impl.cpp" -#include "source/gpu_resource.cpp" -// END YUP GENERATED RIVE RENDERER INCLUDES - +#include "source/renderer/rive_renderer.cpp" +#include "source/renderer/render_context.cpp" +#include "source/renderer/rive_render_paint.cpp" +#include "source/renderer/rive_render_path.cpp" +#include "source/renderer/rive_render_image.cpp" +#include "source/renderer/intersection_board.cpp" +#include "source/renderer/draw.cpp" +#include "source/renderer/gr_triangulator.cpp" +#include "source/renderer/gradient.cpp" +#include "source/renderer/sk_rectanizer_skyline.cpp" +#include "source/renderer/gpu.cpp" +#include "source/renderer/rive_render_factory.cpp" +#include "source/renderer/render_context_helper_impl.cpp" +#include "source/renderer/gpu_resource.cpp" #define readU16LE readU16LE_vec #define readU32LE readU32LE_vec -#include "source/ore/hlsl_struct_layout.cpp" +#include "source/renderer/ore/hlsl_struct_layout.cpp" #undef readU16LE #undef readU32LE -#include "source/ore/ore_bind_group_layout.cpp" -#include "source/ore/ore_binding_map.cpp" +#include "source/renderer/ore/ore_bind_group_layout.cpp" +#include "source/renderer/ore/ore_binding_map.cpp" +// END YUP GENERATED RIVE RENDERER INCLUDES #if __clang__ #pragma clang diagnostic pop diff --git a/thirdparty/rive/rive_windows.cpp b/thirdparty/rive/rive_windows.cpp new file mode 100644 index 000000000..9dc9e9fad --- /dev/null +++ b/thirdparty/rive/rive_windows.cpp @@ -0,0 +1,72 @@ +/* + ============================================================================== + + This file is part of the YUP library. + Copyright (c) 2024 - kunitoki@gmail.com + + YUP is an open source library subject to open-source licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + to use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#include "rive.h" + +#if YUP_RIVE_USE_D3D +#pragma comment (lib, "d3d11.lib") +#pragma comment (lib, "d3dcompiler.lib") +#pragma comment (lib, "dxgi.lib") +#pragma comment (lib, "dxguid.lib") + +#include "source/renderer/d3d/pipeline_manager.cpp" +#include "source/renderer/d3d/d3d_utils.cpp" +#include "source/renderer/d3d11/render_context_d3d_impl.cpp" +//#include "source/renderer/d3d12/d3d12_pipeline_manager.cpp" +//#include "source/renderer/d3d12/d3d12_utils.cpp" +//#include "source/renderer/d3d12/render_context_d3d12_impl.cpp" + +#include "source/renderer/ore/d3d11/ore_bind_group_d3d11.cpp" +#include "source/renderer/ore/d3d11/ore_buffer_d3d11.cpp" +#include "source/renderer/ore/d3d11/ore_context_d3d11.cpp" +#include "source/renderer/ore/d3d11/ore_pipeline_d3d11.cpp" +#include "source/renderer/ore/d3d11/ore_render_pass_d3d11.cpp" +#include "source/renderer/ore/d3d11/ore_sampler_d3d11.cpp" +#include "source/renderer/ore/d3d11/ore_shader_module_d3d11.cpp" +#include "source/renderer/ore/d3d11/ore_texture_d3d11.cpp" +#endif + +#if YUP_RIVE_USE_OPENGL +#pragma comment (lib, "opengl32.lib") + +#include "source/renderer/gl/gl_state.cpp" +#include "source/renderer/gl/gl_utils.cpp" +#include "source/renderer/gl/load_store_actions_ext.cpp" +#include "source/renderer/gl/pls_impl_ext_native.cpp" +#include "source/renderer/gl/pls_impl_rw_texture.cpp" +#include "source/renderer/gl/pls_impl_webgl.cpp" +#include "source/renderer/gl/render_buffer_gl_impl.cpp" +#include "source/renderer/gl/render_context_gl_impl.cpp" +#include "source/renderer/gl/render_target_gl.cpp" + +#include "source/renderer/ore/gl/ore_bind_group_gl.cpp" +#include "source/renderer/ore/gl/ore_buffer_gl.cpp" +#include "source/renderer/ore/gl/ore_context_gl.cpp" +#include "source/renderer/ore/gl/ore_pipeline_gl.cpp" +#define oreCompareFunctionToGL oreCompareFunctionToGL_rp +#include "source/renderer/ore/gl/ore_render_pass_gl.cpp" +#undef oreCompareFunctionToGL +#include "source/renderer/ore/gl/ore_sampler_gl.cpp" +#include "source/renderer/ore/gl/ore_shader_module_gl.cpp" +#define oreFormatToGLInternal oreFormatToGLInternal_tex +#include "source/renderer/ore/gl/ore_texture_gl.cpp" +#undef oreFormatToGLInternal +#endif diff --git a/thirdparty/rive/source/assets/script_asset.cpp b/thirdparty/rive/source/assets/script_asset.cpp index b1df3dc85..841128525 100644 --- a/thirdparty/rive/source/assets/script_asset.cpp +++ b/thirdparty/rive/source/assets/script_asset.cpp @@ -138,9 +138,6 @@ bool ScriptAsset::initScriptedObjectWith(ScriptedObject* object) if (ref == 0) { - fprintf(stderr, - "ScriptAsset doesn't have a generator function %s\n", - name().c_str()); return false; } rive_lua_pushRef(state, ref); diff --git a/thirdparty/rive_renderer/source/d3d/d3d_utils.cpp b/thirdparty/rive/source/renderer/d3d/d3d_utils.cpp similarity index 100% rename from thirdparty/rive_renderer/source/d3d/d3d_utils.cpp rename to thirdparty/rive/source/renderer/d3d/d3d_utils.cpp diff --git a/thirdparty/rive_renderer/source/d3d/pipeline_manager.cpp b/thirdparty/rive/source/renderer/d3d/pipeline_manager.cpp similarity index 100% rename from thirdparty/rive_renderer/source/d3d/pipeline_manager.cpp rename to thirdparty/rive/source/renderer/d3d/pipeline_manager.cpp diff --git a/thirdparty/rive_renderer/source/d3d11/render_context_d3d_impl.cpp b/thirdparty/rive/source/renderer/d3d11/render_context_d3d_impl.cpp similarity index 100% rename from thirdparty/rive_renderer/source/d3d11/render_context_d3d_impl.cpp rename to thirdparty/rive/source/renderer/d3d11/render_context_d3d_impl.cpp diff --git a/thirdparty/rive_renderer/source/d3d12/d3d12_pipeline_manager.cpp b/thirdparty/rive/source/renderer/d3d12/d3d12_pipeline_manager.cpp similarity index 100% rename from thirdparty/rive_renderer/source/d3d12/d3d12_pipeline_manager.cpp rename to thirdparty/rive/source/renderer/d3d12/d3d12_pipeline_manager.cpp diff --git a/thirdparty/rive_renderer/source/d3d12/d3d12_utils.cpp b/thirdparty/rive/source/renderer/d3d12/d3d12_utils.cpp similarity index 100% rename from thirdparty/rive_renderer/source/d3d12/d3d12_utils.cpp rename to thirdparty/rive/source/renderer/d3d12/d3d12_utils.cpp diff --git a/thirdparty/rive_renderer/source/d3d12/render_context_d3d12_impl.cpp b/thirdparty/rive/source/renderer/d3d12/render_context_d3d12_impl.cpp similarity index 100% rename from thirdparty/rive_renderer/source/d3d12/render_context_d3d12_impl.cpp rename to thirdparty/rive/source/renderer/d3d12/render_context_d3d12_impl.cpp diff --git a/thirdparty/rive_renderer/source/draw.cpp b/thirdparty/rive/source/renderer/draw.cpp similarity index 100% rename from thirdparty/rive_renderer/source/draw.cpp rename to thirdparty/rive/source/renderer/draw.cpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/advanced_blend.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/advanced_blend.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/advanced_blend.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/advanced_blend.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/advanced_blend.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/advanced_blend.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/advanced_blend.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/advanced_blend.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/advanced_blend.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/advanced_blend.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/advanced_blend.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/advanced_blend.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/atomic_draw.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/atomic_draw.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/atomic_draw.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/atomic_draw.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/atomic_draw.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/atomic_draw.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/atomic_draw.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/atomic_draw.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/atomic_draw.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/atomic_draw.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/atomic_draw.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/atomic_draw.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/bezier_utils.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/bezier_utils.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/bezier_utils.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/bezier_utils.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/bezier_utils.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/bezier_utils.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/bezier_utils.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/bezier_utils.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/bezier_utils.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/bezier_utils.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/bezier_utils.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/bezier_utils.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/blit_texture_as_draw.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/blit_texture_as_draw.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/blit_texture_as_draw.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/blit_texture_as_draw.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/blit_texture_as_draw.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/blit_texture_as_draw.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/blit_texture_as_draw.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/blit_texture_as_draw.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/blit_texture_as_draw.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/blit_texture_as_draw.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/blit_texture_as_draw.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/blit_texture_as_draw.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/clear_clockwise_atomic_clip.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/clear_clockwise_atomic_clip.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/clear_clockwise_atomic_clip.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/clear_clockwise_atomic_clip.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/clear_clockwise_atomic_clip.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/clear_clockwise_atomic_clip.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/clear_clockwise_atomic_clip.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/clear_clockwise_atomic_clip.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/clear_clockwise_atomic_clip.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/clear_clockwise_atomic_clip.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/clear_clockwise_atomic_clip.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/clear_clockwise_atomic_clip.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/color_ramp.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/color_ramp.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/color_ramp.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/color_ramp.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/color_ramp.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/color_ramp.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/color_ramp.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/color_ramp.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/color_ramp.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/color_ramp.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/color_ramp.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/color_ramp.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/common.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/common.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/common.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/common.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/common.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/common.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/common.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/common.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/common.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/common.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/common.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/common.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/constants.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/constants.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/constants.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/constants.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/constants.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/constants.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/constants.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/constants.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/constants.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/constants.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/constants.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/constants.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/d3d/color_ramp.frag.h b/thirdparty/rive/source/renderer/generated/shaders/d3d/color_ramp.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/d3d/color_ramp.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/d3d/color_ramp.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/d3d/color_ramp.vert.h b/thirdparty/rive/source/renderer/generated/shaders/d3d/color_ramp.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/d3d/color_ramp.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/d3d/color_ramp.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/d3d/render_atlas.vert.h b/thirdparty/rive/source/renderer/generated/shaders/d3d/render_atlas.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/d3d/render_atlas.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/d3d/render_atlas.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/d3d/render_atlas_fill.frag.h b/thirdparty/rive/source/renderer/generated/shaders/d3d/render_atlas_fill.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/d3d/render_atlas_fill.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/d3d/render_atlas_fill.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/d3d/render_atlas_stroke.frag.h b/thirdparty/rive/source/renderer/generated/shaders/d3d/render_atlas_stroke.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/d3d/render_atlas_stroke.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/d3d/render_atlas_stroke.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/d3d/root.sig.h b/thirdparty/rive/source/renderer/generated/shaders/d3d/root.sig.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/d3d/root.sig.h rename to thirdparty/rive/source/renderer/generated/shaders/d3d/root.sig.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/d3d/tessellate.frag.h b/thirdparty/rive/source/renderer/generated/shaders/d3d/tessellate.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/d3d/tessellate.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/d3d/tessellate.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/d3d/tessellate.vert.h b/thirdparty/rive/source/renderer/generated/shaders/d3d/tessellate.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/d3d/tessellate.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/d3d/tessellate.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_borrowed_coverage.frag.exports.h b/thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_atomic_borrowed_coverage.frag.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_borrowed_coverage.frag.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_atomic_borrowed_coverage.frag.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_borrowed_coverage.frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_atomic_borrowed_coverage.frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_borrowed_coverage.frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_atomic_borrowed_coverage.frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_borrowed_coverage.minified.frag b/thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_atomic_borrowed_coverage.minified.frag similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_borrowed_coverage.minified.frag rename to thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_atomic_borrowed_coverage.minified.frag diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_clip.frag.exports.h b/thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_atomic_clip.frag.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_clip.frag.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_atomic_clip.frag.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_clip.frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_atomic_clip.frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_clip.frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_atomic_clip.frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_clip.minified.frag b/thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_atomic_clip.minified.frag similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_clip.minified.frag rename to thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_atomic_clip.minified.frag diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_path.frag.exports.h b/thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_atomic_path.frag.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_path.frag.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_atomic_path.frag.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_path.frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_atomic_path.frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_path.frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_atomic_path.frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_path.minified.frag b/thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_atomic_path.minified.frag similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_path.minified.frag rename to thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_atomic_path.minified.frag diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_clip.frag.exports.h b/thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_clip.frag.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_clip.frag.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_clip.frag.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_clip.frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_clip.frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_clip.frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_clip.frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_clip.minified.frag b/thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_clip.minified.frag similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_clip.minified.frag rename to thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_clip.minified.frag diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_path.frag.exports.h b/thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_path.frag.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_path.frag.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_path.frag.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_path.frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_path.frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_path.frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_path.frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_path.minified.frag b/thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_path.minified.frag similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_path.minified.frag rename to thirdparty/rive/source/renderer/generated/shaders/draw_clockwise_path.minified.frag diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_combinations.metal b/thirdparty/rive/source/renderer/generated/shaders/draw_combinations.metal similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_combinations.metal rename to thirdparty/rive/source/renderer/generated/shaders/draw_combinations.metal diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_fullscreen_quad.minified.vert b/thirdparty/rive/source/renderer/generated/shaders/draw_fullscreen_quad.minified.vert similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_fullscreen_quad.minified.vert rename to thirdparty/rive/source/renderer/generated/shaders/draw_fullscreen_quad.minified.vert diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_fullscreen_quad.vert.exports.h b/thirdparty/rive/source/renderer/generated/shaders/draw_fullscreen_quad.vert.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_fullscreen_quad.vert.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/draw_fullscreen_quad.vert.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_fullscreen_quad.vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/draw_fullscreen_quad.vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_fullscreen_quad.vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/draw_fullscreen_quad.vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_image_mesh.minified.vert b/thirdparty/rive/source/renderer/generated/shaders/draw_image_mesh.minified.vert similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_image_mesh.minified.vert rename to thirdparty/rive/source/renderer/generated/shaders/draw_image_mesh.minified.vert diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_image_mesh.vert.exports.h b/thirdparty/rive/source/renderer/generated/shaders/draw_image_mesh.vert.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_image_mesh.vert.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/draw_image_mesh.vert.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_image_mesh.vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/draw_image_mesh.vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_image_mesh.vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/draw_image_mesh.vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_input_attachment.frag.exports.h b/thirdparty/rive/source/renderer/generated/shaders/draw_input_attachment.frag.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_input_attachment.frag.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/draw_input_attachment.frag.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_input_attachment.frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/draw_input_attachment.frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_input_attachment.frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/draw_input_attachment.frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_input_attachment.minified.frag b/thirdparty/rive/source/renderer/generated/shaders/draw_input_attachment.minified.frag similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_input_attachment.minified.frag rename to thirdparty/rive/source/renderer/generated/shaders/draw_input_attachment.minified.frag diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_mesh.frag.exports.h b/thirdparty/rive/source/renderer/generated/shaders/draw_mesh.frag.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_mesh.frag.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/draw_mesh.frag.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_mesh.frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/draw_mesh.frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_mesh.frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/draw_mesh.frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_mesh.minified.frag b/thirdparty/rive/source/renderer/generated/shaders/draw_mesh.minified.frag similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_mesh.minified.frag rename to thirdparty/rive/source/renderer/generated/shaders/draw_mesh.minified.frag diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_msaa_object.frag.exports.h b/thirdparty/rive/source/renderer/generated/shaders/draw_msaa_object.frag.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_msaa_object.frag.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/draw_msaa_object.frag.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_msaa_object.frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/draw_msaa_object.frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_msaa_object.frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/draw_msaa_object.frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_msaa_object.minified.frag b/thirdparty/rive/source/renderer/generated/shaders/draw_msaa_object.minified.frag similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_msaa_object.minified.frag rename to thirdparty/rive/source/renderer/generated/shaders/draw_msaa_object.minified.frag diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_msaa_resolve.frag.exports.h b/thirdparty/rive/source/renderer/generated/shaders/draw_msaa_resolve.frag.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_msaa_resolve.frag.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/draw_msaa_resolve.frag.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_msaa_resolve.frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/draw_msaa_resolve.frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_msaa_resolve.frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/draw_msaa_resolve.frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_msaa_resolve.minified.frag b/thirdparty/rive/source/renderer/generated/shaders/draw_msaa_resolve.minified.frag similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_msaa_resolve.minified.frag rename to thirdparty/rive/source/renderer/generated/shaders/draw_msaa_resolve.minified.frag diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_path.minified.vert b/thirdparty/rive/source/renderer/generated/shaders/draw_path.minified.vert similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_path.minified.vert rename to thirdparty/rive/source/renderer/generated/shaders/draw_path.minified.vert diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_path.vert.exports.h b/thirdparty/rive/source/renderer/generated/shaders/draw_path.vert.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_path.vert.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/draw_path.vert.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_path.vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/draw_path.vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_path.vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/draw_path.vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_path_common.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/draw_path_common.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_path_common.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/draw_path_common.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_path_common.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/draw_path_common.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_path_common.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/draw_path_common.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_path_common.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/draw_path_common.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_path_common.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/draw_path_common.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_raster_order_path.frag.exports.h b/thirdparty/rive/source/renderer/generated/shaders/draw_raster_order_path.frag.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_raster_order_path.frag.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/draw_raster_order_path.frag.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_raster_order_path.frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/draw_raster_order_path.frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_raster_order_path.frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/draw_raster_order_path.frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_raster_order_path.minified.frag b/thirdparty/rive/source/renderer/generated/shaders/draw_raster_order_path.minified.frag similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/draw_raster_order_path.minified.frag rename to thirdparty/rive/source/renderer/generated/shaders/draw_raster_order_path.minified.frag diff --git a/thirdparty/rive_renderer/source/generated/shaders/flush_uniforms.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/flush_uniforms.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/flush_uniforms.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/flush_uniforms.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/flush_uniforms.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/flush_uniforms.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/flush_uniforms.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/flush_uniforms.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/flush_uniforms.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/flush_uniforms.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/flush_uniforms.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/flush_uniforms.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/glsl.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/glsl.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/glsl.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/glsl.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/glsl.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/glsl.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/glsl.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/glsl.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/glsl.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/glsl.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/glsl.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/glsl.minified.glsl diff --git a/thirdparty/rive/source/renderer/generated/shaders/glsl.stamp b/thirdparty/rive/source/renderer/generated/shaders/glsl.stamp new file mode 100644 index 000000000..e69de29bb diff --git a/thirdparty/rive_renderer/source/generated/shaders/hlsl.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/hlsl.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/hlsl.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/hlsl.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/hlsl.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/hlsl.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/hlsl.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/hlsl.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/hlsl.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/hlsl.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/hlsl.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/hlsl.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/image_draw_uniforms.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/image_draw_uniforms.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/image_draw_uniforms.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/image_draw_uniforms.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/image_draw_uniforms.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/image_draw_uniforms.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/image_draw_uniforms.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/image_draw_uniforms.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/image_draw_uniforms.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/image_draw_uniforms.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/image_draw_uniforms.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/image_draw_uniforms.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/init_clockwise_atomic_workaround.frag.exports.h b/thirdparty/rive/source/renderer/generated/shaders/init_clockwise_atomic_workaround.frag.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/init_clockwise_atomic_workaround.frag.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/init_clockwise_atomic_workaround.frag.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/init_clockwise_atomic_workaround.frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/init_clockwise_atomic_workaround.frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/init_clockwise_atomic_workaround.frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/init_clockwise_atomic_workaround.frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/init_clockwise_atomic_workaround.minified.frag b/thirdparty/rive/source/renderer/generated/shaders/init_clockwise_atomic_workaround.minified.frag similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/init_clockwise_atomic_workaround.minified.frag rename to thirdparty/rive/source/renderer/generated/shaders/init_clockwise_atomic_workaround.minified.frag diff --git a/thirdparty/rive_renderer/source/generated/shaders/ios/color_ramp.air b/thirdparty/rive/source/renderer/generated/shaders/ios/color_ramp.air similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/ios/color_ramp.air rename to thirdparty/rive/source/renderer/generated/shaders/ios/color_ramp.air diff --git a/thirdparty/rive_renderer/source/generated/shaders/ios/draw.air b/thirdparty/rive/source/renderer/generated/shaders/ios/draw.air similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/ios/draw.air rename to thirdparty/rive/source/renderer/generated/shaders/ios/draw.air diff --git a/thirdparty/rive_renderer/source/generated/shaders/ios/rive_pls_ios.metallib b/thirdparty/rive/source/renderer/generated/shaders/ios/rive_pls_ios.metallib similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/ios/rive_pls_ios.metallib rename to thirdparty/rive/source/renderer/generated/shaders/ios/rive_pls_ios.metallib diff --git a/thirdparty/rive_renderer/source/generated/shaders/ios/rive_pls_ios_simulator.metallib b/thirdparty/rive/source/renderer/generated/shaders/ios/rive_pls_ios_simulator.metallib similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/ios/rive_pls_ios_simulator.metallib rename to thirdparty/rive/source/renderer/generated/shaders/ios/rive_pls_ios_simulator.metallib diff --git a/thirdparty/rive_renderer/source/generated/shaders/ios/rive_renderer_appletvos.metallib b/thirdparty/rive/source/renderer/generated/shaders/ios/rive_renderer_appletvos.metallib similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/ios/rive_renderer_appletvos.metallib rename to thirdparty/rive/source/renderer/generated/shaders/ios/rive_renderer_appletvos.metallib diff --git a/thirdparty/rive_renderer/source/generated/shaders/ios/rive_renderer_appletvsimulator.metallib b/thirdparty/rive/source/renderer/generated/shaders/ios/rive_renderer_appletvsimulator.metallib similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/ios/rive_renderer_appletvsimulator.metallib rename to thirdparty/rive/source/renderer/generated/shaders/ios/rive_renderer_appletvsimulator.metallib diff --git a/thirdparty/rive_renderer/source/generated/shaders/ios/rive_renderer_xros.metallib b/thirdparty/rive/source/renderer/generated/shaders/ios/rive_renderer_xros.metallib similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/ios/rive_renderer_xros.metallib rename to thirdparty/rive/source/renderer/generated/shaders/ios/rive_renderer_xros.metallib diff --git a/thirdparty/rive_renderer/source/generated/shaders/ios/rive_renderer_xros_simulator.metallib b/thirdparty/rive/source/renderer/generated/shaders/ios/rive_renderer_xros_simulator.metallib similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/ios/rive_renderer_xros_simulator.metallib rename to thirdparty/rive/source/renderer/generated/shaders/ios/rive_renderer_xros_simulator.metallib diff --git a/thirdparty/rive_renderer/source/generated/shaders/ios/tessellate.air b/thirdparty/rive/source/renderer/generated/shaders/ios/tessellate.air similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/ios/tessellate.air rename to thirdparty/rive/source/renderer/generated/shaders/ios/tessellate.air diff --git a/thirdparty/rive_renderer/source/generated/shaders/macosx/color_ramp.air b/thirdparty/rive/source/renderer/generated/shaders/macosx/color_ramp.air similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/macosx/color_ramp.air rename to thirdparty/rive/source/renderer/generated/shaders/macosx/color_ramp.air diff --git a/thirdparty/rive_renderer/source/generated/shaders/macosx/draw.air b/thirdparty/rive/source/renderer/generated/shaders/macosx/draw.air similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/macosx/draw.air rename to thirdparty/rive/source/renderer/generated/shaders/macosx/draw.air diff --git a/thirdparty/rive_renderer/source/generated/shaders/macosx/rive_pls_macosx.metallib b/thirdparty/rive/source/renderer/generated/shaders/macosx/rive_pls_macosx.metallib similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/macosx/rive_pls_macosx.metallib rename to thirdparty/rive/source/renderer/generated/shaders/macosx/rive_pls_macosx.metallib diff --git a/thirdparty/rive_renderer/source/generated/shaders/macosx/tessellate.air b/thirdparty/rive/source/renderer/generated/shaders/macosx/tessellate.air similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/macosx/tessellate.air rename to thirdparty/rive/source/renderer/generated/shaders/macosx/tessellate.air diff --git a/thirdparty/rive_renderer/source/generated/shaders/metal.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/metal.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/metal.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/metal.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/metal.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/metal.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/metal.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/metal.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/metal.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/metal.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/metal.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/metal.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/pls_load_store_ext.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/pls_load_store_ext.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/pls_load_store_ext.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/pls_load_store_ext.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/pls_load_store_ext.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/pls_load_store_ext.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/pls_load_store_ext.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/pls_load_store_ext.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/pls_load_store_ext.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/pls_load_store_ext.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/pls_load_store_ext.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/pls_load_store_ext.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/render_atlas.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/render_atlas.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/render_atlas.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/render_atlas.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/render_atlas.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/render_atlas.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/render_atlas.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/render_atlas.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/render_atlas.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/render_atlas.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/render_atlas.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/render_atlas.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/resolve_atlas.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/resolve_atlas.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/resolve_atlas.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/resolve_atlas.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/resolve_atlas.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/resolve_atlas.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/resolve_atlas.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/resolve_atlas.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/resolve_atlas.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/resolve_atlas.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/resolve_atlas.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/resolve_atlas.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/rhi.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/rhi.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/rhi.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/rhi.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/rhi.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/rhi.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/rhi.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/rhi.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/rhi.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/rhi.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/rhi.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/rhi.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/rive_pls_ios.metallib.c b/thirdparty/rive/source/renderer/generated/shaders/rive_pls_ios.metallib.c similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/rive_pls_ios.metallib.c rename to thirdparty/rive/source/renderer/generated/shaders/rive_pls_ios.metallib.c diff --git a/thirdparty/rive_renderer/source/generated/shaders/rive_pls_ios_simulator.metallib.c b/thirdparty/rive/source/renderer/generated/shaders/rive_pls_ios_simulator.metallib.c similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/rive_pls_ios_simulator.metallib.c rename to thirdparty/rive/source/renderer/generated/shaders/rive_pls_ios_simulator.metallib.c diff --git a/thirdparty/rive_renderer/source/generated/shaders/rive_pls_macosx.metallib.c b/thirdparty/rive/source/renderer/generated/shaders/rive_pls_macosx.metallib.c similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/rive_pls_macosx.metallib.c rename to thirdparty/rive/source/renderer/generated/shaders/rive_pls_macosx.metallib.c diff --git a/thirdparty/rive_renderer/source/generated/shaders/rive_renderer_appletvos.metallib.c b/thirdparty/rive/source/renderer/generated/shaders/rive_renderer_appletvos.metallib.c similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/rive_renderer_appletvos.metallib.c rename to thirdparty/rive/source/renderer/generated/shaders/rive_renderer_appletvos.metallib.c diff --git a/thirdparty/rive_renderer/source/generated/shaders/rive_renderer_appletvsimulator.metallib.c b/thirdparty/rive/source/renderer/generated/shaders/rive_renderer_appletvsimulator.metallib.c similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/rive_renderer_appletvsimulator.metallib.c rename to thirdparty/rive/source/renderer/generated/shaders/rive_renderer_appletvsimulator.metallib.c diff --git a/thirdparty/rive_renderer/source/generated/shaders/rive_renderer_xros.metallib.c b/thirdparty/rive/source/renderer/generated/shaders/rive_renderer_xros.metallib.c similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/rive_renderer_xros.metallib.c rename to thirdparty/rive/source/renderer/generated/shaders/rive_renderer_xros.metallib.c diff --git a/thirdparty/rive_renderer/source/generated/shaders/rive_renderer_xros_simulator.metallib.c b/thirdparty/rive/source/renderer/generated/shaders/rive_renderer_xros_simulator.metallib.c similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/rive_renderer_xros_simulator.metallib.c rename to thirdparty/rive/source/renderer/generated/shaders/rive_renderer_xros_simulator.metallib.c diff --git a/thirdparty/rive_renderer/source/generated/shaders/specialization.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/specialization.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/specialization.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/specialization.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/specialization.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/specialization.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/specialization.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/specialization.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/specialization.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/specialization.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/specialization.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/specialization.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_atlas_blit.webgpu_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_mesh.webgpu_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.webgpu_fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.webgpu_fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.webgpu_fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.webgpu_fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.webgpu_fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.webgpu_fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.webgpu_fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.webgpu_fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.webgpu_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.webgpu_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.webgpu_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.webgpu_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.webgpu_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.webgpu_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.webgpu_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.webgpu_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.webgpu_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.webgpu_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.webgpu_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.webgpu_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.webgpu_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.webgpu_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_image_rect.webgpu_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_image_rect.webgpu_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_interior_triangles.webgpu_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.webgpu_fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.webgpu_fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.webgpu_fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.webgpu_fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.webgpu_fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.webgpu_fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.webgpu_fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.webgpu_fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.webgpu_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.webgpu_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.webgpu_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.webgpu_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.webgpu_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.webgpu_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.webgpu_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.webgpu_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.webgpu_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.webgpu_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.webgpu_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.webgpu_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.webgpu_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.webgpu_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_draw_path.webgpu_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_draw_path.webgpu_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.webgpu_fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.webgpu_fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.webgpu_fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.webgpu_fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.webgpu_fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.webgpu_fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.webgpu_fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.webgpu_fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.webgpu_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.webgpu_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.webgpu_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.webgpu_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.webgpu_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.webgpu_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.webgpu_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.webgpu_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.webgpu_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.webgpu_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.webgpu_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.webgpu_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.webgpu_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.webgpu_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_init.webgpu_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_init.webgpu_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.webgpu_fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.webgpu_fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.webgpu_fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.webgpu_fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.webgpu_fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.webgpu_fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.webgpu_fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.webgpu_fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.webgpu_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.webgpu_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.webgpu_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.webgpu_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.webgpu_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.webgpu_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.webgpu_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.webgpu_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.webgpu_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.webgpu_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.webgpu_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.webgpu_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.webgpu_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.webgpu_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve.webgpu_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve.webgpu_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.webgpu_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.webgpu_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.webgpu_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.webgpu_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.webgpu_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.webgpu_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.webgpu_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.webgpu_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.webgpu_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.webgpu_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.webgpu_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.webgpu_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.webgpu_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.webgpu_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/atomic_resolve_coalesced.webgpu_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/atomic_resolve_coalesced.webgpu_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/blit_texture_as_draw_filtered.webgpu_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/clear_clockwise_atomic_clip.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/clear_clockwise_atomic_clip.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/clear_clockwise_atomic_clip.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/clear_clockwise_atomic_clip.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/clear_clockwise_atomic_clip.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/clear_clockwise_atomic_clip.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/clear_clockwise_atomic_clip.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/clear_clockwise_atomic_clip.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/clear_clockwise_atomic_clip.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/clear_clockwise_atomic_clip.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/clear_clockwise_atomic_clip.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/clear_clockwise_atomic_clip.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/clear_clockwise_atomic_clip.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/clear_clockwise_atomic_clip.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/clear_clockwise_atomic_clip.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/clear_clockwise_atomic_clip.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/clear_clockwise_atomic_clip.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/clear_clockwise_atomic_clip.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/clear_clockwise_atomic_clip.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/clear_clockwise_atomic_clip.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/clear_clockwise_atomic_clip.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/clear_clockwise_atomic_clip.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/clear_clockwise_atomic_clip.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/clear_clockwise_atomic_clip.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/clear_clockwise_atomic_clip.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/clear_clockwise_atomic_clip.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/clear_clockwise_atomic_clip.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/clear_clockwise_atomic_clip.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/clear_clockwise_atomic_clip.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/clear_clockwise_atomic_clip.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/clear_clockwise_atomic_clip.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/clear_clockwise_atomic_clip.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/clear_clockwise_atomic_clip.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/clear_clockwise_atomic_clip.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/clear_clockwise_atomic_clip.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/clear_clockwise_atomic_clip.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/color_ramp.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/color_ramp.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/color_ramp.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/color_ramp.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/color_ramp.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/color_ramp.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/color_ramp.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/color_ramp.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/color_ramp.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/color_ramp.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/color_ramp.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/color_ramp.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/color_ramp.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/color_ramp.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/color_ramp.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/color_ramp.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/color_ramp.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/color_ramp.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/color_ramp.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/color_ramp.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/color_ramp.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/color_ramp.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/color_ramp.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/color_ramp.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_atlas_blit.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_atlas_blit.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_atlas_blit.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_atlas_blit.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_atlas_blit.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_atlas_blit.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_atlas_blit.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_atlas_blit.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_atlas_blit.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_atlas_blit.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_atlas_blit.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_atlas_blit.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_atlas_blit.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_atlas_blit.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_atlas_blit.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_atlas_blit.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_atlas_blit.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_atlas_blit.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_atlas_blit.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_atlas_blit.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_atlas_blit.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_atlas_blit.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_atlas_blit.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_atlas_blit.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atlas_blit.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atlas_blit.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atlas_blit.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atlas_blit.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atlas_blit.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atlas_blit.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atlas_blit.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atlas_blit.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atlas_blit.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atlas_blit.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atlas_blit.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atlas_blit.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atlas_blit.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atlas_blit.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atlas_blit.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atlas_blit.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atlas_blit.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atlas_blit.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atlas_blit.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atlas_blit.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atlas_blit.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atlas_blit.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atlas_blit.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atlas_blit.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atlas_blit.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atlas_blit.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atlas_blit.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atlas_blit.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atlas_blit.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atlas_blit.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atlas_blit.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atlas_blit.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atlas_blit.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atlas_blit.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atlas_blit.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atlas_blit.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_atlas_blit.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage_interior_triangles.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage_interior_triangles.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage_interior_triangles.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage_interior_triangles.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage_interior_triangles.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage_interior_triangles.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage_interior_triangles.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage_interior_triangles.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage_interior_triangles.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage_interior_triangles.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage_interior_triangles.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_borrowed_coverage_interior_triangles.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_image_mesh.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_interior_triangles.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_path.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_path.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_path.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_path.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_path.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_path.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_path.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_path.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_path.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_path.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_path.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_path.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_path.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_path.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_path.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_path.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_path.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_path.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_path.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_path.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_path.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_path.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_path.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_path.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_path.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_path.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_path.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_path.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_path.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_path.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_path.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_path.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_path.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_path.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_atomic_path.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_atomic_path.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_clip_interior_triangles.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_image_mesh.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_image_mesh.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_image_mesh.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_image_mesh.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_image_mesh.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_image_mesh.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_image_mesh.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_image_mesh.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_image_mesh.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_image_mesh.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_image_mesh.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_image_mesh.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_image_mesh.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_image_mesh.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_image_mesh.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_image_mesh.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_image_mesh.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_image_mesh.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_image_mesh.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_image_mesh.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_image_mesh.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_image_mesh.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_image_mesh.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_image_mesh.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_image_mesh.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_image_mesh.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_image_mesh.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_image_mesh.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_image_mesh.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_image_mesh.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_image_mesh.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_image_mesh.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_image_mesh.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_image_mesh.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_image_mesh.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_image_mesh.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_interior_triangles.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_interior_triangles.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_interior_triangles.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_interior_triangles.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_interior_triangles.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_interior_triangles.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_interior_triangles.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_interior_triangles.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_interior_triangles.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_interior_triangles.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_interior_triangles.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_interior_triangles.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_interior_triangles.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_interior_triangles.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_interior_triangles.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_interior_triangles.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_interior_triangles.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_interior_triangles.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_interior_triangles.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_interior_triangles.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_interior_triangles.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_interior_triangles.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_interior_triangles.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_interior_triangles.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_interior_triangles.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_interior_triangles.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_interior_triangles.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_interior_triangles.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_interior_triangles.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_interior_triangles.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_interior_triangles.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_interior_triangles.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_interior_triangles.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_interior_triangles.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_interior_triangles.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_interior_triangles.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_path.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_path.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_path.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_path.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_path.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_path.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_path.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_path.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_path.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_path.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_path.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_path.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_path.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_path.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_path.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_path.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_path.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_path.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_path.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_path.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_path.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_path.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_path.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_path.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_path.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_path.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_path.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_path.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_path.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_path.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_path.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_path.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_path.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_path.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_clockwise_path.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_clockwise_path.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_fullscreen_quad.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_fullscreen_quad.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_fullscreen_quad.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_fullscreen_quad.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_fullscreen_quad.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_fullscreen_quad.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_fullscreen_quad.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_fullscreen_quad.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_fullscreen_quad.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_fullscreen_quad.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_fullscreen_quad.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_fullscreen_quad.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_image_mesh.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_image_mesh.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_image_mesh.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_image_mesh.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_image_mesh.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_image_mesh.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_image_mesh.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_image_mesh.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_image_mesh.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_image_mesh.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_image_mesh.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_image_mesh.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_image_mesh.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_image_mesh.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_image_mesh.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_image_mesh.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_image_mesh.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_image_mesh.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_image_mesh.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_image_mesh.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_image_mesh.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_image_mesh.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_image_mesh.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_image_mesh.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_input_attachment.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_input_attachment.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_input_attachment.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_input_attachment.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_input_attachment.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_input_attachment.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_input_attachment.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_input_attachment.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_input_attachment.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_input_attachment.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_input_attachment.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_input_attachment.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_interior_triangles.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_interior_triangles.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_interior_triangles.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_interior_triangles.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_interior_triangles.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_interior_triangles.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_interior_triangles.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_interior_triangles.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_interior_triangles.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_interior_triangles.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_interior_triangles.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_interior_triangles.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_interior_triangles.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_interior_triangles.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_interior_triangles.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_interior_triangles.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_interior_triangles.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_interior_triangles.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_interior_triangles.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_interior_triangles.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_interior_triangles.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_interior_triangles.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_interior_triangles.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_interior_triangles.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.noclipdistance_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.noclipdistance_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.noclipdistance_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.noclipdistance_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.noclipdistance_vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.noclipdistance_vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.noclipdistance_vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.noclipdistance_vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.noclipdistance_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.noclipdistance_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.noclipdistance_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.noclipdistance_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_noclipdistance_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_noclipdistance_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_noclipdistance_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_noclipdistance_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_noclipdistance_vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_noclipdistance_vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_noclipdistance_vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_noclipdistance_vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_noclipdistance_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_noclipdistance_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_noclipdistance_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_noclipdistance_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_atlas_blit.webgpu_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_color_seed_attachment.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_color_seed_attachment.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_color_seed_attachment.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_color_seed_attachment.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_color_seed_attachment.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_color_seed_attachment.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_color_seed_attachment.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_color_seed_attachment.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_color_seed_attachment.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_color_seed_attachment.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_color_seed_attachment.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_color_seed_attachment.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.noclipdistance_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.noclipdistance_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.noclipdistance_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.noclipdistance_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.noclipdistance_vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.noclipdistance_vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.noclipdistance_vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.noclipdistance_vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.noclipdistance_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.noclipdistance_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.noclipdistance_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.noclipdistance_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_noclipdistance_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_noclipdistance_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_noclipdistance_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_noclipdistance_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_noclipdistance_vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_noclipdistance_vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_noclipdistance_vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_noclipdistance_vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_noclipdistance_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_noclipdistance_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_noclipdistance_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_noclipdistance_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_image_mesh.webgpu_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.noclipdistance_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.noclipdistance_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.noclipdistance_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.noclipdistance_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.noclipdistance_vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.noclipdistance_vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.noclipdistance_vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.noclipdistance_vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.noclipdistance_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.noclipdistance_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.noclipdistance_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.noclipdistance_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_noclipdistance_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_noclipdistance_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_noclipdistance_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_noclipdistance_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_noclipdistance_vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_noclipdistance_vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_noclipdistance_vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_noclipdistance_vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_noclipdistance_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_noclipdistance_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_noclipdistance_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_noclipdistance_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_path.webgpu_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_path.webgpu_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_resolve.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_resolve.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_resolve.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_resolve.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_resolve.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_resolve.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_resolve.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_resolve.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_resolve.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_resolve.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_resolve.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_resolve.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.noclipdistance_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.noclipdistance_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.noclipdistance_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.noclipdistance_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.noclipdistance_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.noclipdistance_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.noclipdistance_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.noclipdistance_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.webgpu_fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.webgpu_fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.webgpu_fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.webgpu_fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.webgpu_fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.webgpu_fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.webgpu_fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.webgpu_fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.webgpu_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.webgpu_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.webgpu_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.webgpu_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.webgpu_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.webgpu_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.webgpu_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.webgpu_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.webgpu_noclipdistance_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.webgpu_noclipdistance_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.webgpu_noclipdistance_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.webgpu_noclipdistance_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.webgpu_noclipdistance_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.webgpu_noclipdistance_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.webgpu_noclipdistance_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.webgpu_noclipdistance_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.webgpu_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.webgpu_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.webgpu_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.webgpu_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.webgpu_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.webgpu_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_msaa_stencil.webgpu_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_msaa_stencil.webgpu_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_path.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_path.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_path.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_path.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_path.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_path.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_path.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_path.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_path.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_path.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_path.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_path.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_path.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_path.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_path.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_path.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_path.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_path.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_path.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_path.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/draw_path.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/draw_path.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/draw_path.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/draw_path.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/init_clockwise_atomic_workaround.fixedcolor_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/init_clockwise_atomic_workaround.fixedcolor_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/init_clockwise_atomic_workaround.fixedcolor_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/init_clockwise_atomic_workaround.fixedcolor_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/init_clockwise_atomic_workaround.fixedcolor_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/init_clockwise_atomic_workaround.fixedcolor_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/init_clockwise_atomic_workaround.fixedcolor_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/init_clockwise_atomic_workaround.fixedcolor_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/init_clockwise_atomic_workaround.fixedcolor_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/init_clockwise_atomic_workaround.fixedcolor_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/init_clockwise_atomic_workaround.fixedcolor_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/init_clockwise_atomic_workaround.fixedcolor_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/init_clockwise_atomic_workaround.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/init_clockwise_atomic_workaround.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/init_clockwise_atomic_workaround.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/init_clockwise_atomic_workaround.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/init_clockwise_atomic_workaround.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/init_clockwise_atomic_workaround.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/init_clockwise_atomic_workaround.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/init_clockwise_atomic_workaround.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/init_clockwise_atomic_workaround.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/init_clockwise_atomic_workaround.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/init_clockwise_atomic_workaround.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/init_clockwise_atomic_workaround.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas.webgpu_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas.webgpu_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas.webgpu_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas.webgpu_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas.webgpu_vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas.webgpu_vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas.webgpu_vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas.webgpu_vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas.webgpu_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas.webgpu_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas.webgpu_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas.webgpu_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_fill.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_fill.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_fill.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_fill.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_fill.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_fill.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_fill.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_fill.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_fill.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_fill.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_fill.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_fill.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_fill.webgpu_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_fill.webgpu_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_fill.webgpu_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_fill.webgpu_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_fill.webgpu_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_fill.webgpu_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_fill.webgpu_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_fill.webgpu_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_fill.webgpu_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_fill.webgpu_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_fill.webgpu_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_fill.webgpu_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_stroke.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_stroke.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_stroke.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_stroke.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_stroke.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_stroke.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_stroke.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_stroke.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_stroke.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_stroke.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_stroke.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_stroke.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_stroke.webgpu_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_stroke.webgpu_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_stroke.webgpu_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_stroke.webgpu_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_stroke.webgpu_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_stroke.webgpu_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_stroke.webgpu_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_stroke.webgpu_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_stroke.webgpu_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_stroke.webgpu_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/render_atlas_stroke.webgpu_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/render_atlas_stroke.webgpu_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.webgpu_frag.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.webgpu_frag.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.webgpu_frag.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.webgpu_frag.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.webgpu_frag.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.webgpu_frag.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.webgpu_frag.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.webgpu_frag.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.webgpu_frag.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.webgpu_frag.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.webgpu_frag.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.webgpu_frag.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.webgpu_vert.h b/thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.webgpu_vert.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.webgpu_vert.h rename to thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.webgpu_vert.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.webgpu_vert.spirv b/thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.webgpu_vert.spirv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.webgpu_vert.spirv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.webgpu_vert.spirv diff --git a/thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.webgpu_vert.spv b/thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.webgpu_vert.spv similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/spirv/tessellate.webgpu_vert.spv rename to thirdparty/rive/source/renderer/generated/shaders/spirv/tessellate.webgpu_vert.spv diff --git a/thirdparty/rive_renderer/source/generated/shaders/stencil_draw.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/stencil_draw.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/stencil_draw.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/stencil_draw.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/stencil_draw.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/stencil_draw.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/stencil_draw.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/stencil_draw.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/stencil_draw.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/stencil_draw.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/stencil_draw.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/stencil_draw.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/tessellate.glsl.exports.h b/thirdparty/rive/source/renderer/generated/shaders/tessellate.glsl.exports.h similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/tessellate.glsl.exports.h rename to thirdparty/rive/source/renderer/generated/shaders/tessellate.glsl.exports.h diff --git a/thirdparty/rive_renderer/source/generated/shaders/tessellate.glsl.hpp b/thirdparty/rive/source/renderer/generated/shaders/tessellate.glsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/tessellate.glsl.hpp rename to thirdparty/rive/source/renderer/generated/shaders/tessellate.glsl.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/tessellate.minified.glsl b/thirdparty/rive/source/renderer/generated/shaders/tessellate.minified.glsl similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/tessellate.minified.glsl rename to thirdparty/rive/source/renderer/generated/shaders/tessellate.minified.glsl diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_atlas_blit.webgpu_fixedcolor_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_atlas_blit.webgpu_fixedcolor_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_atlas_blit.webgpu_fixedcolor_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_atlas_blit.webgpu_fixedcolor_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_atlas_blit.webgpu_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_atlas_blit.webgpu_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_atlas_blit.webgpu_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_atlas_blit.webgpu_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_atlas_blit.webgpu_vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_atlas_blit.webgpu_vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_atlas_blit.webgpu_vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_atlas_blit.webgpu_vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_image_mesh.webgpu_fixedcolor_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_image_mesh.webgpu_fixedcolor_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_image_mesh.webgpu_fixedcolor_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_image_mesh.webgpu_fixedcolor_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_image_mesh.webgpu_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_image_mesh.webgpu_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_image_mesh.webgpu_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_image_mesh.webgpu_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_image_mesh.webgpu_vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_image_mesh.webgpu_vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_image_mesh.webgpu_vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_image_mesh.webgpu_vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_image_rect.webgpu_fixedcolor_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_image_rect.webgpu_fixedcolor_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_image_rect.webgpu_fixedcolor_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_image_rect.webgpu_fixedcolor_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_image_rect.webgpu_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_image_rect.webgpu_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_image_rect.webgpu_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_image_rect.webgpu_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_image_rect.webgpu_vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_image_rect.webgpu_vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_image_rect.webgpu_vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_image_rect.webgpu_vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_interior_triangles.webgpu_fixedcolor_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_interior_triangles.webgpu_fixedcolor_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_interior_triangles.webgpu_fixedcolor_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_interior_triangles.webgpu_fixedcolor_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_interior_triangles.webgpu_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_interior_triangles.webgpu_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_interior_triangles.webgpu_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_interior_triangles.webgpu_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_interior_triangles.webgpu_vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_interior_triangles.webgpu_vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_interior_triangles.webgpu_vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_interior_triangles.webgpu_vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_path.webgpu_fixedcolor_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_path.webgpu_fixedcolor_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_path.webgpu_fixedcolor_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_path.webgpu_fixedcolor_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_path.webgpu_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_path.webgpu_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_path.webgpu_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_path.webgpu_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_path.webgpu_vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_path.webgpu_vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_draw_path.webgpu_vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_draw_path.webgpu_vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_init.webgpu_fixedcolor_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_init.webgpu_fixedcolor_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_init.webgpu_fixedcolor_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_init.webgpu_fixedcolor_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_init.webgpu_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_init.webgpu_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_init.webgpu_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_init.webgpu_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_init.webgpu_vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_init.webgpu_vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_init.webgpu_vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_init.webgpu_vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_resolve.webgpu_fixedcolor_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_resolve.webgpu_fixedcolor_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_resolve.webgpu_fixedcolor_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_resolve.webgpu_fixedcolor_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_resolve.webgpu_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_resolve.webgpu_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_resolve.webgpu_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_resolve.webgpu_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_resolve.webgpu_vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_resolve.webgpu_vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_resolve.webgpu_vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_resolve.webgpu_vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_resolve_coalesced.webgpu_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_resolve_coalesced.webgpu_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_resolve_coalesced.webgpu_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_resolve_coalesced.webgpu_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_resolve_coalesced.webgpu_vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_resolve_coalesced.webgpu_vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/atomic_resolve_coalesced.webgpu_vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/atomic_resolve_coalesced.webgpu_vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/blit_texture_as_draw_filtered.webgpu_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/blit_texture_as_draw_filtered.webgpu_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/blit_texture_as_draw_filtered.webgpu_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/blit_texture_as_draw_filtered.webgpu_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/blit_texture_as_draw_filtered.webgpu_vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/blit_texture_as_draw_filtered.webgpu_vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/blit_texture_as_draw_filtered.webgpu_vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/blit_texture_as_draw_filtered.webgpu_vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/color_ramp.frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/color_ramp.frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/color_ramp.frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/color_ramp.frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/color_ramp.vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/color_ramp.vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/color_ramp.vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/color_ramp.vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_atlas_blit.webgpu_fixedcolor_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_atlas_blit.webgpu_fixedcolor_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_atlas_blit.webgpu_fixedcolor_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_atlas_blit.webgpu_fixedcolor_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_atlas_blit.webgpu_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_atlas_blit.webgpu_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_atlas_blit.webgpu_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_atlas_blit.webgpu_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_atlas_blit.webgpu_noclipdistance_vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_atlas_blit.webgpu_noclipdistance_vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_atlas_blit.webgpu_noclipdistance_vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_atlas_blit.webgpu_noclipdistance_vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_atlas_blit.webgpu_vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_atlas_blit.webgpu_vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_atlas_blit.webgpu_vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_atlas_blit.webgpu_vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_image_mesh.webgpu_fixedcolor_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_image_mesh.webgpu_fixedcolor_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_image_mesh.webgpu_fixedcolor_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_image_mesh.webgpu_fixedcolor_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_image_mesh.webgpu_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_image_mesh.webgpu_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_image_mesh.webgpu_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_image_mesh.webgpu_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_image_mesh.webgpu_noclipdistance_vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_image_mesh.webgpu_noclipdistance_vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_image_mesh.webgpu_noclipdistance_vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_image_mesh.webgpu_noclipdistance_vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_image_mesh.webgpu_vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_image_mesh.webgpu_vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_image_mesh.webgpu_vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_image_mesh.webgpu_vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_path.webgpu_fixedcolor_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_path.webgpu_fixedcolor_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_path.webgpu_fixedcolor_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_path.webgpu_fixedcolor_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_path.webgpu_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_path.webgpu_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_path.webgpu_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_path.webgpu_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_path.webgpu_noclipdistance_vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_path.webgpu_noclipdistance_vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_path.webgpu_noclipdistance_vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_path.webgpu_noclipdistance_vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_path.webgpu_vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_path.webgpu_vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_path.webgpu_vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_path.webgpu_vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_stencil.frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_stencil.frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_stencil.frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_stencil.frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_stencil.vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_stencil.vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_stencil.vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_stencil.vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_stencil.webgpu_fixedcolor_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_stencil.webgpu_fixedcolor_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_stencil.webgpu_fixedcolor_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_stencil.webgpu_fixedcolor_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_stencil.webgpu_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_stencil.webgpu_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_stencil.webgpu_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_stencil.webgpu_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_stencil.webgpu_noclipdistance_vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_stencil.webgpu_noclipdistance_vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_stencil.webgpu_noclipdistance_vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_stencil.webgpu_noclipdistance_vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_stencil.webgpu_vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_stencil.webgpu_vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/draw_msaa_stencil.webgpu_vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/draw_msaa_stencil.webgpu_vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/render_atlas.webgpu_vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/render_atlas.webgpu_vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/render_atlas.webgpu_vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/render_atlas.webgpu_vert.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/render_atlas_fill.webgpu_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/render_atlas_fill.webgpu_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/render_atlas_fill.webgpu_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/render_atlas_fill.webgpu_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/render_atlas_stroke.webgpu_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/render_atlas_stroke.webgpu_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/render_atlas_stroke.webgpu_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/render_atlas_stroke.webgpu_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/tessellate.webgpu_frag.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/tessellate.webgpu_frag.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/tessellate.webgpu_frag.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/tessellate.webgpu_frag.hpp diff --git a/thirdparty/rive_renderer/source/generated/shaders/wgsl/tessellate.webgpu_vert.hpp b/thirdparty/rive/source/renderer/generated/shaders/wgsl/tessellate.webgpu_vert.hpp similarity index 100% rename from thirdparty/rive_renderer/source/generated/shaders/wgsl/tessellate.webgpu_vert.hpp rename to thirdparty/rive/source/renderer/generated/shaders/wgsl/tessellate.webgpu_vert.hpp diff --git a/thirdparty/rive_renderer/source/gl/gl_state.cpp b/thirdparty/rive/source/renderer/gl/gl_state.cpp similarity index 100% rename from thirdparty/rive_renderer/source/gl/gl_state.cpp rename to thirdparty/rive/source/renderer/gl/gl_state.cpp diff --git a/thirdparty/rive_renderer/source/gl/gl_utils.cpp b/thirdparty/rive/source/renderer/gl/gl_utils.cpp similarity index 100% rename from thirdparty/rive_renderer/source/gl/gl_utils.cpp rename to thirdparty/rive/source/renderer/gl/gl_utils.cpp diff --git a/thirdparty/rive_renderer/source/gl/load_gles_extensions.cpp b/thirdparty/rive/source/renderer/gl/load_gles_extensions.cpp similarity index 100% rename from thirdparty/rive_renderer/source/gl/load_gles_extensions.cpp rename to thirdparty/rive/source/renderer/gl/load_gles_extensions.cpp diff --git a/thirdparty/rive_renderer/source/gl/load_store_actions_ext.cpp b/thirdparty/rive/source/renderer/gl/load_store_actions_ext.cpp similarity index 100% rename from thirdparty/rive_renderer/source/gl/load_store_actions_ext.cpp rename to thirdparty/rive/source/renderer/gl/load_store_actions_ext.cpp diff --git a/thirdparty/rive_renderer/source/gl/pls_impl_ext_native.cpp b/thirdparty/rive/source/renderer/gl/pls_impl_ext_native.cpp similarity index 100% rename from thirdparty/rive_renderer/source/gl/pls_impl_ext_native.cpp rename to thirdparty/rive/source/renderer/gl/pls_impl_ext_native.cpp diff --git a/thirdparty/rive_renderer/source/gl/pls_impl_rw_texture.cpp b/thirdparty/rive/source/renderer/gl/pls_impl_rw_texture.cpp similarity index 100% rename from thirdparty/rive_renderer/source/gl/pls_impl_rw_texture.cpp rename to thirdparty/rive/source/renderer/gl/pls_impl_rw_texture.cpp diff --git a/thirdparty/rive_renderer/source/gl/pls_impl_webgl.cpp b/thirdparty/rive/source/renderer/gl/pls_impl_webgl.cpp similarity index 100% rename from thirdparty/rive_renderer/source/gl/pls_impl_webgl.cpp rename to thirdparty/rive/source/renderer/gl/pls_impl_webgl.cpp diff --git a/thirdparty/rive_renderer/source/gl/render_buffer_gl_impl.cpp b/thirdparty/rive/source/renderer/gl/render_buffer_gl_impl.cpp similarity index 100% rename from thirdparty/rive_renderer/source/gl/render_buffer_gl_impl.cpp rename to thirdparty/rive/source/renderer/gl/render_buffer_gl_impl.cpp diff --git a/thirdparty/rive_renderer/source/gl/render_context_gl_impl.cpp b/thirdparty/rive/source/renderer/gl/render_context_gl_impl.cpp similarity index 100% rename from thirdparty/rive_renderer/source/gl/render_context_gl_impl.cpp rename to thirdparty/rive/source/renderer/gl/render_context_gl_impl.cpp diff --git a/thirdparty/rive_renderer/source/gl/render_target_gl.cpp b/thirdparty/rive/source/renderer/gl/render_target_gl.cpp similarity index 100% rename from thirdparty/rive_renderer/source/gl/render_target_gl.cpp rename to thirdparty/rive/source/renderer/gl/render_target_gl.cpp diff --git a/thirdparty/rive_renderer/source/gpu.cpp b/thirdparty/rive/source/renderer/gpu.cpp similarity index 100% rename from thirdparty/rive_renderer/source/gpu.cpp rename to thirdparty/rive/source/renderer/gpu.cpp diff --git a/thirdparty/rive_renderer/source/gpu_resource.cpp b/thirdparty/rive/source/renderer/gpu_resource.cpp similarity index 100% rename from thirdparty/rive_renderer/source/gpu_resource.cpp rename to thirdparty/rive/source/renderer/gpu_resource.cpp diff --git a/thirdparty/rive_renderer/source/gr_inner_fan_triangulator.hpp b/thirdparty/rive/source/renderer/gr_inner_fan_triangulator.hpp similarity index 100% rename from thirdparty/rive_renderer/source/gr_inner_fan_triangulator.hpp rename to thirdparty/rive/source/renderer/gr_inner_fan_triangulator.hpp diff --git a/thirdparty/rive_renderer/source/gr_triangulator.cpp b/thirdparty/rive/source/renderer/gr_triangulator.cpp similarity index 100% rename from thirdparty/rive_renderer/source/gr_triangulator.cpp rename to thirdparty/rive/source/renderer/gr_triangulator.cpp diff --git a/thirdparty/rive_renderer/source/gr_triangulator.hpp b/thirdparty/rive/source/renderer/gr_triangulator.hpp similarity index 100% rename from thirdparty/rive_renderer/source/gr_triangulator.hpp rename to thirdparty/rive/source/renderer/gr_triangulator.hpp diff --git a/thirdparty/rive_renderer/source/gradient.cpp b/thirdparty/rive/source/renderer/gradient.cpp similarity index 100% rename from thirdparty/rive_renderer/source/gradient.cpp rename to thirdparty/rive/source/renderer/gradient.cpp diff --git a/thirdparty/rive_renderer/source/gradient.hpp b/thirdparty/rive/source/renderer/gradient.hpp similarity index 100% rename from thirdparty/rive_renderer/source/gradient.hpp rename to thirdparty/rive/source/renderer/gradient.hpp diff --git a/thirdparty/rive_renderer/source/instance_chunker.hpp b/thirdparty/rive/source/renderer/instance_chunker.hpp similarity index 100% rename from thirdparty/rive_renderer/source/instance_chunker.hpp rename to thirdparty/rive/source/renderer/instance_chunker.hpp diff --git a/thirdparty/rive_renderer/source/intersection_board.cpp b/thirdparty/rive/source/renderer/intersection_board.cpp similarity index 100% rename from thirdparty/rive_renderer/source/intersection_board.cpp rename to thirdparty/rive/source/renderer/intersection_board.cpp diff --git a/thirdparty/rive_renderer/source/intersection_board.hpp b/thirdparty/rive/source/renderer/intersection_board.hpp similarity index 100% rename from thirdparty/rive_renderer/source/intersection_board.hpp rename to thirdparty/rive/source/renderer/intersection_board.hpp diff --git a/thirdparty/rive_renderer/source/metal/background_shader_compiler.h b/thirdparty/rive/source/renderer/metal/background_shader_compiler.h similarity index 100% rename from thirdparty/rive_renderer/source/metal/background_shader_compiler.h rename to thirdparty/rive/source/renderer/metal/background_shader_compiler.h diff --git a/thirdparty/rive_renderer/source/metal/background_shader_compiler.mm b/thirdparty/rive/source/renderer/metal/background_shader_compiler.mm similarity index 100% rename from thirdparty/rive_renderer/source/metal/background_shader_compiler.mm rename to thirdparty/rive/source/renderer/metal/background_shader_compiler.mm diff --git a/thirdparty/rive_renderer/source/metal/render_context_metal_impl.mm b/thirdparty/rive/source/renderer/metal/render_context_metal_impl.mm similarity index 100% rename from thirdparty/rive_renderer/source/metal/render_context_metal_impl.mm rename to thirdparty/rive/source/renderer/metal/render_context_metal_impl.mm diff --git a/thirdparty/rive_renderer/source/ore/d3d11/ore_bind_group_d3d11.cpp b/thirdparty/rive/source/renderer/ore/d3d11/ore_bind_group_d3d11.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d11/ore_bind_group_d3d11.cpp rename to thirdparty/rive/source/renderer/ore/d3d11/ore_bind_group_d3d11.cpp diff --git a/thirdparty/rive_renderer/source/ore/d3d11/ore_bind_group_d3d11.hpp b/thirdparty/rive/source/renderer/ore/d3d11/ore_bind_group_d3d11.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d11/ore_bind_group_d3d11.hpp rename to thirdparty/rive/source/renderer/ore/d3d11/ore_bind_group_d3d11.hpp diff --git a/thirdparty/rive_renderer/source/ore/d3d11/ore_buffer_d3d11.cpp b/thirdparty/rive/source/renderer/ore/d3d11/ore_buffer_d3d11.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d11/ore_buffer_d3d11.cpp rename to thirdparty/rive/source/renderer/ore/d3d11/ore_buffer_d3d11.cpp diff --git a/thirdparty/rive_renderer/source/ore/d3d11/ore_buffer_d3d11.hpp b/thirdparty/rive/source/renderer/ore/d3d11/ore_buffer_d3d11.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d11/ore_buffer_d3d11.hpp rename to thirdparty/rive/source/renderer/ore/d3d11/ore_buffer_d3d11.hpp diff --git a/thirdparty/rive_renderer/source/ore/d3d11/ore_context_d3d11.cpp b/thirdparty/rive/source/renderer/ore/d3d11/ore_context_d3d11.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d11/ore_context_d3d11.cpp rename to thirdparty/rive/source/renderer/ore/d3d11/ore_context_d3d11.cpp diff --git a/thirdparty/rive_renderer/source/ore/d3d11/ore_pipeline_d3d11.cpp b/thirdparty/rive/source/renderer/ore/d3d11/ore_pipeline_d3d11.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d11/ore_pipeline_d3d11.cpp rename to thirdparty/rive/source/renderer/ore/d3d11/ore_pipeline_d3d11.cpp diff --git a/thirdparty/rive_renderer/source/ore/d3d11/ore_pipeline_d3d11.hpp b/thirdparty/rive/source/renderer/ore/d3d11/ore_pipeline_d3d11.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d11/ore_pipeline_d3d11.hpp rename to thirdparty/rive/source/renderer/ore/d3d11/ore_pipeline_d3d11.hpp diff --git a/thirdparty/rive_renderer/source/ore/d3d11/ore_render_pass_d3d11.cpp b/thirdparty/rive/source/renderer/ore/d3d11/ore_render_pass_d3d11.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d11/ore_render_pass_d3d11.cpp rename to thirdparty/rive/source/renderer/ore/d3d11/ore_render_pass_d3d11.cpp diff --git a/thirdparty/rive_renderer/source/ore/d3d11/ore_render_pass_d3d11.hpp b/thirdparty/rive/source/renderer/ore/d3d11/ore_render_pass_d3d11.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d11/ore_render_pass_d3d11.hpp rename to thirdparty/rive/source/renderer/ore/d3d11/ore_render_pass_d3d11.hpp diff --git a/thirdparty/rive_renderer/source/ore/d3d11/ore_sampler_d3d11.cpp b/thirdparty/rive/source/renderer/ore/d3d11/ore_sampler_d3d11.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d11/ore_sampler_d3d11.cpp rename to thirdparty/rive/source/renderer/ore/d3d11/ore_sampler_d3d11.cpp diff --git a/thirdparty/rive_renderer/source/ore/d3d11/ore_sampler_d3d11.hpp b/thirdparty/rive/source/renderer/ore/d3d11/ore_sampler_d3d11.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d11/ore_sampler_d3d11.hpp rename to thirdparty/rive/source/renderer/ore/d3d11/ore_sampler_d3d11.hpp diff --git a/thirdparty/rive_renderer/source/ore/d3d11/ore_shader_module_d3d11.cpp b/thirdparty/rive/source/renderer/ore/d3d11/ore_shader_module_d3d11.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d11/ore_shader_module_d3d11.cpp rename to thirdparty/rive/source/renderer/ore/d3d11/ore_shader_module_d3d11.cpp diff --git a/thirdparty/rive_renderer/source/ore/d3d11/ore_shader_module_d3d11.hpp b/thirdparty/rive/source/renderer/ore/d3d11/ore_shader_module_d3d11.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d11/ore_shader_module_d3d11.hpp rename to thirdparty/rive/source/renderer/ore/d3d11/ore_shader_module_d3d11.hpp diff --git a/thirdparty/rive_renderer/source/ore/d3d11/ore_texture_d3d11.cpp b/thirdparty/rive/source/renderer/ore/d3d11/ore_texture_d3d11.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d11/ore_texture_d3d11.cpp rename to thirdparty/rive/source/renderer/ore/d3d11/ore_texture_d3d11.cpp diff --git a/thirdparty/rive_renderer/source/ore/d3d11/ore_texture_d3d11.hpp b/thirdparty/rive/source/renderer/ore/d3d11/ore_texture_d3d11.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d11/ore_texture_d3d11.hpp rename to thirdparty/rive/source/renderer/ore/d3d11/ore_texture_d3d11.hpp diff --git a/thirdparty/rive_renderer/source/ore/d3d12/ore_bind_group_d3d12.cpp b/thirdparty/rive/source/renderer/ore/d3d12/ore_bind_group_d3d12.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d12/ore_bind_group_d3d12.cpp rename to thirdparty/rive/source/renderer/ore/d3d12/ore_bind_group_d3d12.cpp diff --git a/thirdparty/rive_renderer/source/ore/d3d12/ore_bind_group_d3d12.hpp b/thirdparty/rive/source/renderer/ore/d3d12/ore_bind_group_d3d12.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d12/ore_bind_group_d3d12.hpp rename to thirdparty/rive/source/renderer/ore/d3d12/ore_bind_group_d3d12.hpp diff --git a/thirdparty/rive_renderer/source/ore/d3d12/ore_bind_group_layout_d3d12.hpp b/thirdparty/rive/source/renderer/ore/d3d12/ore_bind_group_layout_d3d12.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d12/ore_bind_group_layout_d3d12.hpp rename to thirdparty/rive/source/renderer/ore/d3d12/ore_bind_group_layout_d3d12.hpp diff --git a/thirdparty/rive_renderer/source/ore/d3d12/ore_buffer_d3d12.cpp b/thirdparty/rive/source/renderer/ore/d3d12/ore_buffer_d3d12.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d12/ore_buffer_d3d12.cpp rename to thirdparty/rive/source/renderer/ore/d3d12/ore_buffer_d3d12.cpp diff --git a/thirdparty/rive_renderer/source/ore/d3d12/ore_buffer_d3d12.hpp b/thirdparty/rive/source/renderer/ore/d3d12/ore_buffer_d3d12.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d12/ore_buffer_d3d12.hpp rename to thirdparty/rive/source/renderer/ore/d3d12/ore_buffer_d3d12.hpp diff --git a/thirdparty/rive_renderer/source/ore/d3d12/ore_context_d3d12.cpp b/thirdparty/rive/source/renderer/ore/d3d12/ore_context_d3d12.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d12/ore_context_d3d12.cpp rename to thirdparty/rive/source/renderer/ore/d3d12/ore_context_d3d12.cpp diff --git a/thirdparty/rive_renderer/source/ore/d3d12/ore_d3d12_bind_group_apply.hpp b/thirdparty/rive/source/renderer/ore/d3d12/ore_d3d12_bind_group_apply.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d12/ore_d3d12_bind_group_apply.hpp rename to thirdparty/rive/source/renderer/ore/d3d12/ore_d3d12_bind_group_apply.hpp diff --git a/thirdparty/rive_renderer/source/ore/d3d12/ore_d3d12_root_sig.hpp b/thirdparty/rive/source/renderer/ore/d3d12/ore_d3d12_root_sig.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d12/ore_d3d12_root_sig.hpp rename to thirdparty/rive/source/renderer/ore/d3d12/ore_d3d12_root_sig.hpp diff --git a/thirdparty/rive_renderer/source/ore/d3d12/ore_pipeline_d3d12.cpp b/thirdparty/rive/source/renderer/ore/d3d12/ore_pipeline_d3d12.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d12/ore_pipeline_d3d12.cpp rename to thirdparty/rive/source/renderer/ore/d3d12/ore_pipeline_d3d12.cpp diff --git a/thirdparty/rive_renderer/source/ore/d3d12/ore_pipeline_d3d12.hpp b/thirdparty/rive/source/renderer/ore/d3d12/ore_pipeline_d3d12.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d12/ore_pipeline_d3d12.hpp rename to thirdparty/rive/source/renderer/ore/d3d12/ore_pipeline_d3d12.hpp diff --git a/thirdparty/rive_renderer/source/ore/d3d12/ore_render_pass_d3d12.cpp b/thirdparty/rive/source/renderer/ore/d3d12/ore_render_pass_d3d12.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d12/ore_render_pass_d3d12.cpp rename to thirdparty/rive/source/renderer/ore/d3d12/ore_render_pass_d3d12.cpp diff --git a/thirdparty/rive_renderer/source/ore/d3d12/ore_render_pass_d3d12.hpp b/thirdparty/rive/source/renderer/ore/d3d12/ore_render_pass_d3d12.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d12/ore_render_pass_d3d12.hpp rename to thirdparty/rive/source/renderer/ore/d3d12/ore_render_pass_d3d12.hpp diff --git a/thirdparty/rive_renderer/source/ore/d3d12/ore_sampler_d3d12.cpp b/thirdparty/rive/source/renderer/ore/d3d12/ore_sampler_d3d12.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d12/ore_sampler_d3d12.cpp rename to thirdparty/rive/source/renderer/ore/d3d12/ore_sampler_d3d12.cpp diff --git a/thirdparty/rive_renderer/source/ore/d3d12/ore_sampler_d3d12.hpp b/thirdparty/rive/source/renderer/ore/d3d12/ore_sampler_d3d12.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d12/ore_sampler_d3d12.hpp rename to thirdparty/rive/source/renderer/ore/d3d12/ore_sampler_d3d12.hpp diff --git a/thirdparty/rive_renderer/source/ore/d3d12/ore_shader_module_d3d12.cpp b/thirdparty/rive/source/renderer/ore/d3d12/ore_shader_module_d3d12.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d12/ore_shader_module_d3d12.cpp rename to thirdparty/rive/source/renderer/ore/d3d12/ore_shader_module_d3d12.cpp diff --git a/thirdparty/rive_renderer/source/ore/d3d12/ore_shader_module_d3d12.hpp b/thirdparty/rive/source/renderer/ore/d3d12/ore_shader_module_d3d12.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d12/ore_shader_module_d3d12.hpp rename to thirdparty/rive/source/renderer/ore/d3d12/ore_shader_module_d3d12.hpp diff --git a/thirdparty/rive_renderer/source/ore/d3d12/ore_texture_d3d12.cpp b/thirdparty/rive/source/renderer/ore/d3d12/ore_texture_d3d12.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d12/ore_texture_d3d12.cpp rename to thirdparty/rive/source/renderer/ore/d3d12/ore_texture_d3d12.cpp diff --git a/thirdparty/rive_renderer/source/ore/d3d12/ore_texture_d3d12.hpp b/thirdparty/rive/source/renderer/ore/d3d12/ore_texture_d3d12.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/d3d12/ore_texture_d3d12.hpp rename to thirdparty/rive/source/renderer/ore/d3d12/ore_texture_d3d12.hpp diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_bind_group_gl.cpp b/thirdparty/rive/source/renderer/ore/gl/ore_bind_group_gl.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_bind_group_gl.cpp rename to thirdparty/rive/source/renderer/ore/gl/ore_bind_group_gl.cpp diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_bind_group_gl.hpp b/thirdparty/rive/source/renderer/ore/gl/ore_bind_group_gl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_bind_group_gl.hpp rename to thirdparty/rive/source/renderer/ore/gl/ore_bind_group_gl.hpp diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_buffer_gl.cpp b/thirdparty/rive/source/renderer/ore/gl/ore_buffer_gl.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_buffer_gl.cpp rename to thirdparty/rive/source/renderer/ore/gl/ore_buffer_gl.cpp diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_buffer_gl.hpp b/thirdparty/rive/source/renderer/ore/gl/ore_buffer_gl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_buffer_gl.hpp rename to thirdparty/rive/source/renderer/ore/gl/ore_buffer_gl.hpp diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_buffer_gl.mm b/thirdparty/rive/source/renderer/ore/gl/ore_buffer_gl.mm similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_buffer_gl.mm rename to thirdparty/rive/source/renderer/ore/gl/ore_buffer_gl.mm diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_context_gl.cpp b/thirdparty/rive/source/renderer/ore/gl/ore_context_gl.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_context_gl.cpp rename to thirdparty/rive/source/renderer/ore/gl/ore_context_gl.cpp diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_context_gl.mm b/thirdparty/rive/source/renderer/ore/gl/ore_context_gl.mm similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_context_gl.mm rename to thirdparty/rive/source/renderer/ore/gl/ore_context_gl.mm diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_pipeline_gl.cpp b/thirdparty/rive/source/renderer/ore/gl/ore_pipeline_gl.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_pipeline_gl.cpp rename to thirdparty/rive/source/renderer/ore/gl/ore_pipeline_gl.cpp diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_pipeline_gl.hpp b/thirdparty/rive/source/renderer/ore/gl/ore_pipeline_gl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_pipeline_gl.hpp rename to thirdparty/rive/source/renderer/ore/gl/ore_pipeline_gl.hpp diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_pipeline_gl.mm b/thirdparty/rive/source/renderer/ore/gl/ore_pipeline_gl.mm similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_pipeline_gl.mm rename to thirdparty/rive/source/renderer/ore/gl/ore_pipeline_gl.mm diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_render_pass_gl.cpp b/thirdparty/rive/source/renderer/ore/gl/ore_render_pass_gl.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_render_pass_gl.cpp rename to thirdparty/rive/source/renderer/ore/gl/ore_render_pass_gl.cpp diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_render_pass_gl.hpp b/thirdparty/rive/source/renderer/ore/gl/ore_render_pass_gl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_render_pass_gl.hpp rename to thirdparty/rive/source/renderer/ore/gl/ore_render_pass_gl.hpp diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_render_pass_gl.mm b/thirdparty/rive/source/renderer/ore/gl/ore_render_pass_gl.mm similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_render_pass_gl.mm rename to thirdparty/rive/source/renderer/ore/gl/ore_render_pass_gl.mm diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_sampler_gl.cpp b/thirdparty/rive/source/renderer/ore/gl/ore_sampler_gl.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_sampler_gl.cpp rename to thirdparty/rive/source/renderer/ore/gl/ore_sampler_gl.cpp diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_sampler_gl.hpp b/thirdparty/rive/source/renderer/ore/gl/ore_sampler_gl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_sampler_gl.hpp rename to thirdparty/rive/source/renderer/ore/gl/ore_sampler_gl.hpp diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_sampler_gl.mm b/thirdparty/rive/source/renderer/ore/gl/ore_sampler_gl.mm similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_sampler_gl.mm rename to thirdparty/rive/source/renderer/ore/gl/ore_sampler_gl.mm diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_shader_module_gl.cpp b/thirdparty/rive/source/renderer/ore/gl/ore_shader_module_gl.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_shader_module_gl.cpp rename to thirdparty/rive/source/renderer/ore/gl/ore_shader_module_gl.cpp diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_shader_module_gl.hpp b/thirdparty/rive/source/renderer/ore/gl/ore_shader_module_gl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_shader_module_gl.hpp rename to thirdparty/rive/source/renderer/ore/gl/ore_shader_module_gl.hpp diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_shader_module_gl.mm b/thirdparty/rive/source/renderer/ore/gl/ore_shader_module_gl.mm similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_shader_module_gl.mm rename to thirdparty/rive/source/renderer/ore/gl/ore_shader_module_gl.mm diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_texture_gl.cpp b/thirdparty/rive/source/renderer/ore/gl/ore_texture_gl.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_texture_gl.cpp rename to thirdparty/rive/source/renderer/ore/gl/ore_texture_gl.cpp diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_texture_gl.hpp b/thirdparty/rive/source/renderer/ore/gl/ore_texture_gl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_texture_gl.hpp rename to thirdparty/rive/source/renderer/ore/gl/ore_texture_gl.hpp diff --git a/thirdparty/rive_renderer/source/ore/gl/ore_texture_gl.mm b/thirdparty/rive/source/renderer/ore/gl/ore_texture_gl.mm similarity index 100% rename from thirdparty/rive_renderer/source/ore/gl/ore_texture_gl.mm rename to thirdparty/rive/source/renderer/ore/gl/ore_texture_gl.mm diff --git a/thirdparty/rive_renderer/source/ore/hlsl_struct_layout.cpp b/thirdparty/rive/source/renderer/ore/hlsl_struct_layout.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/hlsl_struct_layout.cpp rename to thirdparty/rive/source/renderer/ore/hlsl_struct_layout.cpp diff --git a/thirdparty/rive_renderer/source/ore/metal/ore_bind_group_metal.hpp b/thirdparty/rive/source/renderer/ore/metal/ore_bind_group_metal.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/metal/ore_bind_group_metal.hpp rename to thirdparty/rive/source/renderer/ore/metal/ore_bind_group_metal.hpp diff --git a/thirdparty/rive_renderer/source/ore/metal/ore_bind_group_metal.mm b/thirdparty/rive/source/renderer/ore/metal/ore_bind_group_metal.mm similarity index 100% rename from thirdparty/rive_renderer/source/ore/metal/ore_bind_group_metal.mm rename to thirdparty/rive/source/renderer/ore/metal/ore_bind_group_metal.mm diff --git a/thirdparty/rive_renderer/source/ore/metal/ore_buffer_metal.hpp b/thirdparty/rive/source/renderer/ore/metal/ore_buffer_metal.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/metal/ore_buffer_metal.hpp rename to thirdparty/rive/source/renderer/ore/metal/ore_buffer_metal.hpp diff --git a/thirdparty/rive_renderer/source/ore/metal/ore_buffer_metal.mm b/thirdparty/rive/source/renderer/ore/metal/ore_buffer_metal.mm similarity index 100% rename from thirdparty/rive_renderer/source/ore/metal/ore_buffer_metal.mm rename to thirdparty/rive/source/renderer/ore/metal/ore_buffer_metal.mm diff --git a/thirdparty/rive_renderer/source/ore/metal/ore_context_metal.mm b/thirdparty/rive/source/renderer/ore/metal/ore_context_metal.mm similarity index 100% rename from thirdparty/rive_renderer/source/ore/metal/ore_context_metal.mm rename to thirdparty/rive/source/renderer/ore/metal/ore_context_metal.mm diff --git a/thirdparty/rive_renderer/source/ore/metal/ore_pipeline_metal.hpp b/thirdparty/rive/source/renderer/ore/metal/ore_pipeline_metal.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/metal/ore_pipeline_metal.hpp rename to thirdparty/rive/source/renderer/ore/metal/ore_pipeline_metal.hpp diff --git a/thirdparty/rive_renderer/source/ore/metal/ore_pipeline_metal.mm b/thirdparty/rive/source/renderer/ore/metal/ore_pipeline_metal.mm similarity index 100% rename from thirdparty/rive_renderer/source/ore/metal/ore_pipeline_metal.mm rename to thirdparty/rive/source/renderer/ore/metal/ore_pipeline_metal.mm diff --git a/thirdparty/rive_renderer/source/ore/metal/ore_render_pass_metal.hpp b/thirdparty/rive/source/renderer/ore/metal/ore_render_pass_metal.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/metal/ore_render_pass_metal.hpp rename to thirdparty/rive/source/renderer/ore/metal/ore_render_pass_metal.hpp diff --git a/thirdparty/rive_renderer/source/ore/metal/ore_render_pass_metal.mm b/thirdparty/rive/source/renderer/ore/metal/ore_render_pass_metal.mm similarity index 100% rename from thirdparty/rive_renderer/source/ore/metal/ore_render_pass_metal.mm rename to thirdparty/rive/source/renderer/ore/metal/ore_render_pass_metal.mm diff --git a/thirdparty/rive_renderer/source/ore/metal/ore_sampler_metal.hpp b/thirdparty/rive/source/renderer/ore/metal/ore_sampler_metal.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/metal/ore_sampler_metal.hpp rename to thirdparty/rive/source/renderer/ore/metal/ore_sampler_metal.hpp diff --git a/thirdparty/rive_renderer/source/ore/metal/ore_sampler_metal.mm b/thirdparty/rive/source/renderer/ore/metal/ore_sampler_metal.mm similarity index 100% rename from thirdparty/rive_renderer/source/ore/metal/ore_sampler_metal.mm rename to thirdparty/rive/source/renderer/ore/metal/ore_sampler_metal.mm diff --git a/thirdparty/rive_renderer/source/ore/metal/ore_shader_module_metal.hpp b/thirdparty/rive/source/renderer/ore/metal/ore_shader_module_metal.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/metal/ore_shader_module_metal.hpp rename to thirdparty/rive/source/renderer/ore/metal/ore_shader_module_metal.hpp diff --git a/thirdparty/rive_renderer/source/ore/metal/ore_shader_module_metal.mm b/thirdparty/rive/source/renderer/ore/metal/ore_shader_module_metal.mm similarity index 100% rename from thirdparty/rive_renderer/source/ore/metal/ore_shader_module_metal.mm rename to thirdparty/rive/source/renderer/ore/metal/ore_shader_module_metal.mm diff --git a/thirdparty/rive_renderer/source/ore/metal/ore_texture_metal.hpp b/thirdparty/rive/source/renderer/ore/metal/ore_texture_metal.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/metal/ore_texture_metal.hpp rename to thirdparty/rive/source/renderer/ore/metal/ore_texture_metal.hpp diff --git a/thirdparty/rive_renderer/source/ore/metal/ore_texture_metal.mm b/thirdparty/rive/source/renderer/ore/metal/ore_texture_metal.mm similarity index 100% rename from thirdparty/rive_renderer/source/ore/metal/ore_texture_metal.mm rename to thirdparty/rive/source/renderer/ore/metal/ore_texture_metal.mm diff --git a/thirdparty/rive_renderer/source/ore/ore_bind_group_layout.cpp b/thirdparty/rive/source/renderer/ore/ore_bind_group_layout.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/ore_bind_group_layout.cpp rename to thirdparty/rive/source/renderer/ore/ore_bind_group_layout.cpp diff --git a/thirdparty/rive_renderer/source/ore/ore_binding_map.cpp b/thirdparty/rive/source/renderer/ore/ore_binding_map.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/ore_binding_map.cpp rename to thirdparty/rive/source/renderer/ore/ore_binding_map.cpp diff --git a/thirdparty/rive_renderer/source/ore/vulkan/ore_bind_group_layout_vulkan.hpp b/thirdparty/rive/source/renderer/ore/vulkan/ore_bind_group_layout_vulkan.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/vulkan/ore_bind_group_layout_vulkan.hpp rename to thirdparty/rive/source/renderer/ore/vulkan/ore_bind_group_layout_vulkan.hpp diff --git a/thirdparty/rive_renderer/source/ore/vulkan/ore_bind_group_vulkan.cpp b/thirdparty/rive/source/renderer/ore/vulkan/ore_bind_group_vulkan.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/vulkan/ore_bind_group_vulkan.cpp rename to thirdparty/rive/source/renderer/ore/vulkan/ore_bind_group_vulkan.cpp diff --git a/thirdparty/rive_renderer/source/ore/vulkan/ore_bind_group_vulkan.hpp b/thirdparty/rive/source/renderer/ore/vulkan/ore_bind_group_vulkan.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/vulkan/ore_bind_group_vulkan.hpp rename to thirdparty/rive/source/renderer/ore/vulkan/ore_bind_group_vulkan.hpp diff --git a/thirdparty/rive_renderer/source/ore/vulkan/ore_buffer_vulkan.cpp b/thirdparty/rive/source/renderer/ore/vulkan/ore_buffer_vulkan.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/vulkan/ore_buffer_vulkan.cpp rename to thirdparty/rive/source/renderer/ore/vulkan/ore_buffer_vulkan.cpp diff --git a/thirdparty/rive_renderer/source/ore/vulkan/ore_buffer_vulkan.hpp b/thirdparty/rive/source/renderer/ore/vulkan/ore_buffer_vulkan.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/vulkan/ore_buffer_vulkan.hpp rename to thirdparty/rive/source/renderer/ore/vulkan/ore_buffer_vulkan.hpp diff --git a/thirdparty/rive_renderer/source/ore/vulkan/ore_context_vulkan.cpp b/thirdparty/rive/source/renderer/ore/vulkan/ore_context_vulkan.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/vulkan/ore_context_vulkan.cpp rename to thirdparty/rive/source/renderer/ore/vulkan/ore_context_vulkan.cpp diff --git a/thirdparty/rive_renderer/source/ore/vulkan/ore_pipeline_vulkan.cpp b/thirdparty/rive/source/renderer/ore/vulkan/ore_pipeline_vulkan.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/vulkan/ore_pipeline_vulkan.cpp rename to thirdparty/rive/source/renderer/ore/vulkan/ore_pipeline_vulkan.cpp diff --git a/thirdparty/rive_renderer/source/ore/vulkan/ore_pipeline_vulkan.hpp b/thirdparty/rive/source/renderer/ore/vulkan/ore_pipeline_vulkan.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/vulkan/ore_pipeline_vulkan.hpp rename to thirdparty/rive/source/renderer/ore/vulkan/ore_pipeline_vulkan.hpp diff --git a/thirdparty/rive_renderer/source/ore/vulkan/ore_render_pass_vulkan.cpp b/thirdparty/rive/source/renderer/ore/vulkan/ore_render_pass_vulkan.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/vulkan/ore_render_pass_vulkan.cpp rename to thirdparty/rive/source/renderer/ore/vulkan/ore_render_pass_vulkan.cpp diff --git a/thirdparty/rive_renderer/source/ore/vulkan/ore_render_pass_vulkan.hpp b/thirdparty/rive/source/renderer/ore/vulkan/ore_render_pass_vulkan.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/vulkan/ore_render_pass_vulkan.hpp rename to thirdparty/rive/source/renderer/ore/vulkan/ore_render_pass_vulkan.hpp diff --git a/thirdparty/rive_renderer/source/ore/vulkan/ore_sampler_vulkan.cpp b/thirdparty/rive/source/renderer/ore/vulkan/ore_sampler_vulkan.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/vulkan/ore_sampler_vulkan.cpp rename to thirdparty/rive/source/renderer/ore/vulkan/ore_sampler_vulkan.cpp diff --git a/thirdparty/rive_renderer/source/ore/vulkan/ore_sampler_vulkan.hpp b/thirdparty/rive/source/renderer/ore/vulkan/ore_sampler_vulkan.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/vulkan/ore_sampler_vulkan.hpp rename to thirdparty/rive/source/renderer/ore/vulkan/ore_sampler_vulkan.hpp diff --git a/thirdparty/rive_renderer/source/ore/vulkan/ore_shader_module_vulkan.cpp b/thirdparty/rive/source/renderer/ore/vulkan/ore_shader_module_vulkan.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/vulkan/ore_shader_module_vulkan.cpp rename to thirdparty/rive/source/renderer/ore/vulkan/ore_shader_module_vulkan.cpp diff --git a/thirdparty/rive_renderer/source/ore/vulkan/ore_shader_module_vulkan.hpp b/thirdparty/rive/source/renderer/ore/vulkan/ore_shader_module_vulkan.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/vulkan/ore_shader_module_vulkan.hpp rename to thirdparty/rive/source/renderer/ore/vulkan/ore_shader_module_vulkan.hpp diff --git a/thirdparty/rive_renderer/source/ore/vulkan/ore_texture_vulkan.cpp b/thirdparty/rive/source/renderer/ore/vulkan/ore_texture_vulkan.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/vulkan/ore_texture_vulkan.cpp rename to thirdparty/rive/source/renderer/ore/vulkan/ore_texture_vulkan.cpp diff --git a/thirdparty/rive_renderer/source/ore/vulkan/ore_texture_vulkan.hpp b/thirdparty/rive/source/renderer/ore/vulkan/ore_texture_vulkan.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/vulkan/ore_texture_vulkan.hpp rename to thirdparty/rive/source/renderer/ore/vulkan/ore_texture_vulkan.hpp diff --git a/thirdparty/rive_renderer/source/ore/vulkan/ore_vulkan_dsl.hpp b/thirdparty/rive/source/renderer/ore/vulkan/ore_vulkan_dsl.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/vulkan/ore_vulkan_dsl.hpp rename to thirdparty/rive/source/renderer/ore/vulkan/ore_vulkan_dsl.hpp diff --git a/thirdparty/rive_renderer/source/ore/wgpu/ore_bind_group_layout_wgpu.hpp b/thirdparty/rive/source/renderer/ore/wgpu/ore_bind_group_layout_wgpu.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/wgpu/ore_bind_group_layout_wgpu.hpp rename to thirdparty/rive/source/renderer/ore/wgpu/ore_bind_group_layout_wgpu.hpp diff --git a/thirdparty/rive_renderer/source/ore/wgpu/ore_bind_group_wgpu.cpp b/thirdparty/rive/source/renderer/ore/wgpu/ore_bind_group_wgpu.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/wgpu/ore_bind_group_wgpu.cpp rename to thirdparty/rive/source/renderer/ore/wgpu/ore_bind_group_wgpu.cpp diff --git a/thirdparty/rive_renderer/source/ore/wgpu/ore_bind_group_wgpu.hpp b/thirdparty/rive/source/renderer/ore/wgpu/ore_bind_group_wgpu.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/wgpu/ore_bind_group_wgpu.hpp rename to thirdparty/rive/source/renderer/ore/wgpu/ore_bind_group_wgpu.hpp diff --git a/thirdparty/rive_renderer/source/ore/wgpu/ore_buffer_wgpu.cpp b/thirdparty/rive/source/renderer/ore/wgpu/ore_buffer_wgpu.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/wgpu/ore_buffer_wgpu.cpp rename to thirdparty/rive/source/renderer/ore/wgpu/ore_buffer_wgpu.cpp diff --git a/thirdparty/rive_renderer/source/ore/wgpu/ore_buffer_wgpu.hpp b/thirdparty/rive/source/renderer/ore/wgpu/ore_buffer_wgpu.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/wgpu/ore_buffer_wgpu.hpp rename to thirdparty/rive/source/renderer/ore/wgpu/ore_buffer_wgpu.hpp diff --git a/thirdparty/rive_renderer/source/ore/wgpu/ore_context_wgpu.cpp b/thirdparty/rive/source/renderer/ore/wgpu/ore_context_wgpu.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/wgpu/ore_context_wgpu.cpp rename to thirdparty/rive/source/renderer/ore/wgpu/ore_context_wgpu.cpp diff --git a/thirdparty/rive_renderer/source/ore/wgpu/ore_pipeline_wgpu.cpp b/thirdparty/rive/source/renderer/ore/wgpu/ore_pipeline_wgpu.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/wgpu/ore_pipeline_wgpu.cpp rename to thirdparty/rive/source/renderer/ore/wgpu/ore_pipeline_wgpu.cpp diff --git a/thirdparty/rive_renderer/source/ore/wgpu/ore_pipeline_wgpu.hpp b/thirdparty/rive/source/renderer/ore/wgpu/ore_pipeline_wgpu.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/wgpu/ore_pipeline_wgpu.hpp rename to thirdparty/rive/source/renderer/ore/wgpu/ore_pipeline_wgpu.hpp diff --git a/thirdparty/rive_renderer/source/ore/wgpu/ore_render_pass_wgpu.cpp b/thirdparty/rive/source/renderer/ore/wgpu/ore_render_pass_wgpu.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/wgpu/ore_render_pass_wgpu.cpp rename to thirdparty/rive/source/renderer/ore/wgpu/ore_render_pass_wgpu.cpp diff --git a/thirdparty/rive_renderer/source/ore/wgpu/ore_render_pass_wgpu.hpp b/thirdparty/rive/source/renderer/ore/wgpu/ore_render_pass_wgpu.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/wgpu/ore_render_pass_wgpu.hpp rename to thirdparty/rive/source/renderer/ore/wgpu/ore_render_pass_wgpu.hpp diff --git a/thirdparty/rive_renderer/source/ore/wgpu/ore_sampler_wgpu.cpp b/thirdparty/rive/source/renderer/ore/wgpu/ore_sampler_wgpu.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/wgpu/ore_sampler_wgpu.cpp rename to thirdparty/rive/source/renderer/ore/wgpu/ore_sampler_wgpu.cpp diff --git a/thirdparty/rive_renderer/source/ore/wgpu/ore_sampler_wgpu.hpp b/thirdparty/rive/source/renderer/ore/wgpu/ore_sampler_wgpu.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/wgpu/ore_sampler_wgpu.hpp rename to thirdparty/rive/source/renderer/ore/wgpu/ore_sampler_wgpu.hpp diff --git a/thirdparty/rive_renderer/source/ore/wgpu/ore_shader_module_wgpu.cpp b/thirdparty/rive/source/renderer/ore/wgpu/ore_shader_module_wgpu.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/wgpu/ore_shader_module_wgpu.cpp rename to thirdparty/rive/source/renderer/ore/wgpu/ore_shader_module_wgpu.cpp diff --git a/thirdparty/rive_renderer/source/ore/wgpu/ore_shader_module_wgpu.hpp b/thirdparty/rive/source/renderer/ore/wgpu/ore_shader_module_wgpu.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/wgpu/ore_shader_module_wgpu.hpp rename to thirdparty/rive/source/renderer/ore/wgpu/ore_shader_module_wgpu.hpp diff --git a/thirdparty/rive_renderer/source/ore/wgpu/ore_texture_wgpu.cpp b/thirdparty/rive/source/renderer/ore/wgpu/ore_texture_wgpu.cpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/wgpu/ore_texture_wgpu.cpp rename to thirdparty/rive/source/renderer/ore/wgpu/ore_texture_wgpu.cpp diff --git a/thirdparty/rive_renderer/source/ore/wgpu/ore_texture_wgpu.hpp b/thirdparty/rive/source/renderer/ore/wgpu/ore_texture_wgpu.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/wgpu/ore_texture_wgpu.hpp rename to thirdparty/rive/source/renderer/ore/wgpu/ore_texture_wgpu.hpp diff --git a/thirdparty/rive_renderer/source/ore/wgpu/ore_wgpu_layout.hpp b/thirdparty/rive/source/renderer/ore/wgpu/ore_wgpu_layout.hpp similarity index 100% rename from thirdparty/rive_renderer/source/ore/wgpu/ore_wgpu_layout.hpp rename to thirdparty/rive/source/renderer/ore/wgpu/ore_wgpu_layout.hpp diff --git a/thirdparty/rive_renderer/source/render_context.cpp b/thirdparty/rive/source/renderer/render_context.cpp similarity index 100% rename from thirdparty/rive_renderer/source/render_context.cpp rename to thirdparty/rive/source/renderer/render_context.cpp diff --git a/thirdparty/rive_renderer/source/render_context_helper_impl.cpp b/thirdparty/rive/source/renderer/render_context_helper_impl.cpp similarity index 100% rename from thirdparty/rive_renderer/source/render_context_helper_impl.cpp rename to thirdparty/rive/source/renderer/render_context_helper_impl.cpp diff --git a/thirdparty/rive_renderer/source/rive_render_factory.cpp b/thirdparty/rive/source/renderer/rive_render_factory.cpp similarity index 100% rename from thirdparty/rive_renderer/source/rive_render_factory.cpp rename to thirdparty/rive/source/renderer/rive_render_factory.cpp diff --git a/thirdparty/rive_renderer/source/rive_render_image.cpp b/thirdparty/rive/source/renderer/rive_render_image.cpp similarity index 100% rename from thirdparty/rive_renderer/source/rive_render_image.cpp rename to thirdparty/rive/source/renderer/rive_render_image.cpp diff --git a/thirdparty/rive_renderer/source/rive_render_paint.cpp b/thirdparty/rive/source/renderer/rive_render_paint.cpp similarity index 100% rename from thirdparty/rive_renderer/source/rive_render_paint.cpp rename to thirdparty/rive/source/renderer/rive_render_paint.cpp diff --git a/thirdparty/rive_renderer/source/rive_render_paint.hpp b/thirdparty/rive/source/renderer/rive_render_paint.hpp similarity index 100% rename from thirdparty/rive_renderer/source/rive_render_paint.hpp rename to thirdparty/rive/source/renderer/rive_render_paint.hpp diff --git a/thirdparty/rive_renderer/source/rive_render_path.cpp b/thirdparty/rive/source/renderer/rive_render_path.cpp similarity index 100% rename from thirdparty/rive_renderer/source/rive_render_path.cpp rename to thirdparty/rive/source/renderer/rive_render_path.cpp diff --git a/thirdparty/rive_renderer/source/rive_render_path.hpp b/thirdparty/rive/source/renderer/rive_render_path.hpp similarity index 100% rename from thirdparty/rive_renderer/source/rive_render_path.hpp rename to thirdparty/rive/source/renderer/rive_render_path.hpp diff --git a/thirdparty/rive_renderer/source/rive_renderer.cpp b/thirdparty/rive/source/renderer/rive_renderer.cpp similarity index 100% rename from thirdparty/rive_renderer/source/rive_renderer.cpp rename to thirdparty/rive/source/renderer/rive_renderer.cpp diff --git a/thirdparty/rive_renderer/source/shaders/Makefile b/thirdparty/rive/source/renderer/shaders/Makefile similarity index 100% rename from thirdparty/rive_renderer/source/shaders/Makefile rename to thirdparty/rive/source/renderer/shaders/Makefile diff --git a/thirdparty/rive_renderer/source/shaders/advanced_blend.glsl b/thirdparty/rive/source/renderer/shaders/advanced_blend.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/advanced_blend.glsl rename to thirdparty/rive/source/renderer/shaders/advanced_blend.glsl diff --git a/thirdparty/rive_renderer/source/shaders/atomic_draw.glsl b/thirdparty/rive/source/renderer/shaders/atomic_draw.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/atomic_draw.glsl rename to thirdparty/rive/source/renderer/shaders/atomic_draw.glsl diff --git a/thirdparty/rive_renderer/source/shaders/bezier_utils.glsl b/thirdparty/rive/source/renderer/shaders/bezier_utils.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/bezier_utils.glsl rename to thirdparty/rive/source/renderer/shaders/bezier_utils.glsl diff --git a/thirdparty/rive_renderer/source/shaders/blit_texture_as_draw.glsl b/thirdparty/rive/source/renderer/shaders/blit_texture_as_draw.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/blit_texture_as_draw.glsl rename to thirdparty/rive/source/renderer/shaders/blit_texture_as_draw.glsl diff --git a/thirdparty/rive_renderer/source/shaders/clear_clockwise_atomic_clip.glsl b/thirdparty/rive/source/renderer/shaders/clear_clockwise_atomic_clip.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/clear_clockwise_atomic_clip.glsl rename to thirdparty/rive/source/renderer/shaders/clear_clockwise_atomic_clip.glsl diff --git a/thirdparty/rive_renderer/source/shaders/color_ramp.glsl b/thirdparty/rive/source/renderer/shaders/color_ramp.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/color_ramp.glsl rename to thirdparty/rive/source/renderer/shaders/color_ramp.glsl diff --git a/thirdparty/rive_renderer/source/shaders/common.glsl b/thirdparty/rive/source/renderer/shaders/common.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/common.glsl rename to thirdparty/rive/source/renderer/shaders/common.glsl diff --git a/thirdparty/rive_renderer/source/shaders/constants.glsl b/thirdparty/rive/source/renderer/shaders/constants.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/constants.glsl rename to thirdparty/rive/source/renderer/shaders/constants.glsl diff --git a/thirdparty/rive_renderer/source/shaders/draw_clockwise_atomic_borrowed_coverage.frag b/thirdparty/rive/source/renderer/shaders/draw_clockwise_atomic_borrowed_coverage.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/draw_clockwise_atomic_borrowed_coverage.frag rename to thirdparty/rive/source/renderer/shaders/draw_clockwise_atomic_borrowed_coverage.frag diff --git a/thirdparty/rive_renderer/source/shaders/draw_clockwise_atomic_clip.frag b/thirdparty/rive/source/renderer/shaders/draw_clockwise_atomic_clip.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/draw_clockwise_atomic_clip.frag rename to thirdparty/rive/source/renderer/shaders/draw_clockwise_atomic_clip.frag diff --git a/thirdparty/rive_renderer/source/shaders/draw_clockwise_atomic_path.frag b/thirdparty/rive/source/renderer/shaders/draw_clockwise_atomic_path.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/draw_clockwise_atomic_path.frag rename to thirdparty/rive/source/renderer/shaders/draw_clockwise_atomic_path.frag diff --git a/thirdparty/rive_renderer/source/shaders/draw_clockwise_clip.frag b/thirdparty/rive/source/renderer/shaders/draw_clockwise_clip.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/draw_clockwise_clip.frag rename to thirdparty/rive/source/renderer/shaders/draw_clockwise_clip.frag diff --git a/thirdparty/rive_renderer/source/shaders/draw_clockwise_path.frag b/thirdparty/rive/source/renderer/shaders/draw_clockwise_path.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/draw_clockwise_path.frag rename to thirdparty/rive/source/renderer/shaders/draw_clockwise_path.frag diff --git a/thirdparty/rive_renderer/source/shaders/draw_fullscreen_quad.vert b/thirdparty/rive/source/renderer/shaders/draw_fullscreen_quad.vert similarity index 100% rename from thirdparty/rive_renderer/source/shaders/draw_fullscreen_quad.vert rename to thirdparty/rive/source/renderer/shaders/draw_fullscreen_quad.vert diff --git a/thirdparty/rive_renderer/source/shaders/draw_image_mesh.vert b/thirdparty/rive/source/renderer/shaders/draw_image_mesh.vert similarity index 100% rename from thirdparty/rive_renderer/source/shaders/draw_image_mesh.vert rename to thirdparty/rive/source/renderer/shaders/draw_image_mesh.vert diff --git a/thirdparty/rive_renderer/source/shaders/draw_input_attachment.frag b/thirdparty/rive/source/renderer/shaders/draw_input_attachment.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/draw_input_attachment.frag rename to thirdparty/rive/source/renderer/shaders/draw_input_attachment.frag diff --git a/thirdparty/rive_renderer/source/shaders/draw_mesh.frag b/thirdparty/rive/source/renderer/shaders/draw_mesh.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/draw_mesh.frag rename to thirdparty/rive/source/renderer/shaders/draw_mesh.frag diff --git a/thirdparty/rive_renderer/source/shaders/draw_msaa_object.frag b/thirdparty/rive/source/renderer/shaders/draw_msaa_object.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/draw_msaa_object.frag rename to thirdparty/rive/source/renderer/shaders/draw_msaa_object.frag diff --git a/thirdparty/rive_renderer/source/shaders/draw_msaa_resolve.frag b/thirdparty/rive/source/renderer/shaders/draw_msaa_resolve.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/draw_msaa_resolve.frag rename to thirdparty/rive/source/renderer/shaders/draw_msaa_resolve.frag diff --git a/thirdparty/rive_renderer/source/shaders/draw_path.vert b/thirdparty/rive/source/renderer/shaders/draw_path.vert similarity index 100% rename from thirdparty/rive_renderer/source/shaders/draw_path.vert rename to thirdparty/rive/source/renderer/shaders/draw_path.vert diff --git a/thirdparty/rive_renderer/source/shaders/draw_path_common.glsl b/thirdparty/rive/source/renderer/shaders/draw_path_common.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/draw_path_common.glsl rename to thirdparty/rive/source/renderer/shaders/draw_path_common.glsl diff --git a/thirdparty/rive_renderer/source/shaders/draw_raster_order_path.frag b/thirdparty/rive/source/renderer/shaders/draw_raster_order_path.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/draw_raster_order_path.frag rename to thirdparty/rive/source/renderer/shaders/draw_raster_order_path.frag diff --git a/thirdparty/rive_renderer/source/shaders/flush_uniforms.glsl b/thirdparty/rive/source/renderer/shaders/flush_uniforms.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/flush_uniforms.glsl rename to thirdparty/rive/source/renderer/shaders/flush_uniforms.glsl diff --git a/thirdparty/rive_renderer/source/shaders/glsl.glsl b/thirdparty/rive/source/renderer/shaders/glsl.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/glsl.glsl rename to thirdparty/rive/source/renderer/shaders/glsl.glsl diff --git a/thirdparty/rive_renderer/source/shaders/hlsl.glsl b/thirdparty/rive/source/renderer/shaders/hlsl.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/hlsl.glsl rename to thirdparty/rive/source/renderer/shaders/hlsl.glsl diff --git a/thirdparty/rive_renderer/source/shaders/image_draw_uniforms.glsl b/thirdparty/rive/source/renderer/shaders/image_draw_uniforms.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/image_draw_uniforms.glsl rename to thirdparty/rive/source/renderer/shaders/image_draw_uniforms.glsl diff --git a/thirdparty/rive_renderer/source/shaders/init_clockwise_atomic_workaround.frag b/thirdparty/rive/source/renderer/shaders/init_clockwise_atomic_workaround.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/init_clockwise_atomic_workaround.frag rename to thirdparty/rive/source/renderer/shaders/init_clockwise_atomic_workaround.frag diff --git a/thirdparty/rive_renderer/source/shaders/metal.glsl b/thirdparty/rive/source/renderer/shaders/metal.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/metal.glsl rename to thirdparty/rive/source/renderer/shaders/metal.glsl diff --git a/thirdparty/rive_renderer/source/shaders/metal/color_ramp.metal b/thirdparty/rive/source/renderer/shaders/metal/color_ramp.metal similarity index 100% rename from thirdparty/rive_renderer/source/shaders/metal/color_ramp.metal rename to thirdparty/rive/source/renderer/shaders/metal/color_ramp.metal diff --git a/thirdparty/rive_renderer/source/shaders/metal/draw.metal b/thirdparty/rive/source/renderer/shaders/metal/draw.metal similarity index 100% rename from thirdparty/rive_renderer/source/shaders/metal/draw.metal rename to thirdparty/rive/source/renderer/shaders/metal/draw.metal diff --git a/thirdparty/rive_renderer/source/shaders/metal/generate_draw_combinations.py b/thirdparty/rive/source/renderer/shaders/metal/generate_draw_combinations.py similarity index 100% rename from thirdparty/rive_renderer/source/shaders/metal/generate_draw_combinations.py rename to thirdparty/rive/source/renderer/shaders/metal/generate_draw_combinations.py diff --git a/thirdparty/rive_renderer/source/shaders/metal/tessellate.metal b/thirdparty/rive/source/renderer/shaders/metal/tessellate.metal similarity index 100% rename from thirdparty/rive_renderer/source/shaders/metal/tessellate.metal rename to thirdparty/rive/source/renderer/shaders/metal/tessellate.metal diff --git a/thirdparty/rive_renderer/source/shaders/minify.py b/thirdparty/rive/source/renderer/shaders/minify.py similarity index 100% rename from thirdparty/rive_renderer/source/shaders/minify.py rename to thirdparty/rive/source/renderer/shaders/minify.py diff --git a/thirdparty/rive_renderer/source/shaders/pls_load_store_ext.glsl b/thirdparty/rive/source/renderer/shaders/pls_load_store_ext.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/pls_load_store_ext.glsl rename to thirdparty/rive/source/renderer/shaders/pls_load_store_ext.glsl diff --git a/thirdparty/rive_renderer/source/shaders/render_atlas.glsl b/thirdparty/rive/source/renderer/shaders/render_atlas.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/render_atlas.glsl rename to thirdparty/rive/source/renderer/shaders/render_atlas.glsl diff --git a/thirdparty/rive_renderer/source/shaders/resolve_atlas.glsl b/thirdparty/rive/source/renderer/shaders/resolve_atlas.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/resolve_atlas.glsl rename to thirdparty/rive/source/renderer/shaders/resolve_atlas.glsl diff --git a/thirdparty/rive_renderer/source/shaders/rhi.glsl b/thirdparty/rive/source/renderer/shaders/rhi.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/rhi.glsl rename to thirdparty/rive/source/renderer/shaders/rhi.glsl diff --git a/thirdparty/rive_renderer/source/shaders/specialization.glsl b/thirdparty/rive/source/renderer/shaders/specialization.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/specialization.glsl rename to thirdparty/rive/source/renderer/shaders/specialization.glsl diff --git a/thirdparty/rive_renderer/source/shaders/spirv/atomic_base.glsl b/thirdparty/rive/source/renderer/shaders/spirv/atomic_base.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/atomic_base.glsl rename to thirdparty/rive/source/renderer/shaders/spirv/atomic_base.glsl diff --git a/thirdparty/rive_renderer/source/shaders/spirv/atomic_draw_atlas_blit.main b/thirdparty/rive/source/renderer/shaders/spirv/atomic_draw_atlas_blit.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/atomic_draw_atlas_blit.main rename to thirdparty/rive/source/renderer/shaders/spirv/atomic_draw_atlas_blit.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/atomic_draw_image_mesh.main b/thirdparty/rive/source/renderer/shaders/spirv/atomic_draw_image_mesh.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/atomic_draw_image_mesh.main rename to thirdparty/rive/source/renderer/shaders/spirv/atomic_draw_image_mesh.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/atomic_draw_image_rect.main b/thirdparty/rive/source/renderer/shaders/spirv/atomic_draw_image_rect.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/atomic_draw_image_rect.main rename to thirdparty/rive/source/renderer/shaders/spirv/atomic_draw_image_rect.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/atomic_draw_interior_triangles.main b/thirdparty/rive/source/renderer/shaders/spirv/atomic_draw_interior_triangles.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/atomic_draw_interior_triangles.main rename to thirdparty/rive/source/renderer/shaders/spirv/atomic_draw_interior_triangles.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/atomic_draw_path.main b/thirdparty/rive/source/renderer/shaders/spirv/atomic_draw_path.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/atomic_draw_path.main rename to thirdparty/rive/source/renderer/shaders/spirv/atomic_draw_path.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/atomic_init.main b/thirdparty/rive/source/renderer/shaders/spirv/atomic_init.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/atomic_init.main rename to thirdparty/rive/source/renderer/shaders/spirv/atomic_init.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/atomic_resolve.main b/thirdparty/rive/source/renderer/shaders/spirv/atomic_resolve.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/atomic_resolve.main rename to thirdparty/rive/source/renderer/shaders/spirv/atomic_resolve.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/atomic_resolve_coalesced.main b/thirdparty/rive/source/renderer/shaders/spirv/atomic_resolve_coalesced.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/atomic_resolve_coalesced.main rename to thirdparty/rive/source/renderer/shaders/spirv/atomic_resolve_coalesced.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/blit_texture_as_draw_filtered.main b/thirdparty/rive/source/renderer/shaders/spirv/blit_texture_as_draw_filtered.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/blit_texture_as_draw_filtered.main rename to thirdparty/rive/source/renderer/shaders/spirv/blit_texture_as_draw_filtered.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/clear_clockwise_atomic_clip.main b/thirdparty/rive/source/renderer/shaders/spirv/clear_clockwise_atomic_clip.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/clear_clockwise_atomic_clip.main rename to thirdparty/rive/source/renderer/shaders/spirv/clear_clockwise_atomic_clip.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/color_ramp.main b/thirdparty/rive/source/renderer/shaders/spirv/color_ramp.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/color_ramp.main rename to thirdparty/rive/source/renderer/shaders/spirv/color_ramp.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_atlas_blit.main b/thirdparty/rive/source/renderer/shaders/spirv/draw_atlas_blit.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_atlas_blit.main rename to thirdparty/rive/source/renderer/shaders/spirv/draw_atlas_blit.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_atlas_blit.main b/thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_atlas_blit.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_atlas_blit.main rename to thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_atlas_blit.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_atomic_atlas_blit.main b/thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_atomic_atlas_blit.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_atomic_atlas_blit.main rename to thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_atomic_atlas_blit.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_atomic_borrowed_coverage.frag b/thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_atomic_borrowed_coverage.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_atomic_borrowed_coverage.frag rename to thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_atomic_borrowed_coverage.frag diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_atomic_borrowed_coverage_interior_triangles.frag b/thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_atomic_borrowed_coverage_interior_triangles.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_atomic_borrowed_coverage_interior_triangles.frag rename to thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_atomic_borrowed_coverage_interior_triangles.frag diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_atomic_clip.frag b/thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_atomic_clip.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_atomic_clip.frag rename to thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_atomic_clip.frag diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.frag b/thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.frag rename to thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_atomic_clip_interior_triangles.frag diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_atomic_image_mesh.main b/thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_atomic_image_mesh.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_atomic_image_mesh.main rename to thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_atomic_image_mesh.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_atomic_interior_triangles.main b/thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_atomic_interior_triangles.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_atomic_interior_triangles.main rename to thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_atomic_interior_triangles.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_atomic_path.main b/thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_atomic_path.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_atomic_path.main rename to thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_atomic_path.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_clip.main b/thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_clip.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_clip.main rename to thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_clip.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_clip_interior_triangles.main b/thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_clip_interior_triangles.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_clip_interior_triangles.main rename to thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_clip_interior_triangles.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_image_mesh.main b/thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_image_mesh.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_image_mesh.main rename to thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_image_mesh.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_interior_triangles.main b/thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_interior_triangles.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_interior_triangles.main rename to thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_interior_triangles.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_path.main b/thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_path.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_clockwise_path.main rename to thirdparty/rive/source/renderer/shaders/spirv/draw_clockwise_path.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_fullscreen_quad.vert b/thirdparty/rive/source/renderer/shaders/spirv/draw_fullscreen_quad.vert similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_fullscreen_quad.vert rename to thirdparty/rive/source/renderer/shaders/spirv/draw_fullscreen_quad.vert diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_image_mesh.main b/thirdparty/rive/source/renderer/shaders/spirv/draw_image_mesh.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_image_mesh.main rename to thirdparty/rive/source/renderer/shaders/spirv/draw_image_mesh.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_input_attachment.frag b/thirdparty/rive/source/renderer/shaders/spirv/draw_input_attachment.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_input_attachment.frag rename to thirdparty/rive/source/renderer/shaders/spirv/draw_input_attachment.frag diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_interior_triangles.main b/thirdparty/rive/source/renderer/shaders/spirv/draw_interior_triangles.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_interior_triangles.main rename to thirdparty/rive/source/renderer/shaders/spirv/draw_interior_triangles.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_msaa_atlas_blit.main b/thirdparty/rive/source/renderer/shaders/spirv/draw_msaa_atlas_blit.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_msaa_atlas_blit.main rename to thirdparty/rive/source/renderer/shaders/spirv/draw_msaa_atlas_blit.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_msaa_color_seed_attachment.frag b/thirdparty/rive/source/renderer/shaders/spirv/draw_msaa_color_seed_attachment.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_msaa_color_seed_attachment.frag rename to thirdparty/rive/source/renderer/shaders/spirv/draw_msaa_color_seed_attachment.frag diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_msaa_image_mesh.main b/thirdparty/rive/source/renderer/shaders/spirv/draw_msaa_image_mesh.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_msaa_image_mesh.main rename to thirdparty/rive/source/renderer/shaders/spirv/draw_msaa_image_mesh.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_msaa_path.main b/thirdparty/rive/source/renderer/shaders/spirv/draw_msaa_path.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_msaa_path.main rename to thirdparty/rive/source/renderer/shaders/spirv/draw_msaa_path.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_msaa_resolve.frag b/thirdparty/rive/source/renderer/shaders/spirv/draw_msaa_resolve.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_msaa_resolve.frag rename to thirdparty/rive/source/renderer/shaders/spirv/draw_msaa_resolve.frag diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_msaa_stencil.main b/thirdparty/rive/source/renderer/shaders/spirv/draw_msaa_stencil.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_msaa_stencil.main rename to thirdparty/rive/source/renderer/shaders/spirv/draw_msaa_stencil.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/draw_path.main b/thirdparty/rive/source/renderer/shaders/spirv/draw_path.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/draw_path.main rename to thirdparty/rive/source/renderer/shaders/spirv/draw_path.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv/init_clockwise_atomic_workaround.frag b/thirdparty/rive/source/renderer/shaders/spirv/init_clockwise_atomic_workaround.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/init_clockwise_atomic_workaround.frag rename to thirdparty/rive/source/renderer/shaders/spirv/init_clockwise_atomic_workaround.frag diff --git a/thirdparty/rive_renderer/source/shaders/spirv/render_atlas.vert b/thirdparty/rive/source/renderer/shaders/spirv/render_atlas.vert similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/render_atlas.vert rename to thirdparty/rive/source/renderer/shaders/spirv/render_atlas.vert diff --git a/thirdparty/rive_renderer/source/shaders/spirv/render_atlas_common.glsl b/thirdparty/rive/source/renderer/shaders/spirv/render_atlas_common.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/render_atlas_common.glsl rename to thirdparty/rive/source/renderer/shaders/spirv/render_atlas_common.glsl diff --git a/thirdparty/rive_renderer/source/shaders/spirv/render_atlas_fill.frag b/thirdparty/rive/source/renderer/shaders/spirv/render_atlas_fill.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/render_atlas_fill.frag rename to thirdparty/rive/source/renderer/shaders/spirv/render_atlas_fill.frag diff --git a/thirdparty/rive_renderer/source/shaders/spirv/render_atlas_stroke.frag b/thirdparty/rive/source/renderer/shaders/spirv/render_atlas_stroke.frag similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/render_atlas_stroke.frag rename to thirdparty/rive/source/renderer/shaders/spirv/render_atlas_stroke.frag diff --git a/thirdparty/rive_renderer/source/shaders/spirv/tessellate.main b/thirdparty/rive/source/renderer/shaders/spirv/tessellate.main similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv/tessellate.main rename to thirdparty/rive/source/renderer/shaders/spirv/tessellate.main diff --git a/thirdparty/rive_renderer/source/shaders/spirv_binary_to_header.py b/thirdparty/rive/source/renderer/shaders/spirv_binary_to_header.py similarity index 100% rename from thirdparty/rive_renderer/source/shaders/spirv_binary_to_header.py rename to thirdparty/rive/source/renderer/shaders/spirv_binary_to_header.py diff --git a/thirdparty/rive_renderer/source/shaders/stencil_draw.glsl b/thirdparty/rive/source/renderer/shaders/stencil_draw.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/stencil_draw.glsl rename to thirdparty/rive/source/renderer/shaders/stencil_draw.glsl diff --git a/thirdparty/rive_renderer/source/shaders/tessellate.glsl b/thirdparty/rive/source/renderer/shaders/tessellate.glsl similarity index 100% rename from thirdparty/rive_renderer/source/shaders/tessellate.glsl rename to thirdparty/rive/source/renderer/shaders/tessellate.glsl diff --git a/thirdparty/rive_renderer/source/shaders/wgsl_to_header.py b/thirdparty/rive/source/renderer/shaders/wgsl_to_header.py similarity index 100% rename from thirdparty/rive_renderer/source/shaders/wgsl_to_header.py rename to thirdparty/rive/source/renderer/shaders/wgsl_to_header.py diff --git a/thirdparty/rive_renderer/source/sk_rectanizer_skyline.cpp b/thirdparty/rive/source/renderer/sk_rectanizer_skyline.cpp similarity index 100% rename from thirdparty/rive_renderer/source/sk_rectanizer_skyline.cpp rename to thirdparty/rive/source/renderer/sk_rectanizer_skyline.cpp diff --git a/thirdparty/rive_renderer/source/sort_key_builder.hpp b/thirdparty/rive/source/renderer/sort_key_builder.hpp similarity index 100% rename from thirdparty/rive_renderer/source/sort_key_builder.hpp rename to thirdparty/rive/source/renderer/sort_key_builder.hpp diff --git a/thirdparty/rive_renderer/source/vulkan/common_layouts.hpp b/thirdparty/rive/source/renderer/vulkan/common_layouts.hpp similarity index 100% rename from thirdparty/rive_renderer/source/vulkan/common_layouts.hpp rename to thirdparty/rive/source/renderer/vulkan/common_layouts.hpp diff --git a/thirdparty/rive_renderer/source/vulkan/draw_pipeline_layout_vulkan.cpp b/thirdparty/rive/source/renderer/vulkan/draw_pipeline_layout_vulkan.cpp similarity index 100% rename from thirdparty/rive_renderer/source/vulkan/draw_pipeline_layout_vulkan.cpp rename to thirdparty/rive/source/renderer/vulkan/draw_pipeline_layout_vulkan.cpp diff --git a/thirdparty/rive_renderer/source/vulkan/draw_pipeline_layout_vulkan.hpp b/thirdparty/rive/source/renderer/vulkan/draw_pipeline_layout_vulkan.hpp similarity index 100% rename from thirdparty/rive_renderer/source/vulkan/draw_pipeline_layout_vulkan.hpp rename to thirdparty/rive/source/renderer/vulkan/draw_pipeline_layout_vulkan.hpp diff --git a/thirdparty/rive_renderer/source/vulkan/draw_pipeline_vulkan.cpp b/thirdparty/rive/source/renderer/vulkan/draw_pipeline_vulkan.cpp similarity index 100% rename from thirdparty/rive_renderer/source/vulkan/draw_pipeline_vulkan.cpp rename to thirdparty/rive/source/renderer/vulkan/draw_pipeline_vulkan.cpp diff --git a/thirdparty/rive_renderer/source/vulkan/draw_pipeline_vulkan.hpp b/thirdparty/rive/source/renderer/vulkan/draw_pipeline_vulkan.hpp similarity index 100% rename from thirdparty/rive_renderer/source/vulkan/draw_pipeline_vulkan.hpp rename to thirdparty/rive/source/renderer/vulkan/draw_pipeline_vulkan.hpp diff --git a/thirdparty/rive_renderer/source/vulkan/draw_shader_vulkan.cpp b/thirdparty/rive/source/renderer/vulkan/draw_shader_vulkan.cpp similarity index 100% rename from thirdparty/rive_renderer/source/vulkan/draw_shader_vulkan.cpp rename to thirdparty/rive/source/renderer/vulkan/draw_shader_vulkan.cpp diff --git a/thirdparty/rive_renderer/source/vulkan/draw_shader_vulkan.hpp b/thirdparty/rive/source/renderer/vulkan/draw_shader_vulkan.hpp similarity index 100% rename from thirdparty/rive_renderer/source/vulkan/draw_shader_vulkan.hpp rename to thirdparty/rive/source/renderer/vulkan/draw_shader_vulkan.hpp diff --git a/thirdparty/rive_renderer/source/vulkan/pipeline_manager_vulkan.cpp b/thirdparty/rive/source/renderer/vulkan/pipeline_manager_vulkan.cpp similarity index 100% rename from thirdparty/rive_renderer/source/vulkan/pipeline_manager_vulkan.cpp rename to thirdparty/rive/source/renderer/vulkan/pipeline_manager_vulkan.cpp diff --git a/thirdparty/rive_renderer/source/vulkan/pipeline_manager_vulkan.hpp b/thirdparty/rive/source/renderer/vulkan/pipeline_manager_vulkan.hpp similarity index 100% rename from thirdparty/rive_renderer/source/vulkan/pipeline_manager_vulkan.hpp rename to thirdparty/rive/source/renderer/vulkan/pipeline_manager_vulkan.hpp diff --git a/thirdparty/rive_renderer/source/vulkan/render_context_vulkan_impl.cpp b/thirdparty/rive/source/renderer/vulkan/render_context_vulkan_impl.cpp similarity index 100% rename from thirdparty/rive_renderer/source/vulkan/render_context_vulkan_impl.cpp rename to thirdparty/rive/source/renderer/vulkan/render_context_vulkan_impl.cpp diff --git a/thirdparty/rive_renderer/source/vulkan/render_pass_vulkan.cpp b/thirdparty/rive/source/renderer/vulkan/render_pass_vulkan.cpp similarity index 100% rename from thirdparty/rive_renderer/source/vulkan/render_pass_vulkan.cpp rename to thirdparty/rive/source/renderer/vulkan/render_pass_vulkan.cpp diff --git a/thirdparty/rive_renderer/source/vulkan/render_pass_vulkan.hpp b/thirdparty/rive/source/renderer/vulkan/render_pass_vulkan.hpp similarity index 100% rename from thirdparty/rive_renderer/source/vulkan/render_pass_vulkan.hpp rename to thirdparty/rive/source/renderer/vulkan/render_pass_vulkan.hpp diff --git a/thirdparty/rive_renderer/source/vulkan/render_target_vulkan.cpp b/thirdparty/rive/source/renderer/vulkan/render_target_vulkan.cpp similarity index 100% rename from thirdparty/rive_renderer/source/vulkan/render_target_vulkan.cpp rename to thirdparty/rive/source/renderer/vulkan/render_target_vulkan.cpp diff --git a/thirdparty/rive_renderer/source/vulkan/vkutil.cpp b/thirdparty/rive/source/renderer/vulkan/vkutil.cpp similarity index 100% rename from thirdparty/rive_renderer/source/vulkan/vkutil.cpp rename to thirdparty/rive/source/renderer/vulkan/vkutil.cpp diff --git a/thirdparty/rive_renderer/source/vulkan/vulkan_context.cpp b/thirdparty/rive/source/renderer/vulkan/vulkan_context.cpp similarity index 100% rename from thirdparty/rive_renderer/source/vulkan/vulkan_context.cpp rename to thirdparty/rive/source/renderer/vulkan/vulkan_context.cpp diff --git a/thirdparty/rive_renderer/source/vulkan/vulkan_memory_allocator.cpp b/thirdparty/rive/source/renderer/vulkan/vulkan_memory_allocator.cpp similarity index 100% rename from thirdparty/rive_renderer/source/vulkan/vulkan_memory_allocator.cpp rename to thirdparty/rive/source/renderer/vulkan/vulkan_memory_allocator.cpp diff --git a/thirdparty/rive_renderer/source/vulkan/vulkan_shaders.cpp b/thirdparty/rive/source/renderer/vulkan/vulkan_shaders.cpp similarity index 100% rename from thirdparty/rive_renderer/source/vulkan/vulkan_shaders.cpp rename to thirdparty/rive/source/renderer/vulkan/vulkan_shaders.cpp diff --git a/thirdparty/rive_renderer/source/vulkan/vulkan_shaders.hpp b/thirdparty/rive/source/renderer/vulkan/vulkan_shaders.hpp similarity index 100% rename from thirdparty/rive_renderer/source/vulkan/vulkan_shaders.hpp rename to thirdparty/rive/source/renderer/vulkan/vulkan_shaders.hpp diff --git a/thirdparty/rive_renderer/source/webgpu/render_context_webgpu_impl.cpp b/thirdparty/rive/source/renderer/webgpu/render_context_webgpu_impl.cpp similarity index 100% rename from thirdparty/rive_renderer/source/webgpu/render_context_webgpu_impl.cpp rename to thirdparty/rive/source/renderer/webgpu/render_context_webgpu_impl.cpp diff --git a/thirdparty/rive_renderer/source/webgpu/wagyu-port/include/webgpu/webgpu.h b/thirdparty/rive/source/renderer/webgpu/wagyu-port/include/webgpu/webgpu.h similarity index 100% rename from thirdparty/rive_renderer/source/webgpu/wagyu-port/include/webgpu/webgpu.h rename to thirdparty/rive/source/renderer/webgpu/wagyu-port/include/webgpu/webgpu.h diff --git a/thirdparty/rive_renderer/source/webgpu/wagyu-port/include/webgpu/webgpu_cpp.h b/thirdparty/rive/source/renderer/webgpu/wagyu-port/include/webgpu/webgpu_cpp.h similarity index 100% rename from thirdparty/rive_renderer/source/webgpu/wagyu-port/include/webgpu/webgpu_cpp.h rename to thirdparty/rive/source/renderer/webgpu/wagyu-port/include/webgpu/webgpu_cpp.h diff --git a/thirdparty/rive_renderer/source/webgpu/wagyu-port/include/webgpu/webgpu_cpp_chained_struct.h b/thirdparty/rive/source/renderer/webgpu/wagyu-port/include/webgpu/webgpu_cpp_chained_struct.h similarity index 100% rename from thirdparty/rive_renderer/source/webgpu/wagyu-port/include/webgpu/webgpu_cpp_chained_struct.h rename to thirdparty/rive/source/renderer/webgpu/wagyu-port/include/webgpu/webgpu_cpp_chained_struct.h diff --git a/thirdparty/rive_renderer/source/webgpu/wagyu-port/include/webgpu/webgpu_enum_class_bitmasks.h b/thirdparty/rive/source/renderer/webgpu/wagyu-port/include/webgpu/webgpu_enum_class_bitmasks.h similarity index 100% rename from thirdparty/rive_renderer/source/webgpu/wagyu-port/include/webgpu/webgpu_enum_class_bitmasks.h rename to thirdparty/rive/source/renderer/webgpu/wagyu-port/include/webgpu/webgpu_enum_class_bitmasks.h diff --git a/thirdparty/rive_renderer/source/webgpu/wagyu-port/include/webgpu/webgpu_wagyu.h b/thirdparty/rive/source/renderer/webgpu/wagyu-port/include/webgpu/webgpu_wagyu.h similarity index 100% rename from thirdparty/rive_renderer/source/webgpu/wagyu-port/include/webgpu/webgpu_wagyu.h rename to thirdparty/rive/source/renderer/webgpu/wagyu-port/include/webgpu/webgpu_wagyu.h diff --git a/thirdparty/rive_renderer/source/webgpu/wagyu-port/src/webgpu.c b/thirdparty/rive/source/renderer/webgpu/wagyu-port/src/webgpu.c similarity index 100% rename from thirdparty/rive_renderer/source/webgpu/wagyu-port/src/webgpu.c rename to thirdparty/rive/source/renderer/webgpu/wagyu-port/src/webgpu.c diff --git a/thirdparty/rive_renderer/source/webgpu/wagyu-port/webgpu-port.py b/thirdparty/rive/source/renderer/webgpu/wagyu-port/webgpu-port.py similarity index 100% rename from thirdparty/rive_renderer/source/webgpu/wagyu-port/webgpu-port.py rename to thirdparty/rive/source/renderer/webgpu/wagyu-port/webgpu-port.py diff --git a/thirdparty/rive_renderer/source/webgpu/webgpu_compat.h b/thirdparty/rive/source/renderer/webgpu/webgpu_compat.h similarity index 100% rename from thirdparty/rive_renderer/source/webgpu/webgpu_compat.h rename to thirdparty/rive/source/renderer/webgpu/webgpu_compat.h diff --git a/thirdparty/rive_renderer/rive_renderer.h b/thirdparty/rive_renderer/rive_renderer.h deleted file mode 100644 index cccc7323f..000000000 --- a/thirdparty/rive_renderer/rive_renderer.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - ============================================================================== - - This file is part of the YUP library. - Copyright (c) 2024 - kunitoki@gmail.com - - YUP is an open source library subject to open-source licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - to use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -/* - ============================================================================== - - BEGIN_YUP_MODULE_DECLARATION - - ID: rive_renderer - vendor: rive - version: 0.1.155 - name: Rive Renderer. - description: The Rive Renderer is a vector and raster graphics renderer custom-built for Rive content, for animation, and for runtime. - website: https://github.com/rive-app/rive-runtime - license: MIT - - dependencies: rive rive_decoders glad - searchpaths: include source source/generated/shaders - macFrameworks: Metal QuartzCore - appleDefines: ORE_BACKEND_METAL=1 RIVE_OBJC_EXCEPTIONS=1 - iosDefines: RIVE_IOS=1 - iosSimDefines: RIVE_IOS_SIMULATOR=1 - linuxDefines: RIVE_DESKTOP_GL=1 ORE_BACKEND_GL=1 - wasmDefines: RIVE_WEBGL=1 ORE_BACKEND_GL=1 - wasmOptions: -sUSE_SDL=2 - wasmLinkOptions: -sUSE_SDL=2 -sMAX_WEBGL_VERSION=2 - windowsDefines: ORE_BACKEND_D3D11=1 ORE_BACKEND_GL=1 - androidDefines: RIVE_ANDROID=1 ORE_BACKEND_GL=1 - androidLibs: EGL GLESv3 - - END_YUP_MODULE_DECLARATION - - ============================================================================== -*/ - -#pragma once - -//============================================================================== -/** Config: YUP_RIVE_USE_METAL - Enables the use of the Metal renderer on macOS (the default is enabled). -*/ -#ifndef YUP_RIVE_USE_METAL -#define YUP_RIVE_USE_METAL 1 -#endif - -/** Config: YUP_RIVE_USE_D3D - Enables the use of the Direct3D renderer on Windows (the default is enabled). -*/ -#ifndef YUP_RIVE_USE_D3D -#define YUP_RIVE_USE_D3D 1 -#endif - -/** Config: YUP_RIVE_USE_OPENGL - Enables the use of the OpenGL renderer on platform that support it but where it is not used by default (in - the specific case Windows). -*/ -#ifndef YUP_RIVE_USE_OPENGL -#define YUP_RIVE_USE_OPENGL 0 -#endif - -/** Config: YUP_RIVE_USE_DAWN - Enables the use of the Dawn renderer on platform that support it. -*/ -#ifndef YUP_RIVE_USE_DAWN -#define YUP_RIVE_USE_DAWN 0 -#endif - -//============================================================================== -/** Config: YUP_RIVE_OPENGL_MAJOR - Enables a specific OpenGL major version. Must be at least 4. -*/ -#ifndef YUP_RIVE_OPENGL_MAJOR -#define YUP_RIVE_OPENGL_MAJOR 4 -#endif - -/** Config: YUP_RIVE_OPENGL_MINOR - Enables a specific OpenGL minor version. Must be at least 5 (OpenGL 4.5+, required for compute shaders). -*/ -#ifndef YUP_RIVE_OPENGL_MINOR -#define YUP_RIVE_OPENGL_MINOR 5 -#endif - -/** Config: YUP_RIVE_OPENGLES_MAJOR - Enables a specific OpenGL ES major version. Must be at least 3. -*/ -#ifndef YUP_RIVE_OPENGLES_MAJOR -#define YUP_RIVE_OPENGLES_MAJOR 3 -#endif - -/** Config: YUP_RIVE_OPENGLES_MINOR - Enables a specific OpenGL ES minor version. Must be at least 1 (OpenGL ES 3.1+, required for compute shaders). -*/ -#ifndef YUP_RIVE_OPENGLES_MINOR -#define YUP_RIVE_OPENGLES_MINOR 1 -#endif - -//============================================================================== - -#if YUP_RIVE_USE_OPENGL -#if !defined(RIVE_DESKTOP_GL) && !defined(RIVE_WEBGL) -#define RIVE_DESKTOP_GL 1 -#endif -#endif - -#if YUP_RIVE_USE_DAWN -#if !defined(RIVE_DAWN) -#define RIVE_DAWN 1 -#endif -#endif - -//============================================================================== - -#if !defined (YUP_RIVE_RENDERER_NO_INCLUDES) - -#if __GNUC__ - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#elif __clang__ - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wattributes" -#elif _MSC_VER - __pragma (warning (push)) - __pragma (warning (disable: 4244)) -#endif - -// Public API -#include -#include -#include -#include - -// Internals -#include -#include - -#if __GNUC__ - #pragma GCC diagnostic pop -#elif __clang__ - #pragma clang diagnostic pop -#elif _MSC_VER - __pragma (warning (pop)) -#endif - -#endif // YUP_RIVE_RENDERER_NO_INCLUDES diff --git a/thirdparty/rive_renderer/rive_renderer_apple.mm b/thirdparty/rive_renderer/rive_renderer_apple.mm deleted file mode 100644 index b6b079e62..000000000 --- a/thirdparty/rive_renderer/rive_renderer_apple.mm +++ /dev/null @@ -1,51 +0,0 @@ -/* - ============================================================================== - - This file is part of the YUP library. - Copyright (c) 2024 - kunitoki@gmail.com - - YUP is an open source library subject to open-source licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - to use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -#define YUP_RIVE_RENDERER_NO_INCLUDES 1 -#include "rive_renderer.h" -#undef YUP_RIVE_RENDERER_NO_INCLUDES - -#if YUP_RIVE_USE_METAL -#include - -#include "source/metal/render_context_metal_impl.mm" -#include "source/metal/background_shader_compiler.mm" - -#if TARGET_OS_SIMULATOR -#include "source/generated/shaders/rive_pls_ios_simulator.metallib.c" -#elif TARGET_OS_IOS -#include "source/generated/shaders/rive_pls_ios.metallib.c" -#elif TARGET_OS_MAC -#include "source/generated/shaders/rive_pls_macosx.metallib.c" -#endif - -#include "source/ore/metal/ore_bind_group_metal.mm" -#include "source/ore/metal/ore_context_metal.mm" -#include "source/ore/metal/ore_texture_metal.mm" -#include "source/ore/metal/ore_shader_module_metal.mm" -#include "source/ore/metal/ore_sampler_metal.mm" -#define kMetalVertexBufferBase kMetalVertexBufferBase_render_pass -#include "source/ore/metal/ore_render_pass_metal.mm" -#undef kMetalVertexBufferBase -#include "source/ore/metal/ore_pipeline_metal.mm" -#include "source/ore/metal/ore_buffer_metal.mm" -#include "source/ore/metal/ore_bind_group_metal.mm" -#endif diff --git a/thirdparty/rive_renderer/rive_renderer_emscripten.cpp b/thirdparty/rive_renderer/rive_renderer_emscripten.cpp deleted file mode 100644 index cf5afa5b6..000000000 --- a/thirdparty/rive_renderer/rive_renderer_emscripten.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - ============================================================================== - - This file is part of the YUP library. - Copyright (c) 2024 - kunitoki@gmail.com - - YUP is an open source library subject to open-source licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - to use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -#include "rive_renderer.h" - -#if __clang__ - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wshorten-64-to-32" - #pragma clang diagnostic ignored "-Wattributes" -#endif - -#if RIVE_WEBGPU -#include "source/webgpu/render_context_webgpu_impl.cpp" - -#include "source/ore/wgpu/ore_bind_group_wgpu.cpp" -#include "source/ore/wgpu/ore_buffer_wgpu.cpp" -#include "source/ore/wgpu/ore_context_wgpu.cpp" -#include "source/ore/wgpu/ore_pipeline_wgpu.cpp" -#include "source/ore/wgpu/ore_render_pass_wgpu.cpp" -#include "source/ore/wgpu/ore_sampler_wgpu.cpp" -#include "source/ore/wgpu/ore_shader_module_wgpu.cpp" -#include "source/ore/wgpu/ore_texture_wgpu.cpp" - -#elif RIVE_WEBGL -#include "source/gl/gl_state.cpp" -#include "source/gl/gl_utils.cpp" -#include "source/gl/load_store_actions_ext.cpp" -#include "source/gl/pls_impl_webgl.cpp" -#include "source/gl/render_buffer_gl_impl.cpp" -#include "source/gl/render_context_gl_impl.cpp" -#include "source/gl/render_target_gl.cpp" - -#include "source/ore/gl/ore_bind_group_gl.cpp" -#include "source/ore/gl/ore_buffer_gl.cpp" -#include "source/ore/gl/ore_context_gl.cpp" -#include "source/ore/gl/ore_pipeline_gl.cpp" -#define oreCompareFunctionToGL oreCompareFunctionToGL_rp -#include "source/ore/gl/ore_render_pass_gl.cpp" -#undef oreCompareFunctionToGL -#include "source/ore/gl/ore_sampler_gl.cpp" -#include "source/ore/gl/ore_shader_module_gl.cpp" -#define oreFormatToGLInternal oreFormatToGLInternal_tex -#include "source/ore/gl/ore_texture_gl.cpp" -#undef oreFormatToGLInternal - -#else -#error "No renderer backend defined" - -#endif - -#if __clang__ - #pragma clang diagnostic pop -#endif diff --git a/thirdparty/rive_renderer/rive_renderer_windows.cpp b/thirdparty/rive_renderer/rive_renderer_windows.cpp deleted file mode 100644 index 543b79849..000000000 --- a/thirdparty/rive_renderer/rive_renderer_windows.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - ============================================================================== - - This file is part of the YUP library. - Copyright (c) 2024 - kunitoki@gmail.com - - YUP is an open source library subject to open-source licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - to use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -#include "rive_renderer.h" - -#if YUP_RIVE_USE_D3D -#pragma comment (lib, "d3d11.lib") -#pragma comment (lib, "d3dcompiler.lib") -#pragma comment (lib, "dxgi.lib") -#pragma comment (lib, "dxguid.lib") - -#include "source/d3d/pipeline_manager.cpp" -#include "source/d3d/d3d_utils.cpp" -#include "source/d3d11/render_context_d3d_impl.cpp" -//#include "source/d3d12/d3d12_pipeline_manager.cpp" -//#include "source/d3d12/d3d12_utils.cpp" -//#include "source/d3d12/render_context_d3d12_impl.cpp" - -#include "source/ore/d3d11/ore_bind_group_d3d11.cpp" -#include "source/ore/d3d11/ore_buffer_d3d11.cpp" -#include "source/ore/d3d11/ore_context_d3d11.cpp" -#include "source/ore/d3d11/ore_pipeline_d3d11.cpp" -#include "source/ore/d3d11/ore_render_pass_d3d11.cpp" -#include "source/ore/d3d11/ore_sampler_d3d11.cpp" -#include "source/ore/d3d11/ore_shader_module_d3d11.cpp" -#include "source/ore/d3d11/ore_texture_d3d11.cpp" -#endif - -#if YUP_RIVE_USE_OPENGL -#pragma comment (lib, "opengl32.lib") - -#include "source/gl/gl_state.cpp" -#include "source/gl/gl_utils.cpp" -#include "source/gl/load_store_actions_ext.cpp" -#include "source/gl/pls_impl_ext_native.cpp" -#include "source/gl/pls_impl_rw_texture.cpp" -#include "source/gl/pls_impl_webgl.cpp" -#include "source/gl/render_buffer_gl_impl.cpp" -#include "source/gl/render_context_gl_impl.cpp" -#include "source/gl/render_target_gl.cpp" - -#include "source/ore/gl/ore_bind_group_gl.cpp" -#include "source/ore/gl/ore_buffer_gl.cpp" -#include "source/ore/gl/ore_context_gl.cpp" -#include "source/ore/gl/ore_pipeline_gl.cpp" -#define oreCompareFunctionToGL oreCompareFunctionToGL_rp -#include "source/ore/gl/ore_render_pass_gl.cpp" -#undef oreCompareFunctionToGL -#include "source/ore/gl/ore_sampler_gl.cpp" -#include "source/ore/gl/ore_shader_module_gl.cpp" -#define oreFormatToGLInternal oreFormatToGLInternal_tex -#include "source/ore/gl/ore_texture_gl.cpp" -#undef oreFormatToGLInternal -#endif diff --git a/thirdparty/rive_vendor_versions.json b/thirdparty/rive_vendor_versions.json index 4b4cab593..af1942a81 100644 --- a/thirdparty/rive_vendor_versions.json +++ b/thirdparty/rive_vendor_versions.json @@ -6,12 +6,6 @@ "ref": "runtime-v0.1.155", "version": "0.1.155" }, - { - "checkout": "244aa55bd623d03608576a0874b15f1c5bdc4a35", - "name": "rive_renderer", - "ref": "runtime-v0.1.155", - "version": "0.1.155" - }, { "checkout": "244aa55bd623d03608576a0874b15f1c5bdc4a35", "name": "glad", diff --git a/tools/rive_inspect.py b/tools/rive_inspect.py new file mode 100644 index 000000000..13a74e838 --- /dev/null +++ b/tools/rive_inspect.py @@ -0,0 +1,509 @@ +#!/usr/bin/env python3 + +# ============================================================================== +# +# This file is part of the YUP library. +# Copyright (c) 2026 - kunitoki@gmail.com +# +# YUP is an open source library subject to open-source licensing. +# +# The code included in this file is provided under the terms of the ISC license +# http://www.isc.org/downloads/software-support-policy/isc-license. Permission +# to use, copy, modify, and/or distribute this software for any purpose with or +# without fee is hereby granted provided that the above copyright notice and +# this permission notice appear in all copies. +# +# YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER +# EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE +# DISCLAIMED. +# +# ============================================================================== + +"""Inspect the contents of a Rive (.riv) runtime file. + +Parses the Rive v7 binary format directly (no rive runtime needed) and lets you +inspect the object table of a .riv file: + + rive_inspect.py alien.riv names + rive_inspect.py alien.riv names --json + rive_inspect.py alien.riv info "Pata01" + rive_inspect.py alien.riv info 150 + rive_inspect.py alien.riv tree + rive_inspect.py alien.riv tree --node "Pata01" + +Property keys and object type keys are resolved against the vendored rive +generated headers (thirdparty/rive/include/rive/generated), which is also how +the rive runtime itself decodes the file. Object hierarchy follows the runtime's +own resolution: parentId is an index into the owning artboard's component list +(components in file order, the artboard itself at index 0), not the raw file +position. +""" + +from __future__ import annotations + +import argparse +import glob +import json +import re +import struct +import sys +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any + +SCRIPT_DIR = Path(__file__).resolve().parent +REPO_ROOT = SCRIPT_DIR.parent +DEFAULT_RIVE_INCLUDE = REPO_ROOT / "thirdparty" / "rive" / "include" / "rive" + +# Core*Type::id values, matching the vendored rive field types. +TYPE_UINT, TYPE_STRING, TYPE_DOUBLE, TYPE_COLOR, TYPE_BOOL, TYPE_BYTES = 0, 1, 2, 3, 4, 5 +TYPE_NAMES = {TYPE_UINT: "uint", TYPE_STRING: "string", TYPE_DOUBLE: "double", TYPE_COLOR: "color", TYPE_BOOL: "bool", TYPE_BYTES: "bytes"} + +# ----------------------------------------------------------------------------- +# Binary readers (LEB128 varints, little-endian values). + + +def read_varuint(data: bytes, pos: int) -> tuple[int, int]: + result = 0 + shift = 0 + while True: + byte = data[pos] + pos += 1 + result |= (byte & 0x7F) << shift + if not (byte & 0x80): + return result, pos + shift += 7 + + +def read_f32(data: bytes, pos: int) -> tuple[float, int]: + return struct.unpack_from(" tuple[int, int]: + return struct.unpack_from(" tuple[str, int]: + length, pos = read_varuint(data, pos) + return data[pos : pos + length].decode("utf-8", "replace"), pos + length + + +def skip_bytes(data: bytes, pos: int) -> int: + length, pos = read_varuint(data, pos) + return pos + length + + +# ----------------------------------------------------------------------------- +# Schema built from the vendored rive generated headers. + +PROPERTY_KEY_RE = re.compile(r"static const uint16_t (\w+)PropertyKey = (\d+);") +TYPE_KEY_RE = re.compile(r"static const uint16_t typeKey = (\d+);") +CLASS_RE = re.compile(r"class (\w+Base)\b") +ARTBOARD_TYPE_KEY = 1 # ArtboardBase::typeKey + + +@dataclass +class PropertySchema: + """Decoding information extracted from the vendored rive generated headers.""" + + property_names: dict[int, str] # property key -> name (first class wins) + property_types: dict[int, int] # property key -> Core*Type id + type_names: dict[int, str] # object type key -> class name + component_keys: set[int] # object type keys that derive from ComponentBase + parent_key: int # property key of the parent index + name_key: int # property key of the object name + + +def build_property_schema(rive_include: Path) -> PropertySchema: + generated_dir = rive_include / "generated" + if not generated_dir.is_dir(): + raise SystemExit(f"Rive generated headers not found under {generated_dir}") + + property_names: dict[int, str] = {} + class_props: dict[str, dict[str, int]] = {} + type_names: dict[int, str] = {} + component_keys: set[int] = set() + + for header in sorted(glob.glob(str(generated_dir / "**" / "*_base.hpp"), recursive=True)): + text = Path(header).read_text(encoding="utf-8") + + class_match = CLASS_RE.search(text) + if class_match is None: + continue + class_name = class_match.group(1)[: -len("Base")] + + for key_match in PROPERTY_KEY_RE.finditer(text): + name, key = key_match.group(1), int(key_match.group(2)) + class_props.setdefault(class_name, {})[name] = key + property_names.setdefault(key, name) + + type_match = TYPE_KEY_RE.search(text) + if type_match is None: + continue + type_key = int(type_match.group(1)) + type_names.setdefault(type_key, class_name) + + # ComponentBase::typeKey (10) listed in isTypeOf means the class derives + # from ComponentBase, i.e. it occupies an artboard component slot. + if re.search(r"case ComponentBase::typeKey:", text): + component_keys.add(type_key) + + # Property key -> Core*Type id from CoreRegistry::propertyFieldId. + property_types: dict[int, int] = {} + registry_path = generated_dir / "core_registry.hpp" + registry_text = registry_path.read_text(encoding="utf-8") + function_match = re.search( + r"static int propertyFieldId\(int propertyKey\)\s*\{(.*?)\n \}", + registry_text, + re.DOTALL, + ) + if function_match is None: + raise SystemExit(f"Could not locate CoreRegistry::propertyFieldId in {registry_path}") + + # re.split keeps the case-group text BEFORE each "return ::id;". + parts = re.split(r"return (\w+)::id;", function_match.group(1)) + type_ids = {"CoreUintType": TYPE_UINT, "CoreStringType": TYPE_STRING, "CoreDoubleType": TYPE_DOUBLE, "CoreColorType": TYPE_COLOR, "CoreBoolType": TYPE_BOOL} + for index in range(1, len(parts), 2): + type_id = type_ids.get(parts[index]) + if type_id is None: + continue + for case_match in re.finditer(r"case (\w+)::(\w+)PropertyKey:", parts[index - 1]): + class_name, prop_name = case_match.group(1), case_match.group(2) + if class_name.endswith("Base"): + class_name = class_name[: -len("Base")] + key = class_props.get(class_name, {}).get(prop_name) + if key is not None: + property_types[key] = type_id + + # Properties stored as raw bytes, only handled by their owning class. + for header in sorted(glob.glob(str(generated_dir / "**" / "*_base.hpp"), recursive=True)): + text = Path(header).read_text(encoding="utf-8") + for match in re.finditer(r"case (\w+)PropertyKey:[\s\S]{0,300}?CoreBytesType::deserialize\(reader\);", text): + key = property_names.get(match.group(1)) + if key is not None: + property_types[key] = TYPE_BYTES + + return PropertySchema( + property_names=property_names, + property_types=property_types, + type_names=type_names, + component_keys=component_keys, + parent_key=class_props.get("Component", {}).get("parentId", 5), + name_key=class_props.get("Component", {}).get("name", 4), + ) + + +# ----------------------------------------------------------------------------- +# Rive file parsing. + + +@dataclass +class RivObject: + index: int + type_key: int + props: dict[int, Any] + + def name(self, schema: PropertySchema) -> str | None: + value = self.props.get(schema.name_key) + return value if isinstance(value, str) else None + + +class RivFile: + """A parsed Rive runtime file.""" + + def __init__(self, path: Path, schema: PropertySchema): + self.path = path + self.schema = schema + self.major: int = 0 + self.minor: int = 0 + self.file_id: int = 0 + self.objects: list[RivObject] = [] + self._parent: dict[int, int | None] = {} + self._parse() + + def _parse(self) -> None: + data = self.path.read_bytes() + + if data[:4] != b"RIVE": + raise SystemExit(f"{self.path} is not a Rive file (missing 'RIVE' magic)") + + pos = 4 + self.major, pos = read_varuint(data, pos) + self.minor, pos = read_varuint(data, pos) + self.file_id, pos = read_varuint(data, pos) + + # File table-of-contents: property keys this runtime version may not know, + # packed as 2-bit field indices (4 per uint32). + toc_keys: list[int] = [] + while True: + key, pos = read_varuint(data, pos) + if key == 0: + break + toc_keys.append(key) + + toc_types: dict[int, int] = {} + current, bit = 0, 8 + for index, key in enumerate(toc_keys): + if bit == 8: + current, pos = read_u32(data, pos) + bit = 0 + toc_types[key] = (current >> bit) & 3 + bit += 2 + + schema = self.schema + objects: list[RivObject] = [] + + # parentId resolves into the owning artboard's component list: the + # artboard itself is slot 0, followed by every component (or null object) + # in file order until the next artboard, matching Artboard::m_Objects. + slot_to_file: list[dict[int, int]] = [] + object_slot: list[int | None] = [] + object_artboard: list[int | None] = [] + + while pos < len(data): + file_index = len(objects) + type_key, pos = read_varuint(data, pos) + props: dict[int, Any] = {} + while True: + prop_key, pos = read_varuint(data, pos) + if prop_key == 0: + break + + type_id = schema.property_types.get(prop_key) + if type_id is None: + # Unknown to this runtime: fall back to the file's own TOC. + type_id = {0: TYPE_UINT, 1: TYPE_STRING, 2: TYPE_DOUBLE, 3: TYPE_COLOR}[toc_types.get(prop_key, 0)] + + if type_id == TYPE_UINT: + value, pos = read_varuint(data, pos) + elif type_id == TYPE_STRING: + value, pos = read_string(data, pos) + elif type_id == TYPE_DOUBLE: + value, pos = read_f32(data, pos) + elif type_id == TYPE_COLOR: + value, pos = read_u32(data, pos) + elif type_id == TYPE_BOOL: + value = data[pos] == 1 + pos += 1 + else: + start = pos + pos = skip_bytes(data, pos) + value = f"" + + props[prop_key] = value + + objects.append(RivObject(file_index, type_key, props)) + + if type_key == ARTBOARD_TYPE_KEY: + # The artboard is always the first object of its component list. + slot_to_file.append({0: file_index}) + object_artboard.append(len(slot_to_file) - 1) + object_slot.append(0) + elif slot_to_file and (type_key in schema.component_keys or type_key not in schema.type_names): + # Components (and unknown/null objects) occupy the next slot of + # the current artboard; animations, keyframes, assets, etc. do not. + artboard = len(slot_to_file) - 1 + slot = len(slot_to_file[artboard]) + slot_to_file[artboard][slot] = file_index + object_artboard.append(artboard) + object_slot.append(slot) + else: + object_artboard.append(None) + object_slot.append(None) + + # Resolve parentId through each artboard's slot map. + self._parent = {} + for file_index, obj in enumerate(objects): + parent_slot = obj.props.get(schema.parent_key) + artboard = object_artboard[file_index] + if parent_slot is None or artboard is None: + self._parent[file_index] = None + continue + self._parent[file_index] = slot_to_file[artboard].get(parent_slot) + + self.objects = objects + + def type_name(self, type_key: int) -> str: + return self.schema.type_names.get(type_key, f"Type{type_key}") + + def find(self, name_or_index: str | int) -> RivObject | None: + if isinstance(name_or_index, int) or str(name_or_index).isdigit(): + index = int(name_or_index) + if 0 <= index < len(self.objects): + return self.objects[index] + for obj in self.objects: + if obj.name(self.schema) == name_or_index: + return obj + return None + + def parent_of(self, obj: RivObject) -> RivObject | None: + parent_index = self._parent.get(obj.index) + if parent_index is None: + return None + return self.objects[parent_index] + + def children(self, obj: RivObject) -> list[RivObject]: + return [candidate for candidate in self.objects if self._parent.get(candidate.index) == obj.index] + + def label(self, obj: RivObject) -> str: + name = obj.name(self.schema) + return f"{obj.index} [{self.type_name(obj.type_key)}] {name}" if name else f"{obj.index} [{self.type_name(obj.type_key)}]" + + +# ----------------------------------------------------------------------------- +# Output helpers. + + +def _format_value(value: Any, type_id: int | None) -> str: + if type_id == TYPE_COLOR and isinstance(value, int): + return f"0x{value:08x}" + if isinstance(value, str): + return f'"{value}"' + if isinstance(value, float): + return f"{value:g}" + return str(value) + + +# ----------------------------------------------------------------------------- +# Commands. + + +def cmd_names(riv: RivFile, args: argparse.Namespace) -> int: + schema = riv.schema + entries = [] + for obj in riv.objects: + name = obj.name(schema) + if not name: + continue + parent = riv.parent_of(obj) + entry = { + "index": obj.index, + "type": riv.type_name(obj.type_key), + "typeKey": obj.type_key, + "parent": parent.index if parent else None, + "parentName": parent.name(schema) if parent else None, + "name": name, + } + entries.append(entry) + + if args.json: + print(json.dumps(entries, indent=2)) + return 0 + + width = max(len(str(entry["index"])) for entry in entries) if entries else 1 + for entry in entries: + parent = f"{entry['parent']} {entry['parentName']}".strip() if entry["parent"] is not None else "-" + print(f"{entry['index']:>{width}d} {entry['type']:<22} parent={parent:<24} {entry['name']}") + print(f"\n{len(entries)} named object(s) in {riv.path}") + return 0 + + +def cmd_info(riv: RivFile, args: argparse.Namespace) -> int: + target = riv.find(args.node) + if target is None: + raise SystemExit(f"Object '{args.node}' not found") + + schema = riv.schema + parent = riv.parent_of(target) + info = { + "index": target.index, + "type": riv.type_name(target.type_key), + "typeKey": target.type_key, + "parent": parent.index if parent else None, + "parentName": parent.name(schema) if parent else None, + } + + if args.json: + props = {schema.property_names.get(key, str(key)): value for key, value in target.props.items()} + print(json.dumps({**info, "properties": props}, indent=2)) + return 0 + + print(f"object {target.index} [{riv.type_name(target.type_key)}] (typeKey {target.type_key})") + if parent: + parent_name = parent.name(schema) + print(f"parent: {parent.index}{f' ({parent_name})' if parent_name else ''}") + if not target.props: + print("(no properties)") + for key, value in target.props.items(): + name = schema.property_names.get(key, f"key{key}") + type_id = schema.property_types.get(key) + type_suffix = f" ({TYPE_NAMES.get(type_id, '?')})" if type_id is not None else "" + print(f" {name}{type_suffix} = {_format_value(value, type_id)}") + return 0 + + +def cmd_tree(riv: RivFile, args: argparse.Namespace) -> int: + root = riv.find(args.node) if args.node else None + + def print_subtree(obj: RivObject, indent: str, is_last: bool) -> None: + connector = "`- " if is_last else "|- " + print(f"{indent}{connector}{riv.label(obj)}") + children = riv.children(obj) + child_indent = indent + (" " if is_last else "| ") + for index, child in enumerate(children): + print_subtree(child, child_indent, index == len(children) - 1) + + if root is not None: + print(f"subtree rooted at {riv.label(root)}") + children = riv.children(root) + for index, child in enumerate(children): + print_subtree(child, "", index == len(children) - 1) + return 0 + + roots = [obj for obj in riv.objects if riv.parent_of(obj) is None] + for index, obj in enumerate(roots): + print(riv.label(obj)) + children = riv.children(obj) + for child_index, child in enumerate(children): + print_subtree(child, "", child_index == len(children) - 1) + return 0 + + +# ----------------------------------------------------------------------------- +# CLI. + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Inspect the contents of a Rive (.riv) runtime file.") + parser.add_argument("file", help="Path to the .riv file to inspect.") + parser.add_argument( + "--rive-include", + default=str(DEFAULT_RIVE_INCLUDE), + help="Path to the vendored rive include directory used to resolve keys (default: %(default)s).", + ) + + subparsers = parser.add_subparsers(dest="command", required=True) + + names = subparsers.add_parser("names", help="List all named objects.") + names.add_argument("--json", action="store_true", help="Emit a JSON array instead of a table.") + names.set_defaults(func=cmd_names) + + info = subparsers.add_parser("info", help="Dump the properties of one object, by name or index.") + info.add_argument("node", help="Object name or index.") + info.add_argument("--json", action="store_true", help="Emit JSON instead of a table.") + info.set_defaults(func=cmd_info) + + tree = subparsers.add_parser("tree", help="Print the object hierarchy.") + tree.add_argument("--node", help="Root the tree at this object (name or index) instead of the file roots.") + tree.set_defaults(func=cmd_tree) + + return parser.parse_args() + + +def main() -> int: + args = parse_args() + schema = build_property_schema(Path(args.rive_include)) + riv = RivFile(Path(args.file), schema) + try: + return args.func(riv, args) + except BrokenPipeError: + # Piping into tools like `head` closes stdout early; exit quietly. + try: + sys.stdout.close() + except BrokenPipeError: + pass + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/rive_update.py b/tools/rive_update.py index 28760743f..b11bd88b8 100755 --- a/tools/rive_update.py +++ b/tools/rive_update.py @@ -934,7 +934,7 @@ def print_summary(results: list[DependencyResult]) -> None: def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description="Refresh vendored Rive and Rive-adjacent dependencies.") - parser.add_argument("--rive-ref", help="Rive runtime ref used for rive, rive_renderer, and rive_decoders.") + parser.add_argument("--rive-ref", help="Rive runtime ref used for rive (core, renderer sources and shaders), rive_decoders and glad.") parser.add_argument("--dep", action="append", default=[], type=parse_dep_override, help="Override dependency ref as name=ref.") parser.add_argument("--manifest", default=str(DEFAULT_MANIFEST), help="Path to the Rive update manifest.") parser.add_argument("--work-dir", default="build/rive-update", help="Directory for checkouts and generated intermediates.") diff --git a/tools/rive_update_manifest.json b/tools/rive_update_manifest.json index e7b7119e5..df9ad892d 100644 --- a/tools/rive_update_manifest.json +++ b/tools/rive_update_manifest.json @@ -51,6 +51,9 @@ "**/*.h", "**/*.hh", "**/*.hpp" + ], + "preserve_globs": [ + "rive/renderer/**" ] }, { @@ -64,23 +67,14 @@ "**/*.hh", "**/*.hpp", "**/*.mm" + ], + "preserve_globs": [ + "renderer/**" ] - } - ] - }, - { - "name": "rive_renderer", - "repo": "https://github.com/rive-app/rive-runtime.git", - "ref": "main", - "destination": "thirdparty/rive_renderer", - "license": "MIT", - "module_header": "thirdparty/rive_renderer/rive_renderer.h", - "module_version_from_ref": true, - "follow_rive_ref": true, - "copies": [ + }, { - "from": "renderer/include", - "to": "include", + "from": "renderer/include/rive/renderer", + "to": "include/rive/renderer", "include_globs": [ "**/*.h", "**/*.hh", @@ -89,7 +83,7 @@ }, { "from": "renderer/src", - "to": "source", + "to": "source/renderer", "include_globs": [ "**/*.c", "**/*.cc", @@ -118,6 +112,28 @@ } ], "patches": [ + { + "path": "source/assets/script_asset.cpp", + "note": "drop the missing-generator diagnostic: a non-tools runtime only registers scripts whose in-band signature verified, so an unverified script's scripted objects fail this lookup on every advance and spammed stderr with one line per object per frame", + "already_contains": " if (ref == 0)\n {\n return false;\n }", + "replace": { + "from": [ + " if (ref == 0)", + " {", + " fprintf(stderr,", + " \"ScriptAsset doesn't have a generator function %s\\n\",", + " name().c_str());", + " return false;", + " }" + ], + "to": [ + " if (ref == 0)", + " {", + " return false;", + " }" + ] + } + }, { "path": "include/rive/renderer/ore/ore_context_wgpu.hpp", "note": "ContextWGPU owned-encoder mode: add m_ownsCommandEncoder flag", @@ -135,7 +151,7 @@ } }, { - "path": "source/ore/wgpu/ore_context_wgpu.cpp", + "path": "source/renderer/ore/wgpu/ore_context_wgpu.cpp", "note": "ContextWGPU owned-encoder mode: create/submit own encoder when beginFrame has no external one (fixes GpuFrame on Emscripten WebGPU)", "already_contains": "m_ownsCommandEncoder", "replace": { @@ -203,7 +219,7 @@ } }, { - "path": "source/ore/gl/ore_context_gl.cpp", + "path": "source/renderer/ore/gl/ore_context_gl.cpp", "note": "Ore GL render pass: disable the scissor test for the duration of the pass, since the host renderer may leave a stale scissor rect enabled that would clip the pass's clears and draws", "already_contains": " glBindFramebuffer(GL_FRAMEBUFFER, pass->m_glFBO);\n\n glDisable(GL_SCISSOR_TEST);", "replace": { @@ -228,7 +244,7 @@ } }, { - "path": "source/ore/gl/ore_context_gl.cpp", + "path": "source/renderer/ore/gl/ore_context_gl.cpp", "note": "Ore GL render pass: force the color/depth/stencil write masks on before the clears — the host renderer may leave them disabled, which silently discards glClearBuffer* and leaves stale content in reused render targets (trails/ghosting)", "already_contains": " glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);\n glDepthMask(GL_TRUE);\n glStencilMask(0xFF);", "replace": { @@ -250,7 +266,7 @@ "shader_generation": { "source_dir": "renderer/src/shaders", "output_dir": "out/generated", - "destination": "thirdparty/rive_renderer/source/generated/shaders", + "destination": "thirdparty/rive/source/renderer/generated/shaders", "python_package": "ply", "required_tools": { "minify": [ @@ -810,7 +826,8 @@ "**/*.cpp" ], "exclude_globs": [ - "**/*_test.*" + "**/*_test.*", + "renderer/**" ], "include_overrides": { "source/animation/nested_bool.cpp": [ @@ -906,6 +923,11 @@ "#include \"source/lua/lua_data_value.cpp\"", "#undef data_value_namecall" ], + "source/lua/renderer/lua_mesh.cpp": [ + "#define empty yup_rive_lua_mesh_empty", + "#include \"source/lua/renderer/lua_mesh.cpp\"", + "#undef empty" + ], "source/math/hit_test.cpp": [ "#include \"rive/math/hit_test.hpp\"", "#include \"rive/math/mat2d.hpp\"", @@ -925,18 +947,34 @@ } }, { - "path": "thirdparty/rive_renderer/rive_renderer.cpp", + "path": "thirdparty/rive/rive_renderer.cpp", "start_marker": "// BEGIN YUP GENERATED RIVE RENDERER INCLUDES", "end_marker": "// END YUP GENERATED RIVE RENDERER INCLUDES", - "insert_after": "#include \"rive_renderer.h\"", + "insert_after": "#undef YUP_RIVE_NO_INCLUDES", "insert_before": "#if __clang__", - "root": "thirdparty/rive_renderer/source", - "prefix": "source", + "root": "thirdparty/rive/source/renderer", + "prefix": "source/renderer", "include_globs": [ "*.c", "*.cc", - "*.cpp" - ] + "*.cpp", + "ore/*.c", + "ore/*.cc", + "ore/*.cpp" + ], + "exclude_globs": [ + "**/*_test.*", + "ore/*/*" + ], + "include_overrides": { + "source/renderer/ore/hlsl_struct_layout.cpp": [ + "#define readU16LE readU16LE_vec", + "#define readU32LE readU32LE_vec", + "#include \"source/renderer/ore/hlsl_struct_layout.cpp\"", + "#undef readU16LE", + "#undef readU32LE" + ] + } }, { "path": "thirdparty/rive_decoders/rive_decoders.cpp",