Add the cockpit seat and hand steering to the Quest launcher

The Quest launcher's Settings > VR gains a Seat choice (cockpit or
custom, first_person_seat) and a Hand steering switch (hand_steering, off
by default like the runtime), which is only enabled in the first-person
cockpit because the wheel belongs to that seat. About credits heurazy for
the turning steering wheel and hand steering. The runtime side was already
shared with the Quest build.

OPENXR.md records what one Quest 3 race showed: the kart's own wheel
animated (228 draws a frame) with the race camera's view matching the
scene's exactly, but the driver's eye was never calibrated and the
fallback put the wheel centre about 13 cm above eye level. The Quest
declares no hand-tracking permission, so hands are the procedural gloves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
iChris4andClaude Opus 5 committed 2026-09-22 18:11:30 +02:00
1 parent 7c6a6da3d5
commit 8b01b47253
4 files changed
+38 -6

No files matched your search

+9 -5
View File
@@ -417,7 +417,8 @@ copied array, those that bound one outside the window it was set for, the matche
closest position matrix was from the expected one; the first such line with a bound draw also prints
both matrices.
**Hand steering.** `hand_steering = true` (off by default; also in WheelWizard's OpenXR VR settings)
**Hand steering.** `hand_steering = true` (off by default; also in WheelWizard's OpenXR VR settings
and the Quest launcher's Settings > VR, beside the seat)
lets you take hold of the wheel or handlebar with the tracked controllers. Squeeze a grip near it:
past 55 % squeeze, within `wheel_grab_distance` metres of its plane (default 0.35) and near the rim,
or near a bar end, scaled by `wheel_grab_assist`. Once taken, only letting go of the grip releases
@@ -441,7 +442,8 @@ aside while it drives.
**Hands and the separate wheel.** Hands are drawn while hand steering is on: the runtime's own hand
mesh where it offers one (`XR_EXT_hand_tracking` and `XR_FB_hand_tracking_mesh`, requested only when
hand steering is on at launch), otherwise procedural gloves that curl with the squeeze. They and the
hand steering is on at launch), otherwise procedural gloves that curl with the squeeze. The Quest
build declares no hand-tracking permission, so it always draws the gloves. They and the
separate VR wheel or handlebar travel with the stereo packet in metres in the seated frame, and each
eye draws them inside the scene's pass just before the first 2D-layer draw, depth-tested with the
world's own depth mapping, so the kart and the track hide them and the HUD cannot
@@ -764,9 +766,11 @@ ends, including mid-frame flushes, so live setting changes cannot invalidate pen
Lifecycle events and performance (about 43 game FPS) are still open. Apple visionOS packaging
is not implemented.
- Scene-specific comfort options, culling fixes and replay/spectator classification are future work.
- The cockpit seat, the turning wheel and hand steering have not yet been validated in a headset on
this build: the native wheel's match against the race camera's view, bikes and Quacker, and the
Quest. Hand steering needs analog grips (Touch); the simple controller profile cannot grab.
- The cockpit has only been seen in one kart race on a Quest 3 (2026-09-22): the race camera's view
matched the scene's exactly and the kart's own wheel animated (228 draws a frame), but the driver's
eye was never calibrated, and the fallback placed the wheel centre about 13 cm above eye level.
Bikes and Quacker, the PC, and hand steering itself are unvalidated. Hand steering needs analog
grips (Touch); the simple controller profile cannot grab.
- The headset settings panel has no laser beam, only the cursor on the panel itself, and text fields
cannot be typed into without a keyboard.
- The desktop window remains available as a mirror/fallback.
@@ -115,6 +115,8 @@ class SettingsPage(
private fun buildVr() {
val firstPerson = { c: TomlConfig -> c.bool("vr", "first_person") ?: false }
// The steering wheel and hand steering belong to the cockpit seat.
val cockpit = { c: TomlConfig -> firstPerson(c) && stringIndex(c, "vr", "first_person_seat", SEATS) == 0 }
section(R.string.section_vr_camera) {
choice(
R.string.vr_camera, R.string.vr_camera_helper,
@@ -151,6 +153,20 @@ class SettingsPage(
},
enabledIf = firstPerson,
)
choice(
R.string.vr_seat, R.string.vr_seat_helper,
listOf(R.string.vr_seat_cockpit, R.string.vr_seat_custom),
read = { stringIndex(it, "vr", "first_person_seat", SEATS) },
write = { c, index -> c.setString("vr", "first_person_seat", SEATS[index]) },
enabledIf = firstPerson,
)
// heurazy's grab-and-turn wheel: runtime_config.h's kVrHandSteeringDefault is off.
toggle(
R.string.vr_hand_steering, R.string.vr_hand_steering_helper,
read = { it.bool("vr", "hand_steering") ?: false },
write = { c, value -> c.setBool("vr", "hand_steering", value) },
enabledIf = cockpit,
)
slider(
R.string.vr_lean_back, R.string.vr_lean_back_helper, -45.0, 45.0, 1.0,
read = { number(it, "vr", "lean_back_degrees", -45.0, 45.0, 0.0) },
@@ -366,6 +382,7 @@ class SettingsPage(
}
section(R.string.section_about_credits) {
info(R.string.about_credit_title_vr, activity.getString(R.string.about_credit_vr), stacked = true)
info(R.string.about_credit_title_hand_steering, activity.getString(R.string.about_credit_hand_steering), stacked = true)
info(R.string.about_credit_title_wiicompiled, activity.getString(R.string.about_credit_wiicompiled), stacked = true)
info(R.string.about_credit_title_retro_rewind, activity.getString(R.string.about_credit_retro_rewind), stacked = true)
info(R.string.about_credit_title_wheel_wizard, activity.getString(R.string.about_credit_wheel_wizard), stacked = true)
@@ -686,6 +703,8 @@ class SettingsPage(
private companion object {
val ROTATIONS = listOf("yaw", "yaw_pitch", "full")
// The runtime's default ("cockpit") first.
val SEATS = listOf("cockpit", "custom")
// The runtime's default ("boost") first: an absent key reads as index 0.
val PERFORMANCE_LEVELS = listOf("boost", "sustained_high", "sustained_low", "power_savings", "default")
val CONTROLLER_MODES = listOf("wii_remote", "gamepad")
@@ -141,6 +141,7 @@
<string name="about_app_name">WiiCompiled OpenXR VR</string>
<string name="about_app_summary">Mario Kart Wii recompiled to native code, with an OpenXR renderer, running on this headset. No game code and no game data come with it: each game is built from the disc you own.</string>
<string name="about_credit_vr">Meta Quest port and the OpenXR VR renderer by iChris4.</string>
<string name="about_credit_hand_steering">The cockpit\'s turning steering wheel and hand steering by heurazy, from mario-kart-wii-VR-port (GPL v3).</string>
<string name="about_credit_wiicompiled">WiiCompiled, the static recompilation this is built on, by patchzyy and the static recompilation community.</string>
<string name="about_credit_retro_rewind">Retro Rewind by ZPL and team, the mod distribution this app can download and play.</string>
<string name="about_credit_wheel_wizard">Wheel Wizard by Patchzy and WantToBeeMe, the PC launcher this panel is modelled on.</string>
@@ -153,6 +154,7 @@
<string name="about_licence_text">GNU General Public License v3. Not affiliated with, endorsed by, or associated with Nintendo. Mario Kart Wii is a trademark of Nintendo.</string>
<string name="about_kit">Game kit</string>
<string name="about_credit_title_vr">Meta Quest and VR</string>
<string name="about_credit_title_hand_steering">Steering wheel and hand steering</string>
<string name="about_credit_title_wiicompiled">WiiCompiled</string>
<string name="about_credit_title_retro_rewind">Retro Rewind</string>
<string name="about_credit_title_wheel_wizard">Wheel Wizard</string>
@@ -184,6 +186,12 @@
<string name="vr_hide_nothing">Nothing</string>
<string name="vr_hide_driver">Driver</string>
<string name="vr_hide_driver_and_kart">Driver and kart</string>
<string name="vr_seat">Seat</string>
<string name="vr_seat_helper">The cockpit puts you at the driver\'s eyes, life-size, with the steering wheel or handlebar turning within reach. Custom uses the head offsets from the in-headset settings.</string>
<string name="vr_seat_cockpit">Cockpit</string>
<string name="vr_seat_custom">Custom</string>
<string name="vr_hand_steering">Hand steering</string>
<string name="vr_hand_steering_helper">In the cockpit, squeeze a grip near the steering wheel or handlebar to grab it, and turn it to steer. Releasing both grips gives steering back to the stick. Hand steering by heurazy.</string>
<string name="vr_lean_back">Lean back angle</string>
<string name="vr_lean_back_helper">Tilts the race view back for playing reclined. 0 applies no tilt.</string>
+2 -1
View File
@@ -183,7 +183,8 @@ a 2D Horizon OS panel modelled on the PC launcher, WheelWizard VR, and using its
palette. **Home** has the Play button and reports a missing or incomplete `DATA`
(the check is the runtime's own `IsDvdDataRoot`: `files/` and `sys/fst.bin`).
**Settings** edits `Config.toml` in tabs: VR (camera, rotation, driver hiding,
lean back, render scale, VR interpolation, virtual screen size and distance),
seat, hand steering, lean back, render scale, VR interpolation, virtual screen
size and distance),
Graphics (resolution, widescreen, bloom, shader stutter), Controls (controller
mode, vibration, the Wii Remote mapping), Audio, and About (paths, OpenXR
logging). The launch-time geometry (`render_scale`, `hud_distance_meters`,