//// ViveControllerManager.cpp// input-plugins/src/input-plugins//// Created by Sam Gondelman on 6/29/15.// Copyright 2013 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 "ViveControllerManager.h"#include <algorithm>#include <string> #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 <glm/ext.hpp>#include <glm/gtc/quaternion.hpp>#include <ui-plugins/PluginContainer.h>#include <plugins/DisplayPlugin.h> #include <controllers/UserInputMapper.h>#include <Plugins/InputConfiguration.h>#include <controllers/StandardControls.h> extern PoseData _nextSimPoseData; vr::IVRSystem* acquireOpenVrSystem();void releaseOpenVrSystem(); static const QString OPENVR_LAYOUT = QString("OpenVrConfiguration.qml");static const char* CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b";const quint64 CALIBRATION_TIMELAPSE = 1 * USECS_PER_SECOND; static const char* MENU_PARENT = "Avatar";static const char* MENU_NAME = "Vive Controllers";static const char* MENU_PATH = "Avatar" ">" "Vive Controllers"; 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; 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;} static glm::mat4 calculateResetMat() { auto chaperone = vr::VRChaperone(); if (chaperone) { float const UI_RADIUS = 1.0f; float const UI_HEIGHT = 1.6f; float const UI_Z_OFFSET = 0.5; float xSize, zSize; chaperone->GetPlayAreaSize(&xSize, &zSize); glm::vec3 uiPos(0.0f, UI_HEIGHT, UI_RADIUS - (0.5f * zSize) - UI_Z_OFFSET); return glm::inverse(createMatFromQuatAndPos(glm::quat(), uiPos)); } return glm::mat4();} 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"; }} static ViveControllerManager::OutOfRangeDataStrategy stringToOutOfRangeDataStrategy(const QString& string) { if (string == "Drop") { return ViveControllerManager::OutOfRangeDataStrategy::Drop; } else if (string == "Freeze") { return ViveControllerManager::OutOfRangeDataStrategy::Freeze; } 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 (!_desktopMode) { _resetMatCalculated = false; } } if (configurationSettings.contains("hmdDesktopTracking")) { _hmdDesktopTracking = configurationSettings["hmdDesktopTracking"].toBool(); } _inputDevice->configureCalibrationSettings(configurationSettings); saveSettings(); }} QJsonObject ViveControllerManager::configurationSettings() { if (isSupported()) { QJsonObject configurationSettings = _inputDevice->configurationSettings(); configurationSettings["desktopMode"] = _desktopMode; configurationSettings["hmdDesktopTracking"] = _hmdDesktopTracking; return configurationSettings; } return QJsonObject();} QString ViveControllerManager::configurationLayout() { return OPENVR_LAYOUT;} 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; 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; 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;} void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { if (!_system) { return; } if (isDesktopMode() && _desktopMode) { if (!_resetMatCalculated) { _resetMat = calculateResetMat(); _resetMatCalculated = true; } _system->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseStanding, 0, _nextSimPoseData.vrPoses, vr::k_unMaxTrackedDeviceCount); _nextSimPoseData.update(_resetMat); } else if (isDesktopMode()) { _nextSimPoseData.resetToInvalid(); } auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); handleOpenVrEvents(); if (openVrQuitRequested()) { deactivate(); return; } // because update mutates the internal state we need to lock userInputMapper->withLock([&, this]() { _inputDevice->update(deltaTime, inputCalibrationData); }); if (_inputDevice->_trackedControllers == 0 && _registeredWithInputMapper) { userInputMapper->removeDevice(_inputDevice->_deviceID); _registeredWithInputMapper = false; _inputDevice->_poseStateMap.clear(); } if (!_registeredWithInputMapper && _inputDevice->_trackedControllers > 0) { userInputMapper->registerDevice(_inputDevice); _registeredWithInputMapper = true; }} void ViveControllerManager::loadSettings() { Settings settings; QString nameString = getName(); settings.beginGroup(nameString); { if (_inputDevice) { const double DEFAULT_ARM_CIRCUMFERENCE = 0.33; const double DEFAULT_SHOULDER_WIDTH = 0.48; const QString DEFAULT_OUT_OF_RANGE_STRATEGY = "Drop"; _inputDevice->_armCircumference = settings.value("armCircumference", QVariant(DEFAULT_ARM_CIRCUMFERENCE)).toDouble(); _inputDevice->_shoulderWidth = settings.value("shoulderWidth", QVariant(DEFAULT_SHOULDER_WIDTH)).toDouble(); _inputDevice->_outOfRangeDataStrategy = stringToOutOfRangeDataStrategy(settings.value("outOfRangeDataStrategy", QVariant(DEFAULT_OUT_OF_RANGE_STRATEGY)).toString()); } } settings.endGroup();} void ViveControllerManager::saveSettings() const { Settings settings; QString nameString = getName(); settings.beginGroup(nameString); { if (_inputDevice) { settings.setValue(QString("armCircumference"), _inputDevice->_armCircumference); settings.setValue(QString("shoulderWidth"), _inputDevice->_shoulderWidth); settings.setValue(QString("outOfRangeDataStrategy"), outOfRangeDataStrategyToString(_inputDevice->_outOfRangeDataStrategy)); } } settings.endGroup();} ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : controller::InputDevice("Vive"), _system(system) { _configStringMap[Config::None] = QString("None"); _configStringMap[Config::Feet] = QString("Feet"); _configStringMap[Config::FeetAndHips] = QString("FeetAndHips"); _configStringMap[Config::FeetHipsAndChest] = QString("FeetHipsAndChest"); _configStringMap[Config::FeetHipsAndShoulders] = QString("FeetHipsAndShoulders"); _configStringMap[Config::FeetHipsChestAndShoulders] = QString("FeetHipsChestAndShoulders");} void ViveControllerManager::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { _poseStateMap.clear(); _buttonPressedMap.clear(); _validTrackedObjects.clear(); _trackedControllers = 0; if (_headsetName == "") { _headsetName = getOpenVrDeviceName(); if (_headsetName == "HTC") { _headsetName += " Vive"; } } // While the keyboard is open, we defer strictly to the keyboard values if (isOpenVrKeyboardShown()) { _axisStateMap.clear(); return; } PerformanceTimer perfTimer("ViveControllerManager::update"); auto leftHandDeviceIndex = _system->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_LeftHand); auto rightHandDeviceIndex = _system->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_RightHand); handleHandController(deltaTime, leftHandDeviceIndex, inputCalibrationData, true); handleHandController(deltaTime, rightHandDeviceIndex, inputCalibrationData, false); // collect poses for all generic trackers for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) { handleTrackedObject(i, inputCalibrationData); handleHmd(i, inputCalibrationData); } // handle haptics { Locker locker(_lock); if (_leftHapticDuration > 0.0f) { hapticsHelper(deltaTime, true); } if (_rightHapticDuration > 0.0f) { hapticsHelper(deltaTime, false); } } if (leftHandDeviceIndex != vr::k_unTrackedDeviceIndexInvalid) { _trackedControllers++; } if (rightHandDeviceIndex != vr::k_unTrackedDeviceIndexInvalid) { _trackedControllers++; } calibrateFromHandController(inputCalibrationData); calibrateFromUI(inputCalibrationData); updateCalibratedLimbs(inputCalibrationData); _lastSimPoseData = _nextSimPoseData;} void ViveControllerManager::InputDevice::calibrateFromHandController(const controller::InputCalibrationData& inputCalibrationData) { if (checkForCalibrationEvent()) { quint64 currentTime = usecTimestampNow(); if (!_timeTilCalibrationSet) { _timeTilCalibrationSet = true; _timeTilCalibration = currentTime + CALIBRATION_TIMELAPSE; } if (currentTime > _timeTilCalibration && !_triggersPressedHandled) { _triggersPressedHandled = true; calibrateOrUncalibrate(inputCalibrationData); } } else { _triggersPressedHandled = false; _timeTilCalibrationSet = false; }} void ViveControllerManager::InputDevice::calibrateFromUI(const controller::InputCalibrationData& inputCalibrationData) { if (_calibrate) { uncalibrate(); calibrate(inputCalibrationData); emitCalibrationStatus(); _calibrate = false; }} static const float CM_TO_M = 0.01f;static const float M_TO_CM = 100.0f; void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJsonObject configurationSettings) { Locker locker(_lock); if (!configurationSettings.empty()) { auto iter = configurationSettings.begin(); auto end = configurationSettings.end(); bool hmdDesktopTracking = true; bool hmdDesktopMode = false; while (iter != end) { if (iter.key() == "bodyConfiguration") { setConfigFromString(iter.value().toString()); } else if (iter.key() == "headConfiguration") { QJsonObject headObject = iter.value().toObject(); bool overrideHead = headObject["override"].toBool(); if (overrideHead) { _headConfig = HeadConfig::Puck; _headPuckYOffset = headObject["Y"].toDouble() * CM_TO_M; _headPuckZOffset = headObject["Z"].toDouble() * CM_TO_M; } else { _headConfig = HeadConfig::HMD; } } else if (iter.key() == "handConfiguration") { QJsonObject handsObject = iter.value().toObject(); bool overrideHands = handsObject["override"].toBool(); if (overrideHands) { _handConfig = HandConfig::Pucks; _handPuckYOffset = handsObject["Y"].toDouble() * CM_TO_M; _handPuckZOffset = handsObject["Z"].toDouble() * CM_TO_M; } else { _handConfig = HandConfig::HandController; } } else if (iter.key() == "armCircumference") { _armCircumference = (float)iter.value().toDouble() * CM_TO_M; } else if (iter.key() == "shoulderWidth") { _shoulderWidth = (float)iter.value().toDouble() * CM_TO_M; } else if (iter.key() == "hmdDesktopTracking") { hmdDesktopTracking = iter.value().toBool(); } else if (iter.key() == "desktopMode") { hmdDesktopMode = iter.value().toBool(); } else if (iter.key() == "outOfRangeDataStrategy") { _outOfRangeDataStrategy = stringToOutOfRangeDataStrategy(iter.value().toString()); } iter++; } _hmdTrackingEnabled = !(hmdDesktopMode && hmdDesktopTracking); }} void ViveControllerManager::InputDevice::calibrateNextFrame() { Locker locker(_lock); _calibrate = true;} QJsonObject ViveControllerManager::InputDevice::configurationSettings() { Locker locker(_lock); QJsonObject configurationSettings; configurationSettings["trackerConfiguration"] = configToString(_preferedConfig); configurationSettings["HMDHead"] = (_headConfig == HeadConfig::HMD); configurationSettings["handController"] = (_handConfig == HandConfig::HandController); configurationSettings["puckCount"] = (int)_validTrackedObjects.size(); configurationSettings["armCircumference"] = (double)_armCircumference * M_TO_CM; configurationSettings["shoulderWidth"] = (double)_shoulderWidth * M_TO_CM; configurationSettings["outOfRangeDataStrategy"] = outOfRangeDataStrategyToString(_outOfRangeDataStrategy); return configurationSettings;}Lines 1–500 of 1421. Load more
// // ViveControllerManager.cpp // input-plugins/src/input-plugins // // Created by Sam Gondelman on 6/29/15. // Copyright 2013 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 "ViveControllerManager.h" #include <algorithm> #include <string> #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 <glm/ext.hpp> #include <glm/gtc/quaternion.hpp> #include <ui-plugins/PluginContainer.h> #include <plugins/DisplayPlugin.h> #include <controllers/UserInputMapper.h> #include <Plugins/InputConfiguration.h> #include <controllers/StandardControls.h> extern PoseData _nextSimPoseData; vr::IVRSystem* acquireOpenVrSystem(); void releaseOpenVrSystem(); static const QString OPENVR_LAYOUT = QString("OpenVrConfiguration.qml"); static const char* CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b"; const quint64 CALIBRATION_TIMELAPSE = 1 * USECS_PER_SECOND; static const char* MENU_PARENT = "Avatar"; static const char* MENU_NAME = "Vive Controllers"; static const char* MENU_PATH = "Avatar" ">" "Vive Controllers"; 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; 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; } static glm::mat4 calculateResetMat() { auto chaperone = vr::VRChaperone(); if (chaperone) { float const UI_RADIUS = 1.0f; float const UI_HEIGHT = 1.6f; float const UI_Z_OFFSET = 0.5; float xSize, zSize; chaperone->GetPlayAreaSize(&xSize, &zSize); glm::vec3 uiPos(0.0f, UI_HEIGHT, UI_RADIUS - (0.5f * zSize) - UI_Z_OFFSET); return glm::inverse(createMatFromQuatAndPos(glm::quat(), uiPos)); } return glm::mat4(); } 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"; } } static ViveControllerManager::OutOfRangeDataStrategy stringToOutOfRangeDataStrategy(const QString& string) { if (string == "Drop") { return ViveControllerManager::OutOfRangeDataStrategy::Drop; } else if (string == "Freeze") { return ViveControllerManager::OutOfRangeDataStrategy::Freeze; } 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 (!_desktopMode) { _resetMatCalculated = false; } } if (configurationSettings.contains("hmdDesktopTracking")) { _hmdDesktopTracking = configurationSettings["hmdDesktopTracking"].toBool(); } _inputDevice->configureCalibrationSettings(configurationSettings); saveSettings(); } } QJsonObject ViveControllerManager::configurationSettings() { if (isSupported()) { QJsonObject configurationSettings = _inputDevice->configurationSettings(); configurationSettings["desktopMode"] = _desktopMode; configurationSettings["hmdDesktopTracking"] = _hmdDesktopTracking; return configurationSettings; } return QJsonObject(); } QString ViveControllerManager::configurationLayout() { return OPENVR_LAYOUT; } 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; 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; 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; } void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { if (!_system) { return; } if (isDesktopMode() && _desktopMode) { if (!_resetMatCalculated) { _resetMat = calculateResetMat(); _resetMatCalculated = true; } _system->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseStanding, 0, _nextSimPoseData.vrPoses, vr::k_unMaxTrackedDeviceCount); _nextSimPoseData.update(_resetMat); } else if (isDesktopMode()) { _nextSimPoseData.resetToInvalid(); } auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); handleOpenVrEvents(); if (openVrQuitRequested()) { deactivate(); return; } // because update mutates the internal state we need to lock userInputMapper->withLock([&, this]() { _inputDevice->update(deltaTime, inputCalibrationData); }); if (_inputDevice->_trackedControllers == 0 && _registeredWithInputMapper) { userInputMapper->removeDevice(_inputDevice->_deviceID); _registeredWithInputMapper = false; _inputDevice->_poseStateMap.clear(); } if (!_registeredWithInputMapper && _inputDevice->_trackedControllers > 0) { userInputMapper->registerDevice(_inputDevice); _registeredWithInputMapper = true; } } void ViveControllerManager::loadSettings() { Settings settings; QString nameString = getName(); settings.beginGroup(nameString); { if (_inputDevice) { const double DEFAULT_ARM_CIRCUMFERENCE = 0.33; const double DEFAULT_SHOULDER_WIDTH = 0.48; const QString DEFAULT_OUT_OF_RANGE_STRATEGY = "Drop"; _inputDevice->_armCircumference = settings.value("armCircumference", QVariant(DEFAULT_ARM_CIRCUMFERENCE)).toDouble(); _inputDevice->_shoulderWidth = settings.value("shoulderWidth", QVariant(DEFAULT_SHOULDER_WIDTH)).toDouble(); _inputDevice->_outOfRangeDataStrategy = stringToOutOfRangeDataStrategy(settings.value("outOfRangeDataStrategy", QVariant(DEFAULT_OUT_OF_RANGE_STRATEGY)).toString()); } } settings.endGroup(); } void ViveControllerManager::saveSettings() const { Settings settings; QString nameString = getName(); settings.beginGroup(nameString); { if (_inputDevice) { settings.setValue(QString("armCircumference"), _inputDevice->_armCircumference); settings.setValue(QString("shoulderWidth"), _inputDevice->_shoulderWidth); settings.setValue(QString("outOfRangeDataStrategy"), outOfRangeDataStrategyToString(_inputDevice->_outOfRangeDataStrategy)); } } settings.endGroup(); } ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : controller::InputDevice("Vive"), _system(system) { _configStringMap[Config::None] = QString("None"); _configStringMap[Config::Feet] = QString("Feet"); _configStringMap[Config::FeetAndHips] = QString("FeetAndHips"); _configStringMap[Config::FeetHipsAndChest] = QString("FeetHipsAndChest"); _configStringMap[Config::FeetHipsAndShoulders] = QString("FeetHipsAndShoulders"); _configStringMap[Config::FeetHipsChestAndShoulders] = QString("FeetHipsChestAndShoulders"); } void ViveControllerManager::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { _poseStateMap.clear(); _buttonPressedMap.clear(); _validTrackedObjects.clear(); _trackedControllers = 0; if (_headsetName == "") { _headsetName = getOpenVrDeviceName(); if (_headsetName == "HTC") { _headsetName += " Vive"; } } // While the keyboard is open, we defer strictly to the keyboard values if (isOpenVrKeyboardShown()) { _axisStateMap.clear(); return; } PerformanceTimer perfTimer("ViveControllerManager::update"); auto leftHandDeviceIndex = _system->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_LeftHand); auto rightHandDeviceIndex = _system->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_RightHand); handleHandController(deltaTime, leftHandDeviceIndex, inputCalibrationData, true); handleHandController(deltaTime, rightHandDeviceIndex, inputCalibrationData, false); // collect poses for all generic trackers for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) { handleTrackedObject(i, inputCalibrationData); handleHmd(i, inputCalibrationData); } // handle haptics { Locker locker(_lock); if (_leftHapticDuration > 0.0f) { hapticsHelper(deltaTime, true); } if (_rightHapticDuration > 0.0f) { hapticsHelper(deltaTime, false); } } if (leftHandDeviceIndex != vr::k_unTrackedDeviceIndexInvalid) { _trackedControllers++; } if (rightHandDeviceIndex != vr::k_unTrackedDeviceIndexInvalid) { _trackedControllers++; } calibrateFromHandController(inputCalibrationData); calibrateFromUI(inputCalibrationData); updateCalibratedLimbs(inputCalibrationData); _lastSimPoseData = _nextSimPoseData; } void ViveControllerManager::InputDevice::calibrateFromHandController(const controller::InputCalibrationData& inputCalibrationData) { if (checkForCalibrationEvent()) { quint64 currentTime = usecTimestampNow(); if (!_timeTilCalibrationSet) { _timeTilCalibrationSet = true; _timeTilCalibration = currentTime + CALIBRATION_TIMELAPSE; } if (currentTime > _timeTilCalibration && !_triggersPressedHandled) { _triggersPressedHandled = true; calibrateOrUncalibrate(inputCalibrationData); } } else { _triggersPressedHandled = false; _timeTilCalibrationSet = false; } } void ViveControllerManager::InputDevice::calibrateFromUI(const controller::InputCalibrationData& inputCalibrationData) { if (_calibrate) { uncalibrate(); calibrate(inputCalibrationData); emitCalibrationStatus(); _calibrate = false; } } static const float CM_TO_M = 0.01f; static const float M_TO_CM = 100.0f; void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJsonObject configurationSettings) { Locker locker(_lock); if (!configurationSettings.empty()) { auto iter = configurationSettings.begin(); auto end = configurationSettings.end(); bool hmdDesktopTracking = true; bool hmdDesktopMode = false; while (iter != end) { if (iter.key() == "bodyConfiguration") { setConfigFromString(iter.value().toString()); } else if (iter.key() == "headConfiguration") { QJsonObject headObject = iter.value().toObject(); bool overrideHead = headObject["override"].toBool(); if (overrideHead) { _headConfig = HeadConfig::Puck; _headPuckYOffset = headObject["Y"].toDouble() * CM_TO_M; _headPuckZOffset = headObject["Z"].toDouble() * CM_TO_M; } else { _headConfig = HeadConfig::HMD; } } else if (iter.key() == "handConfiguration") { QJsonObject handsObject = iter.value().toObject(); bool overrideHands = handsObject["override"].toBool(); if (overrideHands) { _handConfig = HandConfig::Pucks; _handPuckYOffset = handsObject["Y"].toDouble() * CM_TO_M; _handPuckZOffset = handsObject["Z"].toDouble() * CM_TO_M; } else { _handConfig = HandConfig::HandController; } } else if (iter.key() == "armCircumference") { _armCircumference = (float)iter.value().toDouble() * CM_TO_M; } else if (iter.key() == "shoulderWidth") { _shoulderWidth = (float)iter.value().toDouble() * CM_TO_M; } else if (iter.key() == "hmdDesktopTracking") { hmdDesktopTracking = iter.value().toBool(); } else if (iter.key() == "desktopMode") { hmdDesktopMode = iter.value().toBool(); } else if (iter.key() == "outOfRangeDataStrategy") { _outOfRangeDataStrategy = stringToOutOfRangeDataStrategy(iter.value().toString()); } iter++; } _hmdTrackingEnabled = !(hmdDesktopMode && hmdDesktopTracking); } } void ViveControllerManager::InputDevice::calibrateNextFrame() { Locker locker(_lock); _calibrate = true; } QJsonObject ViveControllerManager::InputDevice::configurationSettings() { Locker locker(_lock); QJsonObject configurationSettings; configurationSettings["trackerConfiguration"] = configToString(_preferedConfig); configurationSettings["HMDHead"] = (_headConfig == HeadConfig::HMD); configurationSettings["handController"] = (_handConfig == HandConfig::HandController); configurationSettings["puckCount"] = (int)_validTrackedObjects.size(); configurationSettings["armCircumference"] = (double)_armCircumference * M_TO_CM; configurationSettings["shoulderWidth"] = (double)_shoulderWidth * M_TO_CM; configurationSettings["outOfRangeDataStrategy"] = outOfRangeDataStrategyToString(_outOfRangeDataStrategy); return configurationSettings; }