From 88aabac10fb8851d00de4062fee1446d8abc1f57 Mon Sep 17 00:00:00 2001 From: Tom Birdsong Date: Wed, 1 Dec 2021 12:04:14 -0500 Subject: [PATCH] feat(XR): Remap OpenVR gamepad for OpenXR gamepad support --- .../InteractorStyleTrackballCamera/index.js | 13 +++-- .../Core/RenderWindowInteractor/Constants.js | 14 ++++- .../Core/RenderWindowInteractor/index.js | 53 ++++++++++--------- .../Rendering/OpenGL/RenderWindow/index.js | 4 ++ 4 files changed, 55 insertions(+), 29 deletions(-) diff --git a/Sources/Interaction/Style/InteractorStyleTrackballCamera/index.js b/Sources/Interaction/Style/InteractorStyleTrackballCamera/index.js index c07be922153..b918e2fc6c6 100644 --- a/Sources/Interaction/Style/InteractorStyleTrackballCamera/index.js +++ b/Sources/Interaction/Style/InteractorStyleTrackballCamera/index.js @@ -58,7 +58,7 @@ function vtkInteractorStyleTrackballCamera(publicAPI, model) { ed && ed.pressed && ed.device === Device.RightController && - ed.input === Input.TrackPad + (ed.input === Input.Trigger || ed.input === Input.TrackPad) ) { publicAPI.startCameraPose(); return; @@ -67,7 +67,7 @@ function vtkInteractorStyleTrackballCamera(publicAPI, model) { ed && !ed.pressed && ed.device === Device.RightController && - ed.input === Input.TrackPad && + (ed.input === Input.Trigger || ed.input === Input.TrackPad) && model.state === States.IS_CAMERA_POSE ) { publicAPI.endCameraPose(); @@ -91,13 +91,18 @@ function vtkInteractorStyleTrackballCamera(publicAPI, model) { const oldTrans = camera.getPhysicalTranslation(); // look at the y axis to determine how fast / what direction to move - const speed = ed.gamepad.axes[1]; + const speed = 0.5; // ed.gamepad.axes[1]; // 0.05 meters / frame movement const pscale = speed * 0.05 * camera.getPhysicalScale(); // convert orientation to world coordinate direction - const dir = camera.physicalOrientationToWorldDirection(ed.orientation); + const dir = camera.physicalOrientationToWorldDirection([ + ed.orientation.x, + ed.orientation.y, + ed.orientation.z, + ed.orientation.w, + ]); camera.setPhysicalTranslation( oldTrans[0] + dir[0] * pscale, diff --git a/Sources/Rendering/Core/RenderWindowInteractor/Constants.js b/Sources/Rendering/Core/RenderWindowInteractor/Constants.js index 79a4de2fd8a..546bdc42522 100644 --- a/Sources/Rendering/Core/RenderWindowInteractor/Constants.js +++ b/Sources/Rendering/Core/RenderWindowInteractor/Constants.js @@ -9,10 +9,22 @@ export const Input = { Trigger: 1, TrackPad: 2, Grip: 3, - ApplicationMenu: 4, + Thumbstick: 4, + A: 5, + B: 6, + ApplicationMenu: 7, // Not exposed in WebXR API +}; + +export const Axis = { + Unknown: 0, + TouchpadX: 1, + TouchpadY: 2, + ThumbstickX: 3, + ThumbstickY: 4, }; export default { Device, Input, + Axis, }; diff --git a/Sources/Rendering/Core/RenderWindowInteractor/index.js b/Sources/Rendering/Core/RenderWindowInteractor/index.js index b5ec257e2f8..26581e9ec9c 100644 --- a/Sources/Rendering/Core/RenderWindowInteractor/index.js +++ b/Sources/Rendering/Core/RenderWindowInteractor/index.js @@ -12,11 +12,13 @@ const { vtkWarningMacro, vtkErrorMacro, normalizeWheel, vtkOnceErrorMacro } = // ---------------------------------------------------------------------------- const deviceInputMap = { - 'OpenVR Gamepad': [ - Input.TrackPad, + 'xr-standard': [ Input.Trigger, Input.Grip, - Input.ApplicationMenu, + Input.TrackPad, + Input.Thumbstick, + Input.A, + Input.B, ], }; @@ -415,55 +417,58 @@ function vtkRenderWindowInteractor(publicAPI, model) { } }; - publicAPI.updateGamepads = (displayId) => { - const gamepads = navigator.getGamepads(); - + publicAPI.updateXRGamepads = (xrSession, xrFrame, xrRefSpace) => { // watch for when buttons change state and fire events - for (let i = 0; i < gamepads.length; ++i) { - const gp = gamepads[i]; - if (gp && gp.displayId === displayId) { + xrSession.inputSources.forEach((inputSource) => { + const pose = xrFrame.getPose(inputSource.gripSpace, xrRefSpace); + const gp = inputSource.gamepad; + const hand = inputSource.handedness; + if (gp) { if (!(gp.index in model.lastGamepadValues)) { - model.lastGamepadValues[gp.index] = { buttons: {} }; + model.lastGamepadValues[gp.index] = { + left: { buttons: {} }, + right: { buttons: {} }, + }; } for (let b = 0; b < gp.buttons.length; ++b) { - if (!(b in model.lastGamepadValues[gp.index].buttons)) { - model.lastGamepadValues[gp.index].buttons[b] = false; + if (!(b in model.lastGamepadValues[gp.index][hand].buttons)) { + model.lastGamepadValues[gp.index][hand].buttons[b] = false; } if ( - model.lastGamepadValues[gp.index].buttons[b] !== + model.lastGamepadValues[gp.index][hand].buttons[b] !== gp.buttons[b].pressed ) { publicAPI.button3DEvent({ gamepad: gp, - position: gp.pose.position, - orientation: gp.pose.orientation, + position: pose.transform.position, + orientation: pose.transform.orientation, pressed: gp.buttons[b].pressed, device: - gp.hand === 'left' + inputSource.handedness === 'left' ? Device.LeftController : Device.RightController, input: - deviceInputMap[gp.id] && deviceInputMap[gp.id][b] - ? deviceInputMap[gp.id][b] + deviceInputMap[gp.mapping] && deviceInputMap[gp.mapping][b] + ? deviceInputMap[gp.mapping][b] : Input.Trigger, }); - model.lastGamepadValues[gp.index].buttons[b] = + model.lastGamepadValues[gp.index][hand].buttons[b] = gp.buttons[b].pressed; } - if (model.lastGamepadValues[gp.index].buttons[b]) { + if (model.lastGamepadValues[gp.index][hand].buttons[b]) { publicAPI.move3DEvent({ gamepad: gp, - position: gp.pose.position, - orientation: gp.pose.orientation, + position: pose.transform.position, + orientation: pose.transform.orientation, device: - gp.hand === 'left' + inputSource.handedness === 'left' ? Device.LeftController : Device.RightController, }); } } } - } + }); }; publicAPI.handleMouseMove = (event) => { diff --git a/Sources/Rendering/OpenGL/RenderWindow/index.js b/Sources/Rendering/OpenGL/RenderWindow/index.js index ed6d87ed230..36897d8dc2f 100644 --- a/Sources/Rendering/OpenGL/RenderWindow/index.js +++ b/Sources/Rendering/OpenGL/RenderWindow/index.js @@ -354,6 +354,10 @@ function vtkOpenGLRenderWindow(publicAPI, model) { publicAPI.xrRender = async (t, frame) => { const xrSession = frame.session; + model.renderable + .getInteractor() + .updateXRGamepads(xrSession, frame, model.xrReferenceSpace); + model.xrSceneFrame = model.xrSession.requestAnimationFrame( publicAPI.xrRender );