mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 05:00:27 +02:00
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.
186 lines
8.4 KiB
C++
186 lines
8.4 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
#pragma once
|
|
|
|
#if defined(MKW_ENABLE_OPENXR)
|
|
|
|
#include "vr/openxr_driving.h"
|
|
#include "vr/openxr_runtime.h"
|
|
#include "vr/openxr_settings_panel.h"
|
|
#include "vr/openxr_wii_remote.h"
|
|
#include "vr/steering_wheel.h"
|
|
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <string>
|
|
|
|
namespace mkw::vr {
|
|
|
|
// The rectangle the game's picture occupies on whichever virtual screen is
|
|
// showing it, in the application reference space: the target the Wii Remote
|
|
// pointer is aimed at. The pose faces +Z with +X right and +Y up across the
|
|
// picture. Invalid when no screen can be pointed at (for instance a race with
|
|
// its 2D layer left stretched across the eyes).
|
|
struct OpenXRPointerScreen {
|
|
bool valid = false;
|
|
XrPosef pose{{0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f}};
|
|
float half_width_meters = 0.0f;
|
|
float half_height_meters = 0.0f;
|
|
};
|
|
|
|
// OpenXR action-based controller input.
|
|
//
|
|
// Quest Touch controllers are not visible to SDL's joystick layer (the OS does
|
|
// not expose them as HID gamepads), so a standalone headset build would have no
|
|
// input at all. This module syncs an OpenXR action set on the pacing thread and
|
|
// feeds a virtual SDL joystick (SDL_AttachVirtualJoystick) that Aurora's
|
|
// existing controller code opens and assigns to a port exactly like a physical
|
|
// pad. What the game then sees on that port depends on OpenXRControllerMode:
|
|
//
|
|
// Wii Remote (default): the port is served through KPAD as a Wii Remote with a
|
|
// Nunchuk, like DolphinXR's OpenXR Wii Remote. Every XR frame the aim and grip
|
|
// poses are located at the measured current time, turned into both
|
|
// accelerometers and the IR pointer (the right aim ray against the virtual
|
|
// screen the renderer is showing), and published through openxr_wii_remote.h.
|
|
// Buttons are adapted from DolphinXR's "OpenXR Wii Remote" profile (see
|
|
// RemoteButtons).
|
|
// The game's rumble drives both controllers' haptics.
|
|
//
|
|
// Gamepad: the virtual joystick is read through PAD as a GameCube controller,
|
|
// and every binding the settings overlay offers applies:
|
|
// right A / B -> gamepad South / East (GameCube A / B)
|
|
// left X / Y -> gamepad West / North (GameCube X / Y)
|
|
// index triggers -> left / right trigger axes
|
|
// grip squeezes -> left / right shoulder buttons
|
|
// left / right thumbstick-> left / right stick axes, clicks -> stick buttons
|
|
// left menu -> Start
|
|
//
|
|
// Both are bound for the Oculus Touch profile; khr/simple_controller gets
|
|
// select/menu and the poses so an unknown runtime still offers something.
|
|
//
|
|
// In the first-person cockpit (openxr_driving.h) the grip poses are located in
|
|
// the seated frame every frame. With hand steering on, a squeezed grip near the
|
|
// steering wheel or handlebar takes hold of it; while held, the wheel replaces
|
|
// the left stick's X axis in both presentations and that grip no longer reaches
|
|
// the game (C on the Nunchuk, a shoulder on the gamepad).
|
|
//
|
|
// Left Y (both thumbsticks clicked together as a gamepad) opens the in-headset
|
|
// settings panel (openxr_settings_panel.h). While it is open, and until every
|
|
// button has been released after it closes, the game sees idle controllers: the
|
|
// panel button, the pointer and the triggers belong to the panel.
|
|
//
|
|
// Lifetime: Create after the session exists (attaches the action set, which
|
|
// OpenXR permits once per session), Sync once per xrWaitFrame, Idle while the
|
|
// session is not running, Destroy before the session is destroyed. All of them
|
|
// run on the XR pacing thread; SDL's virtual joystick setters and the Wii
|
|
// Remote bridge are internally locked, so the game thread may read concurrently.
|
|
class OpenXRInput final {
|
|
public:
|
|
explicit OpenXRInput(OpenXRLogCallback logger = {});
|
|
~OpenXRInput();
|
|
|
|
OpenXRInput(const OpenXRInput&) = delete;
|
|
OpenXRInput& operator=(const OpenXRInput&) = delete;
|
|
|
|
// Creates the action set/actions/spaces and attaches them to the session.
|
|
// Returns false (with LastError set) if the runtime rejects the action set;
|
|
// the caller continues without controller input rather than failing VR.
|
|
bool Create(OpenXRRuntime& runtime);
|
|
void Destroy();
|
|
|
|
// xrSyncActions + state reads, then publishes to the virtual gamepad and
|
|
// the Wii Remote bridge. predicted_display_time is the frame's XrTime;
|
|
// screen is where the Wii Remote pointer can land this frame, and
|
|
// settings_panel where the settings panel is (its whole rectangle). seat is
|
|
// the immersive seated frame, invalid outside an immersive race.
|
|
void Sync(XrTime predicted_display_time, const OpenXRPointerScreen& screen,
|
|
const OpenXRPointerScreen& settings_panel, const driving::SeatFrame& seat);
|
|
|
|
// The cockpit state the last Sync published (also OpenXRReadDriving()).
|
|
const DrivingSnapshot& Driving() const noexcept { return m_driving; }
|
|
|
|
// Publishes a remote with nothing held, at rest and not pointing, and stops
|
|
// the haptics, for frames without focused input.
|
|
void Idle();
|
|
|
|
// Rumble for the given hand (0 = left, 1 = right); amplitude 0..1.
|
|
void ApplyHaptic(uint32_t hand, float amplitude, XrDuration duration);
|
|
|
|
bool IsCreated() const noexcept { return m_created; }
|
|
bool HasVirtualGamepad() const noexcept { return m_joystick_id != 0; }
|
|
const std::string& LastError() const noexcept { return m_last_error; }
|
|
|
|
private:
|
|
static constexpr uint32_t kHands = 2;
|
|
|
|
bool CreateActions();
|
|
bool SuggestBindings();
|
|
void CreatePoseSpaces();
|
|
void DestroyPoseSpaces();
|
|
void LoadInputClock();
|
|
XrTime InputSampleTime(XrTime predicted_display_time) const;
|
|
bool AttachVirtualGamepad();
|
|
void DetachVirtualGamepad();
|
|
// `withheld` publishes a remote at rest with nothing held and no pointer,
|
|
// while still tracking motion so releasing it does not read as a jolt.
|
|
void PublishWiiRemote(XrTime input_time, const OpenXRPointerScreen& screen,
|
|
const std::array<wii_remote::HandInputs, kHands>& hands, uint32_t injected_buttons,
|
|
bool withheld);
|
|
void PublishSettingsPanel(XrTime input_time, const OpenXRPointerScreen& panel,
|
|
const settings_panel::Frame& frame);
|
|
// Hand steering: locates the grips in the seated frame, runs the wheel and
|
|
// hands its steering to `hands` before the game sees them.
|
|
void UpdateDriving(XrTime display_time, const driving::SeatFrame& seat,
|
|
std::array<wii_remote::HandInputs, kHands>& hands, bool withheld);
|
|
void ResetDriving();
|
|
void UpdateRumble();
|
|
void StopRumble();
|
|
bool Check(XrResult result, const char* operation);
|
|
void Log(OpenXRLogLevel level, const std::string& message) const noexcept;
|
|
|
|
OpenXRLogCallback m_logger;
|
|
OpenXRRuntime* m_runtime = nullptr;
|
|
XrActionSet m_action_set = XR_NULL_HANDLE;
|
|
XrAction m_thumbstick = XR_NULL_HANDLE;
|
|
XrAction m_thumbstick_click = XR_NULL_HANDLE;
|
|
XrAction m_trigger = XR_NULL_HANDLE;
|
|
XrAction m_squeeze = XR_NULL_HANDLE;
|
|
XrAction m_button_primary = XR_NULL_HANDLE; // A / X
|
|
XrAction m_button_secondary = XR_NULL_HANDLE; // B / Y
|
|
XrAction m_menu = XR_NULL_HANDLE;
|
|
XrAction m_aim_pose = XR_NULL_HANDLE;
|
|
XrAction m_grip_pose = XR_NULL_HANDLE;
|
|
XrAction m_haptic = XR_NULL_HANDLE;
|
|
XrPath m_hand_paths[kHands]{};
|
|
XrSpace m_aim_spaces[kHands]{};
|
|
XrSpace m_grip_spaces[kHands]{};
|
|
// xrConvertWin32PerformanceCounterToTimeKHR / xrConvertTimespecTimeToTimeKHR,
|
|
// when the runtime offers them; the input time falls back to display time.
|
|
PFN_xrVoidFunction m_convert_now_to_xr_time = nullptr;
|
|
wii_remote::MotionTracker m_motion[kHands];
|
|
wii_remote::PointerFilter m_pointer;
|
|
settings_panel::Controls m_panel_controls;
|
|
XrTime m_last_input_time = 0;
|
|
bool m_panel_select_held = false;
|
|
std::array<float, 2> m_horizon{1.0f, 0.0f};
|
|
bool m_haptics_active[kHands]{};
|
|
uint32_t m_joystick_id = 0; // SDL_JoystickID; 0 when detached
|
|
void* m_joystick = nullptr; // SDL_Joystick*
|
|
SteeringWheel m_wheel;
|
|
WheelReferenceLatch m_wheel_reference;
|
|
driving::WheelVisual m_wheel_visual;
|
|
std::array<bool, kHands> m_wheel_held{};
|
|
XrTime m_wheel_time = 0;
|
|
bool m_wheel_uses_geometry = false;
|
|
bool m_wheel_bike = false;
|
|
DrivingSnapshot m_driving{};
|
|
bool m_created = false;
|
|
bool m_logged_sync_failure = false;
|
|
bool m_logged_pointer = false;
|
|
std::string m_last_error;
|
|
};
|
|
|
|
} // namespace mkw::vr
|
|
|
|
#endif // defined(MKW_ENABLE_OPENXR)
|