mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 09:00:28 +02:00
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.
125 lines
4.3 KiB
C++
125 lines
4.3 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
//
|
|
// Hand steering's hand-off to the game and the wheel's displayed angle, tested
|
|
// without a headset (vr/openxr_driving.h).
|
|
|
|
#include "vr/openxr_driving.h"
|
|
|
|
#include <cmath>
|
|
#include <iostream>
|
|
|
|
namespace {
|
|
|
|
using namespace mkw::vr;
|
|
|
|
int g_failures = 0;
|
|
|
|
void Check(bool condition, const char* what) {
|
|
if (!condition) {
|
|
++g_failures;
|
|
std::cerr << "FAILED: " << what << '\n';
|
|
}
|
|
}
|
|
|
|
void CheckNear(float actual, float expected, const char* what, float tolerance = 1.0e-3f) {
|
|
if (!(std::fabs(actual - expected) <= tolerance)) {
|
|
++g_failures;
|
|
std::cerr << "FAILED: " << what << " (expected " << expected << ", got " << actual << ")\n";
|
|
}
|
|
}
|
|
|
|
std::array<wii_remote::HandInputs, 2> Hands() {
|
|
std::array<wii_remote::HandInputs, 2> hands{};
|
|
hands[0].stick_x = -0.3f;
|
|
hands[0].stick_y = 0.8f;
|
|
hands[0].squeeze = 1.0f;
|
|
hands[1].squeeze = 1.0f;
|
|
return hands;
|
|
}
|
|
|
|
void TestHandOff() {
|
|
WheelState wheel{};
|
|
wheel.steering = 0.6f;
|
|
auto hands = Hands();
|
|
driving::ApplyHandSteering(hands, wheel);
|
|
CheckNear(hands[0].stick_x, -0.3f, "an unheld wheel leaves the stick alone");
|
|
CheckNear(hands[0].squeeze, 1.0f, "an unheld wheel leaves the grips alone");
|
|
|
|
wheel.held = {false, true};
|
|
hands = Hands();
|
|
driving::ApplyHandSteering(hands, wheel);
|
|
CheckNear(hands[0].stick_x, 0.6f, "a held wheel steers through the left stick");
|
|
CheckNear(hands[0].stick_y, 0.8f, "the stick keeps aiming items");
|
|
CheckNear(hands[0].squeeze, 1.0f, "a free hand's grip still reaches the game");
|
|
CheckNear(hands[1].squeeze, 0.0f, "a holding grip does not press C or a shoulder");
|
|
|
|
wheel.held = {true, true};
|
|
wheel.steering = 3.0f;
|
|
hands = Hands();
|
|
driving::ApplyHandSteering(hands, wheel);
|
|
CheckNear(hands[0].stick_x, 1.0f, "steering is clamped to full lock");
|
|
CheckNear(hands[0].squeeze, 0.0f, "both holding grips are released for the game");
|
|
|
|
wheel.steering = std::nanf("");
|
|
hands = Hands();
|
|
driving::ApplyHandSteering(hands, wheel);
|
|
CheckNear(hands[0].stick_x, -0.3f, "a non-finite wheel never reaches the game");
|
|
}
|
|
|
|
void TestMaxAngle() {
|
|
WheelTuning tuning{};
|
|
CheckNear(driving::MaxWheelAngle(false, tuning), 90.0f * 0.01745329252f, "kart full lock");
|
|
CheckNear(driving::MaxWheelAngle(true, tuning), 45.0f * 0.01745329252f, "bike full lock");
|
|
tuning.kartDegrees = 1000.0f;
|
|
CheckNear(driving::MaxWheelAngle(false, tuning), 180.0f * 0.01745329252f, "full lock is capped");
|
|
}
|
|
|
|
void TestVisual() {
|
|
driving::WheelVisual visual;
|
|
const float max = driving::MaxWheelAngle(false, WheelTuning{});
|
|
float angle = 0.0f;
|
|
for (int i = 0; i < 90; ++i) {
|
|
angle = visual.Update(false, 0.0f, 1.0f, max, 1.0f / 90.0f);
|
|
}
|
|
CheckNear(angle, max, "the wheel follows full right stick to full lock", 1e-3f);
|
|
angle = visual.Update(false, 0.0f, -1.0f, max, 1.0f / 90.0f);
|
|
Check(angle > 0.0f && angle < max, "a flicked stick eases the wheel rather than snapping it");
|
|
angle = visual.Update(true, -2.5f, 1.0f, max, 1.0f / 90.0f);
|
|
CheckNear(angle, -2.5f, "a held wheel shows the hands' angle exactly");
|
|
}
|
|
|
|
void TestSeatFrame() {
|
|
driving::SeatFrame seat;
|
|
seat.valid = true;
|
|
seat.base = {1.0f, 1.5f, -0.5f};
|
|
auto m = driving::SeatFromApp(seat, {1.2f, 1.2f, -0.9f}, {0, 0, 0, 1});
|
|
CheckNear(m[3], 0.2f, "seat x is relative to the head");
|
|
CheckNear(m[7], -0.3f, "seat y is relative to the head");
|
|
CheckNear(m[11], -0.4f, "seat z is relative to the head");
|
|
CheckNear(m[0], 1.0f, "unrotated grip keeps its axes");
|
|
|
|
// Leaning back by 90 degrees: the eye transforms place seat point P at
|
|
// base + R_lean * P, so an app-space point straight up from the head is
|
|
// straight ahead (-Z) in the seat.
|
|
seat.lean_back_radians = 1.5707963f;
|
|
m = driving::SeatFromApp(seat, {1.0f, 2.5f, -0.5f}, {0, 0, 0, 1});
|
|
CheckNear(m[3], 0.0f, "lean keeps x");
|
|
CheckNear(m[7], 0.0f, "lean moves up out of y", 1e-5f);
|
|
CheckNear(m[11], -1.0f, "lean turns up into forward", 1e-5f);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main() {
|
|
TestHandOff();
|
|
TestMaxAngle();
|
|
TestVisual();
|
|
TestSeatFrame();
|
|
if (g_failures != 0) {
|
|
std::cerr << g_failures << " check(s) failed\n";
|
|
return 1;
|
|
}
|
|
std::cout << "vr hand steering tests passed\n";
|
|
return 0;
|
|
}
|