From 84c6bb47976da7a2725582c68064d0ec9765783a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 20 Dec 2018 14:34:12 +1300 Subject: [PATCH] Introduce optional timestamp into internal Controller axis values This can be used to determine if consecutive identifical values should be output. --- .../controllers/src/controllers/AxisValue.cpp | 18 +++++++++++ .../controllers/src/controllers/AxisValue.h | 31 +++++++++++++++++++ .../src/controllers/InputDevice.cpp | 6 ++-- .../controllers/src/controllers/InputDevice.h | 5 +-- .../src/input-plugins/KeyboardMouseDevice.cpp | 30 +++++++++--------- .../src/input-plugins/TouchscreenDevice.cpp | 13 ++++---- .../TouchscreenVirtualPadDevice.cpp | 18 ++++++----- plugins/hifiSdl2/src/Joystick.cpp | 6 ++-- .../oculus/src/OculusControllerManager.cpp | 16 +++++----- plugins/openvr/src/ViveControllerManager.cpp | 14 ++++----- 10 files changed, 106 insertions(+), 51 deletions(-) create mode 100644 libraries/controllers/src/controllers/AxisValue.cpp create mode 100644 libraries/controllers/src/controllers/AxisValue.h diff --git a/libraries/controllers/src/controllers/AxisValue.cpp b/libraries/controllers/src/controllers/AxisValue.cpp new file mode 100644 index 0000000000..49201dbf7a --- /dev/null +++ b/libraries/controllers/src/controllers/AxisValue.cpp @@ -0,0 +1,18 @@ +// +// AxisValue.cpp +// +// Created by David Rowe on 14 Dec 2018. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AxisValue.h" + +namespace controller { + + AxisValue::AxisValue(const float value, const quint64 timestamp) : + value(value), timestamp(timestamp) { } + +} diff --git a/libraries/controllers/src/controllers/AxisValue.h b/libraries/controllers/src/controllers/AxisValue.h new file mode 100644 index 0000000000..67fd5736b8 --- /dev/null +++ b/libraries/controllers/src/controllers/AxisValue.h @@ -0,0 +1,31 @@ +// +// AxisValue.h +// +// Created by David Rowe on 13 Dec 2018. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_controllers_AxisValue_h +#define hifi_controllers_AxisValue_h + +#include + +namespace controller { + + struct AxisValue { + public: + float value { 0.0f }; + // The value can be timestamped to determine if consecutive identical values should be output (e.g., mouse movement). + quint64 timestamp { 0 }; + + AxisValue() {} + AxisValue(const float value, const quint64 timestamp); + }; + +} + +#endif // hifi_controllers_AxisValue_h diff --git a/libraries/controllers/src/controllers/InputDevice.cpp b/libraries/controllers/src/controllers/InputDevice.cpp index a907842a17..e46bb3c8b5 100644 --- a/libraries/controllers/src/controllers/InputDevice.cpp +++ b/libraries/controllers/src/controllers/InputDevice.cpp @@ -26,12 +26,12 @@ namespace controller { return 0.0f; } - float InputDevice::getAxis(int channel) const { + AxisValue InputDevice::getAxis(int channel) const { auto axis = _axisStateMap.find(channel); if (axis != _axisStateMap.end()) { return (*axis).second; } else { - return 0.0f; + return AxisValue(); } } @@ -71,7 +71,7 @@ namespace controller { float InputDevice::getValue(ChannelType channelType, uint16_t channel) const { switch (channelType) { case ChannelType::AXIS: - return getAxis(channel); + return getAxis(channel).value; case ChannelType::BUTTON: return getButton(channel); diff --git a/libraries/controllers/src/controllers/InputDevice.h b/libraries/controllers/src/controllers/InputDevice.h index 7479ef7b75..1d4ab1c5d8 100644 --- a/libraries/controllers/src/controllers/InputDevice.h +++ b/libraries/controllers/src/controllers/InputDevice.h @@ -16,6 +16,7 @@ #include +#include "AxisValue.h" #include "Pose.h" #include "Input.h" #include "StandardControls.h" @@ -103,12 +104,12 @@ public: using Pointer = std::shared_ptr; typedef std::unordered_set ButtonPressedMap; - typedef std::map AxisStateMap; + typedef std::map AxisStateMap; typedef std::map PoseStateMap; // Get current state for each channel float getButton(int channel) const; - float getAxis(int channel) const; + AxisValue getAxis(int channel) const; Pose getPose(int channel) const; float getValue(const Input& input) const; diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index c14db86ae9..ebc5a5cd3e 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -27,15 +27,15 @@ void KeyboardMouseDevice::pluginUpdate(float deltaTime, const controller::InputC _inputDevice->update(deltaTime, inputCalibrationData); eraseMouseClicked(); - _inputDevice->_axisStateMap[MOUSE_AXIS_X] = _lastCursor.x(); - _inputDevice->_axisStateMap[MOUSE_AXIS_Y] = _lastCursor.y(); + _inputDevice->_axisStateMap[MOUSE_AXIS_X].value = _lastCursor.x(); + _inputDevice->_axisStateMap[MOUSE_AXIS_Y].value = _lastCursor.y(); QPoint currentMove = _lastCursor - _previousCursor; - _inputDevice->_axisStateMap[MOUSE_AXIS_X_POS] = (currentMove.x() > 0 ? currentMove.x() : 0.0f); - _inputDevice->_axisStateMap[MOUSE_AXIS_X_NEG] = (currentMove.x() < 0 ? -currentMove.x() : 0.0f); + _inputDevice->_axisStateMap[MOUSE_AXIS_X_POS].value = (currentMove.x() > 0 ? currentMove.x() : 0.0f); + _inputDevice->_axisStateMap[MOUSE_AXIS_X_NEG].value = (currentMove.x() < 0 ? -currentMove.x() : 0.0f); // Y mouse is inverted positive is pointing up the screen - _inputDevice->_axisStateMap[MOUSE_AXIS_Y_POS] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); - _inputDevice->_axisStateMap[MOUSE_AXIS_Y_NEG] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); + _inputDevice->_axisStateMap[MOUSE_AXIS_Y_POS].value = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); + _inputDevice->_axisStateMap[MOUSE_AXIS_Y_NEG].value = (currentMove.y() > 0 ? currentMove.y() : 0.0f); _previousCursor = _lastCursor; }); @@ -124,10 +124,11 @@ void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event) { void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) { auto currentMove = event->angleDelta() / 120.0f; - _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_X_POS).getChannel()] = (currentMove.x() > 0 ? currentMove.x() : 0.0f); - _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_X_NEG).getChannel()] = (currentMove.x() < 0 ? -currentMove.x() : 0.0f); - _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_Y_POS).getChannel()] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); - _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_Y_NEG).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); + // ####### TODO: Use AxisValue timestamps? + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_X_POS).getChannel()].value = (currentMove.x() > 0 ? currentMove.x() : 0, 0); + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_X_NEG).getChannel()].value = (currentMove.x() < 0 ? -currentMove.x() : 0, 0); + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_Y_POS).getChannel()].value = (currentMove.y() > 0 ? currentMove.y() : 0); + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_Y_NEG).getChannel()].value = (currentMove.y() < 0 ? -currentMove.y() : 0); } glm::vec2 evalAverageTouchPoints(const QList& points) { @@ -167,11 +168,12 @@ void KeyboardMouseDevice::touchUpdateEvent(const QTouchEvent* event) { } else { auto currentMove = currentPos - _lastTouch; - _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_POS).getChannel()] = (currentMove.x > 0 ? currentMove.x : 0.0f); - _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_NEG).getChannel()] = (currentMove.x < 0 ? -currentMove.x : 0.0f); + // ####### TODO: Use AxisValue timestamp fields? + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_POS).getChannel()].value = (currentMove.x > 0 ? currentMove.x : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_NEG).getChannel()].value = (currentMove.x < 0 ? -currentMove.x : 0.0f); // Y mouse is inverted positive is pointing up the screen - _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_POS).getChannel()] = (currentMove.y < 0 ? -currentMove.y : 0.0f); - _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_NEG).getChannel()] = (currentMove.y > 0 ? currentMove.y : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_POS).getChannel()].value = (currentMove.y < 0 ? -currentMove.y : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_NEG).getChannel()].value = (currentMove.y > 0 ? currentMove.y : 0.0f); } _lastTouch = currentPos; diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenDevice.cpp b/libraries/input-plugins/src/input-plugins/TouchscreenDevice.cpp index 20952df4d7..804ab3585b 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/TouchscreenDevice.cpp @@ -38,28 +38,29 @@ void TouchscreenDevice::pluginUpdate(float deltaTime, const controller::InputCal _inputDevice->update(deltaTime, inputCalibrationData); }); + // ####### TODO: Use AxisValue timestamp fields? float distanceScaleX, distanceScaleY; if (_touchPointCount == 1) { if (_firstTouchVec.x < _currentTouchVec.x) { distanceScaleX = (_currentTouchVec.x - _firstTouchVec.x) / _screenDPIScale.x; - _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_POS).getChannel()] = distanceScaleX; + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_POS).getChannel()].value = distanceScaleX; } else if (_firstTouchVec.x > _currentTouchVec.x) { distanceScaleX = (_firstTouchVec.x - _currentTouchVec.x) / _screenDPIScale.x; - _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_NEG).getChannel()] = distanceScaleX; + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_NEG).getChannel()].value = distanceScaleX; } // Y axis is inverted, positive is pointing up the screen if (_firstTouchVec.y > _currentTouchVec.y) { distanceScaleY = (_firstTouchVec.y - _currentTouchVec.y) / _screenDPIScale.y; - _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_POS).getChannel()] = distanceScaleY; + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_POS).getChannel()].value = distanceScaleY; } else if (_firstTouchVec.y < _currentTouchVec.y) { distanceScaleY = (_currentTouchVec.y - _firstTouchVec.y) / _screenDPIScale.y; - _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_NEG).getChannel()] = distanceScaleY; + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_NEG).getChannel()].value = distanceScaleY; } } else if (_touchPointCount == 2) { if (_pinchScale > _lastPinchScale && _pinchScale != 0) { - _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_GESTURE_PINCH_POS).getChannel()] = 1.0f; + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_GESTURE_PINCH_POS).getChannel()].value = 1.0f; } else if (_pinchScale != 0) { - _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_GESTURE_PINCH_NEG).getChannel()] = 1.0f; + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_GESTURE_PINCH_NEG).getChannel()].value = 1.0; } _lastPinchScale = _pinchScale; } diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp index 32194e1b84..247f484c95 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp @@ -135,8 +135,8 @@ glm::vec2 TouchscreenVirtualPadDevice::clippedPointInCircle(float radius, glm::v void TouchscreenVirtualPadDevice::processInputDeviceForMove(VirtualPad::Manager& virtualPadManager) { vec2 clippedPoint = clippedPointInCircle(_fixedRadiusForCalc, _moveRefTouchPoint, _moveCurrentTouchPoint); - _inputDevice->_axisStateMap[controller::LX] = (clippedPoint.x - _moveRefTouchPoint.x) / _fixedRadiusForCalc; - _inputDevice->_axisStateMap[controller::LY] = (clippedPoint.y - _moveRefTouchPoint.y) / _fixedRadiusForCalc; + _inputDevice->_axisStateMap[controller::LX].value = (clippedPoint.x - _moveRefTouchPoint.x) / _fixedRadiusForCalc; + _inputDevice->_axisStateMap[controller::LY].value = (clippedPoint.y - _moveRefTouchPoint.y) / _fixedRadiusForCalc; virtualPadManager.getLeftVirtualPad()->setFirstTouch(_moveRefTouchPoint); virtualPadManager.getLeftVirtualPad()->setCurrentTouch(clippedPoint); @@ -148,8 +148,10 @@ void TouchscreenVirtualPadDevice::processInputDeviceForView() { // We use average across how many times we've got touchUpdate events. // Using the average instead of the full deltaX and deltaY, makes deltaTime in MyAvatar dont't accelerate rotation when there is a low touchUpdate rate (heavier domains). // (Because it multiplies this input value by deltaTime (with a coefficient)). - _inputDevice->_axisStateMap[controller::RX] = _viewTouchUpdateCount == 0 ? 0 : (_viewCurrentTouchPoint.x - _viewRefTouchPoint.x) / _viewTouchUpdateCount; - _inputDevice->_axisStateMap[controller::RY] = _viewTouchUpdateCount == 0 ? 0 : (_viewCurrentTouchPoint.y - _viewRefTouchPoint.y) / _viewTouchUpdateCount; + _inputDevice->_axisStateMap[controller::RX].value = + _viewTouchUpdateCount == 0 ? 0 : (_viewCurrentTouchPoint.x - _viewRefTouchPoint.x) / _viewTouchUpdateCount; + _inputDevice->_axisStateMap[controller::RY].value = + _viewTouchUpdateCount == 0 ? 0 : (_viewCurrentTouchPoint.y - _viewRefTouchPoint.y) / _viewTouchUpdateCount; // after use, save last touch point as ref _viewRefTouchPoint = _viewCurrentTouchPoint; @@ -442,8 +444,8 @@ void TouchscreenVirtualPadDevice::moveTouchUpdate(glm::vec2 touchPoint) { void TouchscreenVirtualPadDevice::moveTouchEnd() { if (_moveHasValidTouch) { // do stuff once _moveHasValidTouch = false; - _inputDevice->_axisStateMap[controller::LX] = 0; - _inputDevice->_axisStateMap[controller::LY] = 0; + _inputDevice->_axisStateMap[controller::LX].value = 0; + _inputDevice->_axisStateMap[controller::LY].value = 0; } } @@ -465,8 +467,8 @@ void TouchscreenVirtualPadDevice::viewTouchUpdate(glm::vec2 touchPoint) { void TouchscreenVirtualPadDevice::viewTouchEnd() { if (_viewHasValidTouch) { // do stuff once _viewHasValidTouch = false; - _inputDevice->_axisStateMap[controller::RX] = 0; - _inputDevice->_axisStateMap[controller::RY] = 0; + _inputDevice->_axisStateMap[controller::RX].value = 0; + _inputDevice->_axisStateMap[controller::RY].value = 0; } } diff --git a/plugins/hifiSdl2/src/Joystick.cpp b/plugins/hifiSdl2/src/Joystick.cpp index be4ad6e4ee..ad0b459544 100644 --- a/plugins/hifiSdl2/src/Joystick.cpp +++ b/plugins/hifiSdl2/src/Joystick.cpp @@ -46,8 +46,8 @@ void Joystick::closeJoystick() { void Joystick::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { for (auto axisState : _axisStateMap) { - if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) { - _axisStateMap[axisState.first] = 0.0f; + if (fabsf(axisState.second.value) < CONTROLLER_THRESHOLD) { + _axisStateMap[axisState.first].value = 0.0f; } } } @@ -59,7 +59,7 @@ void Joystick::focusOutEvent() { void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) { SDL_GameControllerAxis axis = (SDL_GameControllerAxis) event.axis; - _axisStateMap[makeInput((controller::StandardAxisChannel)axis).getChannel()] = (float)event.value / MAX_AXIS; + _axisStateMap[makeInput((controller::StandardAxisChannel)axis).getChannel()].value = (float)event.value / MAX_AXIS; } void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { diff --git a/plugins/oculus/src/OculusControllerManager.cpp b/plugins/oculus/src/OculusControllerManager.cpp index 392d990638..76ff4a1755 100644 --- a/plugins/oculus/src/OculusControllerManager.cpp +++ b/plugins/oculus/src/OculusControllerManager.cpp @@ -258,15 +258,15 @@ void OculusControllerManager::TouchDevice::update(float deltaTime, using namespace controller; // Axes const auto& inputState = _parent._touchInputState; - _axisStateMap[LX] = inputState.Thumbstick[ovrHand_Left].x; - _axisStateMap[LY] = inputState.Thumbstick[ovrHand_Left].y; - _axisStateMap[LT] = inputState.IndexTrigger[ovrHand_Left]; - _axisStateMap[LEFT_GRIP] = inputState.HandTrigger[ovrHand_Left]; + _axisStateMap[LX].value = inputState.Thumbstick[ovrHand_Left].x; + _axisStateMap[LY].value = inputState.Thumbstick[ovrHand_Left].y; + _axisStateMap[LT].value = inputState.IndexTrigger[ovrHand_Left]; + _axisStateMap[LEFT_GRIP].value = inputState.HandTrigger[ovrHand_Left]; - _axisStateMap[RX] = inputState.Thumbstick[ovrHand_Right].x; - _axisStateMap[RY] = inputState.Thumbstick[ovrHand_Right].y; - _axisStateMap[RT] = inputState.IndexTrigger[ovrHand_Right]; - _axisStateMap[RIGHT_GRIP] = inputState.HandTrigger[ovrHand_Right]; + _axisStateMap[RX].value = inputState.Thumbstick[ovrHand_Right].x; + _axisStateMap[RY].value = inputState.Thumbstick[ovrHand_Right].y; + _axisStateMap[RT].value = inputState.IndexTrigger[ovrHand_Right]; + _axisStateMap[RIGHT_GRIP].value = inputState.HandTrigger[ovrHand_Right]; // Buttons for (const auto& pair : BUTTON_MAP) { diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 53c23403a5..3aea5f1ce0 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -928,8 +928,8 @@ void ViveControllerManager::InputDevice::partitionTouchpad(int sButton, int xAxi const float CENTER_DEADBAND = 0.6f; const float DIAGONAL_DIVIDE_IN_RADIANS = PI / 4.0f; if (_buttonPressedMap.find(sButton) != _buttonPressedMap.end()) { - float absX = abs(_axisStateMap[xAxis]); - float absY = abs(_axisStateMap[yAxis]); + float absX = abs(_axisStateMap[xAxis].value); + float absY = abs(_axisStateMap[yAxis].value); glm::vec2 cartesianQuadrantI(absX, absY); float angle = glm::atan(cartesianQuadrantI.y / cartesianQuadrantI.x); float radius = glm::length(cartesianQuadrantI); @@ -956,10 +956,10 @@ void ViveControllerManager::InputDevice::handleAxisEvent(float deltaTime, uint32 } else { stick = _filteredRightStick.process(deltaTime, stick); } - _axisStateMap[isLeftHand ? LX : RX] = stick.x; - _axisStateMap[isLeftHand ? LY : RY] = stick.y; + _axisStateMap[isLeftHand ? LX : RX].value = stick.x; + _axisStateMap[isLeftHand ? LY : RY].value = stick.y; } else if (axis == vr::k_EButton_SteamVR_Trigger) { - _axisStateMap[isLeftHand ? LT : RT] = x; + _axisStateMap[isLeftHand ? LT : RT].value = x; // The click feeling on the Vive controller trigger represents a value of *precisely* 1.0, // so we can expose that as an additional button if (x >= 1.0f) { @@ -1000,7 +1000,7 @@ void ViveControllerManager::InputDevice::handleButtonEvent(float deltaTime, uint if (button == vr::k_EButton_ApplicationMenu) { _buttonPressedMap.insert(isLeftHand ? LEFT_APP_MENU : RIGHT_APP_MENU); } else if (button == vr::k_EButton_Grip) { - _axisStateMap[isLeftHand ? LEFT_GRIP : RIGHT_GRIP] = 1.0f; + _axisStateMap[isLeftHand ? LEFT_GRIP : RIGHT_GRIP].value = 1.0f; } else if (button == vr::k_EButton_SteamVR_Trigger) { _buttonPressedMap.insert(isLeftHand ? LT : RT); } else if (button == vr::k_EButton_SteamVR_Touchpad) { @@ -1008,7 +1008,7 @@ void ViveControllerManager::InputDevice::handleButtonEvent(float deltaTime, uint } } else { if (button == vr::k_EButton_Grip) { - _axisStateMap[isLeftHand ? LEFT_GRIP : RIGHT_GRIP] = 0.0f; + _axisStateMap[isLeftHand ? LEFT_GRIP : RIGHT_GRIP].value = 0.0f; } }