//// ViveControllerManager.cpp//// Created by Sam Gondelman on 6/29/15.// Copyright 2013 High Fidelity, Inc.// Copyright 2020 Vircadia contributors.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "ViveControllerManager.h"#include <algorithm>#include <string> #ifdef _WIN32#pragma warning( push )#pragma warning( disable : 4091 )#pragma warning( disable : 4334 )#endif #ifdef VIVE_PRO_EYE#include <SRanipal.h>#include <SRanipal_Eye.h>#include <SRanipal_Enums.h>#include <interface_gesture.hpp>#endif #ifdef _WIN32#pragma warning( pop )#endif #include <PerfStat.h>#include <PathUtils.h>#include <GeometryCache.h>#include <gpu/Batch.h>#include <gpu/Context.h>#include <DeferredLightingEffect.h>#include <NumericalConstants.h>#include <ui-plugins/PluginContainer.h>#include <UserActivityLogger.h>#include <NumericalConstants.h>#include <Preferences.h>#include <SettingHandle.h>#include <OffscreenUi.h>#include <GLMHelpers.h>#include <AvatarConstants.h>#include <glm/ext.hpp>#include <glm/gtc/quaternion.hpp>#include <ui-plugins/PluginContainer.h>#include <plugins/DisplayPlugin.h>#include <ThreadHelpers.h> #include <controllers/UserInputMapper.h>#include <plugins/InputConfiguration.h>#include <controllers/StandardControls.h> #include "OpenVrDisplayPlugin.h" extern PoseData _nextSimPoseData; vr::IVRSystem* acquireOpenVrSystem();void releaseOpenVrSystem(); static const QString OPENVR_LAYOUT = QString("OpenVrConfiguration.qml");const quint64 CALIBRATION_TIMELAPSE = 1 * USECS_PER_SECOND; static const int MIN_HEAD = 1;static const int MIN_PUCK_COUNT = 2;static const int MIN_FEET_AND_HIPS = 3;static const int MIN_FEET_HIPS_CHEST = 4;static const int MIN_FEET_HIPS_SHOULDERS = 5;static const int MIN_FEET_HIPS_CHEST_SHOULDERS = 6; static const int FIRST_FOOT = 0;static const int SECOND_FOOT = 1;static const int HIP = 2;static const int CHEST = 3; #ifdef VIVE_PRO_EYEenum ViveHandJointIndex { HAND = 0, THUMB_1, THUMB_2, THUMB_3, THUMB_4, INDEX_1, INDEX_2, INDEX_3, INDEX_4, MIDDLE_1, MIDDLE_2, MIDDLE_3, MIDDLE_4, RING_1, RING_2, RING_3, RING_4, PINKY_1, PINKY_2, PINKY_3, PINKY_4, Size};#endif const char* ViveControllerManager::NAME { "OpenVR" }; const std::map<vr::ETrackingResult, QString> TRACKING_RESULT_TO_STRING = { {vr::TrackingResult_Uninitialized, QString("vr::TrackingResult_Uninitialized")}, {vr::TrackingResult_Calibrating_InProgress, QString("vr::TrackingResult_Calibrating_InProgess")}, {vr::TrackingResult_Calibrating_OutOfRange, QString("TrackingResult_Calibrating_OutOfRange")}, {vr::TrackingResult_Running_OK, QString("TrackingResult_Running_Ok")}, {vr::TrackingResult_Running_OutOfRange, QString("TrackingResult_Running_OutOfRange")}}; static glm::mat4 computeOffset(glm::mat4 defaultToReferenceMat, glm::mat4 defaultJointMat, controller::Pose puckPose) { glm::mat4 poseMat = createMatFromQuatAndPos(puckPose.rotation, puckPose.translation); glm::mat4 referenceJointMat = defaultToReferenceMat * defaultJointMat; return glm::inverse(poseMat) * referenceJointMat;} static bool sortPucksYPosition(const PuckPosePair& firstPuck, const PuckPosePair& secondPuck) { return (firstPuck.second.translation.y < secondPuck.second.translation.y);} static bool determineLimbOrdering(const controller::Pose& poseA, const controller::Pose& poseB, glm::vec3 axis, glm::vec3 axisOrigin) { glm::vec3 poseAPosition = poseA.getTranslation(); glm::vec3 poseBPosition = poseB.getTranslation(); glm::vec3 poseAVector = poseAPosition - axisOrigin; glm::vec3 poseBVector = poseBPosition - axisOrigin; float poseAProjection = glm::dot(poseAVector, axis); float poseBProjection = glm::dot(poseBVector, axis); return (poseAProjection > poseBProjection);} static glm::vec3 getReferenceHeadXAxis(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) { glm::mat4 finalHead = defaultToReferenceMat * defaultHead; return glmExtractRotation(finalHead) * Vectors::UNIT_X;} static glm::vec3 getReferenceHeadPosition(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) { glm::mat4 finalHead = defaultToReferenceMat * defaultHead; return extractTranslation(finalHead);} static QString deviceTrackingResultToString(vr::ETrackingResult trackingResult) { QString result; auto iterator = TRACKING_RESULT_TO_STRING.find(trackingResult); if (iterator != TRACKING_RESULT_TO_STRING.end()) { return iterator->second; } return result;} #ifdef VIVE_PRO_EYEclass ViveProEyeReadThread : public QThread {public: ViveProEyeReadThread() { setObjectName("OpenVR ViveProEye Read Thread"); } void run() override { while (!quit) { ViveSR::anipal::Eye::EyeData eyeData; int result = ViveSR::anipal::Eye::GetEyeData(&eyeData); { QMutexLocker locker(&eyeDataMutex); eyeDataBuffer.getEyeDataResult = result; if (result == ViveSR::Error::WORK) { uint64_t leftValids = eyeData.verbose_data.left.eye_data_validata_bit_mask; uint64_t rightValids = eyeData.verbose_data.right.eye_data_validata_bit_mask; eyeDataBuffer.leftDirectionValid = (leftValids & (uint64_t)ViveSR::anipal::Eye::SINGLE_EYE_DATA_GAZE_DIRECTION_VALIDITY) > (uint64_t)0; eyeDataBuffer.rightDirectionValid = (rightValids & (uint64_t)ViveSR::anipal::Eye::SINGLE_EYE_DATA_GAZE_DIRECTION_VALIDITY) > (uint64_t)0; eyeDataBuffer.leftOpennessValid = (leftValids & (uint64_t)ViveSR::anipal::Eye::SINGLE_EYE_DATA_EYE_OPENNESS_VALIDITY) > (uint64_t)0; eyeDataBuffer.rightOpennessValid = (rightValids & (uint64_t)ViveSR::anipal::Eye::SINGLE_EYE_DATA_EYE_OPENNESS_VALIDITY) > (uint64_t)0; float *leftGaze = eyeData.verbose_data.left.gaze_direction_normalized.elem_; float *rightGaze = eyeData.verbose_data.right.gaze_direction_normalized.elem_; eyeDataBuffer.leftEyeGaze = glm::vec3(leftGaze[0], leftGaze[1], leftGaze[2]); eyeDataBuffer.rightEyeGaze = glm::vec3(rightGaze[0], rightGaze[1], rightGaze[2]); eyeDataBuffer.leftEyeOpenness = eyeData.verbose_data.left.eye_openness; eyeDataBuffer.rightEyeOpenness = eyeData.verbose_data.right.eye_openness; } } } } bool quit { false }; // mutex and buffer for moving data from this thread to the other one QMutex eyeDataMutex; EyeDataBuffer eyeDataBuffer;};#endif static QString outOfRangeDataStrategyToString(ViveControllerManager::OutOfRangeDataStrategy strategy) { switch (strategy) { default: case ViveControllerManager::OutOfRangeDataStrategy::None: return "None"; case ViveControllerManager::OutOfRangeDataStrategy::Freeze: return "Freeze"; case ViveControllerManager::OutOfRangeDataStrategy::Drop: return "Drop"; case ViveControllerManager::OutOfRangeDataStrategy::DropAfterDelay: return "DropAfterDelay"; }} static ViveControllerManager::OutOfRangeDataStrategy stringToOutOfRangeDataStrategy(const QString& string) { if (string == "Drop") { return ViveControllerManager::OutOfRangeDataStrategy::Drop; } else if (string == "Freeze") { return ViveControllerManager::OutOfRangeDataStrategy::Freeze; } else if (string == "DropAfterDelay") { return ViveControllerManager::OutOfRangeDataStrategy::DropAfterDelay; } else { return ViveControllerManager::OutOfRangeDataStrategy::None; }} bool ViveControllerManager::isDesktopMode() { if (_container) { return !_container->getActiveDisplayPlugin()->isHmd(); } return false;} void ViveControllerManager::calibrate() { if (isSupported()) { _inputDevice->calibrateNextFrame(); }} bool ViveControllerManager::uncalibrate() { if (isSupported()) { _inputDevice->uncalibrate(); return true; } return false;} bool ViveControllerManager::isSupported() const { return openVrSupported();} void ViveControllerManager::setConfigurationSettings(const QJsonObject configurationSettings) { if (isSupported()) { if (configurationSettings.contains("desktopMode")) { _desktopMode = configurationSettings["desktopMode"].toBool(); } if (configurationSettings.contains("hmdDesktopTracking")) { _hmdDesktopTracking = configurationSettings["hmdDesktopTracking"].toBool(); } if (configurationSettings.contains("eyeTrackingEnabled")) { _eyeTrackingEnabled = configurationSettings["eyeTrackingEnabled"].toBool(); } _inputDevice->configureCalibrationSettings(configurationSettings); saveSettings(); }} QJsonObject ViveControllerManager::configurationSettings() { if (isSupported()) { QJsonObject configurationSettings = _inputDevice->configurationSettings(); configurationSettings["desktopMode"] = _desktopMode; configurationSettings["hmdDesktopTracking"] = _hmdDesktopTracking; configurationSettings["eyeTrackingEnabled"] = _eyeTrackingEnabled; return configurationSettings; } return QJsonObject();} QString ViveControllerManager::configurationLayout() { return OPENVR_LAYOUT;} bool isDeviceIndexActive(vr::IVRSystem*& system, uint32_t deviceIndex) { if (!system) { return false; } if (deviceIndex != vr::k_unTrackedDeviceIndexInvalid && system->GetTrackedDeviceClass(deviceIndex) == vr::TrackedDeviceClass_Controller && system->IsTrackedDeviceConnected(deviceIndex)) { vr::EDeviceActivityLevel activityLevel = system->GetTrackedDeviceActivityLevel(deviceIndex); if (activityLevel == vr::k_EDeviceActivityLevel_UserInteraction) { return true; } } return false;} bool isHandControllerActive(vr::IVRSystem*& system, vr::ETrackedControllerRole deviceRole) { if (!system) { return false; } auto deviceIndex = system->GetTrackedDeviceIndexForControllerRole(deviceRole); return isDeviceIndexActive(system, deviceIndex);} bool areBothHandControllersActive(vr::IVRSystem*& system) { return isHandControllerActive(system, vr::TrackedControllerRole_LeftHand) && isHandControllerActive(system, vr::TrackedControllerRole_RightHand);} #ifdef VIVE_PRO_EYEvoid ViveControllerManager::enableGestureDetection() { if (_viveCameraHandTracker) { return; } if (!ViveSR::anipal::Eye::IsViveProEye()) { return; } // #define HAND_TRACKER_USE_EXTERNAL_TRANSFORM 1 #ifdef HAND_TRACKER_USE_EXTERNAL_TRANSFORM UseExternalTransform(true); // camera hand tracker results are in HMD frame#else UseExternalTransform(false); // camera hand tracker results are in sensor frame#endif GestureOption options; // defaults are GestureBackendAuto and GestureModeSkeleton GestureFailure gestureFailure = StartGestureDetection(&options); switch (gestureFailure) { case GestureFailureNone: qDebug() << "StartGestureDetection success"; _viveCameraHandTracker = true; break; case GestureFailureOpenCL: qDebug() << "StartGestureDetection (Only on Windows) OpenCL is not supported on the machine"; break; case GestureFailureCamera: qDebug() << "StartGestureDetection Start camera failed"; break; case GestureFailureInternal: qDebug() << "StartGestureDetection Internal errors"; break; case GestureFailureCPUOnPC: qDebug() << "StartGestureDetection CPU backend is not supported on Windows"; break; }} void ViveControllerManager::disableGestureDetection() { if (!_viveCameraHandTracker) { return; } StopGestureDetection(); _viveCameraHandTracker = false;}#endif bool ViveControllerManager::activate() { InputPlugin::activate(); loadSettings(); if (!_system) { _system = acquireOpenVrSystem(); } if (!_system) { return false; } enableOpenVrKeyboard(_container); // register with UserInputMapper auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); userInputMapper->registerDevice(_inputDevice); _registeredWithInputMapper = true; #ifdef VIVE_PRO_EYE if (ViveSR::anipal::Eye::IsViveProEye()) { qDebug() << "Vive Pro eye-tracking detected"; int error = ViveSR::anipal::Initial(ViveSR::anipal::Eye::ANIPAL_TYPE_EYE, NULL); if (error == ViveSR::Error::WORK) { _viveProEye = true; qDebug() << "Successfully initialize Eye engine."; } else if (error == ViveSR::Error::RUNTIME_NOT_FOUND) { _viveProEye = false; qDebug() << "please follows SRanipal SDK guide to install SR_Runtime first"; } else { _viveProEye = false; qDebug() << "Failed to initialize Eye engine. please refer to ViveSR error code:" << error; } if (_viveProEye) { _viveProEyeReadThread = std::make_shared<ViveProEyeReadThread>(); connect(_viveProEyeReadThread.get(), &QThread::started, [] { setThreadName("ViveProEyeReadThread"); }); _viveProEyeReadThread->start(QThread::HighPriority); } }#endif return true;} void ViveControllerManager::deactivate() { InputPlugin::deactivate(); disableOpenVrKeyboard(); if (_system) { _container->makeRenderingContextCurrent(); releaseOpenVrSystem(); _system = nullptr; } _inputDevice->_poseStateMap.clear(); // unregister with UserInputMapper auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); userInputMapper->removeDevice(_inputDevice->_deviceID); _registeredWithInputMapper = false; #ifdef VIVE_PRO_EYE if (_viveProEyeReadThread) { _viveProEyeReadThread->quit = true; _viveProEyeReadThread->wait(); _viveProEyeReadThread = nullptr; ViveSR::anipal::Release(ViveSR::anipal::Eye::ANIPAL_TYPE_EYE); }#endif saveSettings();} bool ViveControllerManager::isHeadControllerMounted() const { if (_inputDevice && _inputDevice->isHeadControllerMounted()) { return true; } vr::EDeviceActivityLevel activityLevel = _system->GetTrackedDeviceActivityLevel(vr::k_unTrackedDeviceIndex_Hmd); return activityLevel == vr::k_EDeviceActivityLevel_UserInteraction;} #ifdef VIVE_PRO_EYEvoid ViveControllerManager::invalidateEyeInputs() { _inputDevice->_poseStateMap[controller::LEFT_EYE].valid = false; _inputDevice->_poseStateMap[controller::RIGHT_EYE].valid = false; _inputDevice->_axisStateMap[controller::EYEBLINK_L].valid = false; _inputDevice->_axisStateMap[controller::EYEBLINK_R].valid = false;} void ViveControllerManager::updateEyeTracker(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { if (!isHeadControllerMounted()) { invalidateEyeInputs(); return; } EyeDataBuffer eyeDataBuffer; { // GetEyeData takes around 4ms to finish, so we run it on a thread. QMutexLocker locker(&_viveProEyeReadThread->eyeDataMutex); memcpy(&eyeDataBuffer, &_viveProEyeReadThread->eyeDataBuffer, sizeof(eyeDataBuffer)); } if (eyeDataBuffer.getEyeDataResult != ViveSR::Error::WORK) { invalidateEyeInputs(); return; } // only update from buffer values if the new data is "valid" if (!eyeDataBuffer.leftDirectionValid) { eyeDataBuffer.leftEyeGaze = _prevEyeData.leftEyeGaze; eyeDataBuffer.leftDirectionValid = _prevEyeData.leftDirectionValid; } if (!eyeDataBuffer.rightDirectionValid) { eyeDataBuffer.rightEyeGaze = _prevEyeData.rightEyeGaze; eyeDataBuffer.rightDirectionValid = _prevEyeData.rightDirectionValid; } if (!eyeDataBuffer.leftOpennessValid) { eyeDataBuffer.leftEyeOpenness = _prevEyeData.leftEyeOpenness; eyeDataBuffer.leftOpennessValid = _prevEyeData.leftOpennessValid; } if (!eyeDataBuffer.rightOpennessValid) { eyeDataBuffer.rightEyeOpenness = _prevEyeData.rightEyeOpenness; eyeDataBuffer.rightOpennessValid = _prevEyeData.rightOpennessValid; } _prevEyeData = eyeDataBuffer; // transform data into what the controller system expects. Lines 1–500 of 2097. Load more
// // ViveControllerManager.cpp // // Created by Sam Gondelman on 6/29/15. // Copyright 2013 High Fidelity, Inc. // Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "ViveControllerManager.h" #include <algorithm> #include <string> #ifdef _WIN32 #pragma warning( push ) #pragma warning( disable : 4091 ) #pragma warning( disable : 4334 ) #endif #ifdef VIVE_PRO_EYE #include <SRanipal.h> #include <SRanipal_Eye.h> #include <SRanipal_Enums.h> #include <interface_gesture.hpp> #endif #ifdef _WIN32 #pragma warning( pop ) #endif #include <PerfStat.h> #include <PathUtils.h> #include <GeometryCache.h> #include <gpu/Batch.h> #include <gpu/Context.h> #include <DeferredLightingEffect.h> #include <NumericalConstants.h> #include <ui-plugins/PluginContainer.h> #include <UserActivityLogger.h> #include <NumericalConstants.h> #include <Preferences.h> #include <SettingHandle.h> #include <OffscreenUi.h> #include <GLMHelpers.h> #include <AvatarConstants.h> #include <glm/ext.hpp> #include <glm/gtc/quaternion.hpp> #include <ui-plugins/PluginContainer.h> #include <plugins/DisplayPlugin.h> #include <ThreadHelpers.h> #include <controllers/UserInputMapper.h> #include <plugins/InputConfiguration.h> #include <controllers/StandardControls.h> #include "OpenVrDisplayPlugin.h" extern PoseData _nextSimPoseData; vr::IVRSystem* acquireOpenVrSystem(); void releaseOpenVrSystem(); static const QString OPENVR_LAYOUT = QString("OpenVrConfiguration.qml"); const quint64 CALIBRATION_TIMELAPSE = 1 * USECS_PER_SECOND; static const int MIN_HEAD = 1; static const int MIN_PUCK_COUNT = 2; static const int MIN_FEET_AND_HIPS = 3; static const int MIN_FEET_HIPS_CHEST = 4; static const int MIN_FEET_HIPS_SHOULDERS = 5; static const int MIN_FEET_HIPS_CHEST_SHOULDERS = 6; static const int FIRST_FOOT = 0; static const int SECOND_FOOT = 1; static const int HIP = 2; static const int CHEST = 3; #ifdef VIVE_PRO_EYE enum ViveHandJointIndex { HAND = 0, THUMB_1, THUMB_2, THUMB_3, THUMB_4, INDEX_1, INDEX_2, INDEX_3, INDEX_4, MIDDLE_1, MIDDLE_2, MIDDLE_3, MIDDLE_4, RING_1, RING_2, RING_3, RING_4, PINKY_1, PINKY_2, PINKY_3, PINKY_4, Size }; #endif const char* ViveControllerManager::NAME { "OpenVR" }; const std::map<vr::ETrackingResult, QString> TRACKING_RESULT_TO_STRING = { {vr::TrackingResult_Uninitialized, QString("vr::TrackingResult_Uninitialized")}, {vr::TrackingResult_Calibrating_InProgress, QString("vr::TrackingResult_Calibrating_InProgess")}, {vr::TrackingResult_Calibrating_OutOfRange, QString("TrackingResult_Calibrating_OutOfRange")}, {vr::TrackingResult_Running_OK, QString("TrackingResult_Running_Ok")}, {vr::TrackingResult_Running_OutOfRange, QString("TrackingResult_Running_OutOfRange")} }; static glm::mat4 computeOffset(glm::mat4 defaultToReferenceMat, glm::mat4 defaultJointMat, controller::Pose puckPose) { glm::mat4 poseMat = createMatFromQuatAndPos(puckPose.rotation, puckPose.translation); glm::mat4 referenceJointMat = defaultToReferenceMat * defaultJointMat; return glm::inverse(poseMat) * referenceJointMat; } static bool sortPucksYPosition(const PuckPosePair& firstPuck, const PuckPosePair& secondPuck) { return (firstPuck.second.translation.y < secondPuck.second.translation.y); } static bool determineLimbOrdering(const controller::Pose& poseA, const controller::Pose& poseB, glm::vec3 axis, glm::vec3 axisOrigin) { glm::vec3 poseAPosition = poseA.getTranslation(); glm::vec3 poseBPosition = poseB.getTranslation(); glm::vec3 poseAVector = poseAPosition - axisOrigin; glm::vec3 poseBVector = poseBPosition - axisOrigin; float poseAProjection = glm::dot(poseAVector, axis); float poseBProjection = glm::dot(poseBVector, axis); return (poseAProjection > poseBProjection); } static glm::vec3 getReferenceHeadXAxis(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) { glm::mat4 finalHead = defaultToReferenceMat * defaultHead; return glmExtractRotation(finalHead) * Vectors::UNIT_X; } static glm::vec3 getReferenceHeadPosition(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) { glm::mat4 finalHead = defaultToReferenceMat * defaultHead; return extractTranslation(finalHead); } static QString deviceTrackingResultToString(vr::ETrackingResult trackingResult) { QString result; auto iterator = TRACKING_RESULT_TO_STRING.find(trackingResult); if (iterator != TRACKING_RESULT_TO_STRING.end()) { return iterator->second; } return result; } #ifdef VIVE_PRO_EYE class ViveProEyeReadThread : public QThread { public: ViveProEyeReadThread() { setObjectName("OpenVR ViveProEye Read Thread"); } void run() override { while (!quit) { ViveSR::anipal::Eye::EyeData eyeData; int result = ViveSR::anipal::Eye::GetEyeData(&eyeData); { QMutexLocker locker(&eyeDataMutex); eyeDataBuffer.getEyeDataResult = result; if (result == ViveSR::Error::WORK) { uint64_t leftValids = eyeData.verbose_data.left.eye_data_validata_bit_mask; uint64_t rightValids = eyeData.verbose_data.right.eye_data_validata_bit_mask; eyeDataBuffer.leftDirectionValid = (leftValids & (uint64_t)ViveSR::anipal::Eye::SINGLE_EYE_DATA_GAZE_DIRECTION_VALIDITY) > (uint64_t)0; eyeDataBuffer.rightDirectionValid = (rightValids & (uint64_t)ViveSR::anipal::Eye::SINGLE_EYE_DATA_GAZE_DIRECTION_VALIDITY) > (uint64_t)0; eyeDataBuffer.leftOpennessValid = (leftValids & (uint64_t)ViveSR::anipal::Eye::SINGLE_EYE_DATA_EYE_OPENNESS_VALIDITY) > (uint64_t)0; eyeDataBuffer.rightOpennessValid = (rightValids & (uint64_t)ViveSR::anipal::Eye::SINGLE_EYE_DATA_EYE_OPENNESS_VALIDITY) > (uint64_t)0; float *leftGaze = eyeData.verbose_data.left.gaze_direction_normalized.elem_; float *rightGaze = eyeData.verbose_data.right.gaze_direction_normalized.elem_; eyeDataBuffer.leftEyeGaze = glm::vec3(leftGaze[0], leftGaze[1], leftGaze[2]); eyeDataBuffer.rightEyeGaze = glm::vec3(rightGaze[0], rightGaze[1], rightGaze[2]); eyeDataBuffer.leftEyeOpenness = eyeData.verbose_data.left.eye_openness; eyeDataBuffer.rightEyeOpenness = eyeData.verbose_data.right.eye_openness; } } } } bool quit { false }; // mutex and buffer for moving data from this thread to the other one QMutex eyeDataMutex; EyeDataBuffer eyeDataBuffer; }; #endif static QString outOfRangeDataStrategyToString(ViveControllerManager::OutOfRangeDataStrategy strategy) { switch (strategy) { default: case ViveControllerManager::OutOfRangeDataStrategy::None: return "None"; case ViveControllerManager::OutOfRangeDataStrategy::Freeze: return "Freeze"; case ViveControllerManager::OutOfRangeDataStrategy::Drop: return "Drop"; case ViveControllerManager::OutOfRangeDataStrategy::DropAfterDelay: return "DropAfterDelay"; } } static ViveControllerManager::OutOfRangeDataStrategy stringToOutOfRangeDataStrategy(const QString& string) { if (string == "Drop") { return ViveControllerManager::OutOfRangeDataStrategy::Drop; } else if (string == "Freeze") { return ViveControllerManager::OutOfRangeDataStrategy::Freeze; } else if (string == "DropAfterDelay") { return ViveControllerManager::OutOfRangeDataStrategy::DropAfterDelay; } else { return ViveControllerManager::OutOfRangeDataStrategy::None; } } bool ViveControllerManager::isDesktopMode() { if (_container) { return !_container->getActiveDisplayPlugin()->isHmd(); } return false; } void ViveControllerManager::calibrate() { if (isSupported()) { _inputDevice->calibrateNextFrame(); } } bool ViveControllerManager::uncalibrate() { if (isSupported()) { _inputDevice->uncalibrate(); return true; } return false; } bool ViveControllerManager::isSupported() const { return openVrSupported(); } void ViveControllerManager::setConfigurationSettings(const QJsonObject configurationSettings) { if (isSupported()) { if (configurationSettings.contains("desktopMode")) { _desktopMode = configurationSettings["desktopMode"].toBool(); } if (configurationSettings.contains("hmdDesktopTracking")) { _hmdDesktopTracking = configurationSettings["hmdDesktopTracking"].toBool(); } if (configurationSettings.contains("eyeTrackingEnabled")) { _eyeTrackingEnabled = configurationSettings["eyeTrackingEnabled"].toBool(); } _inputDevice->configureCalibrationSettings(configurationSettings); saveSettings(); } } QJsonObject ViveControllerManager::configurationSettings() { if (isSupported()) { QJsonObject configurationSettings = _inputDevice->configurationSettings(); configurationSettings["desktopMode"] = _desktopMode; configurationSettings["hmdDesktopTracking"] = _hmdDesktopTracking; configurationSettings["eyeTrackingEnabled"] = _eyeTrackingEnabled; return configurationSettings; } return QJsonObject(); } QString ViveControllerManager::configurationLayout() { return OPENVR_LAYOUT; } bool isDeviceIndexActive(vr::IVRSystem*& system, uint32_t deviceIndex) { if (!system) { return false; } if (deviceIndex != vr::k_unTrackedDeviceIndexInvalid && system->GetTrackedDeviceClass(deviceIndex) == vr::TrackedDeviceClass_Controller && system->IsTrackedDeviceConnected(deviceIndex)) { vr::EDeviceActivityLevel activityLevel = system->GetTrackedDeviceActivityLevel(deviceIndex); if (activityLevel == vr::k_EDeviceActivityLevel_UserInteraction) { return true; } } return false; } bool isHandControllerActive(vr::IVRSystem*& system, vr::ETrackedControllerRole deviceRole) { if (!system) { return false; } auto deviceIndex = system->GetTrackedDeviceIndexForControllerRole(deviceRole); return isDeviceIndexActive(system, deviceIndex); } bool areBothHandControllersActive(vr::IVRSystem*& system) { return isHandControllerActive(system, vr::TrackedControllerRole_LeftHand) && isHandControllerActive(system, vr::TrackedControllerRole_RightHand); } #ifdef VIVE_PRO_EYE void ViveControllerManager::enableGestureDetection() { if (_viveCameraHandTracker) { return; } if (!ViveSR::anipal::Eye::IsViveProEye()) { return; } // #define HAND_TRACKER_USE_EXTERNAL_TRANSFORM 1 #ifdef HAND_TRACKER_USE_EXTERNAL_TRANSFORM UseExternalTransform(true); // camera hand tracker results are in HMD frame #else UseExternalTransform(false); // camera hand tracker results are in sensor frame #endif GestureOption options; // defaults are GestureBackendAuto and GestureModeSkeleton GestureFailure gestureFailure = StartGestureDetection(&options); switch (gestureFailure) { case GestureFailureNone: qDebug() << "StartGestureDetection success"; _viveCameraHandTracker = true; break; case GestureFailureOpenCL: qDebug() << "StartGestureDetection (Only on Windows) OpenCL is not supported on the machine"; break; case GestureFailureCamera: qDebug() << "StartGestureDetection Start camera failed"; break; case GestureFailureInternal: qDebug() << "StartGestureDetection Internal errors"; break; case GestureFailureCPUOnPC: qDebug() << "StartGestureDetection CPU backend is not supported on Windows"; break; } } void ViveControllerManager::disableGestureDetection() { if (!_viveCameraHandTracker) { return; } StopGestureDetection(); _viveCameraHandTracker = false; } #endif bool ViveControllerManager::activate() { InputPlugin::activate(); loadSettings(); if (!_system) { _system = acquireOpenVrSystem(); } if (!_system) { return false; } enableOpenVrKeyboard(_container); // register with UserInputMapper auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); userInputMapper->registerDevice(_inputDevice); _registeredWithInputMapper = true; #ifdef VIVE_PRO_EYE if (ViveSR::anipal::Eye::IsViveProEye()) { qDebug() << "Vive Pro eye-tracking detected"; int error = ViveSR::anipal::Initial(ViveSR::anipal::Eye::ANIPAL_TYPE_EYE, NULL); if (error == ViveSR::Error::WORK) { _viveProEye = true; qDebug() << "Successfully initialize Eye engine."; } else if (error == ViveSR::Error::RUNTIME_NOT_FOUND) { _viveProEye = false; qDebug() << "please follows SRanipal SDK guide to install SR_Runtime first"; } else { _viveProEye = false; qDebug() << "Failed to initialize Eye engine. please refer to ViveSR error code:" << error; } if (_viveProEye) { _viveProEyeReadThread = std::make_shared<ViveProEyeReadThread>(); connect(_viveProEyeReadThread.get(), &QThread::started, [] { setThreadName("ViveProEyeReadThread"); }); _viveProEyeReadThread->start(QThread::HighPriority); } } #endif return true; } void ViveControllerManager::deactivate() { InputPlugin::deactivate(); disableOpenVrKeyboard(); if (_system) { _container->makeRenderingContextCurrent(); releaseOpenVrSystem(); _system = nullptr; } _inputDevice->_poseStateMap.clear(); // unregister with UserInputMapper auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); userInputMapper->removeDevice(_inputDevice->_deviceID); _registeredWithInputMapper = false; #ifdef VIVE_PRO_EYE if (_viveProEyeReadThread) { _viveProEyeReadThread->quit = true; _viveProEyeReadThread->wait(); _viveProEyeReadThread = nullptr; ViveSR::anipal::Release(ViveSR::anipal::Eye::ANIPAL_TYPE_EYE); } #endif saveSettings(); } bool ViveControllerManager::isHeadControllerMounted() const { if (_inputDevice && _inputDevice->isHeadControllerMounted()) { return true; } vr::EDeviceActivityLevel activityLevel = _system->GetTrackedDeviceActivityLevel(vr::k_unTrackedDeviceIndex_Hmd); return activityLevel == vr::k_EDeviceActivityLevel_UserInteraction; } #ifdef VIVE_PRO_EYE void ViveControllerManager::invalidateEyeInputs() { _inputDevice->_poseStateMap[controller::LEFT_EYE].valid = false; _inputDevice->_poseStateMap[controller::RIGHT_EYE].valid = false; _inputDevice->_axisStateMap[controller::EYEBLINK_L].valid = false; _inputDevice->_axisStateMap[controller::EYEBLINK_R].valid = false; } void ViveControllerManager::updateEyeTracker(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { if (!isHeadControllerMounted()) { invalidateEyeInputs(); return; } EyeDataBuffer eyeDataBuffer; { // GetEyeData takes around 4ms to finish, so we run it on a thread. QMutexLocker locker(&_viveProEyeReadThread->eyeDataMutex); memcpy(&eyeDataBuffer, &_viveProEyeReadThread->eyeDataBuffer, sizeof(eyeDataBuffer)); } if (eyeDataBuffer.getEyeDataResult != ViveSR::Error::WORK) { invalidateEyeInputs(); return; } // only update from buffer values if the new data is "valid" if (!eyeDataBuffer.leftDirectionValid) { eyeDataBuffer.leftEyeGaze = _prevEyeData.leftEyeGaze; eyeDataBuffer.leftDirectionValid = _prevEyeData.leftDirectionValid; } if (!eyeDataBuffer.rightDirectionValid) { eyeDataBuffer.rightEyeGaze = _prevEyeData.rightEyeGaze; eyeDataBuffer.rightDirectionValid = _prevEyeData.rightDirectionValid; } if (!eyeDataBuffer.leftOpennessValid) { eyeDataBuffer.leftEyeOpenness = _prevEyeData.leftEyeOpenness; eyeDataBuffer.leftOpennessValid = _prevEyeData.leftOpennessValid; } if (!eyeDataBuffer.rightOpennessValid) { eyeDataBuffer.rightEyeOpenness = _prevEyeData.rightEyeOpenness; eyeDataBuffer.rightOpennessValid = _prevEyeData.rightOpennessValid; } _prevEyeData = eyeDataBuffer; // transform data into what the controller system expects.