Commit 05d736b3 authored by Jacob DeWitt's avatar Jacob DeWitt Committed by Commit Bot

Update WebXR gamepads to latest version of xr-standard mapping

The mapping is defined by the spec at:
https://immersive-web.github.io/webxr/#xr-standard-gamepad-mapping
Now, only a primary trigger is required for a controller to claim
xr-standard mapping, and the order of some of the buttons has changed.

Centralize the logic of how to order buttons + input axes, as well as
where/when to add placeholder values in the XRStandardGamepadBuilder
class.

Bug: 979246
Change-Id: Iefb116ea603c0a7237575d189686fd2daee9de90
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1728345
Commit-Queue: Jacob DeWitt <jacde@chromium.org>
Reviewed-by: default avatarAlexander Cooper <alcooper@chromium.org>
Reviewed-by: default avatarBrian Sheedy <bsheedy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#684446}
parent 1dc0388b
...@@ -170,34 +170,6 @@ XrBrowserTestBase::RuntimeType XrBrowserTestBase::GetRuntimeType() const { ...@@ -170,34 +170,6 @@ XrBrowserTestBase::RuntimeType XrBrowserTestBase::GetRuntimeType() const {
return XrBrowserTestBase::RuntimeType::RUNTIME_NONE; return XrBrowserTestBase::RuntimeType::RUNTIME_NONE;
} }
device::XrAxisType XrBrowserTestBase::GetPrimaryAxisType() const {
auto runtime = GetRuntimeType();
switch (runtime) {
case XrBrowserTestBase::RuntimeType::RUNTIME_OPENVR:
return device::XrAxisType::kTrackpad;
case XrBrowserTestBase::RuntimeType::RUNTIME_WMR:
case XrBrowserTestBase::RuntimeType::RUNTIME_OPENXR:
return device::XrAxisType::kJoystick;
case XrBrowserTestBase::RuntimeType::RUNTIME_NONE:
return device::XrAxisType::kNone;
}
NOTREACHED();
}
device::XrAxisType XrBrowserTestBase::GetSecondaryAxisType() const {
auto runtime = GetRuntimeType();
switch (runtime) {
case XrBrowserTestBase::RuntimeType::RUNTIME_OPENVR:
return device::XrAxisType::kJoystick;
case XrBrowserTestBase::RuntimeType::RUNTIME_WMR:
case XrBrowserTestBase::RuntimeType::RUNTIME_OPENXR:
return device::XrAxisType::kTrackpad;
case XrBrowserTestBase::RuntimeType::RUNTIME_NONE:
return device::XrAxisType::kNone;
}
NOTREACHED();
}
GURL XrBrowserTestBase::GetFileUrlForHtmlTestFile( GURL XrBrowserTestBase::GetFileUrlForHtmlTestFile(
const std::string& test_name) { const std::string& test_name) {
return ui_test_utils::GetTestUrl( return ui_test_utils::GetTestUrl(
......
...@@ -85,8 +85,6 @@ class XrBrowserTestBase : public InProcessBrowserTest { ...@@ -85,8 +85,6 @@ class XrBrowserTestBase : public InProcessBrowserTest {
void TearDown() override; void TearDown() override;
virtual RuntimeType GetRuntimeType() const; virtual RuntimeType GetRuntimeType() const;
device::XrAxisType GetPrimaryAxisType() const;
device::XrAxisType GetSecondaryAxisType() const;
// Returns a GURL to the XR test HTML file of the given name, e.g. // Returns a GURL to the XR test HTML file of the given name, e.g.
// GetHtmlTestFile("foo") returns a GURL for the foo.html file in the XR // GetHtmlTestFile("foo") returns a GURL for the foo.html file in the XR
......
...@@ -128,16 +128,13 @@ class WebXrControllerInputMock : public MockXRDeviceHookBase { ...@@ -128,16 +128,13 @@ class WebXrControllerInputMock : public MockXRDeviceHookBase {
UpdateControllerAndWait(index, controller_data); UpdateControllerAndWait(index, controller_data);
} }
unsigned int CreateAndConnectMinimalGamepad( unsigned int CreateAndConnectMinimalGamepad() {
device::XrAxisType primary_axis_type) { // Create a controller that only supports select via a trigger, i.e. it has
// Create a controller that only supports select and one TrackPad, i.e. it // just enough data to be considered a gamepad.
// has just enough data to be considered a gamepad.
uint64_t supported_buttons = uint64_t supported_buttons =
device::XrButtonMaskFromId(device::XrButtonId::kAxisTrigger) | device::XrButtonMaskFromId(device::XrButtonId::kAxisTrigger);
device::XrButtonMaskFromId(device::XrButtonId::kAxisPrimary);
std::map<device::XrButtonId, unsigned int> axis_types = { std::map<device::XrButtonId, unsigned int> axis_types = {
{device::XrButtonId::kAxisPrimary, primary_axis_type},
{device::XrButtonId::kAxisTrigger, device::XrAxisType::kTrigger}, {device::XrButtonId::kAxisTrigger, device::XrAxisType::kTrigger},
}; };
...@@ -188,8 +185,10 @@ class WebXrControllerInputMock : public MockXRDeviceHookBase { ...@@ -188,8 +185,10 @@ class WebXrControllerInputMock : public MockXRDeviceHookBase {
} }
private: private:
// kAxisTrackpad is the first entry in XrButtonId that maps to an axis and the
// subsequent entries are also for input axes.
device::XrButtonId GetAxisId(unsigned int offset) { device::XrButtonId GetAxisId(unsigned int offset) {
return static_cast<device::XrButtonId>(device::XrButtonId::kAxisPrimary + return static_cast<device::XrButtonId>(device::XrButtonId::kAxisTrackpad +
offset); offset);
} }
...@@ -222,8 +221,7 @@ IN_PROC_MULTI_CLASS_BROWSER_TEST_F2(WebXrVrOpenVrBrowserTest, ...@@ -222,8 +221,7 @@ IN_PROC_MULTI_CLASS_BROWSER_TEST_F2(WebXrVrOpenVrBrowserTest,
WebXrVrBrowserTestBase, WebXrVrBrowserTestBase,
TestInputHandednessChange) { TestInputHandednessChange) {
WebXrControllerInputMock my_mock; WebXrControllerInputMock my_mock;
unsigned int controller_index = unsigned int controller_index = my_mock.CreateAndConnectMinimalGamepad();
my_mock.CreateAndConnectMinimalGamepad(t->GetPrimaryAxisType());
t->LoadUrlAndAwaitInitialization( t->LoadUrlAndAwaitInitialization(
t->GetFileUrlForHtmlTestFile("test_webxr_input_same_object")); t->GetFileUrlForHtmlTestFile("test_webxr_input_same_object"));
...@@ -273,11 +271,11 @@ IN_PROC_MULTI_CLASS_BROWSER_TEST_F2(WebXrVrOpenVrBrowserTest, ...@@ -273,11 +271,11 @@ IN_PROC_MULTI_CLASS_BROWSER_TEST_F2(WebXrVrOpenVrBrowserTest,
// TODO(crbug.com/963676): Figure out if the race is a product or test bug. // TODO(crbug.com/963676): Figure out if the race is a product or test bug.
// There's a potential for a race causing the input sources change event to // There's a potential for a race causing the input sources change event to
// fire multiple times if we disconnect a controller that has a gamepad. // fire multiple times if we disconnect a controller that has a gamepad.
// Even just a select trigger is sufficient to have an xr-standard mapping, so
// just expose a grip trigger instead so that we don't connect a gamepad.
uint64_t insufficient_buttons = uint64_t insufficient_buttons =
device::XrButtonMaskFromId(device::XrButtonId::kAxisTrigger); device::XrButtonMaskFromId(device::XrButtonId::kGrip);
std::map<device::XrButtonId, unsigned int> insufficient_axis_types = { std::map<device::XrButtonId, unsigned int> insufficient_axis_types = {};
{device::XrButtonId::kAxisTrigger, device::XrAxisType::kTrigger},
};
unsigned int controller_index = my_mock.CreateAndConnectController( unsigned int controller_index = my_mock.CreateAndConnectController(
device::ControllerRole::kControllerRoleRight, insufficient_axis_types, device::ControllerRole::kControllerRoleRight, insufficient_axis_types,
insufficient_buttons); insufficient_buttons);
...@@ -311,8 +309,7 @@ IN_PROC_MULTI_CLASS_BROWSER_TEST_F2(WebXrVrOpenVrBrowserTest, ...@@ -311,8 +309,7 @@ IN_PROC_MULTI_CLASS_BROWSER_TEST_F2(WebXrVrOpenVrBrowserTest,
// it is both added and removed. // it is both added and removed.
// Since we're changing the controller state without disconnecting it, we can // Since we're changing the controller state without disconnecting it, we can
// (and should) use the minimal gamepad here. // (and should) use the minimal gamepad here.
controller_index = controller_index = my_mock.CreateAndConnectMinimalGamepad();
my_mock.CreateAndConnectMinimalGamepad(t->GetPrimaryAxisType());
t->PollJavaScriptBooleanOrFail("inputChangeEvents === 3", t->PollJavaScriptBooleanOrFail("inputChangeEvents === 3",
WebXrVrBrowserTestBase::kPollTimeoutShort); WebXrVrBrowserTestBase::kPollTimeoutShort);
t->RunJavaScriptOrFail("updateCachedInputSource(0)"); t->RunJavaScriptOrFail("updateCachedInputSource(0)");
...@@ -346,21 +343,19 @@ IN_PROC_BROWSER_TEST_F(WebXrVrOpenVrBrowserTest, TestInputGamepadSameObject) { ...@@ -346,21 +343,19 @@ IN_PROC_BROWSER_TEST_F(WebXrVrOpenVrBrowserTest, TestInputGamepadSameObject) {
// Create a set of buttons and axes that don't have enough data to be made // Create a set of buttons and axes that don't have enough data to be made
// into an xr-standard gamepad (which we expect the runtimes to not report). // into an xr-standard gamepad (which we expect the runtimes to not report).
// Note that we need to set the trigger axis because of how OpenVR handles // Even just setting the select trigger is now enough to create an xr-standard
// selects. // gamepad, so we only set the grip trigger in this case.
uint64_t insufficient_buttons = uint64_t insufficient_buttons =
device::XrButtonMaskFromId(device::XrButtonId::kAxisTrigger); device::XrButtonMaskFromId(device::XrButtonId::kGrip);
std::map<device::XrButtonId, unsigned int> insufficient_axis_types = { std::map<device::XrButtonId, unsigned int> insufficient_axis_types = {};
{device::XrButtonId::kAxisTrigger, device::XrAxisType::kTrigger},
};
// Create a set of buttons and axes that we expect to have enough data to be // Create a set of buttons and axes that we expect to have enough data to be
// made into an xr-standard gamepad (which we expect the runtimes to report). // made into an xr-standard gamepad (which we expect the runtimes to report).
uint64_t sufficient_buttons = uint64_t sufficient_buttons =
device::XrButtonMaskFromId(device::XrButtonId::kAxisTrigger) | device::XrButtonMaskFromId(device::XrButtonId::kAxisTrigger) |
device::XrButtonMaskFromId(device::XrButtonId::kAxisPrimary); device::XrButtonMaskFromId(device::XrButtonId::kAxisTrackpad);
std::map<device::XrButtonId, unsigned int> sufficient_axis_types = { std::map<device::XrButtonId, unsigned int> sufficient_axis_types = {
{device::XrButtonId::kAxisPrimary, device::XrAxisType::kTrackpad}, {device::XrButtonId::kAxisTrackpad, device::XrAxisType::kTrackpad},
{device::XrButtonId::kAxisTrigger, device::XrAxisType::kTrigger}, {device::XrButtonId::kAxisTrigger, device::XrAxisType::kTrigger},
}; };
...@@ -381,7 +376,7 @@ IN_PROC_BROWSER_TEST_F(WebXrVrOpenVrBrowserTest, TestInputGamepadSameObject) { ...@@ -381,7 +376,7 @@ IN_PROC_BROWSER_TEST_F(WebXrVrOpenVrBrowserTest, TestInputGamepadSameObject) {
RunJavaScriptOrFail("updateCachedInputSource(0)"); RunJavaScriptOrFail("updateCachedInputSource(0)");
// Toggle a button and confirm that the controller is still the same. // Toggle a button and confirm that the controller is still the same.
my_mock.PressReleasePrimaryTrigger(controller_index); my_mock.ToggleButtons(controller_index, insufficient_buttons);
RunJavaScriptOrFail("validateCachedSourcePresence(true)"); RunJavaScriptOrFail("validateCachedSourcePresence(true)");
RunJavaScriptOrFail("validateCurrentAndCachedGamepadMatch()"); RunJavaScriptOrFail("validateCurrentAndCachedGamepadMatch()");
...@@ -442,17 +437,16 @@ IN_PROC_MULTI_CLASS_BROWSER_TEST_F2(WebXrVrOpenVrBrowserTest, ...@@ -442,17 +437,16 @@ IN_PROC_MULTI_CLASS_BROWSER_TEST_F2(WebXrVrOpenVrBrowserTest,
TestGamepadMinimumData) { TestGamepadMinimumData) {
WebXrControllerInputMock my_mock; WebXrControllerInputMock my_mock;
unsigned int controller_index = unsigned int controller_index = my_mock.CreateAndConnectMinimalGamepad();
my_mock.CreateAndConnectMinimalGamepad(t->GetPrimaryAxisType());
t->LoadUrlAndAwaitInitialization( t->LoadUrlAndAwaitInitialization(
t->GetFileUrlForHtmlTestFile("test_webxr_gamepad_support")); t->GetFileUrlForHtmlTestFile("test_webxr_gamepad_support"));
t->EnterSessionWithUserGestureOrFail(); t->EnterSessionWithUserGestureOrFail();
// We only actually connect the data for the two buttons, but WMR expects // We only actually connect the data for the one button, but WMR expects
// the WMR controller (which has all of the required and optional buttons) // the WMR controller (which has all of the required and optional buttons)
// and so adds dummy/placeholder buttons regardless of what data we send up. // and so adds dummy/placeholder buttons regardless of what data we send up.
std::string button_count = "2"; std::string button_count = "1";
if (t->GetRuntimeType() == XrBrowserTestBase::RuntimeType::RUNTIME_WMR) if (t->GetRuntimeType() == XrBrowserTestBase::RuntimeType::RUNTIME_WMR)
button_count = "4"; button_count = "4";
...@@ -462,19 +456,12 @@ IN_PROC_MULTI_CLASS_BROWSER_TEST_F2(WebXrVrOpenVrBrowserTest, ...@@ -462,19 +456,12 @@ IN_PROC_MULTI_CLASS_BROWSER_TEST_F2(WebXrVrOpenVrBrowserTest,
// Press the trigger and set the axis to a non-zero amount, so we can ensure // Press the trigger and set the axis to a non-zero amount, so we can ensure
// we aren't getting just default gamepad data. // we aren't getting just default gamepad data.
my_mock.TogglePrimaryTrigger(controller_index); my_mock.TogglePrimaryTrigger(controller_index);
my_mock.SetAxes(controller_index, device::XrButtonId::kAxisPrimary, 0.5,
-0.5);
my_mock.ToggleButtonTouches(controller_index,
device::XrButtonId::kAxisPrimary);
// The trigger should be button 0, and the first set of axes should have it's // The trigger should be button 0.
// value set.
t->PollJavaScriptBooleanOrFail("isMappingEqualTo('xr-standard')", t->PollJavaScriptBooleanOrFail("isMappingEqualTo('xr-standard')",
WebXrVrBrowserTestBase::kPollTimeoutShort); WebXrVrBrowserTestBase::kPollTimeoutShort);
t->PollJavaScriptBooleanOrFail("isButtonPressedEqualTo(0, true)", t->PollJavaScriptBooleanOrFail("isButtonPressedEqualTo(0, true)",
WebXrVrBrowserTestBase::kPollTimeoutShort); WebXrVrBrowserTestBase::kPollTimeoutShort);
t->PollJavaScriptBooleanOrFail("areAxesValuesEqualTo(0, 0.5, -0.5)",
WebXrVrBrowserTestBase::kPollTimeoutShort);
t->RunJavaScriptOrFail("done()"); t->RunJavaScriptOrFail("done()");
t->EndTest(); t->EndTest();
} }
...@@ -492,14 +479,14 @@ IN_PROC_MULTI_CLASS_BROWSER_TEST_F2(WebXrVrOpenVrBrowserTest, ...@@ -492,14 +479,14 @@ IN_PROC_MULTI_CLASS_BROWSER_TEST_F2(WebXrVrOpenVrBrowserTest,
// Create a controller that supports all reserved buttons. // Create a controller that supports all reserved buttons.
uint64_t supported_buttons = uint64_t supported_buttons =
device::XrButtonMaskFromId(device::XrButtonId::kAxisTrigger) | device::XrButtonMaskFromId(device::XrButtonId::kAxisTrigger) |
device::XrButtonMaskFromId(device::XrButtonId::kAxisPrimary) | device::XrButtonMaskFromId(device::XrButtonId::kAxisTrackpad) |
device::XrButtonMaskFromId(device::XrButtonId::kAxisSecondary) | device::XrButtonMaskFromId(device::XrButtonId::kAxisThumbstick) |
device::XrButtonMaskFromId(device::XrButtonId::kGrip); device::XrButtonMaskFromId(device::XrButtonId::kGrip);
std::map<device::XrButtonId, unsigned int> axis_types = { std::map<device::XrButtonId, unsigned int> axis_types = {
{device::XrButtonId::kAxisPrimary, t->GetPrimaryAxisType()}, {device::XrButtonId::kAxisTrackpad, device::XrAxisType::kTrackpad},
{device::XrButtonId::kAxisTrigger, device::XrAxisType::kTrigger}, {device::XrButtonId::kAxisTrigger, device::XrAxisType::kTrigger},
{device::XrButtonId::kAxisSecondary, t->GetSecondaryAxisType()}, {device::XrButtonId::kAxisThumbstick, device::XrAxisType::kJoystick},
}; };
unsigned int controller_index = my_mock.CreateAndConnectController( unsigned int controller_index = my_mock.CreateAndConnectController(
...@@ -512,14 +499,21 @@ IN_PROC_MULTI_CLASS_BROWSER_TEST_F2(WebXrVrOpenVrBrowserTest, ...@@ -512,14 +499,21 @@ IN_PROC_MULTI_CLASS_BROWSER_TEST_F2(WebXrVrOpenVrBrowserTest,
// Setup some state on the optional buttons (as TestGamepadMinimumData should // Setup some state on the optional buttons (as TestGamepadMinimumData should
// ensure proper state on the required buttons). // ensure proper state on the required buttons).
// Set a value on the secondary set of axes. // Set a value on the touchpad.
my_mock.SetAxes(controller_index, device::XrButtonId::kAxisSecondary, 0.25, my_mock.SetAxes(controller_index, device::XrButtonId::kAxisTrackpad, 0.25,
-0.25); -0.25);
// Set the secondary trackpad/joystick to be touched. // Set the touchpad to be touched.
my_mock.ToggleButtonTouches( my_mock.ToggleButtonTouches(
controller_index, controller_index,
device::XrButtonMaskFromId(device::XrButtonId::kAxisSecondary)); device::XrButtonMaskFromId(device::XrButtonId::kAxisTrackpad));
// Also test the thumbstick.
my_mock.SetAxes(controller_index, device::XrButtonId::kAxisThumbstick, 0.67,
-0.67);
my_mock.ToggleButtons(
controller_index,
device::XrButtonMaskFromId(device::XrButtonId::kAxisThumbstick));
// Set the grip button to be pressed. // Set the grip button to be pressed.
my_mock.ToggleButtons(controller_index, my_mock.ToggleButtons(controller_index,
...@@ -533,17 +527,27 @@ IN_PROC_MULTI_CLASS_BROWSER_TEST_F2(WebXrVrOpenVrBrowserTest, ...@@ -533,17 +527,27 @@ IN_PROC_MULTI_CLASS_BROWSER_TEST_F2(WebXrVrOpenVrBrowserTest,
t->PollJavaScriptBooleanOrFail("isButtonCountEqualTo(4)", t->PollJavaScriptBooleanOrFail("isButtonCountEqualTo(4)",
WebXrVrBrowserTestBase::kPollTimeoutShort); WebXrVrBrowserTestBase::kPollTimeoutShort);
// The secondary set of axes should be set appropriately. // The touchpad axes should be set appropriately.
t->PollJavaScriptBooleanOrFail("areAxesValuesEqualTo(1, 0.25, -0.25)", t->PollJavaScriptBooleanOrFail("areAxesValuesEqualTo(0, 0.25, -0.25)",
WebVrBrowserTestBase::kPollTimeoutShort); WebVrBrowserTestBase::kPollTimeoutShort);
// Button 2 is reserved for the Grip, and should be pressed. // The thumbstick axes should be set appropriately.
t->PollJavaScriptBooleanOrFail("isButtonPressedEqualTo(2, true)", t->PollJavaScriptBooleanOrFail("areAxesValuesEqualTo(1, 0.67, -0.67)",
WebVrBrowserTestBase::kPollTimeoutShort);
// Button 1 is reserved for the Grip, and should be pressed.
t->PollJavaScriptBooleanOrFail("isButtonPressedEqualTo(1, true)",
WebVrBrowserTestBase::kPollTimeoutShort);
// Button 2 is reserved for the trackpad and should be touched but not
// pressed.
t->PollJavaScriptBooleanOrFail("isButtonPressedEqualTo(2, false)",
WebVrBrowserTestBase::kPollTimeoutShort);
t->PollJavaScriptBooleanOrFail("isButtonTouchedEqualTo(2, true)",
WebVrBrowserTestBase::kPollTimeoutShort); WebVrBrowserTestBase::kPollTimeoutShort);
// Button 3 is reserved for the secondary trackpad/joystick and should be // Button 3 is reserved for the thumbstick and should be touched and pressed.
// touched but not pressed. t->PollJavaScriptBooleanOrFail("isButtonPressedEqualTo(3, true)",
t->PollJavaScriptBooleanOrFail("isButtonPressedEqualTo(3, false)",
WebVrBrowserTestBase::kPollTimeoutShort); WebVrBrowserTestBase::kPollTimeoutShort);
t->PollJavaScriptBooleanOrFail("isButtonTouchedEqualTo(3, true)", t->PollJavaScriptBooleanOrFail("isButtonTouchedEqualTo(3, true)",
WebVrBrowserTestBase::kPollTimeoutShort); WebVrBrowserTestBase::kPollTimeoutShort);
...@@ -565,13 +569,13 @@ IN_PROC_BROWSER_TEST_F(WebXrVrOpenVrBrowserTest, TestInputAxesWithNoButton) { ...@@ -565,13 +569,13 @@ IN_PROC_BROWSER_TEST_F(WebXrVrOpenVrBrowserTest, TestInputAxesWithNoButton) {
// secondary axis. (Though it is a valid axis) // secondary axis. (Though it is a valid axis)
uint64_t supported_buttons = uint64_t supported_buttons =
device::XrButtonMaskFromId(device::XrButtonId::kAxisTrigger) | device::XrButtonMaskFromId(device::XrButtonId::kAxisTrigger) |
device::XrButtonMaskFromId(device::XrButtonId::kAxisPrimary) | device::XrButtonMaskFromId(device::XrButtonId::kAxisTrackpad) |
device::XrButtonMaskFromId(device::XrButtonId::kGrip); device::XrButtonMaskFromId(device::XrButtonId::kGrip);
std::map<device::XrButtonId, unsigned int> axis_types = { std::map<device::XrButtonId, unsigned int> axis_types = {
{device::XrButtonId::kAxisPrimary, GetPrimaryAxisType()}, {device::XrButtonId::kAxisTrackpad, device::XrAxisType::kTrackpad},
{device::XrButtonId::kAxisTrigger, device::XrAxisType::kTrigger}, {device::XrButtonId::kAxisTrigger, device::XrAxisType::kTrigger},
{device::XrButtonId::kAxisSecondary, GetSecondaryAxisType()}, {device::XrButtonId::kAxisThumbstick, device::XrAxisType::kJoystick},
}; };
unsigned int controller_index = my_mock.CreateAndConnectController( unsigned int controller_index = my_mock.CreateAndConnectController(
...@@ -585,7 +589,7 @@ IN_PROC_BROWSER_TEST_F(WebXrVrOpenVrBrowserTest, TestInputAxesWithNoButton) { ...@@ -585,7 +589,7 @@ IN_PROC_BROWSER_TEST_F(WebXrVrOpenVrBrowserTest, TestInputAxesWithNoButton) {
// Setup some state on the optional buttons (as TestGamepadMinimumData should // Setup some state on the optional buttons (as TestGamepadMinimumData should
// ensure proper state on the required buttons). // ensure proper state on the required buttons).
// Set a value on the secondary set of axes. // Set a value on the secondary set of axes.
my_mock.SetAxes(controller_index, device::XrButtonId::kAxisSecondary, 0.25, my_mock.SetAxes(controller_index, device::XrButtonId::kAxisThumbstick, 0.25,
-0.25); -0.25);
// Controller should meet the requirements for the 'xr-standard' mapping. // Controller should meet the requirements for the 'xr-standard' mapping.
PollJavaScriptBooleanOrFail("isMappingEqualTo('xr-standard')", PollJavaScriptBooleanOrFail("isMappingEqualTo('xr-standard')",
...@@ -619,11 +623,11 @@ IN_PROC_BROWSER_TEST_F(WebXrVrOpenVrBrowserTest, TestGamepadReservedData) { ...@@ -619,11 +623,11 @@ IN_PROC_BROWSER_TEST_F(WebXrVrOpenVrBrowserTest, TestGamepadReservedData) {
// extra button to guarantee that the reserved button is held. // extra button to guarantee that the reserved button is held.
uint64_t supported_buttons = uint64_t supported_buttons =
device::XrButtonMaskFromId(device::XrButtonId::kAxisTrigger) | device::XrButtonMaskFromId(device::XrButtonId::kAxisTrigger) |
device::XrButtonMaskFromId(device::XrButtonId::kAxisPrimary) | device::XrButtonMaskFromId(device::XrButtonId::kAxisTrackpad) |
device::XrButtonMaskFromId(device::XrButtonId::kA); device::XrButtonMaskFromId(device::XrButtonId::kA);
std::map<device::XrButtonId, unsigned int> axis_types = { std::map<device::XrButtonId, unsigned int> axis_types = {
{device::XrButtonId::kAxisPrimary, device::XrAxisType::kJoystick}, {device::XrButtonId::kAxisTrackpad, device::XrAxisType::kTrackpad},
{device::XrButtonId::kAxisTrigger, device::XrAxisType::kTrigger}, {device::XrButtonId::kAxisTrigger, device::XrAxisType::kTrigger},
}; };
...@@ -639,7 +643,7 @@ IN_PROC_BROWSER_TEST_F(WebXrVrOpenVrBrowserTest, TestGamepadReservedData) { ...@@ -639,7 +643,7 @@ IN_PROC_BROWSER_TEST_F(WebXrVrOpenVrBrowserTest, TestGamepadReservedData) {
// should be ignored. // should be ignored.
my_mock.ToggleButtons(controller_index, UINT64_MAX); my_mock.ToggleButtons(controller_index, UINT64_MAX);
// Index 2 and 3 are reserved for the grip and secondary joystick. // Index 1 and 3 are reserved for the grip and joystick.
// As our controller doesn't support them, they should be present but not // As our controller doesn't support them, they should be present but not
// pressed, and our "extra" button should be index 4 and should be pressed. // pressed, and our "extra" button should be index 4 and should be pressed.
PollJavaScriptBooleanOrFail("isMappingEqualTo('xr-standard')", PollJavaScriptBooleanOrFail("isMappingEqualTo('xr-standard')",
...@@ -647,9 +651,9 @@ IN_PROC_BROWSER_TEST_F(WebXrVrOpenVrBrowserTest, TestGamepadReservedData) { ...@@ -647,9 +651,9 @@ IN_PROC_BROWSER_TEST_F(WebXrVrOpenVrBrowserTest, TestGamepadReservedData) {
PollJavaScriptBooleanOrFail("isButtonCountEqualTo(5)", kPollTimeoutShort); PollJavaScriptBooleanOrFail("isButtonCountEqualTo(5)", kPollTimeoutShort);
PollJavaScriptBooleanOrFail("isButtonPressedEqualTo(0, true)", PollJavaScriptBooleanOrFail("isButtonPressedEqualTo(0, true)",
kPollTimeoutShort); kPollTimeoutShort);
PollJavaScriptBooleanOrFail("isButtonPressedEqualTo(1, true)", PollJavaScriptBooleanOrFail("isButtonPressedEqualTo(1, false)",
kPollTimeoutShort); kPollTimeoutShort);
PollJavaScriptBooleanOrFail("isButtonPressedEqualTo(2, false)", PollJavaScriptBooleanOrFail("isButtonPressedEqualTo(2, true)",
kPollTimeoutShort); kPollTimeoutShort);
PollJavaScriptBooleanOrFail("isButtonPressedEqualTo(3, false)", PollJavaScriptBooleanOrFail("isButtonPressedEqualTo(3, false)",
kPollTimeoutShort); kPollTimeoutShort);
...@@ -672,11 +676,11 @@ IN_PROC_BROWSER_TEST_F(WebXrVrOpenVrBrowserTest, TestGamepadOptionalData) { ...@@ -672,11 +676,11 @@ IN_PROC_BROWSER_TEST_F(WebXrVrOpenVrBrowserTest, TestGamepadOptionalData) {
// Create a controller that supports the trigger, primary axis, and grip // Create a controller that supports the trigger, primary axis, and grip
uint64_t supported_buttons = uint64_t supported_buttons =
device::XrButtonMaskFromId(device::XrButtonId::kAxisTrigger) | device::XrButtonMaskFromId(device::XrButtonId::kAxisTrigger) |
device::XrButtonMaskFromId(device::XrButtonId::kAxisPrimary) | device::XrButtonMaskFromId(device::XrButtonId::kAxisTrackpad) |
device::XrButtonMaskFromId(device::XrButtonId::kGrip); device::XrButtonMaskFromId(device::XrButtonId::kGrip);
std::map<device::XrButtonId, unsigned int> axis_types = { std::map<device::XrButtonId, unsigned int> axis_types = {
{device::XrButtonId::kAxisPrimary, GetPrimaryAxisType()}, {device::XrButtonId::kAxisTrackpad, device::XrAxisType::kTrackpad},
{device::XrButtonId::kAxisTrigger, device::XrAxisType::kTrigger}, {device::XrButtonId::kAxisTrigger, device::XrAxisType::kTrigger},
}; };
...@@ -708,8 +712,7 @@ IN_PROC_MULTI_CLASS_BROWSER_TEST_F2(WebXrVrOpenVrBrowserTest, ...@@ -708,8 +712,7 @@ IN_PROC_MULTI_CLASS_BROWSER_TEST_F2(WebXrVrOpenVrBrowserTest,
TestControllerInputRegistered) { TestControllerInputRegistered) {
WebXrControllerInputMock my_mock; WebXrControllerInputMock my_mock;
unsigned int controller_index = unsigned int controller_index = my_mock.CreateAndConnectMinimalGamepad();
my_mock.CreateAndConnectMinimalGamepad(t->GetPrimaryAxisType());
// Load the test page and enter presentation. // Load the test page and enter presentation.
t->LoadUrlAndAwaitInitialization( t->LoadUrlAndAwaitInitialization(
......
...@@ -28,6 +28,8 @@ if (enable_vr) { ...@@ -28,6 +28,8 @@ if (enable_vr) {
"util/sample_queue.h", "util/sample_queue.h",
"util/sliding_average.cc", "util/sliding_average.cc",
"util/sliding_average.h", "util/sliding_average.h",
"util/xr_standard_gamepad_builder.cc",
"util/xr_standard_gamepad_builder.h",
"vr_device.h", "vr_device.h",
"vr_device_base.cc", "vr_device_base.cc",
"vr_device_base.h", "vr_device_base.h",
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "device/gamepad/public/cpp/gamepads.h" #include "device/gamepad/public/cpp/gamepads.h"
#include "device/vr/util/gamepad_builder.h" #include "device/vr/util/xr_standard_gamepad_builder.h"
#include "device/vr/vr_device.h" #include "device/vr/vr_device.h"
#include "third_party/libovr/src/Include/OVR_CAPI.h" #include "third_party/libovr/src/Include/OVR_CAPI.h"
#include "ui/gfx/transform.h" #include "ui/gfx/transform.h"
...@@ -196,50 +196,83 @@ device::mojom::XRHandedness OculusToMojomHand(ovrHandType hand) { ...@@ -196,50 +196,83 @@ device::mojom::XRHandedness OculusToMojomHand(ovrHandType hand) {
} }
} }
class OculusGamepadBuilder : public GamepadBuilder { class OculusGamepadBuilder : public XRStandardGamepadBuilder {
public: public:
// TODO(https://crbug.com/942201): Get correct ID string once WebXR spec issue
// #550 (https://github.com/immersive-web/webxr/issues/550) is resolved.
OculusGamepadBuilder(ovrInputState state, ovrHandType hand) OculusGamepadBuilder(ovrInputState state, ovrHandType hand)
: GamepadBuilder("oculus-touch", : XRStandardGamepadBuilder(OculusToMojomHand(hand)),
GamepadMapping::kXrStandard, state_(state),
OculusToMojomHand(hand)), ovr_hand_(hand) {
state_(state) {} switch (ovr_hand_) {
case ovrHand_Left:
SetPrimaryButton(GetTouchTriggerButton(ovrTouch_LIndexTrigger,
state_.IndexTrigger[ovr_hand_]));
SetSecondaryButton(GetTriggerButton(state_.HandTrigger[ovr_hand_]));
SetThumbstickData(GetThumbstickData(ovrButton_LThumb));
AddOptionalButtonData(GetStandardButton(ovrButton_X));
AddOptionalButtonData(GetStandardButton(ovrButton_Y));
AddOptionalButtonData(GetTouchButton(ovrTouch_LThumbRest));
break;
case ovrHand_Right:
SetPrimaryButton(GetTouchTriggerButton(ovrTouch_RIndexTrigger,
state_.IndexTrigger[ovr_hand_]));
SetSecondaryButton(GetTriggerButton(state_.HandTrigger[ovr_hand_]));
SetThumbstickData(GetThumbstickData(ovrButton_RThumb));
AddOptionalButtonData(GetStandardButton(ovrButton_A));
AddOptionalButtonData(GetStandardButton(ovrButton_B));
AddOptionalButtonData(GetTouchButton(ovrTouch_RThumbRest));
break;
default:
DLOG(WARNING) << "Unsupported hand configuration.";
}
}
~OculusGamepadBuilder() override = default; ~OculusGamepadBuilder() override = default;
void AddStandardButton(ovrButton id) { private:
GamepadButton GetStandardButton(ovrButton id) {
bool pressed = (state_.Buttons & id) != 0; bool pressed = (state_.Buttons & id) != 0;
bool touched = (state_.Touches & id) != 0; bool touched = (state_.Touches & id) != 0;
double value = pressed ? 1.0 : 0.0; double value = pressed ? 1.0 : 0.0;
AddButton(GamepadButton(pressed, touched, value)); return GamepadButton(pressed, touched, value);
} }
void AddTouchButton(ovrTouch id) { GamepadButton GetTouchButton(ovrTouch id) {
bool touched = (state_.Touches & id) != 0; bool touched = (state_.Touches & id) != 0;
AddButton(GamepadButton(false, touched, 0.0f)); return GamepadButton(false, touched, 0.0f);
} }
void AddTriggerButton(float value) { GamepadButton GetTriggerButton(float value) {
value = ApplyTriggerDeadzone(value); value = ApplyTriggerDeadzone(value);
bool pressed = value != 0; bool pressed = value != 0;
bool touched = pressed; bool touched = pressed;
AddButton(GamepadButton(pressed, touched, value)); return GamepadButton(pressed, touched, value);
} }
void AddTouchTriggerButton(ovrTouch id, float value) { GamepadButton GetTouchTriggerButton(ovrTouch id, float value) {
value = ApplyTriggerDeadzone(value); value = ApplyTriggerDeadzone(value);
bool pressed = value != 0; bool pressed = value != 0;
bool touched = (state_.Touches & id) != 0; bool touched = (state_.Touches & id) != 0;
AddButton(GamepadButton(pressed, touched, value)); return GamepadButton(pressed, touched, value);
} }
bool IsValid() const override { GamepadBuilder::ButtonData GetThumbstickData(ovrButton id) {
return GamepadBuilder::IsValid() && GetHandedness() != GamepadHand::kNone; GamepadButton button = GetStandardButton(id);
GamepadBuilder::ButtonData data;
data.touched = button.touched;
data.pressed = button.pressed;
data.value = button.value;
// Invert the y axis because -1 is up in the Gamepad API but down in Oculus.
data.type = GamepadBuilder::ButtonData::Type::kThumbstick;
data.x_axis = state_.Thumbstick[ovr_hand_].x;
data.y_axis = -state_.Thumbstick[ovr_hand_].y;
return data;
} }
private: private:
ovrInputState state_; ovrInputState state_;
ovrHandType ovr_hand_;
DISALLOW_COPY_AND_ASSIGN(OculusGamepadBuilder); DISALLOW_COPY_AND_ASSIGN(OculusGamepadBuilder);
}; };
...@@ -274,12 +307,18 @@ mojom::XRGamepadDataPtr OculusGamepadHelper::GetGamepadData( ...@@ -274,12 +307,18 @@ mojom::XRGamepadDataPtr OculusGamepadHelper::GetGamepadData(
// Order of buttons 1-4 is dictated by the xr-standard Gamepad mapping. // Order of buttons 1-4 is dictated by the xr-standard Gamepad mapping.
// Buttons 5-7 are in order of decreasing importance. // Buttons 5-7 are in order of decreasing importance.
// 1) index trigger (primary trigger/button) // 1) index trigger (primary trigger/button)
// 2) thumb joystick button // 2) hand trigger (secondary trigger/button)
// 3) hand trigger (primary trigger/button) // 3) EMPTY (no touchpad press)
// 4) EMPTY (no secondary joystick/touchpad exists) // 4) thumbstick press
// 5) A or X // 5) A or X
// 6) B or Y // 6) B or Y
// 7) thumbrest touch sensor // 7) thumbrest touch sensor
//
// Order of axes 1-4 is dictated by the xr-standard Gamepad mapping.
// 1) EMPTY (no touchpad)
// 2) EMPTY (no touchpad)
// 3) thumbstick X
// 4) thumbstick Y
base::Optional<Gamepad> OculusGamepadHelper::CreateGamepad(ovrSession session, base::Optional<Gamepad> OculusGamepadHelper::CreateGamepad(ovrSession session,
ovrHandType hand) { ovrHandType hand) {
ovrInputState input_touch; ovrInputState input_touch;
...@@ -290,37 +329,6 @@ base::Optional<Gamepad> OculusGamepadHelper::CreateGamepad(ovrSession session, ...@@ -290,37 +329,6 @@ base::Optional<Gamepad> OculusGamepadHelper::CreateGamepad(ovrSession session,
} }
OculusGamepadBuilder touch(input_touch, hand); OculusGamepadBuilder touch(input_touch, hand);
// Invert the y axis because -1 is up in the Gamepad API, but down in Oculus.
touch.AddAxis(input_touch.Thumbstick[hand].x);
touch.AddAxis(-input_touch.Thumbstick[hand].y);
switch (hand) {
case ovrHand_Left:
touch.AddTouchTriggerButton(ovrTouch_LIndexTrigger,
input_touch.IndexTrigger[hand]);
touch.AddStandardButton(ovrButton_LThumb);
touch.AddTriggerButton(input_touch.HandTrigger[hand]);
touch.AddPlaceholderButton();
touch.AddStandardButton(ovrButton_X);
touch.AddStandardButton(ovrButton_Y);
touch.AddTouchButton(ovrTouch_LThumbRest);
break;
case ovrHand_Right:
touch.AddTouchTriggerButton(ovrTouch_RIndexTrigger,
input_touch.IndexTrigger[hand]);
touch.AddStandardButton(ovrButton_RThumb);
touch.AddTriggerButton(input_touch.HandTrigger[hand]);
touch.AddPlaceholderButton();
touch.AddStandardButton(ovrButton_A);
touch.AddStandardButton(ovrButton_B);
touch.AddTouchButton(ovrTouch_RThumbRest);
break;
default:
DLOG(WARNING) << "Unsupported hand configuration.";
return base::nullopt;
}
return touch.GetGamepad(); return touch.GetGamepad();
} }
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "device/gamepad/public/cpp/gamepads.h" #include "device/gamepad/public/cpp/gamepads.h"
#include "device/vr/util/gamepad_builder.h" #include "device/vr/util/gamepad_builder.h"
#include "device/vr/util/xr_standard_gamepad_builder.h"
#include "device/vr/vr_device.h" #include "device/vr/vr_device.h"
#include "third_party/openvr/src/headers/openvr.h" #include "third_party/openvr/src/headers/openvr.h"
#include "ui/gfx/transform.h" #include "ui/gfx/transform.h"
...@@ -64,16 +65,21 @@ std::map<vr::EVRButtonId, GamepadBuilder::ButtonData> GetAxesButtons( ...@@ -64,16 +65,21 @@ std::map<vr::EVRButtonId, GamepadBuilder::ButtonData> GetAxesButtons(
double x_axis = controller_state.rAxis[j].x; double x_axis = controller_state.rAxis[j].x;
double y_axis = -controller_state.rAxis[j].y; double y_axis = -controller_state.rAxis[j].y;
if (axis_type == vr::k_eControllerAxis_Joystick) {
button_data.type = GamepadBuilder::ButtonData::Type::kThumbstick;
// We only want to apply the deadzone to joysticks, since various
// runtimes may not have already done that, but touchpads should
// be fine.
x_axis = std::fabs(x_axis) < kJoystickDeadzone ? 0 : x_axis;
y_axis = std::fabs(y_axis) < kJoystickDeadzone ? 0 : y_axis;
} else if (axis_type == vr::k_eControllerAxis_TrackPad) {
button_data.type = GamepadBuilder::ButtonData::Type::kTouchpad;
}
switch (axis_type) { switch (axis_type) {
case vr::k_eControllerAxis_Joystick: case vr::k_eControllerAxis_Joystick:
// We only want to apply the deadzone to joysticks, since various
// runtimes may not have already done that, but touchpads should
// be fine.
x_axis = std::fabs(x_axis) < kJoystickDeadzone ? 0 : x_axis;
y_axis = std::fabs(y_axis) < kJoystickDeadzone ? 0 : y_axis;
FALLTHROUGH;
case vr::k_eControllerAxis_TrackPad: { case vr::k_eControllerAxis_TrackPad: {
button_data.has_both_axes = true;
button_data.x_axis = x_axis; button_data.x_axis = x_axis;
button_data.y_axis = y_axis; button_data.y_axis = y_axis;
vr::EVRButtonId button_id = GetAxisId(j); vr::EVRButtonId button_id = GetAxisId(j);
...@@ -202,7 +208,7 @@ mojom::XRGamepadDataPtr OpenVRGamepadHelper::GetGamepadData( ...@@ -202,7 +208,7 @@ mojom::XRGamepadDataPtr OpenVRGamepadHelper::GetGamepadData(
GamepadBuilder::ButtonData data = button_data_pair.second; GamepadBuilder::ButtonData data = button_data_pair.second;
gamepad->buttons.push_back(GetMojomGamepadButton(data)); gamepad->buttons.push_back(GetMojomGamepadButton(data));
if (data.has_both_axes) { if (data.type != GamepadBuilder::ButtonData::Type::kButton) {
gamepad->axes.push_back(data.x_axis); gamepad->axes.push_back(data.x_axis);
gamepad->axes.push_back(data.y_axis); gamepad->axes.push_back(data.y_axis);
} }
...@@ -247,7 +253,7 @@ mojom::XRGamepadDataPtr OpenVRGamepadHelper::GetGamepadData( ...@@ -247,7 +253,7 @@ mojom::XRGamepadDataPtr OpenVRGamepadHelper::GetGamepadData(
} }
// Helper classes and WebXR Getters // Helper classes and WebXR Getters
class OpenVRGamepadBuilder : public GamepadBuilder { class OpenVRGamepadBuilder : public XRStandardGamepadBuilder {
public: public:
enum class AxesRequirement { enum class AxesRequirement {
kOptional = 0, kOptional = 0,
...@@ -258,74 +264,112 @@ class OpenVRGamepadBuilder : public GamepadBuilder { ...@@ -258,74 +264,112 @@ class OpenVRGamepadBuilder : public GamepadBuilder {
uint32_t controller_id, uint32_t controller_id,
vr::VRControllerState_t controller_state, vr::VRControllerState_t controller_state,
device::mojom::XRHandedness handedness) device::mojom::XRHandedness handedness)
: GamepadBuilder(GetGamepadId(vr_system, controller_id), : XRStandardGamepadBuilder(handedness),
GamepadMapping::kXrStandard,
handedness),
controller_state_(controller_state) { controller_state_(controller_state) {
supported_buttons_ = vr_system->GetUint64TrackedDeviceProperty( supported_buttons_ = vr_system->GetUint64TrackedDeviceProperty(
controller_id, vr::Prop_SupportedButtons_Uint64); controller_id, vr::Prop_SupportedButtons_Uint64);
axes_data_ = GetAxesButtons(vr_system, controller_state_, axes_data_ = GetAxesButtons(vr_system, controller_state_,
supported_buttons_, controller_id); supported_buttons_, controller_id);
base::Optional<GamepadBuilder::ButtonData> primary_button =
TryGetAxesOrTriggerButton(vr::k_EButton_SteamVR_Trigger);
if (!primary_button) {
return;
}
SetPrimaryButton(primary_button.value());
base::Optional<GamepadButton> secondary_button =
TryGetButton(vr::k_EButton_Grip);
if (secondary_button) {
SetSecondaryButton(secondary_button.value());
}
base::Optional<GamepadBuilder::ButtonData> touchpad_data =
TryGetNextUnusedButtonOfType(
GamepadBuilder::ButtonData::Type::kTouchpad);
if (touchpad_data) {
SetTouchpadData(touchpad_data.value());
}
base::Optional<GamepadBuilder::ButtonData> thumbstick_data =
TryGetNextUnusedButtonOfType(
GamepadBuilder::ButtonData::Type::kThumbstick);
if (thumbstick_data) {
SetThumbstickData(thumbstick_data.value());
}
// Now that all of the xr-standard reserved buttons have been filled in, we
// add the rest of the buttons in order of decreasing importance.
// First add regular buttons.
for (const auto& id : kWebXRButtonOrder) {
base::Optional<GamepadButton> button = TryGetButton(id);
if (button) {
AddOptionalButtonData(button.value());
}
}
// Finally, add any remaining axis buttons (triggers/josysticks/touchpads)
AddRemainingTriggersAndAxes();
} }
~OpenVRGamepadBuilder() override = default; ~OpenVRGamepadBuilder() override = default;
bool TryAddAxesOrTriggerButton( private:
base::Optional<GamepadBuilder::ButtonData> TryGetAxesOrTriggerButton(
vr::EVRButtonId button_id, vr::EVRButtonId button_id,
AxesRequirement requirement = AxesRequirement::kOptional) { AxesRequirement requirement = AxesRequirement::kOptional) {
if (!IsInAxesData(button_id)) if (!IsInAxesData(button_id))
return false; return base::nullopt;
bool require_axes = (requirement == AxesRequirement::kRequireBoth); bool require_axes = (requirement == AxesRequirement::kRequireBoth);
if (require_axes && !axes_data_[button_id].has_both_axes) if (require_axes &&
return false; axes_data_[button_id].type == GamepadBuilder::ButtonData::Type::kButton)
return base::nullopt;
AddButton(axes_data_[button_id]);
used_axes_.insert(button_id); used_axes_.insert(button_id);
return axes_data_[button_id];
return true;
} }
bool TryAddNextUnusedButtonWithAxes() { base::Optional<GamepadBuilder::ButtonData> TryGetNextUnusedButtonOfType(
GamepadBuilder::ButtonData::Type type) {
for (const auto& axes_data_pair : axes_data_) { for (const auto& axes_data_pair : axes_data_) {
vr::EVRButtonId button_id = axes_data_pair.first; vr::EVRButtonId button_id = axes_data_pair.first;
if (IsUsed(button_id)) if (IsUsed(button_id))
continue; continue;
if (TryAddAxesOrTriggerButton(button_id, AxesRequirement::kRequireBoth)) if (axes_data_pair.second.type != type)
return true; continue;
return TryGetAxesOrTriggerButton(button_id,
AxesRequirement::kRequireBoth);
} }
return false; return base::nullopt;
} }
bool TryAddButton(vr::EVRButtonId button_id) { base::Optional<GamepadButton> TryGetButton(vr::EVRButtonId button_id) {
GamepadButton button; GamepadButton button;
if (TryGetGamepadButton(controller_state_, supported_buttons_, button_id, if (TryGetGamepadButton(controller_state_, supported_buttons_, button_id,
&button)) { &button)) {
AddButton(button); return button;
return true;
} }
return false; return base::nullopt;
} }
// This will add any remaining unused values from axes_data to the gamepad. // This will add any remaining unused values from axes_data to the gamepad.
// Returns a bool indicating whether any additional axes were added. // Returns a bool indicating whether any additional axes were added.
bool AddRemainingTriggersAndAxes() { void AddRemainingTriggersAndAxes() {
bool added_axes = false;
for (const auto& axes_data_pair : axes_data_) { for (const auto& axes_data_pair : axes_data_) {
if (!IsUsed(axes_data_pair.first)) { if (!IsUsed(axes_data_pair.first)) {
added_axes = true; AddOptionalButtonData(axes_data_pair.second);
AddButton(axes_data_pair.second);
} }
} }
return added_axes;
} }
private:
static bool IsControllerHTCVive(vr::IVRSystem* vr_system, static bool IsControllerHTCVive(vr::IVRSystem* vr_system,
uint32_t controller_id) { uint32_t controller_id) {
std::string model = std::string model =
...@@ -364,7 +408,7 @@ class OpenVRGamepadBuilder : public GamepadBuilder { ...@@ -364,7 +408,7 @@ class OpenVRGamepadBuilder : public GamepadBuilder {
const vr::VRControllerState_t controller_state_; const vr::VRControllerState_t controller_state_;
uint64_t supported_buttons_; uint64_t supported_buttons_;
std::map<vr::EVRButtonId, ButtonData> axes_data_; std::map<vr::EVRButtonId, GamepadBuilder::ButtonData> axes_data_;
std::unordered_set<vr::EVRButtonId> used_axes_; std::unordered_set<vr::EVRButtonId> used_axes_;
DISALLOW_COPY_AND_ASSIGN(OpenVRGamepadBuilder); DISALLOW_COPY_AND_ASSIGN(OpenVRGamepadBuilder);
...@@ -377,59 +421,6 @@ base::Optional<Gamepad> OpenVRGamepadHelper::GetXRGamepad( ...@@ -377,59 +421,6 @@ base::Optional<Gamepad> OpenVRGamepadHelper::GetXRGamepad(
device::mojom::XRHandedness handedness) { device::mojom::XRHandedness handedness) {
OpenVRGamepadBuilder builder(vr_system, controller_id, controller_state, OpenVRGamepadBuilder builder(vr_system, controller_id, controller_state,
handedness); handedness);
if (!builder.TryAddAxesOrTriggerButton(vr::k_EButton_SteamVR_Trigger))
return base::nullopt;
if (!builder.TryAddNextUnusedButtonWithAxes())
return base::nullopt;
bool added_placeholder_grip = false;
if (!builder.TryAddButton(vr::k_EButton_Grip)) {
added_placeholder_grip = true;
builder.AddPlaceholderButton();
}
// If we can't find any secondary button with an x and y axis, add a fake
// button. Note that we're not worried about ensuring that the axes data gets
// added, because if there were any other axes to add, we would've added them.
bool added_placeholder_axes = false;
if (!builder.TryAddNextUnusedButtonWithAxes()) {
added_placeholder_axes = true;
builder.AddPlaceholderButton();
}
// Now that all of the xr-standard reserved buttons have been filled in, we
// add the rest of the buttons in order of decreasing importance.
// First add regular buttons
bool added_optional_buttons = false;
for (const auto& button : kWebXRButtonOrder) {
added_optional_buttons =
builder.TryAddButton(button) || added_optional_buttons;
}
// Finally, add any remaining axis buttons (triggers/josysticks/touchpads)
bool added_optional_axes = builder.AddRemainingTriggersAndAxes();
// If we didn't add any optional buttons, we need to remove our placeholder
// buttons.
if (!(added_optional_buttons || added_optional_axes)) {
// If we didn't add any optional buttons, see if we need to remove the most
// recent placeholder (the secondary axes).
// Note that if we added a placeholder axes, the only optional axes that
// should have been added are triggers, and so we don't need to worry about
// the order
if (added_placeholder_axes) {
builder.RemovePlaceholderButton();
// Only if the axes button was a placeholder can we remove the grip
// if it was also a placeholder.
if (added_placeholder_grip) {
builder.RemovePlaceholderButton();
}
}
}
return builder.GetGamepad(); return builder.GetGamepad();
} }
......
...@@ -136,10 +136,10 @@ static const std::map<device::XrButtonId, vr::EVRButtonId> ...@@ -136,10 +136,10 @@ static const std::map<device::XrButtonId, vr::EVRButtonId>
{device::XrButtonId::kA, vr::EVRButtonId::k_EButton_A}, {device::XrButtonId::kA, vr::EVRButtonId::k_EButton_A},
{device::XrButtonId::kProximitySensor, {device::XrButtonId::kProximitySensor,
vr::EVRButtonId::k_EButton_ProximitySensor}, vr::EVRButtonId::k_EButton_ProximitySensor},
{device::XrButtonId::kAxisPrimary, vr::EVRButtonId::k_EButton_Axis0}, {device::XrButtonId::kAxisTrackpad, vr::EVRButtonId::k_EButton_Axis0},
{device::XrButtonId::kAxisTrigger, {device::XrButtonId::kAxisTrigger,
vr::EVRButtonId::k_EButton_SteamVR_Trigger}, vr::EVRButtonId::k_EButton_SteamVR_Trigger},
{device::XrButtonId::kAxisSecondary, vr::EVRButtonId::k_EButton_Axis2}, {device::XrButtonId::kAxisThumbstick, vr::EVRButtonId::k_EButton_Axis2},
{device::XrButtonId::kAxisTertiary, vr::EVRButtonId::k_EButton_Axis3}, {device::XrButtonId::kAxisTertiary, vr::EVRButtonId::k_EButton_Axis3},
{device::XrButtonId::kAxisQuaternary, vr::EVRButtonId::k_EButton_Axis4}, {device::XrButtonId::kAxisQuaternary, vr::EVRButtonId::k_EButton_Axis4},
}; };
......
...@@ -29,9 +29,9 @@ enum XrButtonId { ...@@ -29,9 +29,9 @@ enum XrButtonId {
kDpadDown = 6, kDpadDown = 6,
kA = 7, kA = 7,
kProximitySensor = 31, kProximitySensor = 31,
kAxisPrimary = 32, kAxisTrackpad = 32,
kAxisTrigger = 33, kAxisTrigger = 33,
kAxisSecondary = 34, kAxisThumbstick = 34,
kAxisTertiary = 35, kAxisTertiary = 35,
kAxisQuaternary = 36, kAxisQuaternary = 36,
kMax = 64 kMax = 64
...@@ -49,10 +49,10 @@ inline uint64_t XrButtonMaskFromId(XrButtonId id) { ...@@ -49,10 +49,10 @@ inline uint64_t XrButtonMaskFromId(XrButtonId id) {
} }
inline unsigned int XrAxisOffsetFromId(XrButtonId id) { inline unsigned int XrAxisOffsetFromId(XrButtonId id) {
DCHECK(XrButtonId::kAxisPrimary <= id && DCHECK(XrButtonId::kAxisTrackpad <= id &&
id < XrButtonId::kAxisPrimary + kMaxNumAxes); id < XrButtonId::kAxisTrackpad + kMaxNumAxes);
return static_cast<unsigned int>(id) - return static_cast<unsigned int>(id) -
static_cast<unsigned int>(XrButtonId::kAxisPrimary); static_cast<unsigned int>(XrButtonId::kAxisTrackpad);
} }
struct Color { struct Color {
......
...@@ -43,9 +43,8 @@ GamepadBuilder::~GamepadBuilder() = default; ...@@ -43,9 +43,8 @@ GamepadBuilder::~GamepadBuilder() = default;
bool GamepadBuilder::IsValid() const { bool GamepadBuilder::IsValid() const {
switch (GetMapping()) { switch (GetMapping()) {
case GamepadMapping::kXrStandard: case GamepadMapping::kXrStandard:
// In order to satisfy the XRStandard mapping at least two buttons and one // Just a single primary button is sufficient for the xr-standard mapping.
// set of axes need to have been added. return gamepad_.buttons_length > 0;
return gamepad_.axes_length >= 2 && gamepad_.buttons_length >= 2;
case GamepadMapping::kStandard: case GamepadMapping::kStandard:
case GamepadMapping::kNone: case GamepadMapping::kNone:
// Neither standard requires any buttons to be set, and all other data // Neither standard requires any buttons to be set, and all other data
...@@ -56,7 +55,7 @@ bool GamepadBuilder::IsValid() const { ...@@ -56,7 +55,7 @@ bool GamepadBuilder::IsValid() const {
NOTREACHED(); NOTREACHED();
} }
base::Optional<Gamepad> GamepadBuilder::GetGamepad() const { base::Optional<Gamepad> GamepadBuilder::GetGamepad() {
if (IsValid()) if (IsValid())
return gamepad_; return gamepad_;
...@@ -75,7 +74,7 @@ void GamepadBuilder::AddButton(const GamepadButton& button) { ...@@ -75,7 +74,7 @@ void GamepadBuilder::AddButton(const GamepadButton& button) {
void GamepadBuilder::AddButton(const ButtonData& data) { void GamepadBuilder::AddButton(const ButtonData& data) {
AddButton(GamepadButton(data.pressed, data.touched, data.value)); AddButton(GamepadButton(data.pressed, data.touched, data.value));
if (data.has_both_axes) if (data.type != ButtonData::Type::kButton)
AddAxes(data); AddAxes(data);
} }
...@@ -85,7 +84,14 @@ void GamepadBuilder::AddAxis(double value) { ...@@ -85,7 +84,14 @@ void GamepadBuilder::AddAxis(double value) {
} }
void GamepadBuilder::AddAxes(const ButtonData& data) { void GamepadBuilder::AddAxes(const ButtonData& data) {
DCHECK(data.has_both_axes); DCHECK_NE(data.type, ButtonData::Type::kButton);
if (data.type == ButtonData::Type::kTouchpad && !data.touched) {
// Untouched touchpads must have axes set to 0.
AddPlaceholderAxes();
return;
}
AddAxis(data.x_axis); AddAxis(data.x_axis);
AddAxis(data.y_axis); AddAxis(data.y_axis);
} }
...@@ -106,6 +112,11 @@ void GamepadBuilder::RemovePlaceholderButton() { ...@@ -106,6 +112,11 @@ void GamepadBuilder::RemovePlaceholderButton() {
gamepad_.buttons_length--; gamepad_.buttons_length--;
} }
void GamepadBuilder::AddPlaceholderAxes() {
AddAxis(0.0);
AddAxis(0.0);
}
double GamepadBuilder::ApplyAxisDeadzoneToValue(double value) const { double GamepadBuilder::ApplyAxisDeadzoneToValue(double value) const {
return std::fabs(value) < axis_deadzone_ ? 0 : value; return std::fabs(value) < axis_deadzone_ ? 0 : value;
} }
......
...@@ -17,11 +17,13 @@ class GamepadBuilder { ...@@ -17,11 +17,13 @@ class GamepadBuilder {
// Helper struct that we don't want to pollute the device namespace // Helper struct that we don't want to pollute the device namespace
struct ButtonData { struct ButtonData {
enum class Type { kButton, kThumbstick, kTouchpad };
bool touched = false; bool touched = false;
bool pressed = false; bool pressed = false;
double value = 0.0; double value = 0.0;
bool has_both_axes = false; Type type = Type::kButton;
double x_axis = 0.0; double x_axis = 0.0;
double y_axis = 0.0; double y_axis = 0.0;
}; };
...@@ -32,12 +34,13 @@ class GamepadBuilder { ...@@ -32,12 +34,13 @@ class GamepadBuilder {
virtual ~GamepadBuilder(); virtual ~GamepadBuilder();
virtual bool IsValid() const; virtual bool IsValid() const;
base::Optional<Gamepad> GetGamepad() const; virtual base::Optional<Gamepad> GetGamepad();
void SetAxisDeadzone(double value); void SetAxisDeadzone(double value);
void AddButton(const GamepadButton& button); void AddButton(const GamepadButton& button);
void AddButton(const ButtonData& data); void AddButton(const ButtonData& data);
void AddAxis(double value); void AddAxis(double value);
void AddPlaceholderAxes();
void AddPlaceholderButton(); void AddPlaceholderButton();
void RemovePlaceholderButton(); void RemovePlaceholderButton();
...@@ -48,12 +51,14 @@ class GamepadBuilder { ...@@ -48,12 +51,14 @@ class GamepadBuilder {
GamepadHand GetHandedness() const { return gamepad_.hand; } GamepadHand GetHandedness() const { return gamepad_.hand; }
GamepadMapping GetMapping() const { return gamepad_.mapping; } GamepadMapping GetMapping() const { return gamepad_.mapping; }
Gamepad gamepad_;
private: private:
double axis_deadzone_ = 0.0; double axis_deadzone_ = 0.0;
Gamepad gamepad_;
DISALLOW_COPY_AND_ASSIGN(GamepadBuilder); DISALLOW_COPY_AND_ASSIGN(GamepadBuilder);
}; };
} // namespace device } // namespace device
#endif // DEVICE_VR_UTIL_GAMEPAD_BUILDER_H_ #endif // DEVICE_VR_UTIL_GAMEPAD_BUILDER_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/vr/util/xr_standard_gamepad_builder.h"
namespace device {
XRStandardGamepadBuilder::XRStandardGamepadBuilder(
device::mojom::XRHandedness handedness,
double axis_deadzone)
: handedness_(handedness), axis_deadzone_(axis_deadzone) {}
XRStandardGamepadBuilder::~XRStandardGamepadBuilder() = default;
void XRStandardGamepadBuilder::AddOptionalButtonData(
const GamepadBuilder::ButtonData& data) {
optional_button_data_.push_back(data);
if (data.type != GamepadBuilder::ButtonData::Type::kButton) {
has_optional_axes_ = true;
}
}
void XRStandardGamepadBuilder::AddOptionalButtonData(
const GamepadButton& button) {
GamepadBuilder::ButtonData data;
data.touched = button.touched;
data.pressed = button.pressed;
data.value = button.value;
AddOptionalButtonData(data);
}
base::Optional<Gamepad> XRStandardGamepadBuilder::GetGamepad() const {
if (!primary_button_) {
return base::nullopt;
}
GamepadBuilder builder("", GamepadMapping::kXrStandard, handedness_);
builder.SetAxisDeadzone(axis_deadzone_);
builder.AddButton(primary_button_.value());
const bool has_optional_buttons = !optional_button_data_.empty();
if (secondary_button_) {
builder.AddButton(secondary_button_.value());
} else if (touchpad_data_ || thumbstick_data_ || has_optional_buttons) {
builder.AddPlaceholderButton();
}
if (touchpad_data_) {
builder.AddButton(touchpad_data_.value());
} else if (thumbstick_data_ || has_optional_axes_) {
builder.AddPlaceholderButton();
builder.AddPlaceholderAxes();
} else if (has_optional_buttons) {
// Only add a placeholder button because there are no more axes.
builder.AddPlaceholderButton();
}
if (thumbstick_data_) {
builder.AddButton(thumbstick_data_.value());
} else if (has_optional_axes_) {
builder.AddPlaceholderButton();
builder.AddPlaceholderAxes();
} else if (has_optional_buttons) {
// Only add a placeholder button because there are no more axes.
builder.AddPlaceholderButton();
}
for (const auto& data : optional_button_data_) {
builder.AddButton(data);
}
return builder.GetGamepad();
}
} // namespace device
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef DEVICE_VR_UTIL_XR_STANDARD_GAMEPAD_BUILDER_H_
#define DEVICE_VR_UTIL_XR_STANDARD_GAMEPAD_BUILDER_H_
#include "base/macros.h"
#include "device/vr/public/mojom/isolated_xr_service.mojom.h"
#include "device/vr/util/gamepad_builder.h"
namespace device {
// Centralizes the logic of properly ordering the buttons and input axes for
// xr-standard Gamepads so that the various platforms don't have to worry about
// it themselves.
class XRStandardGamepadBuilder {
public:
XRStandardGamepadBuilder(device::mojom::XRHandedness handedness,
double axis_deadzone = 0.0);
virtual ~XRStandardGamepadBuilder();
void SetPrimaryButton(const GamepadButton& button) {
primary_button_ = button;
}
void SetPrimaryButton(const GamepadBuilder::ButtonData& data) {
SetPrimaryButton(GamepadButton(data.pressed, data.touched, data.value));
}
void SetSecondaryButton(const GamepadButton& button) {
secondary_button_ = button;
}
void SetSecondaryButton(const GamepadBuilder::ButtonData& data) {
SetSecondaryButton(GamepadButton(data.pressed, data.touched, data.value));
}
void SetTouchpadData(const GamepadBuilder::ButtonData& data) {
touchpad_data_ = data;
}
void SetThumbstickData(const GamepadBuilder::ButtonData& data) {
thumbstick_data_ = data;
}
void AddOptionalButtonData(const GamepadBuilder::ButtonData& data);
void AddOptionalButtonData(const GamepadButton& button);
base::Optional<Gamepad> GetGamepad() const;
private:
base::Optional<GamepadButton> primary_button_;
base::Optional<GamepadButton> secondary_button_;
base::Optional<GamepadBuilder::ButtonData> touchpad_data_;
base::Optional<GamepadBuilder::ButtonData> thumbstick_data_;
std::vector<GamepadBuilder::ButtonData> optional_button_data_;
// Has one or more optional buttons that also have associated axes.
bool has_optional_axes_ = false;
device::mojom::XRHandedness handedness_;
double axis_deadzone_;
DISALLOW_COPY_AND_ASSIGN(XRStandardGamepadBuilder);
};
} // namespace device
#endif // DEVICE_VR_UTIL_XR_STANDARD_GAMEPAD_BUILDER_H_
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "device/gamepad/public/cpp/gamepads.h" #include "device/gamepad/public/cpp/gamepads.h"
#include "device/vr/public/mojom/isolated_xr_service.mojom.h" #include "device/vr/public/mojom/isolated_xr_service.mojom.h"
#include "device/vr/util/gamepad_builder.h" #include "device/vr/util/gamepad_builder.h"
#include "device/vr/util/xr_standard_gamepad_builder.h"
#include "device/vr/windows_mixed_reality/mixed_reality_renderloop.h" #include "device/vr/windows_mixed_reality/mixed_reality_renderloop.h"
#include "device/vr/windows_mixed_reality/wrappers/wmr_input_location.h" #include "device/vr/windows_mixed_reality/wrappers/wmr_input_location.h"
#include "device/vr/windows_mixed_reality/wrappers/wmr_input_manager.h" #include "device/vr/windows_mixed_reality/wrappers/wmr_input_manager.h"
...@@ -235,21 +236,17 @@ base::Optional<Gamepad> GetWebXRGamepad(ParsedInputState& input_state) { ...@@ -235,21 +236,17 @@ base::Optional<Gamepad> GetWebXRGamepad(ParsedInputState& input_state) {
if (input_state.source_state && input_state.source_state->description) if (input_state.source_state && input_state.source_state->description)
handedness = input_state.source_state->description->handedness; handedness = input_state.source_state->description->handedness;
// TODO(https://crbug.com/942201): Get correct ID string once WebXR spec issue XRStandardGamepadBuilder builder(handedness, kDeadzoneMinimum);
// #550 (https://github.com/immersive-web/webxr/issues/550) is resolved. builder.SetPrimaryButton(input_state.button_data[ButtonName::kSelect]);
GamepadBuilder builder("windows-mixed-reality", GamepadMapping::kXrStandard, builder.SetSecondaryButton(input_state.button_data[ButtonName::kGrip]);
handedness);
// button_data will either have both kTouchpad and kThumbstick, or neither.
builder.SetAxisDeadzone(kDeadzoneMinimum); if (input_state.button_data.find(ButtonName::kTouchpad) !=
input_state.button_data.end()) {
// The order of these buttons is dictated by the xr-standard Gamepad mapping. builder.SetTouchpadData(input_state.button_data[ButtonName::kTouchpad]);
// Thumbstick is considered the primary 2D input axis, while the touchpad is builder.SetThumbstickData(input_state.button_data[ButtonName::kThumbstick]);
// the secondary 2D input axis. If any of these are missing, map will give }
// us a default version, which is fine.
builder.AddButton(input_state.button_data[ButtonName::kSelect]);
builder.AddButton(input_state.button_data[ButtonName::kThumbstick]);
builder.AddButton(input_state.button_data[ButtonName::kGrip]);
builder.AddButton(input_state.button_data[ButtonName::kTouchpad]);
return builder.GetGamepad(); return builder.GetGamepad();
} }
...@@ -264,7 +261,7 @@ std::unordered_map<ButtonName, GamepadBuilder::ButtonData> ParseButtonState( ...@@ -264,7 +261,7 @@ std::unordered_map<ButtonName, GamepadBuilder::ButtonData> ParseButtonState(
data.pressed = source_state->IsSelectPressed(); data.pressed = source_state->IsSelectPressed();
data.touched = data.pressed; data.touched = data.pressed;
data.value = source_state->SelectPressedValue(); data.value = source_state->SelectPressedValue();
data.has_both_axes = false; data.type = GamepadBuilder::ButtonData::Type::kButton;
button_map[ButtonName::kSelect] = data; button_map[ButtonName::kSelect] = data;
// Add the grip button // Add the grip button
...@@ -272,7 +269,7 @@ std::unordered_map<ButtonName, GamepadBuilder::ButtonData> ParseButtonState( ...@@ -272,7 +269,7 @@ std::unordered_map<ButtonName, GamepadBuilder::ButtonData> ParseButtonState(
data.pressed = source_state->IsGrasped(); data.pressed = source_state->IsGrasped();
data.touched = data.pressed; data.touched = data.pressed;
data.value = data.pressed ? 1.0 : 0.0; data.value = data.pressed ? 1.0 : 0.0;
data.has_both_axes = false; data.type = GamepadBuilder::ButtonData::Type::kButton;
button_map[ButtonName::kGrip] = data; button_map[ButtonName::kGrip] = data;
// Select and grip are the only two required buttons, if we can't get the // Select and grip are the only two required buttons, if we can't get the
...@@ -287,7 +284,7 @@ std::unordered_map<ButtonName, GamepadBuilder::ButtonData> ParseButtonState( ...@@ -287,7 +284,7 @@ std::unordered_map<ButtonName, GamepadBuilder::ButtonData> ParseButtonState(
data.value = data.pressed ? 1.0 : 0.0; data.value = data.pressed ? 1.0 : 0.0;
// Invert the y axis because -1 is up in the Gamepad API, but down in WMR. // Invert the y axis because -1 is up in the Gamepad API, but down in WMR.
data.has_both_axes = true; data.type = GamepadBuilder::ButtonData::Type::kThumbstick;
data.x_axis = source_state->ThumbstickX(); data.x_axis = source_state->ThumbstickX();
data.y_axis = -source_state->ThumbstickY(); data.y_axis = -source_state->ThumbstickY();
...@@ -300,7 +297,7 @@ std::unordered_map<ButtonName, GamepadBuilder::ButtonData> ParseButtonState( ...@@ -300,7 +297,7 @@ std::unordered_map<ButtonName, GamepadBuilder::ButtonData> ParseButtonState(
data.value = data.pressed ? 1.0 : 0.0; data.value = data.pressed ? 1.0 : 0.0;
// The Touchpad does have Axes, but if it's not touched, they are 0. // The Touchpad does have Axes, but if it's not touched, they are 0.
data.has_both_axes = true; data.type = GamepadBuilder::ButtonData::Type::kTouchpad;
if (data.touched) { if (data.touched) {
// Invert the y axis because -1 is up in the Gamepad API, but down in WMR. // Invert the y axis because -1 is up in the Gamepad API, but down in WMR.
data.x_axis = source_state->TouchpadX(); data.x_axis = source_state->TouchpadX();
...@@ -558,21 +555,17 @@ ParsedInputState MixedRealityInputHelper::ParseWindowsSourceState( ...@@ -558,21 +555,17 @@ ParsedInputState MixedRealityInputHelper::ParseWindowsSourceState(
} else if (is_controller) { } else if (is_controller) {
description->target_ray_mode = device::mojom::XRTargetRayMode::POINTING; description->target_ray_mode = device::mojom::XRTargetRayMode::POINTING;
description->handedness = WindowsToMojoHandedness(source->Handedness()); description->handedness = WindowsToMojoHandedness(source->Handedness());
} else {
NOTREACHED();
}
if (is_controller) {
description->profiles.push_back("windows-mixed-reality"); description->profiles.push_back("windows-mixed-reality");
description->profiles.push_back("touchpad-thumbstick-controller"); description->profiles.push_back("touchpad-thumbstick-controller");
source_state->gamepad = GetWebXRGamepad(input_state);
} else {
NOTREACHED();
} }
source_state->description = std::move(description); source_state->description = std::move(description);
input_state.source_state = std::move(source_state); input_state.source_state = std::move(source_state);
input_state.source_state->gamepad = GetWebXRGamepad(input_state);
return input_state; return input_state;
} }
......
...@@ -27,11 +27,11 @@ static const std::map< ...@@ -27,11 +27,11 @@ static const std::map<
{device::XrButtonMaskFromId(device::XrButtonId::kGrip), {device::XrButtonMaskFromId(device::XrButtonId::kGrip),
ABI::Windows::UI::Input::Spatial::SpatialInteractionPressKind_Grasp}, ABI::Windows::UI::Input::Spatial::SpatialInteractionPressKind_Grasp},
// Touchpad. // Touchpad.
{device::XrButtonMaskFromId(device::XrButtonId::kAxisSecondary), {device::XrButtonMaskFromId(device::XrButtonId::kAxisTrackpad),
ABI::Windows::UI::Input::Spatial:: ABI::Windows::UI::Input::Spatial::
SpatialInteractionPressKind_Touchpad}, SpatialInteractionPressKind_Touchpad},
// Joystick. // Joystick.
{device::XrButtonMaskFromId(device::XrButtonId::kAxisPrimary), {device::XrButtonMaskFromId(device::XrButtonId::kAxisThumbstick),
ABI::Windows::UI::Input::Spatial:: ABI::Windows::UI::Input::Spatial::
SpatialInteractionPressKind_Thumbstick}, SpatialInteractionPressKind_Thumbstick},
}; };
......
...@@ -47,21 +47,22 @@ bool MockWMRInputSourceState::SupportsControllerProperties() const { ...@@ -47,21 +47,22 @@ bool MockWMRInputSourceState::SupportsControllerProperties() const {
} }
bool MockWMRInputSourceState::IsThumbstickPressed() const { bool MockWMRInputSourceState::IsThumbstickPressed() const {
return IsButtonPressed(XrButtonId::kAxisPrimary); return IsButtonPressed(XrButtonId::kAxisThumbstick);
} }
bool MockWMRInputSourceState::IsTouchpadPressed() const { bool MockWMRInputSourceState::IsTouchpadPressed() const {
return IsButtonPressed(XrButtonId::kAxisSecondary); return IsButtonPressed(XrButtonId::kAxisTrackpad);
} }
bool MockWMRInputSourceState::IsTouchpadTouched() const { bool MockWMRInputSourceState::IsTouchpadTouched() const {
auto touched = data_.supported_buttons & data_.buttons_touched & auto touched = data_.supported_buttons & data_.buttons_touched &
XrButtonMaskFromId(XrButtonId::kAxisSecondary); XrButtonMaskFromId(XrButtonId::kAxisTrackpad);
return touched != 0; return touched != 0;
} }
double MockWMRInputSourceState::ThumbstickX() const { double MockWMRInputSourceState::ThumbstickX() const {
double val = data_.axis_data[XrAxisOffsetFromId(XrButtonId::kAxisPrimary)].x; double val =
data_.axis_data[XrAxisOffsetFromId(XrButtonId::kAxisThumbstick)].x;
// Should be in [-1, 1] for joysticks. // Should be in [-1, 1] for joysticks.
DCHECK(val <= 1); DCHECK(val <= 1);
DCHECK(val >= -1); DCHECK(val >= -1);
...@@ -70,7 +71,8 @@ double MockWMRInputSourceState::ThumbstickX() const { ...@@ -70,7 +71,8 @@ double MockWMRInputSourceState::ThumbstickX() const {
// Invert the y axis because -1 is up in the Gamepad API, but down in WMR. // Invert the y axis because -1 is up in the Gamepad API, but down in WMR.
double MockWMRInputSourceState::ThumbstickY() const { double MockWMRInputSourceState::ThumbstickY() const {
double val = data_.axis_data[XrAxisOffsetFromId(XrButtonId::kAxisPrimary)].y; double val =
data_.axis_data[XrAxisOffsetFromId(XrButtonId::kAxisThumbstick)].y;
// Should be in [-1, 1] for joysticks. // Should be in [-1, 1] for joysticks.
DCHECK(val <= 1); DCHECK(val <= 1);
DCHECK(val >= -1); DCHECK(val >= -1);
...@@ -78,8 +80,7 @@ double MockWMRInputSourceState::ThumbstickY() const { ...@@ -78,8 +80,7 @@ double MockWMRInputSourceState::ThumbstickY() const {
} }
double MockWMRInputSourceState::TouchpadX() const { double MockWMRInputSourceState::TouchpadX() const {
double val = double val = data_.axis_data[XrAxisOffsetFromId(XrButtonId::kAxisTrackpad)].x;
data_.axis_data[XrAxisOffsetFromId(XrButtonId::kAxisSecondary)].x;
// Should be in [-1, 1] for touchpads. // Should be in [-1, 1] for touchpads.
DCHECK(val <= 1); DCHECK(val <= 1);
DCHECK(val >= -1); DCHECK(val >= -1);
...@@ -88,8 +89,7 @@ double MockWMRInputSourceState::TouchpadX() const { ...@@ -88,8 +89,7 @@ double MockWMRInputSourceState::TouchpadX() const {
// Invert the y axis because -1 is up in the Gamepad API, but down in WMR. // Invert the y axis because -1 is up in the Gamepad API, but down in WMR.
double MockWMRInputSourceState::TouchpadY() const { double MockWMRInputSourceState::TouchpadY() const {
double val = double val = data_.axis_data[XrAxisOffsetFromId(XrButtonId::kAxisTrackpad)].y;
data_.axis_data[XrAxisOffsetFromId(XrButtonId::kAxisSecondary)].y;
// Should be in [-1, 1] for touchpads. // Should be in [-1, 1] for touchpads.
DCHECK(val <= 1); DCHECK(val <= 1);
DCHECK(val >= -1); DCHECK(val >= -1);
......
...@@ -171,22 +171,20 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -171,22 +171,20 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
} }
if (gamepad.mapping == "xr-standard") { if (gamepad.mapping == "xr-standard") {
// Invert the y axis because gamepads follow the convention that -1 if (gamepad.buttons.length >= 3 && gamepad.axes.length >= 2) {
// is up/forwards, but we want to have a forward joystick/touchpad // Handle touchpad movement.
// input result in forward motion for the box. // Invert the y axis because gamepads follow the convention that -1
// is up/forwards, but we want to have a forward joystick/touchpad
// xr-standard Gamepads always have at least one touchpad/joystick. // input result in forward motion for the box.
// Its button will always be in the second slot. let dx = gamepad.axes[0];
let dx = gamepad.axes[0]; let dy = -gamepad.axes[1];
let dy = -gamepad.axes[1]; this.boxes[2].move(dx, dy);
this.boxes[1].move(dx, dy); }
if (gamepad.axes.length >= 4 && gamepad.buttons.length >= 4) { if (gamepad.buttons.length >= 4 && gamepad.axes.length >= 4) {
// If an xr-standard Gamepad has a secondary touchpad/joystick, its // Handle thumbstick movement.
// button will be in the 4th slot and it will use the second pair let dx = gamepad.axes[2];
// of input axes. let dy = -gamepad.axes[3];
dx = gamepad.axes[2];
dy = -gamepad.axes[3];
this.boxes[3].move(dx, dy); this.boxes[3].move(dx, dy);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment