- With tracked hands on, a hand driving khr/simple_controller with camera-tracked joints is bare:
latched through wheel_tracking_grace (Meta drops the select action while a hand is lost),
cleared as soon as a controller's squeeze is back. Its palm joint stands in for the grip and a
grasp from the middle, ring and little fingers' flexion (0 below 1.2 rad, 1 from 3.0) for the
squeeze, so closing a hand on the rim takes hold under the wheel's own press and release. The
grasp never reaches the game's buttons.
- In a cockpit race a bare hand holding the wheel holds the gas (A / South) and a pinch from a free
bare hand uses an item (Z / L) once the hand has been off the wheel for 0.15 s. Only while the
game has the remote's pointer off: KPAD publishes the game's pointer switch for the VR remote,
so the pause menu and the results keep a right pinch as A. The pacing thread logs each change,
to confirm on the headset that MKW turns the pointer off while driving.
- A held bare hand keeps its last joints drawn through a short loss; bare hands get no haptics.
The headset panel's readout shows grasp, hold and pinch. Docs: OPENXR.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- New [vr] hand_tracking (default off, Quest only for now): the Quest launcher's Settings > VR and
the headset panel's VR tab, under hand steering. Two XR_EXT_hand_tracking trackers are located
every XR frame: with the controllers held the Quest builds the joints from their touch sensors
(XR_EXT_hand_tracking_data_source's controller source), once they are put down from its
cameras. The trackers exist only while the option and hand steering are on and also serve the
runtime hand mesh; the extensions (plus XR_FB_hand_tracking_aim) are asked for when either is
on at launch.
- Aurora skins the runtime mesh with the joints themselves (tracked pose times inverse bind pose,
no curl, no grip); runtimes with joints but no mesh get a skeleton; non-finite joints put only
that hand back on its grip curl. AuroraCockpitHand carries the 26 seated-frame joints and radii.
- The manifest declares horizonos.permission.HAND_TRACKING (and the deprecated
com.oculus.permission.HAND_TRACKING), both normal permissions with no prompt on a Quest 3, and
oculus.software.handtracking as optional; without it Horizon OS keeps the app controllers-only.
Bare hands then drive khr/simple_controller, so on Android a hand whose squeeze action is
inactive and select active is treated as bare: with the option off it presses nothing but the
menu gesture, is not drawn and feeds no Wii Remote motion.
- Interaction-profile changes and tracker sources are logged. The pure rules (grasp from finger
flexion, bare latch, pinch gate, bare-hand buttons, flick) live in the OpenXR-free
vr/openxr_hand_tracking.h with mkw_vr_hand_tracking_tests; the driving and flick parts are
wired in the next commits. Docs: OPENXR.md, docs/quest-port.md, README.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The pacing thread aims each immersive window eye through the window itself (AimEyesThroughWindow):
it keeps its position but looks square-on at the window's plane through an off-axis frustum just
around it, so the eye image is the window, at the display's pixel density (about 680x380 per eye
at render_scale 0.8 instead of 1344x1408), with a two-pixel border the mask leaves transparent.
The frame's views carry that pose and field of view to the projection layer.
- vulkan_interop.cpp copies an eye smaller than its AHardwareBuffer into the buffer's corner, and
the Quest layer's imageRect is the rendered part of the swapchain image.
- These eyes are not foveated: their field of view follows the head, which would rebuild the
density map every frame.
- Quest only (kWindowShapedEyesSupported); the PC backends copy whole eyes and keep masking them.
debug.wiicompiled.window_eyes 0 renders them whole and masked again for A/B timing.
- Quest 3, paused Retro Rewind race, render_scale 1.0: Immersive window runs the GPU at level 1
(456 MHz, app GPU 11.0 ms, eyes 8.4 ms) where Immersive needs level 3 (599-640 MHz, 13.4 ms,
10.3 ms), about 42% fewer GPU cycles. No edge artifacts, image as sharp. Docs: OPENXR.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Introduced a new configuration option for immersive window mode in runtime_config.h.
- Updated the parsing and setting functions to handle the immersive window state.
- Modified the OpenXR backend to support rendering with the immersive window, blending the race view with the surrounding environment.
- Enhanced the settings overlay to allow users to select between immersive, immersive window, and flat screen race views.
- Implemented GPU rendering logic for the immersive window mask, ensuring correct visual output in various rendering paths.
- Added tests to validate the immersive window functionality and its interaction with existing race view settings.
The panel was drawn into the eye images, so below a 1.00x OpenXR
resolution its 1440x1080 canvas was minified into a small eye region and
the text became hard to read. It is now submitted as a quad layer of its
own over the scene's projection or menu quad, which the compositor samples
directly at any render scale.
Each backend (D3D12, Windows Vulkan, Quest) makes the panel's swapchain
pair (plus two shared buffers on the Quest) the first time the panel
opens. While it is open, the frame hands Aurora one more stereo target
after the eyes, which the bridge fills with the panel texture or a
transparent image. The panel image follows the eyes' displayed/retained
pairing, so a cancelled frame never shows an unwritten panel. The quad
hangs where the pointer's hits are tested. Aurora leaves the panel out of
the eyes in layer mode, and a backend that cannot make the layer falls
back to drawing it into the eyes.
On the Quest, debug.wiicompiled.panel_layer 0 selects the old path at
run time. Measured there at a race start (render_scale 0.8), the layer
costs nothing while the panel is closed; while it is open, app GPU time is
10.5 ms against 9.7 ms drawn into the eyes, with unchanged frame rates.
The replay tests cover lazy creation, cancelled frames, closed and
unplaced panels, and render-first pacing on D3D12 and Windows Vulkan.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Clicking the right thumbstick flips first person exactly as the F10
checkbox does, and saves it the same way. It works on the VR controllers
in either presentation (with a short tick on the right controller) and on
any other gamepad while VR is running. It never reached the game: SDL's
stick click only lands in Aurora's extended buttons.
A click counts on release, and only if the left thumbstick stayed up and
the headset settings panel stayed closed throughout, so the two-thumbstick
panel chord in gamepad mode never toggles the camera. A gamepad whose
right stick click is bound to a GameCube control on its port, as a button
or in an input expression, keeps it for the game. The XR side only posts
a request; the toggle itself runs on the game thread with the checkbox.
[vr] first_person_toggle_click (default true) and a checkbox under the
camera toggle turn the click off. mkw_vr_camera_toggle_tests covers the
click rule.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every XR frame in the cockpit seat the pacing thread locates both grips in
the seated frame (the immersive base turned by the lean-back angle, the
frame the eye transforms place the vehicle in) and publishes a driving
snapshot. The wheel or handlebar shows the left stick's steering at the
configured full-lock angle, eased; the vehicle's own wheel reads that angle
on the guest thread.
With hand_steering on (off by default), squeezing a grip near the wheel or
handlebar takes hold of it (a short pulse on grab and release); one or two
hands turn it through heurazy's SteeringWheel, and while it is held the
wheel replaces the left stick's X axis in both the Wii Remote and gamepad
presentations, the stick's Y still aims items, and a holding grip no
longer presses C or a shoulder. The settings panel withholds it like any
other input.
The stereo packet carries the cockpit overlay: the hands, in the runtime's
hand mesh (XR_EXT_hand_tracking + XR_FB_hand_tracking_mesh, requested only
when hand steering is on at launch) or procedural gloves, and the separate
VR wheel or handlebar whenever the vehicle's own is not the one turning.
The pure pieces of the first-person cockpit and hand steering, ported from
heurazy's mario-kart-wii-VR-port: the SteeringWheel grab/turn model, the
native wheel vertex rotation, the level seat stabiliser, the seated-eye and
wheel/handlebar geometry, and the XR_FB_hand_tracking_mesh loader. Adds
openxr_driving.h, the OpenXR-free snapshot the pacing thread will publish for
the guest thread, with the hand-off rule (a held wheel replaces the left
stick's X and releases that hand's grip for the game) and the wheel's
displayed angle.
New [vr] keys: first_person_seat (cockpit), cockpit_units_per_meter (100),
steering_wheel (true), native_steering_wheel (true), hand_steering (false)
and the seven wheel_* tuning keys. Nothing reads them yet.
The fresh-config template now writes the first-person defaults the
constants hold (50 / 1.5 / 0); d86dcb0 updated the constants but not the
template.
Tests: mkw_steering_wheel_tests (the fork's), mkw_vr_cockpit_tests,
mkw_vr_hand_steering_tests.
- Created InstallReset.kt to handle the removal of game files, built games, and mod packs while preserving user data.
- Implemented unit tests for InstallReset to ensure correct functionality and file handling.
- Updated RetroRewindPackTest to include new test cases for update and deletion order.
- Enhanced Vulkan interop and OpenXR integration to support new thread scheduling hints for Android.
- Improved error handling and logging for GPU submission failures in Vulkan backend.