From 0d07df14c94d51201760c98f46867cff3e746d08 Mon Sep 17 00:00:00 2001 From: iChris4 Date: Tue, 22 Sep 2026 02:11:10 +0200 Subject: [PATCH] Add the cockpit and hand-steering settings, docs and credits F10 > VR (and the headset panel) gains a Seat choice (cockpit or custom, with the cockpit scale or the existing custom sliders), "Turn the steering wheel", "Use the vehicle's own wheel", "Hand steering (by heurazy)" and its tuning, plus a line saying which wheel the cockpit found and how many draws took the animated copy. "Reset first-person defaults" covers the seat and wheel keys; hand steering keeps its own choice. OPENXR.md documents the seats and a new "Steering wheel and hand steering" section, fixes the stale first-person defaults in the sample configuration, and lists the headset validation still owed. README and THIRD-PARTY-NOTICES credit heurazy's mario-kart-wii-VR-port (GPL-3.0) and the references it credits. --- OPENXR.md | 108 ++++++++++++++++++++++++-- README.md | 6 +- THIRD-PARTY-NOTICES.md | 21 +++++ runtime/src/settings_overlay.cpp | 127 ++++++++++++++++++++++++++++++- 4 files changed, 250 insertions(+), 12 deletions(-) diff --git a/OPENXR.md b/OPENXR.md index 54c24d3..1799f9b 100644 --- a/OPENXR.md +++ b/OPENXR.md @@ -48,16 +48,24 @@ hud_virtual_screen = true stop_at_display_copy = true skip_copy_clears = true first_person = false -first_person_units_per_meter = 30.0 -first_person_head_up_meters = 3.0 +first_person_seat = "cockpit" +cockpit_units_per_meter = 100.0 +first_person_units_per_meter = 50.0 +first_person_head_up_meters = 1.5 first_person_head_forward_meters = 0.0 first_person_head_right_meters = 0.0 first_person_hide_driver = true first_person_hidden_model = 0 first_person_rotation = "yaw" +steering_wheel = true +native_steering_wheel = true +hand_steering = false performance_level = "boost" ``` +The seven `wheel_*` hand-steering tuning keys are described in +[Steering wheel and hand steering](#steering-wheel-and-hand-steering). + To play this installation on the desktop instead, set `enabled = false`, close the game completely, and start it again. These settings are read only at launch. The in-game F10 settings bar also exposes the enable switch, but a restart is still required. @@ -203,6 +211,12 @@ Raw IR camera dots in `KPADGetUnifiedWpadStatus` stay invalid; the game reads th **Settings in the headset.** Left Y opens the settings panel described below; while it is open the controllers operate the panel and the game sees them idle. +**Hand steering.** With `hand_steering` on, in the first-person cockpit, a grip squeezed near the +steering wheel takes hold of it, and while held the wheel steers through the Nunchuk stick's X axis +and that grip stops pressing C; see [Steering wheel and hand +steering](#steering-wheel-and-hand-steering). Turning the wheel moves the controllers, and the game's +own motion detection still reads them, so a sharp enough turn can read as a shake. + `"gamepad"` keeps the controllers one ordinary gamepad read through PAD as a GameCube controller: A/B → South/East, X/Y → West/North, index triggers → trigger axes, grips → shoulders, thumbsticks → sticks (clicks → stick buttons), left menu → Start. Every binding in the F10 controller menu @@ -257,9 +271,23 @@ selection, scrolling and the canvas mapping; `gx_fifo_tests` covers where the pa By default the headset sits where Mario Kart's own chase camera sits, and `world_units_per_meter` of 500 presents the race as a small diorama on a table. Turning on `first_person` moves the camera -to the local driver's head instead, and switches the world scale to -`first_person_units_per_meter`, whose default of 30 is what makes the race read life-size from the -seat. It is a matter of taste rather than a property of the game, so the F10 bar exposes it. +to the local driver's head instead, at one of two seats: + +- `first_person_seat = "cockpit"`, the default, sits you at the driver's own eyes, behind the + steering wheel, at a life-size scale, so the wheel or handlebar is within reach of your hands. + The eye is measured once per race from the character's head bone, while the kart drives straight, + undamaged and at normal size, and then frozen; until then the bind pose, or the vehicle's authored + seat height, stands in. It is kept at least 0.45 m behind the wheel so a long face or a + leaned-forward riding pose cannot put it over the controls. The world scale is + `cockpit_units_per_meter` (default 100) multiplied by the character's eye height over 100 units, + so tall characters sit at a comparable height, and by the player's current size, so a lightning + strike or a mega mushroom resizes the view, the wheel and the grab reach together. The seat + follows the simulation's position and driving direction, never the animated chassis, so damage + spins and tricks do not throw it around. +- `first_person_seat = "custom"` places the head at `first_person_head_up_meters` and its two + companions in the kart's own frame, at `first_person_units_per_meter`. + +Both are a matter of taste rather than properties of the game, so the F10 bar exposes them. The kart is selected through the game's local-screen-to-racer mapping, including online races where your racer is not slot zero. First person requires a locally controlled racer; spectating @@ -280,9 +308,11 @@ All three are the same construction from a forward and an up axis, differing onl they take: pairing a forward with world up is what removes roll. The headset always adds free look on top of whichever is chosen, and only the translation onto the head is common to all three. -The head's place in the kart is `first_person_head_up_meters` and its two companions, measured in -the kart's own frame; the F10 sliders exist because the comfortable value is a matter of taste and -is best judged from inside the headset. +In the cockpit, `"yaw"` takes the kart's own driving direction rather than the chase camera's +lagging heading, from the level seat frame. With the custom seat, the head's place in the kart is +`first_person_head_up_meters` and its two companions, measured in the kart's own frame; the F10 +sliders exist because the comfortable value is a matter of taste and is best judged from inside the +headset. The mode engages only in a single-screen race, the same content that already qualifies for immersive stereo. Menus, split-screen, and the virtual-screen fallback are unaffected, and so is @@ -307,6 +337,65 @@ wide head turn in first person can reveal the edge of what the game decided to d rest of the race instrumentation, the object offsets this reads are specific to the project's supported PAL `RMCP01` translation. +## Steering wheel and hand steering + +Ported from [heurazy's mario-kart-wii-VR-port](https://github.com/heurazy/mario-kart-wii-VR-port) +(GPL-3.0-or-later). It applies to the cockpit seat. + +**The wheel turns.** With `steering_wheel = true` (the default) the kart's steering wheel or the +bike's handlebar turns with your steering: the left stick's deflection at the full-lock angle +(`wheel_kart_degrees` 90, `wheel_bike_degrees` 45), eased so a flicked stick does not snap it round, +or the hands' own angle while they hold it. `native_steering_wheel = true` turns the vehicle's own +model. Karts bake the wheel into the body, so at the race draw boundary the runtime decodes the +body's MDL0 position arrays, turns only the disc around the authored hand grips on a copy, and hands +the copy to the GX thread; Aurora substitutes it into the draws that bind that array with the +player's own model-view matrix (`aurora_set_native_wheel_vertices`), checking each changed vertex's +matrix slot, so an opponent sharing the asset and other joints of the same draw are untouched. The +guest's own vertices are never written, and the copies are dropped after the frame's draws. Bikes +turn their handle part in the game already; its copy is only re-seated on the level cockpit frame so +the bars stay with your hands while the bike banks. If no draw takes the copy for 30 frames the log +says so and the vehicle falls back to a separate VR wheel, which is also what +`native_steering_wheel = false` draws. + +The copy is matched against the race camera's view (`RaceCamera::GetViewMtx` with no dolly offset), +because the scene camera is only set once the draws run. The log reports, once a second, how far +that view is from the scene camera at the seal (`[mkw-vr] cockpit: race camera view vs scene view`) +and how many draws took the copy; the F10 bar shows the same under the steering-wheel settings. + +**Hand steering.** `hand_steering = true` (off by default; also in WheelWizard's OpenXR VR settings) +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 +it. One hand steers by its angle around the hub; two hands steer by the line between them, so leaning +or moving both arms together does not steer, and a hand joining, leaving or crossing the hub keeps +the steering where it was. Turning past full lock is kept, so retracing the gesture returns to the +same centre, while the game's steering saturates at full lock. `wheel_response` scales how quickly +the wheel follows, `wheel_tracking_grace` (seconds) how long a hand that loses tracking keeps hold, +and `wheel_haptics` gives a short pulse on grab and release. + +While the wheel is held it replaces the left stick's X axis, in both the Wii Remote and the gamepad +presentation, and the game keeps its own steering curve. The stick's Y axis still aims items, and a +holding grip no longer reaches the game (it would press C on the Nunchuk or a shoulder on the +gamepad); the triggers, A and the right stick are unchanged. Releasing both grips gives steering back +to the stick. The settings panel withholds the wheel like any other input. + +**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 +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 +(`aurora-main/lib/gfx/cockpit.hpp`). The anchor also carries the frame's exact world scale +(`aurora_set_stereo_scene_anchor_scaled`), and Aurora rescales each eye's head translation to it, so +a scale change between the XR packet and the frame cannot misplace the hands. + +The guest offsets involved (driver, movement, damage, grip frames, bike handle, driver bones and +their world matrices) are PAL `RMCP01` constants listed with the leaf getter or constructor that +proves each in `runtime/src/vr/mkw_vr_first_person.cpp`. `mkw_steering_wheel_tests`, +`mkw_vr_cockpit_tests` and `mkw_vr_hand_steering_tests` cover the grab model, the seat and wheel +geometry and the hand-off to the game; `gx_fifo_tests` covers the per-draw substitution and the +overlay geometry, and `cockpit_gpu_smoke` its depth test on a real GPU. + ## Presentation policy The runtime deliberately fails safe instead of guessing which Mario Kart camera is active: @@ -614,6 +703,9 @@ 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 headset settings panel is drawn into the eye images rather than submitted as its own quad layer, so its text is resampled once more than a compositor layer's would be. It has no laser beam, only the cursor on the panel itself, and text fields cannot be typed into without a keyboard. diff --git a/README.md b/README.md index a42fbfd..4c72da7 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,9 @@ Windows builds can render through an OpenXR runtime on D3D12, or on Vulkan with build, without CPU readback. Menus and unsupported scenes appear as a head-locked virtual screen; a validated single-camera race switches to immersive stereo rendering. VR is opt-in and falls back to the normal desktop renderer if the -runtime or headset is unavailable. See [`OPENXR.md`](OPENXR.md) for setup, configuration, and the +runtime or headset is unavailable. In first person you sit in the cockpit, where the steering wheel +or handlebar turns with your steering, and optional hand steering by heurazy lets you grab it with +the tracked controllers and turn it. See [`OPENXR.md`](OPENXR.md) for setup, configuration, and the current limitations. **Music ducking.** @@ -234,6 +236,8 @@ All translated output is verified against real hardware behavior and most import aurora's Direct3D, Vulkan and OpenGL backends. - **[OpenXR](https://www.khronos.org/openxr/)** - the Khronos cross-platform API used by the experimental VR renderer. +- **heurazy** - the VR cockpit's turning steering wheel and hand steering, ported from + **[mario-kart-wii-VR-port](https://github.com/heurazy/mario-kart-wii-VR-port)** (GPL-3.0). - **[Dolphin Emulator](https://github.com/dolphin-emu/dolphin)** - an invaluable reference for Wii hardware behavior during development, plus the source of the free DSP coefficient ROM and the unmodified default WiiConnect24 bootstrap tree bundled with the runtime. diff --git a/THIRD-PARTY-NOTICES.md b/THIRD-PARTY-NOTICES.md index d606612..73838f7 100644 --- a/THIRD-PARTY-NOTICES.md +++ b/THIRD-PARTY-NOTICES.md @@ -71,6 +71,21 @@ The runtime's Riivolution patch handling is a port of Dolphin's (both marked `SPDX-License-Identifier: GPL-2.0-or-later`). Source: +### heurazy's mario-kart-wii-VR-port - GPL-3.0-or-later + +- Source: +- Author: heurazy +- License: GNU General Public License v3.0 or later, the same license as WiiCompiled. + +The VR cockpit's steering wheel and hand steering are ported from this project: the grab-and-turn +model (`runtime/include/vr/steering_wheel.h`), the native wheel vertex rotation +(`runtime/include/vr/native_wheel_mesh.h`), the level seat (`runtime/include/vr/cockpit_stabilizer.h`), +the runtime hand-mesh loader (`runtime/include/vr/openxr_hand_mesh.h`), the per-draw substitution +(`aurora-main/include/aurora/native_wheel_match.hpp`, `aurora-main/lib/gx/native_wheel.hpp`), the +cockpit overlay renderer (`aurora-main/lib/gfx/cockpit.hpp`), their tests, and the seat, eye and +wheel geometry and guest reads in `runtime/src/vr/mkw_vr_first_person.cpp` and +`runtime/include/vr/mkw_vr_first_person.h`. The files carry that attribution in their headers. + ### pugixml - MIT Copyright (c) 2006-2025 Arseny Kapoulkine. @@ -241,6 +256,12 @@ Not code, but the documentation this project depends on: - [Retro Rewind](https://wiki.tockdom.com/wiki/Retro_Rewind) by ZPL - the mod distribution this project can build as a static profile. No Retro Rewind content is redistributed here; users supply their own copy. +- The references heurazy's mario-kart-wii-VR-port credits for the cockpit, none of whose source is + compiled into this repository: + [AnimalCrossing-VR-MR-Standalone](https://github.com/heurazy/AnimalCrossing-VR-MR-Standalone) + (OpenXR hand meshes), [Cyberpunk VR port](https://github.com/dariulone/cyberpunk-vr-port) + (squeeze-to-grab steering) and [Pulsar](https://github.com/MelgMKW/Pulsar) (Mario Kart Wii class + layouts). --- diff --git a/runtime/src/settings_overlay.cpp b/runtime/src/settings_overlay.cpp index 3c9b4d4..ad695f9 100644 --- a/runtime/src/settings_overlay.cpp +++ b/runtime/src/settings_overlay.cpp @@ -3,6 +3,7 @@ #include "aurora_events.h" #include "controller_button_names.h" #include "controller_mapping_wizard.h" +#include "gx_native_wheel.h" #include "input_bindings.h" #include "game_graphics_options.h" #include "log_export.h" @@ -139,6 +140,14 @@ bool g_vrSkipCopyClears = RuntimeConfigFile::VrSkipCopyClears(true); bool g_vrHudVirtualScreen = RuntimeConfigFile::VrHudVirtualScreen(true); bool g_vrFirstPerson = RuntimeConfigFile::VrFirstPerson(false); float g_vrFirstPersonUnitsPerMeter = RuntimeConfigFile::VrFirstPersonUnitsPerMeter(); +// 0 = cockpit, 1 = custom, matching kVrFirstPersonSeatNames. +constexpr std::array kVrFirstPersonSeatNames{"cockpit", "custom"}; +int g_vrFirstPersonSeat = RuntimeConfigFile::VrFirstPersonSeat() == "custom" ? 1 : 0; +float g_vrCockpitUnitsPerMeter = RuntimeConfigFile::VrCockpitUnitsPerMeter(); +bool g_vrSteeringWheel = RuntimeConfigFile::VrSteeringWheel(); +bool g_vrNativeSteeringWheel = RuntimeConfigFile::VrNativeSteeringWheel(); +bool g_vrHandSteering = RuntimeConfigFile::VrHandSteering(); +mkw::vr::WheelTuning g_vrWheelTuning = RuntimeConfigFile::VrWheelTuning(); float g_vrFirstPersonHeadUp = RuntimeConfigFile::VrFirstPersonHeadUpMeters(); float g_vrFirstPersonHeadForward = RuntimeConfigFile::VrFirstPersonHeadForwardMeters(); float g_vrFirstPersonHeadRight = RuntimeConfigFile::VrFirstPersonHeadRightMeters(); @@ -1113,6 +1122,82 @@ void ApplyVrHudVirtualScreen() { RuntimeConfigFile::VrHudDistanceMeters(2.0f) * unitsPerMeter); } +// The cockpit's steering wheel and hand steering, under the first-person camera. +void DrawVrSteeringWheelSettings() { + ImGui::Separator(); + ImGui::Text("Steering wheel"); + ImGui::BeginDisabled(g_vrFirstPersonSeat != 0); + if (ImGui::Checkbox("Turn the steering wheel", &g_vrSteeringWheel)) { + RuntimeConfigFile::SetVrSteeringWheel(g_vrSteeringWheel); + mkw::vr::MkwVRFirstPersonApplyConfiguredSettings(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("In the cockpit, the kart's steering wheel or the bike's handlebar turns " + "with your steering."); + } + ImGui::BeginDisabled(!g_vrSteeringWheel); + if (ImGui::Checkbox("Use the vehicle's own wheel", &g_vrNativeSteeringWheel)) { + RuntimeConfigFile::SetVrNativeSteeringWheel(g_vrNativeSteeringWheel); + mkw::vr::MkwVRFirstPersonApplyConfiguredSettings(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Turns the wheel or handlebar of the vehicle's own model. Off draws a " + "separate VR wheel instead, which is also what appears when a vehicle's " + "own wheel cannot be animated."); + } + ImGui::EndDisabled(); + if (ImGui::Checkbox("Hand steering (by heurazy)", &g_vrHandSteering)) { + RuntimeConfigFile::SetVrHandSteering(g_vrHandSteering); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Squeeze a grip near the wheel or handlebar to take hold of it, and turn " + "it to steer, with one hand or both. Releasing both grips gives steering " + "back to the stick, which still aims items. The runtime's hand mesh is " + "used when hand steering was on at launch."); + } + if (g_vrHandSteering && ImGui::TreeNode("Hand steering tuning")) { + bool changed = false; + changed |= ImGui::SliderFloat("Kart full lock (degrees)", &g_vrWheelTuning.kartDegrees, + RuntimeConfigFile::kVrWheelDegreesMin, + RuntimeConfigFile::kVrWheelDegreesMax, "%.0f"); + changed |= ImGui::SliderFloat("Bike full lock (degrees)", &g_vrWheelTuning.bikeDegrees, + RuntimeConfigFile::kVrWheelDegreesMin, + RuntimeConfigFile::kVrWheelDegreesMax, "%.0f"); + changed |= ImGui::SliderFloat("Grab reach (m)", &g_vrWheelTuning.grabDistance, + RuntimeConfigFile::kVrWheelGrabDistanceMin, + RuntimeConfigFile::kVrWheelGrabDistanceMax, "%.2f"); + changed |= ImGui::SliderFloat("Grab assist", &g_vrWheelTuning.grabAssist, + RuntimeConfigFile::kVrWheelGrabAssistMin, + RuntimeConfigFile::kVrWheelGrabAssistMax, "%.2f"); + changed |= ImGui::SliderFloat("Response", &g_vrWheelTuning.response, + RuntimeConfigFile::kVrWheelResponseMin, + RuntimeConfigFile::kVrWheelResponseMax, "%.2f"); + changed |= ImGui::SliderFloat("Tracking-loss grace (s)", &g_vrWheelTuning.trackingGrace, + RuntimeConfigFile::kVrWheelTrackingGraceMin, + RuntimeConfigFile::kVrWheelTrackingGraceMax, "%.2f"); + changed |= ImGui::Checkbox("Grab and release pulse", &g_vrWheelTuning.haptics); + if (ImGui::Button("Reset hand steering tuning")) { + g_vrWheelTuning = {}; + changed = true; + } + if (changed) { + RuntimeConfigFile::SetVrWheelTuning(g_vrWheelTuning); + } + ImGui::TreePop(); + } + ImGui::EndDisabled(); + // What the cockpit found, for a report when the wheel does not behave. + const auto anchor = mkw::vr::MkwVRFirstPersonGetAnchor(); + if (anchor.valid && anchor.cockpit) { + ImGui::TextDisabled("Cockpit: %s, %s, %.0f units/m, animated draws %u", + anchor.bike ? "handlebar" : "wheel", + !anchor.native_wheel.valid ? "grips not found" + : anchor.native_mesh_prepared ? "vehicle's own" + : "VR wheel", + anchor.units_per_meter, GxNativeWheel::LastDrawCount()); + } +} + void DrawGraphicsSettings() { g_displayMode = static_cast(aurora_get_display_mode()); struct EffectFlag { @@ -1391,11 +1476,37 @@ void DrawVrSettings() { ImGui::SetTooltip( "Moves the camera to the Player 1 driver's head and keeps the horizon level, " "instead of riding behind the kart. Applies during a single-screen race; menus " - "and split-screen are unaffected. The world scale below replaces " - "world_units_per_meter while it is engaged."); + "and split-screen are unaffected."); + } + constexpr std::array kSeatLabels{"Cockpit", "Custom"}; + if (ImGui::Combo("Seat", &g_vrFirstPersonSeat, kSeatLabels.data(), static_cast(kSeatLabels.size()))) { + RuntimeConfigFile::SetVrFirstPersonSeat(kVrFirstPersonSeatNames[static_cast(g_vrFirstPersonSeat)]); + mkw::vr::MkwVRFirstPersonApplyConfiguredSettings(); + ApplyVrHudVirtualScreen(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip( + "Cockpit sits you at the driver's own eyes, behind the steering wheel, at a " + "life-size scale that allows for the character's height, so the wheel is within " + "reach. Custom places the head by the world scale and offsets below instead."); + } + if (g_vrFirstPersonSeat == 0) { + if (ImGui::SliderFloat("Cockpit scale (units per metre)", &g_vrCockpitUnitsPerMeter, + RuntimeConfigFile::kVrCockpitUnitsPerMeterMin, + RuntimeConfigFile::kVrCockpitUnitsPerMeterMax, "%.0f")) { + RuntimeConfigFile::SetVrCockpitUnitsPerMeter(g_vrCockpitUnitsPerMeter); + mkw::vr::MkwVRFirstPersonApplyConfiguredSettings(); + ApplyVrHudVirtualScreen(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip( + "100 reads life-size for an average-height driver; taller characters raise it " + "further on their own. Raising it shrinks the world around you."); + } } // These are the tuning loop for the anchor: the right head height is a // per-taste value that can only really be judged from inside the headset. + ImGui::BeginDisabled(g_vrFirstPersonSeat == 0); if (ImGui::SliderFloat("World units per metre (first person)", &g_vrFirstPersonUnitsPerMeter, 1.0f, 200.0f, "%.1f")) { RuntimeConfigFile::SetVrFirstPersonUnitsPerMeter(g_vrFirstPersonUnitsPerMeter); @@ -1422,8 +1533,9 @@ void DrawVrSettings() { mkw::vr::MkwVRFirstPersonApplyConfiguredSettings(); } ImGui::PushTextWrapPos(ImGui::GetCursorPosX() + Scaled(380.0f)); - ImGui::TextDisabled("Where the head sits in the kart's own frame."); + ImGui::TextDisabled("Custom seat: where the head sits in the kart's own frame."); ImGui::PopTextWrapPos(); + ImGui::EndDisabled(); constexpr std::array kRotationLabels{"Yaw only", "Yaw + Pitch", "Full rotation"}; if (ImGui::Combo("View rotation", &g_vrFirstPersonRotation, kRotationLabels.data(), static_cast(kRotationLabels.size()))) { @@ -1466,8 +1578,17 @@ void DrawVrSettings() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Removes the vehicle as well, leaving nothing of your own kart."); } + DrawVrSteeringWheelSettings(); ImGui::Separator(); if (ImGui::Button("Reset first-person defaults")) { + g_vrFirstPersonSeat = 0; + g_vrCockpitUnitsPerMeter = RuntimeConfigFile::kVrCockpitUnitsPerMeterDefault; + g_vrSteeringWheel = RuntimeConfigFile::kVrSteeringWheelDefault; + g_vrNativeSteeringWheel = RuntimeConfigFile::kVrNativeSteeringWheelDefault; + RuntimeConfigFile::SetVrFirstPersonSeat(RuntimeConfigFile::kVrFirstPersonSeatDefault); + RuntimeConfigFile::SetVrCockpitUnitsPerMeter(g_vrCockpitUnitsPerMeter); + RuntimeConfigFile::SetVrSteeringWheel(g_vrSteeringWheel); + RuntimeConfigFile::SetVrNativeSteeringWheel(g_vrNativeSteeringWheel); g_vrFirstPersonUnitsPerMeter = RuntimeConfigFile::kVrFirstPersonUnitsPerMeterDefault; g_vrFirstPersonHeadUp = RuntimeConfigFile::kVrFirstPersonHeadUpDefault; g_vrFirstPersonHeadForward = RuntimeConfigFile::kVrFirstPersonHeadForwardDefault;