From 01ef5aabcb0b2f685db9e3a151158437bdc24c86 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 26 Feb 2016 14:38:35 -0800 Subject: [PATCH 1/2] OpenVR: More reliable detection of left and right hands --- plugins/openvr/src/ViveControllerManager.cpp | 110 +++++++++---------- plugins/openvr/src/ViveControllerManager.h | 15 +-- 2 files changed, 61 insertions(+), 64 deletions(-) diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 071d5fd631..749c9ea2e4 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -60,10 +60,10 @@ void ViveControllerManager::activate() { [this] (bool clicked) { this->setRenderControllers(clicked); }, true, true); - if (!_hmd) { - _hmd = acquireOpenVrSystem(); + if (!_system) { + _system = acquireOpenVrSystem(); } - Q_ASSERT(_hmd); + Q_ASSERT(_system); // OpenVR provides 3d mesh representations of the controllers // Disabled controller rendering code @@ -71,7 +71,7 @@ void ViveControllerManager::activate() { auto renderModels = vr::VRRenderModels(); vr::RenderModel_t model; - if (!_hmd->LoadRenderModel(CONTROLLER_MODEL_STRING, &model)) { + if (!_system->LoadRenderModel(CONTROLLER_MODEL_STRING, &model)) { qDebug() << QString("Unable to load render model %1\n").arg(CONTROLLER_MODEL_STRING); } else { model::Mesh* mesh = new model::Mesh(); @@ -118,7 +118,7 @@ void ViveControllerManager::activate() { } */ - // unregister with UserInputMapper + // register with UserInputMapper auto userInputMapper = DependencyManager::get(); userInputMapper->registerDevice(_inputDevice); _registeredWithInputMapper = true; @@ -130,9 +130,9 @@ void ViveControllerManager::deactivate() { _container->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS); _container->removeMenu(MENU_PATH); - if (_hmd) { + if (_system) { releaseOpenVrSystem(); - _hmd = nullptr; + _system = nullptr; } _inputDevice->_poseStateMap.clear(); @@ -226,56 +226,56 @@ void ViveControllerManager::pluginUpdate(float deltaTime, const controller::Inpu void ViveControllerManager::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) { _poseStateMap.clear(); - _buttonPressedMap.clear(); PerformanceTimer perfTimer("ViveControllerManager::update"); + auto leftHandDeviceIndex = _system->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_LeftHand); + auto rightHandDeviceIndex = _system->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_RightHand); + + if (!jointsCaptured) { + handleHandController(leftHandDeviceIndex, inputCalibrationData, true); + handleHandController(rightHandDeviceIndex, inputCalibrationData, false); + } + int numTrackedControllers = 0; - - for (vr::TrackedDeviceIndex_t device = vr::k_unTrackedDeviceIndex_Hmd + 1; - device < vr::k_unMaxTrackedDeviceCount && numTrackedControllers < 2; ++device) { - - if (!_hmd->IsTrackedDeviceConnected(device)) { - continue; - } - - if (_hmd->GetTrackedDeviceClass(device) != vr::TrackedDeviceClass_Controller) { - continue; - } - - if (!_trackedDevicePose[device].bPoseIsValid) { - continue; - } - + if (leftHandDeviceIndex != vr::k_unTrackedDeviceIndexInvalid) { numTrackedControllers++; - bool left = numTrackedControllers == 2; + } + if (rightHandDeviceIndex != vr::k_unTrackedDeviceIndexInvalid) { + numTrackedControllers++; + } + _trackedControllers = numTrackedControllers; +} - if (!jointsCaptured) { - const mat4& mat = _trackedDevicePoseMat4[device]; - const vec3 linearVelocity = _trackedDeviceLinearVelocities[device]; - const vec3 angularVelocity = _trackedDeviceAngularVelocities[device]; - handlePoseEvent(inputCalibrationData, mat, linearVelocity, angularVelocity, numTrackedControllers - 1); - } +void ViveControllerManager::InputDevice::handleHandController(uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand) { + + if (_system->IsTrackedDeviceConnected(deviceIndex) && + _system->GetTrackedDeviceClass(deviceIndex) == vr::TrackedDeviceClass_Controller && + _trackedDevicePose[deviceIndex].bPoseIsValid) { + + // process pose + const mat4& mat = _trackedDevicePoseMat4[deviceIndex]; + const vec3 linearVelocity = _trackedDeviceLinearVelocities[deviceIndex]; + const vec3 angularVelocity = _trackedDeviceAngularVelocities[deviceIndex]; + handlePoseEvent(inputCalibrationData, mat, linearVelocity, angularVelocity, isLeftHand); - // handle inputs vr::VRControllerState_t controllerState = vr::VRControllerState_t(); - if (_hmd->GetControllerState(device, &controllerState)) { - //qDebug() << (numTrackedControllers == 1 ? "Left: " : "Right: "); - //qDebug() << "Trackpad: " << controllerState.rAxis[0].x << " " << controllerState.rAxis[0].y; - //qDebug() << "Trigger: " << controllerState.rAxis[1].x << " " << controllerState.rAxis[1].y; + if (_system->GetControllerState(deviceIndex, &controllerState)) { + + // process each button for (uint32_t i = 0; i < vr::k_EButton_Max; ++i) { auto mask = vr::ButtonMaskFromId((vr::EVRButtonId)i); bool pressed = 0 != (controllerState.ulButtonPressed & mask); - handleButtonEvent(i, pressed, left); + handleButtonEvent(i, pressed, isLeftHand); } + + // process each axis for (uint32_t i = 0; i < vr::k_unControllerStateAxisCount; i++) { - handleAxisEvent(i, controllerState.rAxis[i].x, controllerState.rAxis[i].y, left); + handleAxisEvent(i, controllerState.rAxis[i].x, controllerState.rAxis[i].y, isLeftHand); } } } - - _trackedControllers = numTrackedControllers; } void ViveControllerManager::InputDevice::focusOutEvent() { @@ -284,42 +284,38 @@ void ViveControllerManager::InputDevice::focusOutEvent() { }; // These functions do translation from the Steam IDs to the standard controller IDs -void ViveControllerManager::InputDevice::handleAxisEvent(uint32_t axis, float x, float y, bool left) { +void ViveControllerManager::InputDevice::handleAxisEvent(uint32_t axis, float x, float y, bool isLeftHand) { //FIX ME? It enters here every frame: probably we want to enter only if an event occurs axis += vr::k_EButton_Axis0; using namespace controller; if (axis == vr::k_EButton_SteamVR_Touchpad) { - _axisStateMap[left ? LX : RX] = x; - _axisStateMap[left ? LY : RY] = y; + _axisStateMap[isLeftHand ? LX : RX] = x; + _axisStateMap[isLeftHand ? LY : RY] = y; } else if (axis == vr::k_EButton_SteamVR_Trigger) { - _axisStateMap[left ? LT : RT] = x; + _axisStateMap[isLeftHand ? LT : RT] = x; } } // These functions do translation from the Steam IDs to the standard controller IDs -void ViveControllerManager::InputDevice::handleButtonEvent(uint32_t button, bool pressed, bool left) { +void ViveControllerManager::InputDevice::handleButtonEvent(uint32_t button, bool pressed, bool isLeftHand) { if (!pressed) { return; } if (button == vr::k_EButton_ApplicationMenu) { - _buttonPressedMap.insert(left ? controller::LEFT_PRIMARY_THUMB : controller::RIGHT_PRIMARY_THUMB); + _buttonPressedMap.insert(isLeftHand ? controller::LEFT_PRIMARY_THUMB : controller::RIGHT_PRIMARY_THUMB); } else if (button == vr::k_EButton_Grip) { - // Tony says these are harder to reach, so make them the meta buttons - _buttonPressedMap.insert(left ? controller::LB : controller::RB); + _buttonPressedMap.insert(isLeftHand ? controller::LB : controller::RB); } else if (button == vr::k_EButton_SteamVR_Trigger) { - _buttonPressedMap.insert(left ? controller::LT : controller::RT); + _buttonPressedMap.insert(isLeftHand ? controller::LT : controller::RT); } else if (button == vr::k_EButton_SteamVR_Touchpad) { - _buttonPressedMap.insert(left ? controller::LS : controller::RS); - } else if (button == vr::k_EButton_System) { - //FIX ME: not able to ovrewrite the behaviour of this button - _buttonPressedMap.insert(left ? controller::LEFT_SECONDARY_THUMB : controller::RIGHT_SECONDARY_THUMB); + _buttonPressedMap.insert(isLeftHand ? controller::LS : controller::RS); } } void ViveControllerManager::InputDevice::handlePoseEvent(const controller::InputCalibrationData& inputCalibrationData, const mat4& mat, const vec3& linearVelocity, - const vec3& angularVelocity, bool left) { + const vec3& angularVelocity, bool isLeftHand) { // When the sensor-to-world rotation is identity the coordinate axes look like this: // // user @@ -384,8 +380,8 @@ void ViveControllerManager::InputDevice::handlePoseEvent(const controller::Input static const glm::vec3 leftTranslationOffset = glm::vec3(-1.0f, 1.0f, 1.0f) * CONTROLLER_OFFSET; static const glm::vec3 rightTranslationOffset = CONTROLLER_OFFSET; - auto translationOffset = (left ? leftTranslationOffset : rightTranslationOffset); - auto rotationOffset = (left ? leftRotationOffset : rightRotationOffset); + auto translationOffset = (isLeftHand ? leftTranslationOffset : rightTranslationOffset); + auto rotationOffset = (isLeftHand ? leftRotationOffset : rightRotationOffset); glm::vec3 position = extractTranslation(mat); glm::quat rotation = glm::normalize(glm::quat_cast(mat)); @@ -399,7 +395,7 @@ void ViveControllerManager::InputDevice::handlePoseEvent(const controller::Input // handle change in velocity due to translationOffset avatarPose.velocity = linearVelocity + glm::cross(angularVelocity, position - extractTranslation(mat)); avatarPose.angularVelocity = angularVelocity; - _poseStateMap[left ? controller::LEFT_HAND : controller::RIGHT_HAND] = avatarPose.transform(controllerToAvatar); + _poseStateMap[isLeftHand ? controller::LEFT_HAND : controller::RIGHT_HAND] = avatarPose.transform(controllerToAvatar); } controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableInputs() const { diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 51339cd465..2240a528a9 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -50,7 +50,7 @@ public: private: class InputDevice : public controller::InputDevice { public: - InputDevice(vr::IVRSystem*& hmd) : controller::InputDevice("Vive"), _hmd(hmd) {} + InputDevice(vr::IVRSystem*& system) : controller::InputDevice("Vive"), _system(system) {} private: // Device functions virtual controller::Input::NamedVector getAvailableInputs() const override; @@ -58,13 +58,14 @@ private: virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; virtual void focusOutEvent() override; - void handleButtonEvent(uint32_t button, bool pressed, bool left); - void handleAxisEvent(uint32_t axis, float x, float y, bool left); + void handleHandController(uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand); + void handleButtonEvent(uint32_t button, bool pressed, bool isLeftHand); + void handleAxisEvent(uint32_t axis, float x, float y, bool isLeftHand); void handlePoseEvent(const controller::InputCalibrationData& inputCalibrationData, const mat4& mat, - const vec3& linearVelocity, const vec3& angularVelocity, bool left); + const vec3& linearVelocity, const vec3& angularVelocity, bool isLeftHand); int _trackedControllers { 0 }; - vr::IVRSystem*& _hmd; + vr::IVRSystem*& _system; friend class ViveControllerManager; }; @@ -81,8 +82,8 @@ private: int _rightHandRenderID { 0 }; bool _renderControllers { false }; - vr::IVRSystem* _hmd { nullptr }; - std::shared_ptr _inputDevice { std::make_shared(_hmd) }; + vr::IVRSystem* _system { nullptr }; + std::shared_ptr _inputDevice { std::make_shared(_system) }; static const QString NAME; From 89885805d22ddf3a2c952533a8d97206d95de26e Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Fri, 26 Feb 2016 18:34:33 -0800 Subject: [PATCH 2/2] OpenVR: Added hysteresis to the touch pad dead spot This should make using the Vive touch pad for movement more reliable. --- plugins/openvr/src/ViveControllerManager.cpp | 38 ++++++++++++-------- plugins/openvr/src/ViveControllerManager.h | 37 +++++++++++++++---- 2 files changed, 54 insertions(+), 21 deletions(-) diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 749c9ea2e4..0de8f6891d 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -234,8 +234,8 @@ void ViveControllerManager::InputDevice::update(float deltaTime, const controlle auto rightHandDeviceIndex = _system->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_RightHand); if (!jointsCaptured) { - handleHandController(leftHandDeviceIndex, inputCalibrationData, true); - handleHandController(rightHandDeviceIndex, inputCalibrationData, false); + handleHandController(deltaTime, leftHandDeviceIndex, inputCalibrationData, true); + handleHandController(deltaTime, rightHandDeviceIndex, inputCalibrationData, false); } int numTrackedControllers = 0; @@ -248,7 +248,7 @@ void ViveControllerManager::InputDevice::update(float deltaTime, const controlle _trackedControllers = numTrackedControllers; } -void ViveControllerManager::InputDevice::handleHandController(uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand) { +void ViveControllerManager::InputDevice::handleHandController(float deltaTime, uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand) { if (_system->IsTrackedDeviceConnected(deviceIndex) && _system->GetTrackedDeviceClass(deviceIndex) == vr::TrackedDeviceClass_Controller && @@ -258,7 +258,7 @@ void ViveControllerManager::InputDevice::handleHandController(uint32_t deviceInd const mat4& mat = _trackedDevicePoseMat4[deviceIndex]; const vec3 linearVelocity = _trackedDeviceLinearVelocities[deviceIndex]; const vec3 angularVelocity = _trackedDeviceAngularVelocities[deviceIndex]; - handlePoseEvent(inputCalibrationData, mat, linearVelocity, angularVelocity, isLeftHand); + handlePoseEvent(deltaTime, inputCalibrationData, mat, linearVelocity, angularVelocity, isLeftHand); vr::VRControllerState_t controllerState = vr::VRControllerState_t(); if (_system->GetControllerState(deviceIndex, &controllerState)) { @@ -267,12 +267,12 @@ void ViveControllerManager::InputDevice::handleHandController(uint32_t deviceInd for (uint32_t i = 0; i < vr::k_EButton_Max; ++i) { auto mask = vr::ButtonMaskFromId((vr::EVRButtonId)i); bool pressed = 0 != (controllerState.ulButtonPressed & mask); - handleButtonEvent(i, pressed, isLeftHand); + handleButtonEvent(deltaTime, i, pressed, isLeftHand); } // process each axis for (uint32_t i = 0; i < vr::k_unControllerStateAxisCount; i++) { - handleAxisEvent(i, controllerState.rAxis[i].x, controllerState.rAxis[i].y, isLeftHand); + handleAxisEvent(deltaTime, i, controllerState.rAxis[i].x, controllerState.rAxis[i].y, isLeftHand); } } } @@ -284,36 +284,44 @@ void ViveControllerManager::InputDevice::focusOutEvent() { }; // These functions do translation from the Steam IDs to the standard controller IDs -void ViveControllerManager::InputDevice::handleAxisEvent(uint32_t axis, float x, float y, bool isLeftHand) { +void ViveControllerManager::InputDevice::handleAxisEvent(float deltaTime, uint32_t axis, float x, float y, bool isLeftHand) { //FIX ME? It enters here every frame: probably we want to enter only if an event occurs axis += vr::k_EButton_Axis0; using namespace controller; + if (axis == vr::k_EButton_SteamVR_Touchpad) { - _axisStateMap[isLeftHand ? LX : RX] = x; - _axisStateMap[isLeftHand ? LY : RY] = y; + glm::vec2 stick(x, y); + if (isLeftHand) { + stick = _filteredLeftStick.process(deltaTime, stick); + } else { + stick = _filteredRightStick.process(deltaTime, stick); + } + _axisStateMap[isLeftHand ? LX : RX] = stick.x; + _axisStateMap[isLeftHand ? LY : RY] = stick.y; } else if (axis == vr::k_EButton_SteamVR_Trigger) { _axisStateMap[isLeftHand ? LT : RT] = x; } } // These functions do translation from the Steam IDs to the standard controller IDs -void ViveControllerManager::InputDevice::handleButtonEvent(uint32_t button, bool pressed, bool isLeftHand) { +void ViveControllerManager::InputDevice::handleButtonEvent(float deltaTime, uint32_t button, bool pressed, bool isLeftHand) { if (!pressed) { return; } + using namespace controller; if (button == vr::k_EButton_ApplicationMenu) { - _buttonPressedMap.insert(isLeftHand ? controller::LEFT_PRIMARY_THUMB : controller::RIGHT_PRIMARY_THUMB); + _buttonPressedMap.insert(isLeftHand ? LEFT_PRIMARY_THUMB : RIGHT_PRIMARY_THUMB); } else if (button == vr::k_EButton_Grip) { - _buttonPressedMap.insert(isLeftHand ? controller::LB : controller::RB); + _buttonPressedMap.insert(isLeftHand ? LB : RB); } else if (button == vr::k_EButton_SteamVR_Trigger) { - _buttonPressedMap.insert(isLeftHand ? controller::LT : controller::RT); + _buttonPressedMap.insert(isLeftHand ? LT : RT); } else if (button == vr::k_EButton_SteamVR_Touchpad) { - _buttonPressedMap.insert(isLeftHand ? controller::LS : controller::RS); + _buttonPressedMap.insert(isLeftHand ? LS : RS); } } -void ViveControllerManager::InputDevice::handlePoseEvent(const controller::InputCalibrationData& inputCalibrationData, +void ViveControllerManager::InputDevice::handlePoseEvent(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, const mat4& mat, const vec3& linearVelocity, const vec3& angularVelocity, bool isLeftHand) { // When the sensor-to-world rotation is identity the coordinate axes look like this: diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 2240a528a9..480fbfeb90 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -58,12 +58,39 @@ private: virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; virtual void focusOutEvent() override; - void handleHandController(uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand); - void handleButtonEvent(uint32_t button, bool pressed, bool isLeftHand); - void handleAxisEvent(uint32_t axis, float x, float y, bool isLeftHand); - void handlePoseEvent(const controller::InputCalibrationData& inputCalibrationData, const mat4& mat, + void handleHandController(float deltaTime, uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand); + void handleButtonEvent(float deltaTime, uint32_t button, bool pressed, bool isLeftHand); + void handleAxisEvent(float deltaTime, uint32_t axis, float x, float y, bool isLeftHand); + void handlePoseEvent(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, const mat4& mat, const vec3& linearVelocity, const vec3& angularVelocity, bool isLeftHand); + class FilteredStick { + public: + glm::vec2 process(float deltaTime, const glm::vec2& stick) { + // Use a timer to prevent the stick going to back to zero. + // This to work around the noisy touch pad that will flash back to zero breifly + const float ZERO_HYSTERESIS_PERIOD = 0.2f; // 200 ms + if (glm::length(stick) == 0.0f) { + if (_timer <= 0.0f) { + return glm::vec2(0.0f, 0.0f); + } else { + _timer -= deltaTime; + return _stick; + } + } else { + _timer = ZERO_HYSTERESIS_PERIOD; + _stick = stick; + return stick; + } + } + protected: + float _timer { 0.0f }; + glm::vec2 _stick { 0.0f, 0.0f }; + }; + + FilteredStick _filteredLeftStick; + FilteredStick _filteredRightStick; + int _trackedControllers { 0 }; vr::IVRSystem*& _system; friend class ViveControllerManager; @@ -71,8 +98,6 @@ private: void renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign); - - bool _registeredWithInputMapper { false }; bool _modelLoaded { false }; model::Geometry _modelGeometry;