mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 02:00:14 +02:00
Add support for "none" controller mode: unplug virtual joystick and allow alternative controllers
This commit is contained in:
1 parent
171702bef4
commit
c536f0087d
12 files changed
+212
-120
No files matched your search
@@ -246,8 +246,7 @@ Touch controllers. The port is served through KPAD like a Bluetooth remote
|
|||||||
| Right trigger | B |
|
| Right trigger | B |
|
||||||
| Right B | C (look behind) |
|
| Right B | C (look behind) |
|
||||||
| Right stick up / down | 1 / 2 |
|
| Right stick up / down | 1 / 2 |
|
||||||
| Left X | − |
|
| Left X or left menu | + |
|
||||||
| Left menu | + |
|
|
||||||
| Left stick | Nunchuk stick |
|
| Left stick | Nunchuk stick |
|
||||||
| Left trigger | Z |
|
| Left trigger | Z |
|
||||||
| Left Y | Settings panel (not a Wii button) |
|
| Left Y | Settings panel (not a Wii button) |
|
||||||
@@ -257,7 +256,9 @@ Touch controllers. The port is served through KPAD like a Bluetooth remote
|
|||||||
| Left controller motion | Nunchuk accelerometer |
|
| Left controller motion | Nunchuk accelerometer |
|
||||||
|
|
||||||
Analog inputs count as pressed past half travel. The grips, right stick left / right and the left
|
Analog inputs count as pressed past half travel. The grips, right stick left / right and the left
|
||||||
stick click press no Wii button, and nothing presses HOME. C sits on right B rather than a grip
|
stick click press no Wii button, and nothing presses − or HOME: Mario Kart Wii never reads −. Left
|
||||||
|
X presses + as well as the left menu because the PlayStation VR2's controllers give no usable left
|
||||||
|
menu, so pause would otherwise be out of reach there. C sits on right B rather than a grip
|
||||||
because hand steering holds a grip down for a whole corner, and C is the game's look-behind. The
|
because hand steering holds a grip down for a whole corner, and C is the game's look-behind. The
|
||||||
game's Wii Remote rumble vibrates both controllers, subject to the ordinary controller-vibration
|
game's Wii Remote rumble vibrates both controllers, subject to the ordinary controller-vibration
|
||||||
switch.
|
switch.
|
||||||
@@ -312,6 +313,15 @@ A/B → South/East, X/Y → West/North, index triggers → trigger axes, grips
|
|||||||
applies. Left Y is GameCube Y here, so clicking both thumbsticks together opens the settings panel
|
applies. Left Y is GameCube Y here, so clicking both thumbsticks together opens the settings panel
|
||||||
instead. The right thumbstick click on its own still toggles the first-person camera.
|
instead. The right thumbstick click on its own still toggles the first-person camera.
|
||||||
|
|
||||||
|
`"none"` makes the controllers nothing to the game, for playing with another controller (a desktop
|
||||||
|
gamepad, a USB wheel or a Bluetooth Wii Remote). The virtual gamepad is unplugged, so it takes no
|
||||||
|
port and the other controllers keep theirs; the game sees a switch to or from `"none"` as that
|
||||||
|
controller disconnecting or connecting. The plugging follows the mode on the game thread, where the
|
||||||
|
gamepad is already written, so the pacing thread never waits on SDL's joystick lock for it. The
|
||||||
|
controllers still open the settings panel with left Y, point at it and toggle the first-person camera
|
||||||
|
with a right thumbstick click; they press no game button, drive no Wii Remote, cannot take hold of
|
||||||
|
the cockpit's wheel, and the game's rumble does not reach them.
|
||||||
|
|
||||||
Bindings are suggested for `oculus/touch_controller` (Quest 2, 3 and Pro) and
|
Bindings are suggested for `oculus/touch_controller` (Quest 2, 3 and Pro) and
|
||||||
`khr/simple_controller`. `mkw_vr_wii_remote_tests` checks the accelerometer frame, the pointer
|
`khr/simple_controller`. `mkw_vr_wii_remote_tests` checks the accelerometer frame, the pointer
|
||||||
raycast and debounce, the picture placement and the button profile without a headset.
|
raycast and debounce, the picture placement and the button profile without a headset.
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ class SettingsPage(
|
|||||||
section(R.string.section_controls) {
|
section(R.string.section_controls) {
|
||||||
choice(
|
choice(
|
||||||
R.string.controls_mode, R.string.controls_mode_helper,
|
R.string.controls_mode, R.string.controls_mode_helper,
|
||||||
listOf(R.string.controls_mode_wii_remote, R.string.controls_mode_gamepad),
|
listOf(R.string.controls_mode_wii_remote, R.string.controls_mode_gamepad, R.string.controls_mode_none),
|
||||||
read = { stringIndex(it, "vr", "controller_mode", CONTROLLER_MODES) },
|
read = { stringIndex(it, "vr", "controller_mode", CONTROLLER_MODES) },
|
||||||
write = { c, index -> c.setString("vr", "controller_mode", CONTROLLER_MODES[index]) },
|
write = { c, index -> c.setString("vr", "controller_mode", CONTROLLER_MODES[index]) },
|
||||||
)
|
)
|
||||||
@@ -316,7 +316,7 @@ class SettingsPage(
|
|||||||
info(R.string.controls_map_a, activity.getString(R.string.controls_map_a_value))
|
info(R.string.controls_map_a, activity.getString(R.string.controls_map_a_value))
|
||||||
info(R.string.controls_map_b, activity.getString(R.string.controls_map_b_value))
|
info(R.string.controls_map_b, activity.getString(R.string.controls_map_b_value))
|
||||||
info(R.string.controls_map_12, activity.getString(R.string.controls_map_12_value))
|
info(R.string.controls_map_12, activity.getString(R.string.controls_map_12_value))
|
||||||
info(R.string.controls_map_minus_plus, activity.getString(R.string.controls_map_minus_plus_value))
|
info(R.string.controls_map_plus, activity.getString(R.string.controls_map_plus_value))
|
||||||
info(R.string.controls_map_stick, activity.getString(R.string.controls_map_stick_value))
|
info(R.string.controls_map_stick, activity.getString(R.string.controls_map_stick_value))
|
||||||
info(R.string.controls_map_z, activity.getString(R.string.controls_map_z_value))
|
info(R.string.controls_map_z, activity.getString(R.string.controls_map_z_value))
|
||||||
info(R.string.controls_map_c, activity.getString(R.string.controls_map_c_value))
|
info(R.string.controls_map_c, activity.getString(R.string.controls_map_c_value))
|
||||||
@@ -759,7 +759,7 @@ class SettingsPage(
|
|||||||
val PERFORMANCE_LEVELS = listOf("boost", "sustained_high", "sustained_low", "power_savings", "default")
|
val PERFORMANCE_LEVELS = listOf("boost", "sustained_high", "sustained_low", "power_savings", "default")
|
||||||
// runtime_config.h's kVrFoveationLevels, the default ("off") first.
|
// runtime_config.h's kVrFoveationLevels, the default ("off") first.
|
||||||
val FOVEATION_LEVELS = listOf("off", "low", "medium", "high")
|
val FOVEATION_LEVELS = listOf("off", "low", "medium", "high")
|
||||||
val CONTROLLER_MODES = listOf("wii_remote", "gamepad")
|
val CONTROLLER_MODES = listOf("wii_remote", "gamepad", "none")
|
||||||
val INTERPOLATION_FPS = listOf(0L, 1L, 72L, 90L, 120L)
|
val INTERPOLATION_FPS = listOf(0L, 1L, 72L, 90L, 120L)
|
||||||
val RESOLUTIONS = listOf(1.0, 1.5, 2.0, 3.0, 4.0)
|
val RESOLUTIONS = listOf(1.0, 1.5, 2.0, 3.0, 4.0)
|
||||||
val SUPPORTED_RESOLUTIONS = listOf(0.0, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 8.0)
|
val SUPPORTED_RESOLUTIONS = listOf(0.0, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 8.0)
|
||||||
|
|||||||
@@ -588,9 +588,10 @@
|
|||||||
<!-- Controls tab -->
|
<!-- Controls tab -->
|
||||||
<string name="section_controls">Controllers</string>
|
<string name="section_controls">Controllers</string>
|
||||||
<string name="controls_mode">VR controllers</string>
|
<string name="controls_mode">VR controllers</string>
|
||||||
<string name="controls_mode_helper">What the Touch controllers are to the game.</string>
|
<string name="controls_mode_helper">What the Touch controllers are to the game. None leaves it to another controller, such as a Bluetooth gamepad; left Y still opens the settings panel.</string>
|
||||||
<string name="controls_mode_wii_remote">Wii Remote + Nunchuk</string>
|
<string name="controls_mode_wii_remote">Wii Remote + Nunchuk</string>
|
||||||
<string name="controls_mode_gamepad">Gamepad</string>
|
<string name="controls_mode_gamepad">Gamepad</string>
|
||||||
|
<string name="controls_mode_none">None</string>
|
||||||
<string name="controls_rumble">Vibration</string>
|
<string name="controls_rumble">Vibration</string>
|
||||||
<string name="controls_rumble_helper">The game\'s rumble vibrates the controllers.</string>
|
<string name="controls_rumble_helper">The game\'s rumble vibrates the controllers.</string>
|
||||||
<string name="section_controls_mapping">Wii Remote + Nunchuk buttons</string>
|
<string name="section_controls_mapping">Wii Remote + Nunchuk buttons</string>
|
||||||
@@ -600,8 +601,8 @@
|
|||||||
<string name="controls_map_b_value">Right trigger</string>
|
<string name="controls_map_b_value">Right trigger</string>
|
||||||
<string name="controls_map_12">1 / 2</string>
|
<string name="controls_map_12">1 / 2</string>
|
||||||
<string name="controls_map_12_value">Right stick up / down</string>
|
<string name="controls_map_12_value">Right stick up / down</string>
|
||||||
<string name="controls_map_minus_plus">− / +</string>
|
<string name="controls_map_plus">+</string>
|
||||||
<string name="controls_map_minus_plus_value">Left X / left menu</string>
|
<string name="controls_map_plus_value">Left X or left menu</string>
|
||||||
<string name="controls_map_stick">Nunchuk stick</string>
|
<string name="controls_map_stick">Nunchuk stick</string>
|
||||||
<string name="controls_map_stick_value">Left stick</string>
|
<string name="controls_map_stick_value">Left stick</string>
|
||||||
<string name="controls_map_z">Z</string>
|
<string name="controls_map_z">Z</string>
|
||||||
|
|||||||
+3
-1
@@ -233,7 +233,9 @@ aimed at the virtual screen; see "Controllers" in `OPENXR.md` for the mapping
|
|||||||
and the geometry. With `controller_mode = "gamepad"` it stays an ordinary pad:
|
and the geometry. With `controller_mode = "gamepad"` it stays an ordinary pad:
|
||||||
A/B → South/East, X/Y → West/North, index triggers → trigger axes, grips →
|
A/B → South/East, X/Y → West/North, index triggers → trigger axes, grips →
|
||||||
shoulders, thumbsticks → sticks (clicks → stick buttons), left menu → Start,
|
shoulders, thumbsticks → sticks (clicks → stick buttons), left menu → Start,
|
||||||
and every existing binding, dead zone and overlay setting applies. Bindings are
|
and every existing binding, dead zone and overlay setting applies. With
|
||||||
|
`controller_mode = "none"` the virtual joystick is unplugged, leaving the ports
|
||||||
|
to a Bluetooth gamepad; left Y still opens the settings panel. Bindings are
|
||||||
suggested for `oculus/touch_controller` and `khr/simple_controller`.
|
suggested for `oculus/touch_controller` and `khr/simple_controller`.
|
||||||
|
|
||||||
The manifest declares hand tracking (`horizonos.permission.HAND_TRACKING`, the
|
The manifest declares hand tracking (`horizonos.permission.HAND_TRACKING`, the
|
||||||
|
|||||||
@@ -285,11 +285,12 @@ inline bool IsSupportedVrMirrorView(std::string_view value) {
|
|||||||
}
|
}
|
||||||
// What the tracked VR controllers are to the game: "wii_remote" is a Wii
|
// What the tracked VR controllers are to the game: "wii_remote" is a Wii
|
||||||
// Remote with a Nunchuk (motion and pointer included), "gamepad" one ordinary
|
// Remote with a Nunchuk (motion and pointer included), "gamepad" one ordinary
|
||||||
// controller read as a GameCube pad. Matches mkw::vr::OpenXRControllerMode.
|
// controller read as a GameCube pad, "none" nothing at all (they only open the
|
||||||
|
// settings panel). Matches mkw::vr::OpenXRControllerMode.
|
||||||
inline constexpr const char* kVrControllerModeDefault = "wii_remote";
|
inline constexpr const char* kVrControllerModeDefault = "wii_remote";
|
||||||
|
|
||||||
inline bool IsSupportedVrControllerMode(std::string_view value) {
|
inline bool IsSupportedVrControllerMode(std::string_view value) {
|
||||||
return value == "wii_remote" || value == "gamepad";
|
return value == "wii_remote" || value == "gamepad" || value == "none";
|
||||||
}
|
}
|
||||||
// SDL scancode name, spelled the way SDL_GetScancodeName produces it. An
|
// SDL scancode name, spelled the way SDL_GetScancodeName produces it. An
|
||||||
// empty string leaves the recenter hotkey unbound, menu button only.
|
// empty string leaves the recenter hotkey unbound, menu button only.
|
||||||
@@ -510,8 +511,8 @@ inline void EnsureConfigFile() {
|
|||||||
"# What the headset's controllers are to the game: \"wii_remote\"\n"
|
"# What the headset's controllers are to the game: \"wii_remote\"\n"
|
||||||
"# is a Wii Remote (right hand, with motion and a pointer aimed at\n"
|
"# is a Wii Remote (right hand, with motion and a pointer aimed at\n"
|
||||||
"# the virtual screen) plus a Nunchuk (left hand); \"gamepad\" is one\n"
|
"# the virtual screen) plus a Nunchuk (left hand); \"gamepad\" is one\n"
|
||||||
"# ordinary controller read as a GameCube pad. Changeable live from\n"
|
"# ordinary controller read as a GameCube pad; \"none\" leaves the game\n"
|
||||||
"# the F10 menu.\n"
|
"# to other controllers. Changeable live from the F10 menu.\n"
|
||||||
"controller_mode = \"wii_remote\"\n"
|
"controller_mode = \"wii_remote\"\n"
|
||||||
"# VR interpolation: 0 = Off, 1 = Auto, or 72/90/120 FPS. Live.\n"
|
"# VR interpolation: 0 = Off, 1 = Auto, or 72/90/120 FPS. Live.\n"
|
||||||
"frame_interpolation_fps = 0\n"
|
"frame_interpolation_fps = 0\n"
|
||||||
|
|||||||
@@ -57,6 +57,11 @@ struct OpenXRPointerScreen {
|
|||||||
// left / right thumbstick-> left / right stick axes, clicks -> stick buttons
|
// left / right thumbstick-> left / right stick axes, clicks -> stick buttons
|
||||||
// left menu -> Start
|
// left menu -> Start
|
||||||
//
|
//
|
||||||
|
// None: the virtual joystick is unplugged, so the controllers hold no port and
|
||||||
|
// another controller (a desktop gamepad, a Bluetooth remote) plays in their
|
||||||
|
// place. The game sees them idle, the way it does while the settings panel has
|
||||||
|
// them; they still open the panel and toggle the first-person camera.
|
||||||
|
//
|
||||||
// Both are bound for the Oculus Touch profile; khr/simple_controller gets
|
// Both are bound for the Oculus Touch profile; khr/simple_controller gets
|
||||||
// select/menu and the poses so an unknown runtime still offers something.
|
// select/menu and the poses so an unknown runtime still offers something.
|
||||||
//
|
//
|
||||||
@@ -90,7 +95,8 @@ struct OpenXRPointerScreen {
|
|||||||
// run on the XR pacing thread. The Wii Remote bridge is internally locked, so
|
// run on the XR pacing thread. The Wii Remote bridge is internally locked, so
|
||||||
// the game thread may read it concurrently, and the virtual gamepad is only
|
// the game thread may read it concurrently, and the virtual gamepad is only
|
||||||
// published here: OpenXRApplyVirtualGamepad() performs the SDL writes on the
|
// published here: OpenXRApplyVirtualGamepad() performs the SDL writes on the
|
||||||
// game thread, keeping SDL's joystick lock off this thread entirely.
|
// game thread, and plugs the joystick in or out as the controller mode
|
||||||
|
// changes, keeping SDL's joystick lock off this thread during a session.
|
||||||
class OpenXRInput final {
|
class OpenXRInput final {
|
||||||
public:
|
public:
|
||||||
explicit OpenXRInput(OpenXRLogCallback logger = {});
|
explicit OpenXRInput(OpenXRLogCallback logger = {});
|
||||||
@@ -131,7 +137,6 @@ public:
|
|||||||
void ApplyHaptic(uint32_t hand, float amplitude, XrDuration duration);
|
void ApplyHaptic(uint32_t hand, float amplitude, XrDuration duration);
|
||||||
|
|
||||||
bool IsCreated() const noexcept { return m_created; }
|
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; }
|
const std::string& LastError() const noexcept { return m_last_error; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -143,8 +148,6 @@ private:
|
|||||||
void DestroyPoseSpaces();
|
void DestroyPoseSpaces();
|
||||||
void LoadInputClock();
|
void LoadInputClock();
|
||||||
XrTime InputSampleTime(XrTime predicted_display_time) const;
|
XrTime InputSampleTime(XrTime predicted_display_time) const;
|
||||||
bool AttachVirtualGamepad();
|
|
||||||
void DetachVirtualGamepad();
|
|
||||||
// `withheld` publishes a remote at rest with nothing held and no pointer,
|
// `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.
|
// while still tracking motion so releasing it does not read as a jolt.
|
||||||
void PublishWiiRemote(XrTime input_time, const OpenXRPointerScreen& screen,
|
void PublishWiiRemote(XrTime input_time, const OpenXRPointerScreen& screen,
|
||||||
@@ -197,8 +200,6 @@ private:
|
|||||||
bool m_panel_select_held = false;
|
bool m_panel_select_held = false;
|
||||||
std::array<float, 2> m_horizon{1.0f, 0.0f};
|
std::array<float, 2> m_horizon{1.0f, 0.0f};
|
||||||
bool m_haptics_active[kHands]{};
|
bool m_haptics_active[kHands]{};
|
||||||
uint32_t m_joystick_id = 0; // SDL_JoystickID; 0 when detached
|
|
||||||
void* m_joystick = nullptr; // SDL_Joystick*
|
|
||||||
ClickToggle m_first_person_click;
|
ClickToggle m_first_person_click;
|
||||||
SteeringWheel m_wheel;
|
SteeringWheel m_wheel;
|
||||||
WheelReferenceLatch m_wheel_reference;
|
WheelReferenceLatch m_wheel_reference;
|
||||||
@@ -262,8 +263,9 @@ private:
|
|||||||
std::string m_last_error;
|
std::string m_last_error;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Game thread: writes the gamepad the pacing thread last published, if any.
|
// Game thread: plugs the virtual gamepad in or out as the controller mode asks,
|
||||||
// Does nothing when no OpenXR controllers are attached.
|
// then writes the gamepad the pacing thread last published, if any. Does
|
||||||
|
// nothing when no OpenXR controllers are attached.
|
||||||
void OpenXRApplyVirtualGamepad() noexcept;
|
void OpenXRApplyVirtualGamepad() noexcept;
|
||||||
|
|
||||||
} // namespace mkw::vr
|
} // namespace mkw::vr
|
||||||
|
|||||||
@@ -77,10 +77,11 @@ struct Frame {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Whether the button that opens and closes the panel is held. As a Wii Remote
|
// Whether the button that opens and closes the panel is held. As a Wii Remote
|
||||||
// that is left Y, which has no Wii button; as a gamepad left Y is GameCube Y, so
|
// that is left Y, which has no Wii button, and with the controllers nothing to
|
||||||
// both thumbsticks clicked together stand in for it.
|
// the game it is left Y too; as a gamepad left Y is GameCube Y, so both
|
||||||
|
// thumbsticks clicked together stand in for it.
|
||||||
inline bool ToggleHeld(const std::array<HandInputs, 2>& hands, OpenXRControllerMode mode) noexcept {
|
inline bool ToggleHeld(const std::array<HandInputs, 2>& hands, OpenXRControllerMode mode) noexcept {
|
||||||
if (mode == OpenXRControllerMode::WiiRemote) {
|
if (mode != OpenXRControllerMode::Gamepad) {
|
||||||
return hands[0].secondary;
|
return hands[0].secondary;
|
||||||
}
|
}
|
||||||
return hands[0].thumbstick_click && hands[1].thumbstick_click;
|
return hands[0].thumbstick_click && hands[1].thumbstick_click;
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ enum class OpenXRControllerMode : uint8_t {
|
|||||||
WiiRemote,
|
WiiRemote,
|
||||||
// One ordinary gamepad, read through PAD as a GameCube controller.
|
// One ordinary gamepad, read through PAD as a GameCube controller.
|
||||||
Gamepad,
|
Gamepad,
|
||||||
|
// Nothing to the game: the virtual gamepad is unplugged, so the controllers
|
||||||
|
// hold no port. They still open the settings panel.
|
||||||
|
None,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct OpenXRWiiRemoteSample {
|
struct OpenXRWiiRemoteSample {
|
||||||
@@ -40,7 +43,7 @@ struct OpenXRWiiRemoteSample {
|
|||||||
float distance_meters = 0.0f;
|
float distance_meters = 0.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Live switch between the two presentations; the settings bar and the launch
|
// Live switch between the presentations; the settings bar and the launch
|
||||||
// configuration both go through it.
|
// configuration both go through it.
|
||||||
void OpenXRSetControllerMode(OpenXRControllerMode mode) noexcept;
|
void OpenXRSetControllerMode(OpenXRControllerMode mode) noexcept;
|
||||||
OpenXRControllerMode OpenXRGetControllerMode() noexcept;
|
OpenXRControllerMode OpenXRGetControllerMode() noexcept;
|
||||||
@@ -343,8 +346,10 @@ struct HandInputs {
|
|||||||
// Adapted from DolphinXR's default "OpenXR Wii Remote" profile
|
// Adapted from DolphinXR's default "OpenXR Wii Remote" profile
|
||||||
// (Data/Sys/Profiles/Wiimote):
|
// (Data/Sys/Profiles/Wiimote):
|
||||||
// right A -> A, right trigger -> B, right B -> C, right stick up/down -> 1/2,
|
// right A -> A, right trigger -> B, right B -> C, right stick up/down -> 1/2,
|
||||||
// left X -> -, left menu -> +,
|
// left X and left menu -> +,
|
||||||
// left trigger -> Z, left stick -> Nunchuk stick.
|
// left trigger -> Z, left stick -> Nunchuk stick.
|
||||||
|
// + is on left X as well because the PlayStation VR2's controllers give no
|
||||||
|
// usable left menu, and - has no button because Mario Kart Wii never reads it.
|
||||||
// HOME has no button; left Y opens the settings panel (openxr_settings_panel.h).
|
// HOME has no button; left Y opens the settings panel (openxr_settings_panel.h).
|
||||||
// The grips press nothing: they take hold of the wheel (openxr_driving.h), and C
|
// The grips press nothing: they take hold of the wheel (openxr_driving.h), and C
|
||||||
// is the game's look-behind, which a hand on the wheel would otherwise hold down.
|
// is the game's look-behind, which a hand on the wheel would otherwise hold down.
|
||||||
@@ -360,8 +365,7 @@ inline uint32_t RemoteButtons(const HandInputs& left, const HandInputs& right) n
|
|||||||
press(right.secondary, kButtonC);
|
press(right.secondary, kButtonC);
|
||||||
press(right.stick_y > kPressThreshold, kButtonOne);
|
press(right.stick_y > kPressThreshold, kButtonOne);
|
||||||
press(right.stick_y < -kPressThreshold, kButtonTwo);
|
press(right.stick_y < -kPressThreshold, kButtonTwo);
|
||||||
press(left.primary, kButtonMinus);
|
press(left.primary || left.menu, kButtonPlus);
|
||||||
press(left.menu, kButtonPlus);
|
|
||||||
press(left.trigger > kPressThreshold, kButtonZ);
|
press(left.trigger > kPressThreshold, kButtonZ);
|
||||||
return hold;
|
return hold;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,10 +203,11 @@ int g_vrMirrorView = [] {
|
|||||||
}();
|
}();
|
||||||
// Config spellings and menu labels for the VR controllers, index-matched to
|
// Config spellings and menu labels for the VR controllers, index-matched to
|
||||||
// mkw::vr::OpenXRControllerMode.
|
// mkw::vr::OpenXRControllerMode.
|
||||||
constexpr std::array<const char*, 2> kVrControllerModeNames{"wii_remote", "gamepad"};
|
constexpr std::array<const char*, 3> kVrControllerModeNames{"wii_remote", "gamepad", "none"};
|
||||||
constexpr std::array<const char*, 2> kVrControllerModeLabels{"Wii Remote + Nunchuk", "Gamepad"};
|
constexpr std::array<const char*, 3> kVrControllerModeLabels{"Wii Remote + Nunchuk", "Gamepad", "None"};
|
||||||
static_assert(static_cast<int>(mkw::vr::OpenXRControllerMode::WiiRemote) == 0);
|
static_assert(static_cast<int>(mkw::vr::OpenXRControllerMode::WiiRemote) == 0);
|
||||||
static_assert(static_cast<int>(mkw::vr::OpenXRControllerMode::Gamepad) == 1);
|
static_assert(static_cast<int>(mkw::vr::OpenXRControllerMode::Gamepad) == 1);
|
||||||
|
static_assert(static_cast<int>(mkw::vr::OpenXRControllerMode::None) == 2);
|
||||||
int g_vrControllerMode = [] {
|
int g_vrControllerMode = [] {
|
||||||
const std::string mode = RuntimeConfigFile::VrControllerMode();
|
const std::string mode = RuntimeConfigFile::VrControllerMode();
|
||||||
for (size_t i = 0; i < kVrControllerModeNames.size(); ++i) {
|
for (size_t i = 0; i < kVrControllerModeNames.size(); ++i) {
|
||||||
@@ -878,9 +879,11 @@ void DrawVrControllerSettings() {
|
|||||||
"Wii Remote + Nunchuk: the right controller is a Wii Remote, with motion and a pointer "
|
"Wii Remote + Nunchuk: the right controller is a Wii Remote, with motion and a pointer "
|
||||||
"that lands where you aim on the virtual screen; the left one is the Nunchuk.\n"
|
"that lands where you aim on the virtual screen; the left one is the Nunchuk.\n"
|
||||||
" Right: A = A, trigger = B, B = C (look behind), stick up/down = 1/2\n"
|
" Right: A = A, trigger = B, B = C (look behind), stick up/down = 1/2\n"
|
||||||
" Left: stick = Nunchuk stick, trigger = Z, X = -, menu = +, Y = settings panel\n"
|
" Left: stick = Nunchuk stick, trigger = Z, X or menu = +, Y = settings panel\n"
|
||||||
" The grips press nothing; they take hold of the wheel with hand steering.\n"
|
" The grips press nothing; they take hold of the wheel with hand steering.\n"
|
||||||
"Gamepad: both controllers are one ordinary controller, read as a GameCube pad.\n"
|
"Gamepad: both controllers are one ordinary controller, read as a GameCube pad.\n"
|
||||||
|
"None: the game does not see the VR controllers at all and they take no controller port,\n"
|
||||||
|
"so a desktop gamepad or a Wii Remote plays instead. Left Y still opens the settings panel.\n"
|
||||||
"Applies immediately; the game sees the controller change as a reconnection.");
|
"Applies immediately; the game sees the controller change as a reconnection.");
|
||||||
}
|
}
|
||||||
if (mkw::vr::OpenXRIsRunning() &&
|
if (mkw::vr::OpenXRIsRunning() &&
|
||||||
@@ -2322,9 +2325,9 @@ void DrawVrSettingsPanelWindow() {
|
|||||||
}
|
}
|
||||||
ImGui::TextDisabled("Aim and pull a trigger to change a setting, push a thumbstick to scroll.");
|
ImGui::TextDisabled("Aim and pull a trigger to change a setting, push a thumbstick to scroll.");
|
||||||
ImGui::TextDisabled("%s to close. The game does not see the controllers meanwhile.",
|
ImGui::TextDisabled("%s to close. The game does not see the controllers meanwhile.",
|
||||||
mkw::vr::OpenXRGetControllerMode() == mkw::vr::OpenXRControllerMode::WiiRemote
|
mkw::vr::OpenXRGetControllerMode() == mkw::vr::OpenXRControllerMode::Gamepad
|
||||||
? "Press left Y or Menu"
|
? "Click both thumbsticks or press Menu"
|
||||||
: "Click both thumbsticks or press Menu");
|
: "Press left Y or Menu");
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
if (ImGui::BeginTabBar("Settings")) {
|
if (ImGui::BeginTabBar("Settings")) {
|
||||||
const auto tab = [](const char* label, void (*draw)()) {
|
const auto tab = [](const char* label, void (*draw)()) {
|
||||||
|
|||||||
+132
-84
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
@@ -49,8 +50,15 @@ namespace {
|
|||||||
// slot that passes. So it leaves the gamepad here, and the game thread writes
|
// slot that passes. So it leaves the gamepad here, and the game thread writes
|
||||||
// it to SDL where it already polls controllers.
|
// it to SDL where it already polls controllers.
|
||||||
//
|
//
|
||||||
// m_sdl is the lock the SDL work runs under; the pacing thread only ever takes
|
// The relay also owns the virtual joystick. Attaching and detaching it take the
|
||||||
// m_state, and only for the copy. Both are taken in that order.
|
// same lock, so a live switch to or from the None controller mode (which
|
||||||
|
// unplugs it, so the VR controllers hold no port) is followed on the game
|
||||||
|
// thread too; only the input's creation and destruction plug it in or out
|
||||||
|
// where they run.
|
||||||
|
//
|
||||||
|
// m_sdl is the lock the SDL work, and the joystick, are under; the pacing
|
||||||
|
// thread only ever takes m_state, and only for the copy. Both are taken in that
|
||||||
|
// order.
|
||||||
class VirtualGamepadRelay {
|
class VirtualGamepadRelay {
|
||||||
public:
|
public:
|
||||||
struct Pad {
|
struct Pad {
|
||||||
@@ -58,18 +66,29 @@ public:
|
|||||||
std::array<bool, SDL_GAMEPAD_BUTTON_COUNT> buttons{};
|
std::array<bool, SDL_GAMEPAD_BUTTON_COUNT> buttons{};
|
||||||
};
|
};
|
||||||
|
|
||||||
void Attach(SDL_Joystick* joystick) {
|
// The controllers' input exists: plugs the joystick in unless the mode is
|
||||||
|
// None. `logger` reports a joystick SDL refuses, from whichever thread.
|
||||||
|
void Open(OpenXRLogCallback logger) {
|
||||||
std::scoped_lock lock(m_sdl, m_state);
|
std::scoped_lock lock(m_sdl, m_state);
|
||||||
m_joystick = joystick;
|
m_logger = std::move(logger);
|
||||||
|
m_open = true;
|
||||||
|
m_attach_failed = false;
|
||||||
m_pending = false;
|
m_pending = false;
|
||||||
|
FollowControllerMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Detach() {
|
// The controllers' input is going away: unplugs the joystick.
|
||||||
|
void Close() {
|
||||||
std::scoped_lock lock(m_sdl, m_state);
|
std::scoped_lock lock(m_sdl, m_state);
|
||||||
m_joystick = nullptr;
|
m_open = false;
|
||||||
|
Detach();
|
||||||
m_pending = false;
|
m_pending = false;
|
||||||
|
m_logger = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The plugged-in joystick's SDL_JoystickID, 0 while unplugged.
|
||||||
|
uint32_t JoystickId() const noexcept { return m_id.load(std::memory_order_relaxed); }
|
||||||
|
|
||||||
// Pacing thread.
|
// Pacing thread.
|
||||||
void Publish(const Pad& pad) {
|
void Publish(const Pad& pad) {
|
||||||
std::lock_guard lock(m_state);
|
std::lock_guard lock(m_state);
|
||||||
@@ -77,35 +96,106 @@ public:
|
|||||||
m_pending = true;
|
m_pending = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Game thread. Holding m_sdl here is what keeps Detach from closing the
|
// Game thread. Holding m_sdl here is what keeps Close from closing the
|
||||||
// joystick underneath the writes.
|
// joystick underneath the writes.
|
||||||
void Apply() {
|
void Apply() {
|
||||||
std::lock_guard sdl(m_sdl);
|
std::lock_guard sdl(m_sdl);
|
||||||
|
FollowControllerMode();
|
||||||
Pad pad;
|
Pad pad;
|
||||||
SDL_Joystick* joystick = nullptr;
|
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_state);
|
std::lock_guard lock(m_state);
|
||||||
if (!m_pending || m_joystick == nullptr) {
|
if (!m_pending || m_joystick == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pad = m_pad;
|
pad = m_pad;
|
||||||
joystick = m_joystick;
|
|
||||||
m_pending = false;
|
m_pending = false;
|
||||||
}
|
}
|
||||||
for (int axis = 0; axis < SDL_GAMEPAD_AXIS_COUNT; ++axis) {
|
for (int axis = 0; axis < SDL_GAMEPAD_AXIS_COUNT; ++axis) {
|
||||||
SDL_SetJoystickVirtualAxis(joystick, static_cast<SDL_GamepadAxis>(axis),
|
SDL_SetJoystickVirtualAxis(m_joystick, static_cast<SDL_GamepadAxis>(axis),
|
||||||
pad.axes[static_cast<size_t>(axis)]);
|
pad.axes[static_cast<size_t>(axis)]);
|
||||||
}
|
}
|
||||||
for (int button = 0; button < SDL_GAMEPAD_BUTTON_COUNT; ++button) {
|
for (int button = 0; button < SDL_GAMEPAD_BUTTON_COUNT; ++button) {
|
||||||
SDL_SetJoystickVirtualButton(joystick, static_cast<SDL_GamepadButton>(button),
|
SDL_SetJoystickVirtualButton(m_joystick, static_cast<SDL_GamepadButton>(button),
|
||||||
pad.buttons[static_cast<size_t>(button)]);
|
pad.buttons[static_cast<size_t>(button)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// m_sdl held. A joystick SDL refused is not asked for again until the mode
|
||||||
|
// unplugs it or the input is created anew.
|
||||||
|
void FollowControllerMode() {
|
||||||
|
if (!m_open) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (OpenXRGetControllerMode() == OpenXRControllerMode::None) {
|
||||||
|
m_attach_failed = false;
|
||||||
|
Detach();
|
||||||
|
} else if (m_joystick == nullptr && !m_attach_failed) {
|
||||||
|
m_attach_failed = !Attach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Attach() {
|
||||||
|
SDL_VirtualJoystickDesc desc;
|
||||||
|
SDL_INIT_INTERFACE(&desc);
|
||||||
|
desc.type = SDL_JOYSTICK_TYPE_GAMEPAD;
|
||||||
|
desc.naxes = SDL_GAMEPAD_AXIS_COUNT;
|
||||||
|
desc.nbuttons = SDL_GAMEPAD_BUTTON_COUNT;
|
||||||
|
desc.button_mask = (1u << SDL_GAMEPAD_BUTTON_SOUTH) | (1u << SDL_GAMEPAD_BUTTON_EAST) |
|
||||||
|
(1u << SDL_GAMEPAD_BUTTON_WEST) | (1u << SDL_GAMEPAD_BUTTON_NORTH) |
|
||||||
|
(1u << SDL_GAMEPAD_BUTTON_START) | (1u << SDL_GAMEPAD_BUTTON_LEFT_STICK) |
|
||||||
|
(1u << SDL_GAMEPAD_BUTTON_RIGHT_STICK) |
|
||||||
|
(1u << SDL_GAMEPAD_BUTTON_LEFT_SHOULDER) |
|
||||||
|
(1u << SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER);
|
||||||
|
desc.axis_mask = (1u << SDL_GAMEPAD_AXIS_LEFTX) | (1u << SDL_GAMEPAD_AXIS_LEFTY) |
|
||||||
|
(1u << SDL_GAMEPAD_AXIS_RIGHTX) | (1u << SDL_GAMEPAD_AXIS_RIGHTY) |
|
||||||
|
(1u << SDL_GAMEPAD_AXIS_LEFT_TRIGGER) | (1u << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER);
|
||||||
|
desc.name = "OpenXR Touch Controllers";
|
||||||
|
const SDL_JoystickID id = SDL_AttachVirtualJoystick(&desc);
|
||||||
|
if (id == 0) {
|
||||||
|
Refused("SDL_AttachVirtualJoystick");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SDL_Joystick* joystick = SDL_OpenJoystick(id);
|
||||||
|
if (joystick == nullptr) {
|
||||||
|
Refused("SDL_OpenJoystick");
|
||||||
|
SDL_DetachVirtualJoystick(id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_joystick = joystick;
|
||||||
|
m_id.store(id, std::memory_order_relaxed);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Detach() {
|
||||||
|
const SDL_JoystickID id = m_id.exchange(0, std::memory_order_relaxed);
|
||||||
|
if (m_joystick != nullptr) {
|
||||||
|
SDL_CloseJoystick(m_joystick);
|
||||||
|
m_joystick = nullptr;
|
||||||
|
}
|
||||||
|
if (id != 0) {
|
||||||
|
SDL_DetachVirtualJoystick(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Refused(const char* operation) const {
|
||||||
|
if (!m_logger) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
m_logger(OpenXRLogLevel::Warning, std::string(operation) + " failed: " + SDL_GetError() +
|
||||||
|
"; OpenXR controllers will not reach the game");
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::mutex m_sdl;
|
std::mutex m_sdl;
|
||||||
std::mutex m_state;
|
std::mutex m_state;
|
||||||
|
OpenXRLogCallback m_logger;
|
||||||
|
bool m_open = false;
|
||||||
|
bool m_attach_failed = false;
|
||||||
SDL_Joystick* m_joystick = nullptr;
|
SDL_Joystick* m_joystick = nullptr;
|
||||||
|
std::atomic<uint32_t> m_id{0};
|
||||||
Pad m_pad;
|
Pad m_pad;
|
||||||
bool m_pending = false;
|
bool m_pending = false;
|
||||||
};
|
};
|
||||||
@@ -273,13 +363,18 @@ bool OpenXRInput::Create(OpenXRRuntime& runtime) {
|
|||||||
CreatePoseSpaces();
|
CreatePoseSpaces();
|
||||||
LoadInputClock();
|
LoadInputClock();
|
||||||
LoadHandTracking();
|
LoadHandTracking();
|
||||||
if (!AttachVirtualGamepad()) {
|
Relay().Open(m_logger);
|
||||||
Log(OpenXRLogLevel::Warning,
|
switch (OpenXRGetControllerMode()) {
|
||||||
"SDL refused the virtual gamepad; OpenXR controllers will not reach the game");
|
case OpenXRControllerMode::WiiRemote:
|
||||||
|
Log(OpenXRLogLevel::Info, "OpenXR controller actions attached (Wii Remote + Nunchuk)");
|
||||||
|
break;
|
||||||
|
case OpenXRControllerMode::Gamepad:
|
||||||
|
Log(OpenXRLogLevel::Info, "OpenXR controller actions attached (gamepad)");
|
||||||
|
break;
|
||||||
|
case OpenXRControllerMode::None:
|
||||||
|
Log(OpenXRLogLevel::Info, "OpenXR controller actions attached (none: the game does not see them)");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
Log(OpenXRLogLevel::Info, OpenXRGetControllerMode() == OpenXRControllerMode::WiiRemote
|
|
||||||
? "OpenXR controller actions attached (Wii Remote + Nunchuk)"
|
|
||||||
: "OpenXR controller actions attached (gamepad)");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -739,54 +834,6 @@ void OpenXRApplyVirtualGamepad() noexcept {
|
|||||||
Relay().Apply();
|
Relay().Apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenXRInput::AttachVirtualGamepad() {
|
|
||||||
SDL_VirtualJoystickDesc desc;
|
|
||||||
SDL_INIT_INTERFACE(&desc);
|
|
||||||
desc.type = SDL_JOYSTICK_TYPE_GAMEPAD;
|
|
||||||
desc.naxes = SDL_GAMEPAD_AXIS_COUNT;
|
|
||||||
desc.nbuttons = SDL_GAMEPAD_BUTTON_COUNT;
|
|
||||||
desc.button_mask = (1u << SDL_GAMEPAD_BUTTON_SOUTH) | (1u << SDL_GAMEPAD_BUTTON_EAST) |
|
|
||||||
(1u << SDL_GAMEPAD_BUTTON_WEST) | (1u << SDL_GAMEPAD_BUTTON_NORTH) |
|
|
||||||
(1u << SDL_GAMEPAD_BUTTON_START) | (1u << SDL_GAMEPAD_BUTTON_LEFT_STICK) |
|
|
||||||
(1u << SDL_GAMEPAD_BUTTON_RIGHT_STICK) |
|
|
||||||
(1u << SDL_GAMEPAD_BUTTON_LEFT_SHOULDER) |
|
|
||||||
(1u << SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER);
|
|
||||||
desc.axis_mask = (1u << SDL_GAMEPAD_AXIS_LEFTX) | (1u << SDL_GAMEPAD_AXIS_LEFTY) |
|
|
||||||
(1u << SDL_GAMEPAD_AXIS_RIGHTX) | (1u << SDL_GAMEPAD_AXIS_RIGHTY) |
|
|
||||||
(1u << SDL_GAMEPAD_AXIS_LEFT_TRIGGER) | (1u << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER);
|
|
||||||
desc.name = "OpenXR Touch Controllers";
|
|
||||||
const SDL_JoystickID id = SDL_AttachVirtualJoystick(&desc);
|
|
||||||
if (id == 0) {
|
|
||||||
m_last_error = std::string("SDL_AttachVirtualJoystick failed: ") + SDL_GetError();
|
|
||||||
Log(OpenXRLogLevel::Warning, m_last_error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
SDL_Joystick* joystick = SDL_OpenJoystick(id);
|
|
||||||
if (joystick == nullptr) {
|
|
||||||
m_last_error = std::string("SDL_OpenJoystick failed: ") + SDL_GetError();
|
|
||||||
Log(OpenXRLogLevel::Warning, m_last_error);
|
|
||||||
SDL_DetachVirtualJoystick(id);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
m_joystick_id = id;
|
|
||||||
m_joystick = joystick;
|
|
||||||
Relay().Attach(joystick);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenXRInput::DetachVirtualGamepad() {
|
|
||||||
// Before the handle goes: the game thread may be writing through it.
|
|
||||||
Relay().Detach();
|
|
||||||
if (m_joystick != nullptr) {
|
|
||||||
SDL_CloseJoystick(static_cast<SDL_Joystick*>(m_joystick));
|
|
||||||
m_joystick = nullptr;
|
|
||||||
}
|
|
||||||
if (m_joystick_id != 0) {
|
|
||||||
SDL_DetachVirtualJoystick(m_joystick_id);
|
|
||||||
m_joystick_id = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenXRInput::Destroy() {
|
void OpenXRInput::Destroy() {
|
||||||
// The game must stop reading a remote whose controllers are going away.
|
// The game must stop reading a remote whose controllers are going away.
|
||||||
OpenXRWithdrawWiiRemote();
|
OpenXRWithdrawWiiRemote();
|
||||||
@@ -794,7 +841,7 @@ void OpenXRInput::Destroy() {
|
|||||||
if (m_created) {
|
if (m_created) {
|
||||||
StopRumble();
|
StopRumble();
|
||||||
}
|
}
|
||||||
DetachVirtualGamepad();
|
Relay().Close();
|
||||||
DestroyHandTrackers();
|
DestroyHandTrackers();
|
||||||
m_create_hand_tracker = nullptr;
|
m_create_hand_tracker = nullptr;
|
||||||
m_destroy_hand_tracker = nullptr;
|
m_destroy_hand_tracker = nullptr;
|
||||||
@@ -842,7 +889,7 @@ void OpenXRInput::Idle() {
|
|||||||
}
|
}
|
||||||
m_pointer.Reset();
|
m_pointer.Reset();
|
||||||
m_horizon = {1.0f, 0.0f};
|
m_horizon = {1.0f, 0.0f};
|
||||||
OpenXRPublishWiiRemote(m_joystick_id, OpenXRWiiRemoteSample{});
|
OpenXRPublishWiiRemote(Relay().JoystickId(), OpenXRWiiRemoteSample{});
|
||||||
// The panel stays as it was; only what the controllers were holding is forgotten.
|
// The panel stays as it was; only what the controllers were holding is forgotten.
|
||||||
m_panel_controls.Reset();
|
m_panel_controls.Reset();
|
||||||
m_last_input_time = 0;
|
m_last_input_time = 0;
|
||||||
@@ -855,9 +902,7 @@ void OpenXRInput::Idle() {
|
|||||||
ResetDriving();
|
ResetDriving();
|
||||||
StopRumble();
|
StopRumble();
|
||||||
// Nothing stays held on the gamepad either while input is away.
|
// Nothing stays held on the gamepad either while input is away.
|
||||||
if (m_joystick != nullptr) {
|
Relay().Publish({});
|
||||||
Relay().Publish({});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen& screen,
|
void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen& screen,
|
||||||
@@ -1015,8 +1060,11 @@ void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen&
|
|||||||
// is written back.
|
// is written back.
|
||||||
const bool was_open = OpenXRSettingsPanelOpen();
|
const bool was_open = OpenXRSettingsPanelOpen();
|
||||||
bool open = was_open;
|
bool open = was_open;
|
||||||
const settings_panel::Frame panel =
|
const OpenXRControllerMode mode = OpenXRGetControllerMode();
|
||||||
m_panel_controls.Update(panel_hands, open, dt_seconds, OpenXRGetControllerMode());
|
const settings_panel::Frame panel = m_panel_controls.Update(panel_hands, open, dt_seconds, mode);
|
||||||
|
// While the panel has the controllers, and always when they are nothing to
|
||||||
|
// the game, the game sees them idle.
|
||||||
|
const bool withheld = panel.withheld || mode == OpenXRControllerMode::None;
|
||||||
// Pointer first: the game thread reads it as soon as it sees the panel open.
|
// Pointer first: the game thread reads it as soon as it sees the panel open.
|
||||||
PublishSettingsPanel(input_time, settings_panel, panel);
|
PublishSettingsPanel(input_time, settings_panel, panel);
|
||||||
if (open != was_open) {
|
if (open != was_open) {
|
||||||
@@ -1036,7 +1084,7 @@ void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen&
|
|||||||
|
|
||||||
// The cockpit's wheel before the game reads the controllers: a held wheel
|
// The cockpit's wheel before the game reads the controllers: a held wheel
|
||||||
// steers through the left stick and keeps its grips from the game.
|
// steers through the left stick and keeps its grips from the game.
|
||||||
UpdateDriving(predicted_display_time, seat, hands, panel.withheld);
|
UpdateDriving(predicted_display_time, seat, hands, withheld);
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
// Bare hands in the cockpit. While one of them holds the wheel it holds the
|
// Bare hands in the cockpit. While one of them holds the wheel it holds the
|
||||||
@@ -1044,7 +1092,7 @@ void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen&
|
|||||||
// pause menu, the results, coasting) a right pinch stays A. The game's own
|
// pause menu, the results, coasting) a right pinch stays A. The game's own
|
||||||
// pointer cannot tell the two apart: MKW keeps it on while driving.
|
// pointer cannot tell the two apart: MKW keeps it on while driving.
|
||||||
const bool cockpit_hands =
|
const bool cockpit_hands =
|
||||||
hand_tracking_on && m_driving.cockpit_active && m_driving.hand_steering && !panel.withheld;
|
hand_tracking_on && m_driving.cockpit_active && m_driving.hand_steering && !withheld;
|
||||||
std::array<bool, kHands> bare_held{};
|
std::array<bool, kHands> bare_held{};
|
||||||
for (uint32_t hand = 0; hand < kHands; ++hand) {
|
for (uint32_t hand = 0; hand < kHands; ++hand) {
|
||||||
bare_held[hand] = cockpit_hands && m_bare_latch[hand].Bare() && m_wheel_held[hand];
|
bare_held[hand] = cockpit_hands && m_bare_latch[hand].Bare() && m_wheel_held[hand];
|
||||||
@@ -1069,7 +1117,7 @@ void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen&
|
|||||||
flick[hand] = {m_bare_latch[hand].Tracked(), m_wheel_held[hand],
|
flick[hand] = {m_bare_latch[hand].Tracked(), m_wheel_held[hand],
|
||||||
m_joint_frame.seat_from_joint[hand][hand_tracking::kPalm][7]};
|
m_joint_frame.seat_from_joint[hand][hand_tracking::kPalm][7]};
|
||||||
}
|
}
|
||||||
if (m_flick.Update(flick, dt_seconds) && OpenXRGetControllerMode() == OpenXRControllerMode::WiiRemote) {
|
if (m_flick.Update(flick, dt_seconds) && mode == OpenXRControllerMode::WiiRemote) {
|
||||||
m_flick_start = input_time;
|
m_flick_start = input_time;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1084,14 +1132,14 @@ void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen&
|
|||||||
m_injected_flick_held = injected_flick;
|
m_injected_flick_held = injected_flick;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// While the panel has the controllers, the game sees them idle.
|
// What the game sees of the controllers: nothing held while they are withheld.
|
||||||
static const std::array<wii_remote::HandInputs, kHands> kIdleHands{};
|
static const std::array<wii_remote::HandInputs, kHands> kIdleHands{};
|
||||||
const auto& game_hands = panel.withheld ? kIdleHands : hands;
|
const auto& game_hands = withheld ? kIdleHands : hands;
|
||||||
const wii_remote::HandInputs& left = game_hands[0];
|
const wii_remote::HandInputs& left = game_hands[0];
|
||||||
const wii_remote::HandInputs& right = game_hands[1];
|
const wii_remote::HandInputs& right = game_hands[1];
|
||||||
const auto injected = [&panel](const char* button) { return !panel.withheld && Injected(button); };
|
const auto injected = [withheld](const char* button) { return !withheld && Injected(button); };
|
||||||
|
|
||||||
if (m_joystick != nullptr) {
|
if (Relay().JoystickId() != 0) {
|
||||||
VirtualGamepadRelay::Pad pad;
|
VirtualGamepadRelay::Pad pad;
|
||||||
// OpenXR thumbsticks report +Y up; SDL gamepads report +Y down.
|
// OpenXR thumbsticks report +Y up; SDL gamepads report +Y down.
|
||||||
pad.axes[SDL_GAMEPAD_AXIS_LEFTX] = ToAxis(left.stick_x);
|
pad.axes[SDL_GAMEPAD_AXIS_LEFTX] = ToAxis(left.stick_x);
|
||||||
@@ -1113,8 +1161,8 @@ void OpenXRInput::Sync(XrTime predicted_display_time, const OpenXRPointerScreen&
|
|||||||
Relay().Publish(pad);
|
Relay().Publish(pad);
|
||||||
}
|
}
|
||||||
|
|
||||||
PublishWiiRemote(input_time, screen, game_hands, panel.withheld ? 0u : InjectedWiiRemoteButtons(),
|
PublishWiiRemote(input_time, screen, game_hands, withheld ? 0u : InjectedWiiRemoteButtons(),
|
||||||
panel.withheld);
|
withheld);
|
||||||
UpdateRumble();
|
UpdateRumble();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1215,7 +1263,7 @@ void OpenXRInput::PublishWiiRemote(XrTime input_time, const OpenXRPointerScreen&
|
|||||||
sample.nunchuk_acc = OpenXRWiiRemoteSample{}.nunchuk_acc;
|
sample.nunchuk_acc = OpenXRWiiRemoteSample{}.nunchuk_acc;
|
||||||
m_pointer.Reset();
|
m_pointer.Reset();
|
||||||
m_flick_start = 0;
|
m_flick_start = 0;
|
||||||
OpenXRPublishWiiRemote(m_joystick_id, sample);
|
OpenXRPublishWiiRemote(Relay().JoystickId(), sample);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (m_flick_start != 0) {
|
if (m_flick_start != 0) {
|
||||||
@@ -1252,7 +1300,7 @@ void OpenXRInput::PublishWiiRemote(XrTime input_time, const OpenXRPointerScreen&
|
|||||||
Log(OpenXRLogLevel::Info, "OpenXR Wii Remote pointer reached the virtual screen");
|
Log(OpenXRLogLevel::Info, "OpenXR Wii Remote pointer reached the virtual screen");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OpenXRPublishWiiRemote(m_joystick_id, sample);
|
OpenXRPublishWiiRemote(Relay().JoystickId(), sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenXRInput::ResetDriving() {
|
void OpenXRInput::ResetDriving() {
|
||||||
@@ -1427,7 +1475,7 @@ void OpenXRInput::UpdateDriving(XrTime display_time, const driving::SeatFrame& s
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OpenXRInput::UpdateRumble() {
|
void OpenXRInput::UpdateRumble() {
|
||||||
if (!OpenXRWiiRemoteRumbleRequested() || !OpenXRWiiRemoteOwnsGamepad(m_joystick_id)) {
|
if (!OpenXRWiiRemoteRumbleRequested() || !OpenXRWiiRemoteOwnsGamepad(Relay().JoystickId())) {
|
||||||
StopRumble();
|
StopRumble();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ void CheckNear(float actual, float expected, const char* what, float tolerance =
|
|||||||
constexpr float kDt = 1.0f / 90.0f;
|
constexpr float kDt = 1.0f / 90.0f;
|
||||||
constexpr OpenXRControllerMode kWiiRemote = OpenXRControllerMode::WiiRemote;
|
constexpr OpenXRControllerMode kWiiRemote = OpenXRControllerMode::WiiRemote;
|
||||||
constexpr OpenXRControllerMode kGamepad = OpenXRControllerMode::Gamepad;
|
constexpr OpenXRControllerMode kGamepad = OpenXRControllerMode::Gamepad;
|
||||||
|
constexpr OpenXRControllerMode kNone = OpenXRControllerMode::None;
|
||||||
|
|
||||||
std::array<HandInputs, 2> Released() {
|
std::array<HandInputs, 2> Released() {
|
||||||
return {};
|
return {};
|
||||||
@@ -103,6 +104,20 @@ void ChordOpensAndClosesOnceAsAGamepad() {
|
|||||||
Check(!frame.withheld, "the game gets the controllers back once everything is released");
|
Check(!frame.withheld, "the game gets the controllers back once everything is released");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LeftYOpensThePanelWithoutAGameController() {
|
||||||
|
Controls controls;
|
||||||
|
bool open = false;
|
||||||
|
|
||||||
|
Frame frame = controls.Update(Chord(), open, kDt, kNone);
|
||||||
|
Check(!open, "with no game controller the thumbstick chord does not open the panel");
|
||||||
|
controls.Update(Released(), open, kDt, kNone);
|
||||||
|
frame = controls.Update(LeftY(), open, kDt, kNone);
|
||||||
|
Check(open && frame.open, "with no game controller left Y opens the panel");
|
||||||
|
controls.Update(Released(), open, kDt, kNone);
|
||||||
|
frame = controls.Update(LeftY(), open, kDt, kNone);
|
||||||
|
Check(!open && !frame.open, "and closes it again");
|
||||||
|
}
|
||||||
|
|
||||||
void MenuClosesAndItsPressStaysOutOfTheGame() {
|
void MenuClosesAndItsPressStaysOutOfTheGame() {
|
||||||
Controls controls;
|
Controls controls;
|
||||||
bool open = false;
|
bool open = false;
|
||||||
@@ -213,6 +228,7 @@ void BridgeAccumulatesWheelUntilTaken() {
|
|||||||
int main() {
|
int main() {
|
||||||
LeftYOpensAndClosesOnceAsAWiiRemote();
|
LeftYOpensAndClosesOnceAsAWiiRemote();
|
||||||
ChordOpensAndClosesOnceAsAGamepad();
|
ChordOpensAndClosesOnceAsAGamepad();
|
||||||
|
LeftYOpensThePanelWithoutAGameController();
|
||||||
MenuClosesAndItsPressStaysOutOfTheGame();
|
MenuClosesAndItsPressStaysOutOfTheGame();
|
||||||
SelectWaitsForAReleaseAndFollowsTheTrigger();
|
SelectWaitsForAReleaseAndFollowsTheTrigger();
|
||||||
ThumbstickScrolls();
|
ThumbstickScrolls();
|
||||||
|
|||||||
@@ -259,8 +259,12 @@ void TestButtons() {
|
|||||||
|
|
||||||
right = {};
|
right = {};
|
||||||
left.primary = true;
|
left.primary = true;
|
||||||
|
Check(RemoteButtons(left, right) == kButtonPlus, "left X is +, and nothing presses -");
|
||||||
|
left = {};
|
||||||
left.menu = true;
|
left.menu = true;
|
||||||
Check(RemoteButtons(left, right) == (kButtonMinus | kButtonPlus), "left X and menu are - and +");
|
Check(RemoteButtons(left, right) == kButtonPlus, "left menu is + too");
|
||||||
|
left.primary = true;
|
||||||
|
Check(RemoteButtons(left, right) == kButtonPlus, "left X and menu together are one +");
|
||||||
left = {};
|
left = {};
|
||||||
left.trigger = 0.7f;
|
left.trigger = 0.7f;
|
||||||
Check(RemoteButtons(left, right) == kButtonZ, "left trigger is Z");
|
Check(RemoteButtons(left, right) == kButtonZ, "left trigger is Z");
|
||||||
|
|||||||
Reference in new issue
Block a user