//// OculusControllerManager.cpp// input-plugins/src/input-plugins//// Created by Bradley Austin Davis 2016/03/04.// Copyright 2013-2016 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 "OculusControllerManager.h" #include <QtCore/QLoggingCategory> #include <ui-plugins/PluginContainer.h>#include <controllers/UserInputMapper.h>#include <controllers/StandardControls.h> #include <AvatarConstants.h>#include <PerfStat.h>#include <PathUtils.h>#include <NumericalConstants.h>#include <StreamUtils.h> #include <ovr_capi.h> #include "OculusHelpers.h" using namespace hifi; const char* OculusControllerManager::NAME = "Oculus";const QString OCULUS_LAYOUT = "OculusConfiguration.qml"; const quint64 LOST_TRACKING_DELAY = 3000000; bool OculusControllerManager::isSupported() const { return hifi::ovr::available();} bool OculusControllerManager::activate() { InputPlugin::activate(); checkForConnectedDevices(); return true;} QString OculusControllerManager::configurationLayout() { return OCULUS_LAYOUT;} void OculusControllerManager::setConfigurationSettings(const QJsonObject configurationSettings) { if (configurationSettings.contains("trackControllersInOculusHome")) { _trackControllersInOculusHome.set(configurationSettings["trackControllersInOculusHome"].toBool()); }} QJsonObject OculusControllerManager::configurationSettings() { QJsonObject configurationSettings; configurationSettings["trackControllersInOculusHome"] = _trackControllersInOculusHome.get(); return configurationSettings;} void OculusControllerManager::checkForConnectedDevices() { if (_touch && _remote) { return; } ovr::withSession([&] (ovrSession session) { unsigned int controllerConnected = ovr_GetConnectedControllerTypes(session); if (!_remote && (controllerConnected & ovrControllerType_Remote) == ovrControllerType_Remote) { if (OVR_SUCCESS(ovr_GetInputState(session, ovrControllerType_Remote, &_remoteInputState))) { auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); _remote = std::make_shared<RemoteDevice>(*this); userInputMapper->registerDevice(_remote); } } if (!_touch && (controllerConnected & ovrControllerType_Touch) != 0) { if (OVR_SUCCESS(ovr_GetInputState(session, ovrControllerType_Touch, &_touchInputState))) { auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); _touch = std::make_shared<TouchDevice>(*this); userInputMapper->registerDevice(_touch); } } });} void OculusControllerManager::deactivate() { InputPlugin::deactivate(); // unregister with UserInputMapper auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); if (_touch) { userInputMapper->removeDevice(_touch->getDeviceID()); } if (_remote) { userInputMapper->removeDevice(_remote->getDeviceID()); }} void OculusControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { PerformanceTimer perfTimer("OculusControllerManager::TouchDevice::update"); checkForConnectedDevices(); bool updateRemote = false, updateTouch = false; ovr::withSession([&](ovrSession session) { if (_touch) { updateTouch = OVR_SUCCESS(ovr_GetInputState(session, ovrControllerType_Touch, &_touchInputState)); if (!updateTouch) { qCWarning(oculusLog) << "Unable to read Oculus touch input state" << ovr::getError(); } } if (_remote) { updateRemote = OVR_SUCCESS(ovr_GetInputState(session, ovrControllerType_Remote, &_remoteInputState)); if (!updateRemote) { qCWarning(oculusLog) << "Unable to read Oculus remote input state" << ovr::getError(); } } }); if (_touch && updateTouch) { _touch->update(deltaTime, inputCalibrationData); } if (_remote && updateRemote) { _remote->update(deltaTime, inputCalibrationData); }} void OculusControllerManager::pluginFocusOutEvent() { if (_touch) { _touch->focusOutEvent(); } if (_remote) { _remote->focusOutEvent(); }} void OculusControllerManager::stopHapticPulse(bool leftHand) { if (_touch) { _touch->stopHapticPulse(leftHand); }} QStringList OculusControllerManager::getSubdeviceNames() { QStringList devices; if (_touch) { devices << _touch->getName(); } if (_remote) { devices << _remote->getName(); } return devices;} using namespace controller; static const std::vector<std::pair<ovrButton, StandardButtonChannel>> BUTTON_MAP { { { ovrButton_Up, DU }, { ovrButton_Down, DD }, { ovrButton_Left, DL }, { ovrButton_Right, DR }, { ovrButton_Enter, START }, { ovrButton_Back, BACK }, { ovrButton_X, X }, { ovrButton_Y, Y }, { ovrButton_A, A }, { ovrButton_B, B }, { ovrButton_LThumb, LS }, { ovrButton_RThumb, RS }, //{ ovrButton_LShoulder, LB }, //{ ovrButton_RShoulder, RB },} }; static const std::vector<std::pair<ovrTouch, StandardButtonChannel>> TOUCH_MAP { { { ovrTouch_X, LEFT_PRIMARY_THUMB_TOUCH }, { ovrTouch_Y, LEFT_SECONDARY_THUMB_TOUCH }, { ovrTouch_A, RIGHT_PRIMARY_THUMB_TOUCH }, { ovrTouch_B, RIGHT_SECONDARY_THUMB_TOUCH }, { ovrTouch_LIndexTrigger, LEFT_PRIMARY_INDEX_TOUCH }, { ovrTouch_RIndexTrigger, RIGHT_PRIMARY_INDEX_TOUCH }, { ovrTouch_LThumb, LS_TOUCH }, { ovrTouch_RThumb, RS_TOUCH }, { ovrTouch_LThumbUp, LEFT_THUMB_UP }, { ovrTouch_RThumbUp, RIGHT_THUMB_UP }, { ovrTouch_LIndexPointing, LEFT_INDEX_POINT }, { ovrTouch_RIndexPointing, RIGHT_INDEX_POINT },} }; controller::Input::NamedVector OculusControllerManager::RemoteDevice::getAvailableInputs() const { using namespace controller; QVector<Input::NamedPair> availableInputs { makePair(DU, "DU"), makePair(DD, "DD"), makePair(DL, "DL"), makePair(DR, "DR"), makePair(START, "Start"), makePair(BACK, "Back"), }; return availableInputs;} QString OculusControllerManager::RemoteDevice::getDefaultMappingConfig() const { static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/oculus_remote.json"; return MAPPING_JSON;} void OculusControllerManager::RemoteDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { _buttonPressedMap.clear(); const auto& inputState = _parent._remoteInputState; for (const auto& pair : BUTTON_MAP) { if (inputState.Buttons & pair.first) { _buttonPressedMap.insert(pair.second); } }} void OculusControllerManager::RemoteDevice::focusOutEvent() { _buttonPressedMap.clear();} void OculusControllerManager::TouchDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { _buttonPressedMap.clear(); int numTrackedControllers = 0; quint64 currentTime = usecTimestampNow(); static const auto REQUIRED_HAND_STATUS = ovrStatus_OrientationTracked | ovrStatus_PositionTracked; bool hasInputFocus = ovr::hasInputFocus(); bool trackControllersInOculusHome = _parent._trackControllersInOculusHome.get(); auto tracking = ovr::getTrackingState(); // ovr_GetTrackingState(_parent._session, 0, false); ovr::for_each_hand([&](ovrHandType hand) { ++numTrackedControllers; int controller = (hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND); // Disable hand tracking while in Oculus Dash (Dash renders it's own hands) if (!hasInputFocus && !trackControllersInOculusHome) { _poseStateMap.erase(controller); _poseStateMap[controller].valid = false; return; } if (REQUIRED_HAND_STATUS == (tracking.HandStatusFlags[hand] & REQUIRED_HAND_STATUS)) { _poseStateMap.erase(controller); handlePose(deltaTime, inputCalibrationData, hand, tracking.HandPoses[hand]); _lostTracking[controller] = false; _lastControllerPose[controller] = tracking.HandPoses[hand]; return; } if (_lostTracking[controller]) { if (currentTime > _regainTrackingDeadline[controller]) { _poseStateMap.erase(controller); _poseStateMap[controller].valid = false; return; } } else { quint64 deadlineToRegainTracking = currentTime + LOST_TRACKING_DELAY; _regainTrackingDeadline[controller] = deadlineToRegainTracking; _lostTracking[controller] = true; } handleRotationForUntrackedHand(inputCalibrationData, hand, tracking.HandPoses[hand]); }); if (ovr::hmdMounted()) { handleHeadPose(deltaTime, inputCalibrationData, tracking.HeadPose); } else { _poseStateMap[controller::HEAD].valid = false; } using namespace controller; // Axes const auto& inputState = _parent._touchInputState; _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].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) { if (inputState.Buttons & pair.first) { _buttonPressedMap.insert(pair.second); } } // Touches for (const auto& pair : TOUCH_MAP) { if (inputState.Touches & pair.first) { _buttonPressedMap.insert(pair.second); } } // Haptics { Locker locker(_lock); if (_leftHapticDuration > 0.0f) { _leftHapticDuration -= deltaTime * 1000.0f; // milliseconds } else { stopHapticPulse(true); } if (_rightHapticDuration > 0.0f) { _rightHapticDuration -= deltaTime * 1000.0f; // milliseconds } else { stopHapticPulse(false); } }} void OculusControllerManager::TouchDevice::focusOutEvent() { _axisStateMap.clear(); _buttonPressedMap.clear();}; void OculusControllerManager::TouchDevice::handlePose(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, ovrHandType hand, const ovrPoseStatef& handPose) { auto poseId = hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND; auto& pose = _poseStateMap[poseId]; pose = ovr::toControllerPose(hand, handPose); // transform into avatar frame glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; pose = pose.transform(controllerToAvatar);} void OculusControllerManager::TouchDevice::handleHeadPose(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, const ovrPoseStatef& headPose) { glm::mat4 mat = createMatFromQuatAndPos(ovr::toGlm(headPose.ThePose.Orientation), ovr::toGlm(headPose.ThePose.Position)); //perform a 180 flip to make the HMD face the +z instead of -z, beacuse the head faces +z glm::mat4 matYFlip = mat * Matrices::Y_180; controller::Pose pose(extractTranslation(matYFlip), glmExtractRotation(matYFlip), ovr::toGlm(headPose.LinearVelocity), // XXX * matYFlip ? ovr::toGlm(headPose.AngularVelocity)); glm::mat4 sensorToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; glm::mat4 defaultHeadOffset; if (inputCalibrationData.hmdAvatarAlignmentType == controller::HmdAvatarAlignmentType::Eyes) { // align the eyes of the user with the eyes of the avatar defaultHeadOffset = glm::inverse(inputCalibrationData.defaultCenterEyeMat) * inputCalibrationData.defaultHeadMat; } else { // align the head of the user with the head of the avatar defaultHeadOffset = createMatFromQuatAndPos(Quaternions::IDENTITY, -DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET); } pose.valid = true; _poseStateMap[controller::HEAD] = pose.postTransform(defaultHeadOffset).transform(sensorToAvatar);} void OculusControllerManager::TouchDevice::handleRotationForUntrackedHand(const controller::InputCalibrationData& inputCalibrationData, ovrHandType hand, const ovrPoseStatef& handPose) { auto poseId = (hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND); auto& pose = _poseStateMap[poseId]; auto lastHandPose = _lastControllerPose[poseId]; pose = ovr::toControllerPose(hand, handPose, lastHandPose); glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; pose = pose.transform(controllerToAvatar);} bool OculusControllerManager::TouchDevice::triggerHapticPulse(float strength, float duration, uint16_t index) { if (index > 2) { return false; } controller::Hand hand = (controller::Hand)index; Locker locker(_lock); bool toReturn = true; ovr::withSession([&](ovrSession session) { if (hand == controller::BOTH || hand == controller::LEFT) { if (strength == 0.0f) { _leftHapticStrength = 0.0f; _leftHapticDuration = 0.0f; } else { _leftHapticStrength = (duration > _leftHapticDuration) ? strength : _leftHapticStrength; if (ovr_SetControllerVibration(session, ovrControllerType_LTouch, 1.0f, _leftHapticStrength) != ovrSuccess) { toReturn = false; } _leftHapticDuration = std::max(duration, _leftHapticDuration); } } if (hand == controller::BOTH || hand == controller::RIGHT) { if (strength == 0.0f) { _rightHapticStrength = 0.0f; _rightHapticDuration = 0.0f; } else { _rightHapticStrength = (duration > _rightHapticDuration) ? strength : _rightHapticStrength; if (ovr_SetControllerVibration(session, ovrControllerType_RTouch, 1.0f, _rightHapticStrength) != ovrSuccess) { toReturn = false; } _rightHapticDuration = std::max(duration, _rightHapticDuration); } } }); return toReturn;} void OculusControllerManager::TouchDevice::stopHapticPulse(bool leftHand) { auto handType = (leftHand ? ovrControllerType_LTouch : ovrControllerType_RTouch); ovr::withSession([&](ovrSession session) { ovr_SetControllerVibration(session, handType, 0.0f, 0.0f); });} /*@jsdoc * <p>The <code>Controller.Hardware.OculusTouch</code> object has properties representing the Oculus Rift. The property values * are integer IDs, uniquely identifying each output. <em>Read-only.</em></p> * <p>These outputs can be mapped to actions or functions or <code>Controller.Standard</code> items in a {@link RouteObject} * mapping.</p> * <table> * <thead> * <tr><th>Property</th><th>Type</th><th>Data</th><th>Description</th></tr> * </thead> * <tbody> * <tr><td colspan="4"><strong>Buttons</strong></td></tr> * <tr><td><code>A</code></td><td>number</td><td>number</td><td>"A" button pressed.</td></tr> * <tr><td><code>B</code></td><td>number</td><td>number</td><td>"B" button pressed.</td></tr> * <tr><td><code>X</code></td><td>number</td><td>number</td><td>"X" button pressed.</td></tr> * <tr><td><code>Y</code></td><td>number</td><td>number</td><td>"Y" button pressed.</td></tr> * <tr><td><code>LeftApplicationMenu</code></td><td>number</td><td>number</td><td>Left application menu button pressed. * </td></tr> * <tr><td><code>RightApplicationMenu</code></td><td>number</td><td>number</td><td>Right application menu button pressed. * </td></tr> * <tr><td colspan="4"><strong>Sticks</strong></td></tr> * <tr><td><code>LX</code></td><td>number</td><td>number</td><td>Left stick x-axis scale.</td></tr> * <tr><td><code>LY</code></td><td>number</td><td>number</td><td>Left stick y-axis scale.</td></tr> * <tr><td><code>RX</code></td><td>number</td><td>number</td><td>Right stick x-axis scale.</td></tr> * <tr><td><code>RY</code></td><td>number</td><td>number</td><td>Right stick y-axis scale.</td></tr> * <tr><td><code>LS</code></td><td>number</td><td>number</td><td>Left stick button pressed.</td></tr> * <tr><td><code>RS</code></td><td>number</td><td>number</td><td>Right stick button pressed.</td></tr> * <tr><td><code>LSTouch</code></td><td>number</td><td>number</td><td>Left stick is touched.</td></tr> * <tr><td><code>RSTouch</code></td><td>number</td><td>number</td><td>Right stick is touched.</td></tr> * <tr><td colspan="4"><strong>Triggers</strong></td></tr> * <tr><td><code>LT</code></td><td>number</td><td>number</td><td>Left trigger scale.</td></tr> * <tr><td><code>RT</code></td><td>number</td><td>number</td><td>Right trigger scale.</td></tr> * <tr><td><code>LeftGrip</code></td><td>number</td><td>number</td><td>Left grip scale.</td></tr> * <tr><td><code>RightGrip</code></td><td>number</td><td>number</td><td>Right grip scale.</td></tr> * <tr><td colspan="4"><strong>Finger Abstractions</strong></td></tr> * <tr><td><code>LeftPrimaryThumbTouch</code></td><td>number</td><td>number</td><td>Left thumb touching primary thumb * button.</td></tr> * <tr><td><code>LeftSecondaryThumbTouch</code></td><td>number</td><td>number</td><td>Left thumb touching secondary thumb * button.</td></tr> * <tr><td><code>LeftThumbUp</code></td><td>number</td><td>number</td><td>Left thumb not touching primary or secondary * thumb buttons.</td></tr> * <tr><td><code>RightPrimaryThumbTouch</code></td><td>number</td><td>number</td><td>Right thumb touching primary thumb * button.</td></tr> * <tr><td><code>RightSecondaryThumbTouch</code></td><td>number</td><td>number</td><td>Right thumb touching secondary thumb * button.</td></tr> * <tr><td><code>RightThumbUp</code></td><td>number</td><td>number</td><td>Right thumb not touching primary or secondary * thumb buttons.</td></tr> * <tr><td><code>LeftPrimaryIndexTouch</code></td><td>number</td><td>number</td><td>Left index finger is touching primary * index finger control.</td></tr> * <tr><td><code>LeftIndexPoint</code></td><td>number</td><td>number</td><td>Left index finger is pointing, not touching * primary or secondary index finger controls.</td></tr> * <tr><td><code>RightPrimaryIndexTouch</code></td><td>number</td><td>number</td><td>Right index finger is touching primary * index finger control.</td></tr> * <tr><td><code>RightIndexPoint</code></td><td>number</td><td>number</td><td>Right index finger is pointing, not touching * primary or secondary index finger controls.</td></tr> * <tr><td colspan="4"><strong>Avatar Skeleton</strong></td></tr> * <tr><td><code>Head</code></td><td>number</td><td>{@link Pose}</td><td>Head pose.</td></tr> * <tr><td><code>LeftHand</code></td><td>number</td><td>{@link Pose}</td><td>Left hand pose.</td></tr> * <tr><td><code>RightHand</code></td><td>number</td><td>{@link Pose}</td><td>right hand pose.</td></tr> * </tbody> * </table> * @typedef {object} Controller.Hardware-OculusTouch */controller::Input::NamedVector OculusControllerManager::TouchDevice::getAvailableInputs() const { using namespace controller; QVector<Input::NamedPair> availableInputs{ // buttons makePair(A, "A"), makePair(B, "B"), makePair(X, "X"), makePair(Y, "Y"), // trackpad analogs makePair(LX, "LX"), makePair(LY, "LY"), makePair(RX, "RX"), makePair(RY, "RY"), // triggers makePair(LT, "LT"), makePair(RT, "RT"), // trigger buttons //makePair(LB, "LB"), //makePair(RB, "RB"),Lines 1–500 of 539. Load more
// // OculusControllerManager.cpp // input-plugins/src/input-plugins // // Created by Bradley Austin Davis 2016/03/04. // Copyright 2013-2016 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 "OculusControllerManager.h" #include <QtCore/QLoggingCategory> #include <ui-plugins/PluginContainer.h> #include <controllers/UserInputMapper.h> #include <controllers/StandardControls.h> #include <AvatarConstants.h> #include <PerfStat.h> #include <PathUtils.h> #include <NumericalConstants.h> #include <StreamUtils.h> #include <ovr_capi.h> #include "OculusHelpers.h" using namespace hifi; const char* OculusControllerManager::NAME = "Oculus"; const QString OCULUS_LAYOUT = "OculusConfiguration.qml"; const quint64 LOST_TRACKING_DELAY = 3000000; bool OculusControllerManager::isSupported() const { return hifi::ovr::available(); } bool OculusControllerManager::activate() { InputPlugin::activate(); checkForConnectedDevices(); return true; } QString OculusControllerManager::configurationLayout() { return OCULUS_LAYOUT; } void OculusControllerManager::setConfigurationSettings(const QJsonObject configurationSettings) { if (configurationSettings.contains("trackControllersInOculusHome")) { _trackControllersInOculusHome.set(configurationSettings["trackControllersInOculusHome"].toBool()); } } QJsonObject OculusControllerManager::configurationSettings() { QJsonObject configurationSettings; configurationSettings["trackControllersInOculusHome"] = _trackControllersInOculusHome.get(); return configurationSettings; } void OculusControllerManager::checkForConnectedDevices() { if (_touch && _remote) { return; } ovr::withSession([&] (ovrSession session) { unsigned int controllerConnected = ovr_GetConnectedControllerTypes(session); if (!_remote && (controllerConnected & ovrControllerType_Remote) == ovrControllerType_Remote) { if (OVR_SUCCESS(ovr_GetInputState(session, ovrControllerType_Remote, &_remoteInputState))) { auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); _remote = std::make_shared<RemoteDevice>(*this); userInputMapper->registerDevice(_remote); } } if (!_touch && (controllerConnected & ovrControllerType_Touch) != 0) { if (OVR_SUCCESS(ovr_GetInputState(session, ovrControllerType_Touch, &_touchInputState))) { auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); _touch = std::make_shared<TouchDevice>(*this); userInputMapper->registerDevice(_touch); } } }); } void OculusControllerManager::deactivate() { InputPlugin::deactivate(); // unregister with UserInputMapper auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); if (_touch) { userInputMapper->removeDevice(_touch->getDeviceID()); } if (_remote) { userInputMapper->removeDevice(_remote->getDeviceID()); } } void OculusControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { PerformanceTimer perfTimer("OculusControllerManager::TouchDevice::update"); checkForConnectedDevices(); bool updateRemote = false, updateTouch = false; ovr::withSession([&](ovrSession session) { if (_touch) { updateTouch = OVR_SUCCESS(ovr_GetInputState(session, ovrControllerType_Touch, &_touchInputState)); if (!updateTouch) { qCWarning(oculusLog) << "Unable to read Oculus touch input state" << ovr::getError(); } } if (_remote) { updateRemote = OVR_SUCCESS(ovr_GetInputState(session, ovrControllerType_Remote, &_remoteInputState)); if (!updateRemote) { qCWarning(oculusLog) << "Unable to read Oculus remote input state" << ovr::getError(); } } }); if (_touch && updateTouch) { _touch->update(deltaTime, inputCalibrationData); } if (_remote && updateRemote) { _remote->update(deltaTime, inputCalibrationData); } } void OculusControllerManager::pluginFocusOutEvent() { if (_touch) { _touch->focusOutEvent(); } if (_remote) { _remote->focusOutEvent(); } } void OculusControllerManager::stopHapticPulse(bool leftHand) { if (_touch) { _touch->stopHapticPulse(leftHand); } } QStringList OculusControllerManager::getSubdeviceNames() { QStringList devices; if (_touch) { devices << _touch->getName(); } if (_remote) { devices << _remote->getName(); } return devices; } using namespace controller; static const std::vector<std::pair<ovrButton, StandardButtonChannel>> BUTTON_MAP { { { ovrButton_Up, DU }, { ovrButton_Down, DD }, { ovrButton_Left, DL }, { ovrButton_Right, DR }, { ovrButton_Enter, START }, { ovrButton_Back, BACK }, { ovrButton_X, X }, { ovrButton_Y, Y }, { ovrButton_A, A }, { ovrButton_B, B }, { ovrButton_LThumb, LS }, { ovrButton_RThumb, RS }, //{ ovrButton_LShoulder, LB }, //{ ovrButton_RShoulder, RB }, } }; static const std::vector<std::pair<ovrTouch, StandardButtonChannel>> TOUCH_MAP { { { ovrTouch_X, LEFT_PRIMARY_THUMB_TOUCH }, { ovrTouch_Y, LEFT_SECONDARY_THUMB_TOUCH }, { ovrTouch_A, RIGHT_PRIMARY_THUMB_TOUCH }, { ovrTouch_B, RIGHT_SECONDARY_THUMB_TOUCH }, { ovrTouch_LIndexTrigger, LEFT_PRIMARY_INDEX_TOUCH }, { ovrTouch_RIndexTrigger, RIGHT_PRIMARY_INDEX_TOUCH }, { ovrTouch_LThumb, LS_TOUCH }, { ovrTouch_RThumb, RS_TOUCH }, { ovrTouch_LThumbUp, LEFT_THUMB_UP }, { ovrTouch_RThumbUp, RIGHT_THUMB_UP }, { ovrTouch_LIndexPointing, LEFT_INDEX_POINT }, { ovrTouch_RIndexPointing, RIGHT_INDEX_POINT }, } }; controller::Input::NamedVector OculusControllerManager::RemoteDevice::getAvailableInputs() const { using namespace controller; QVector<Input::NamedPair> availableInputs { makePair(DU, "DU"), makePair(DD, "DD"), makePair(DL, "DL"), makePair(DR, "DR"), makePair(START, "Start"), makePair(BACK, "Back"), }; return availableInputs; } QString OculusControllerManager::RemoteDevice::getDefaultMappingConfig() const { static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/oculus_remote.json"; return MAPPING_JSON; } void OculusControllerManager::RemoteDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { _buttonPressedMap.clear(); const auto& inputState = _parent._remoteInputState; for (const auto& pair : BUTTON_MAP) { if (inputState.Buttons & pair.first) { _buttonPressedMap.insert(pair.second); } } } void OculusControllerManager::RemoteDevice::focusOutEvent() { _buttonPressedMap.clear(); } void OculusControllerManager::TouchDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { _buttonPressedMap.clear(); int numTrackedControllers = 0; quint64 currentTime = usecTimestampNow(); static const auto REQUIRED_HAND_STATUS = ovrStatus_OrientationTracked | ovrStatus_PositionTracked; bool hasInputFocus = ovr::hasInputFocus(); bool trackControllersInOculusHome = _parent._trackControllersInOculusHome.get(); auto tracking = ovr::getTrackingState(); // ovr_GetTrackingState(_parent._session, 0, false); ovr::for_each_hand([&](ovrHandType hand) { ++numTrackedControllers; int controller = (hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND); // Disable hand tracking while in Oculus Dash (Dash renders it's own hands) if (!hasInputFocus && !trackControllersInOculusHome) { _poseStateMap.erase(controller); _poseStateMap[controller].valid = false; return; } if (REQUIRED_HAND_STATUS == (tracking.HandStatusFlags[hand] & REQUIRED_HAND_STATUS)) { _poseStateMap.erase(controller); handlePose(deltaTime, inputCalibrationData, hand, tracking.HandPoses[hand]); _lostTracking[controller] = false; _lastControllerPose[controller] = tracking.HandPoses[hand]; return; } if (_lostTracking[controller]) { if (currentTime > _regainTrackingDeadline[controller]) { _poseStateMap.erase(controller); _poseStateMap[controller].valid = false; return; } } else { quint64 deadlineToRegainTracking = currentTime + LOST_TRACKING_DELAY; _regainTrackingDeadline[controller] = deadlineToRegainTracking; _lostTracking[controller] = true; } handleRotationForUntrackedHand(inputCalibrationData, hand, tracking.HandPoses[hand]); }); if (ovr::hmdMounted()) { handleHeadPose(deltaTime, inputCalibrationData, tracking.HeadPose); } else { _poseStateMap[controller::HEAD].valid = false; } using namespace controller; // Axes const auto& inputState = _parent._touchInputState; _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].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) { if (inputState.Buttons & pair.first) { _buttonPressedMap.insert(pair.second); } } // Touches for (const auto& pair : TOUCH_MAP) { if (inputState.Touches & pair.first) { _buttonPressedMap.insert(pair.second); } } // Haptics { Locker locker(_lock); if (_leftHapticDuration > 0.0f) { _leftHapticDuration -= deltaTime * 1000.0f; // milliseconds } else { stopHapticPulse(true); } if (_rightHapticDuration > 0.0f) { _rightHapticDuration -= deltaTime * 1000.0f; // milliseconds } else { stopHapticPulse(false); } } } void OculusControllerManager::TouchDevice::focusOutEvent() { _axisStateMap.clear(); _buttonPressedMap.clear(); }; void OculusControllerManager::TouchDevice::handlePose(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, ovrHandType hand, const ovrPoseStatef& handPose) { auto poseId = hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND; auto& pose = _poseStateMap[poseId]; pose = ovr::toControllerPose(hand, handPose); // transform into avatar frame glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; pose = pose.transform(controllerToAvatar); } void OculusControllerManager::TouchDevice::handleHeadPose(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, const ovrPoseStatef& headPose) { glm::mat4 mat = createMatFromQuatAndPos(ovr::toGlm(headPose.ThePose.Orientation), ovr::toGlm(headPose.ThePose.Position)); //perform a 180 flip to make the HMD face the +z instead of -z, beacuse the head faces +z glm::mat4 matYFlip = mat * Matrices::Y_180; controller::Pose pose(extractTranslation(matYFlip), glmExtractRotation(matYFlip), ovr::toGlm(headPose.LinearVelocity), // XXX * matYFlip ? ovr::toGlm(headPose.AngularVelocity)); glm::mat4 sensorToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; glm::mat4 defaultHeadOffset; if (inputCalibrationData.hmdAvatarAlignmentType == controller::HmdAvatarAlignmentType::Eyes) { // align the eyes of the user with the eyes of the avatar defaultHeadOffset = glm::inverse(inputCalibrationData.defaultCenterEyeMat) * inputCalibrationData.defaultHeadMat; } else { // align the head of the user with the head of the avatar defaultHeadOffset = createMatFromQuatAndPos(Quaternions::IDENTITY, -DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET); } pose.valid = true; _poseStateMap[controller::HEAD] = pose.postTransform(defaultHeadOffset).transform(sensorToAvatar); } void OculusControllerManager::TouchDevice::handleRotationForUntrackedHand(const controller::InputCalibrationData& inputCalibrationData, ovrHandType hand, const ovrPoseStatef& handPose) { auto poseId = (hand == ovrHand_Left ? controller::LEFT_HAND : controller::RIGHT_HAND); auto& pose = _poseStateMap[poseId]; auto lastHandPose = _lastControllerPose[poseId]; pose = ovr::toControllerPose(hand, handPose, lastHandPose); glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; pose = pose.transform(controllerToAvatar); } bool OculusControllerManager::TouchDevice::triggerHapticPulse(float strength, float duration, uint16_t index) { if (index > 2) { return false; } controller::Hand hand = (controller::Hand)index; Locker locker(_lock); bool toReturn = true; ovr::withSession([&](ovrSession session) { if (hand == controller::BOTH || hand == controller::LEFT) { if (strength == 0.0f) { _leftHapticStrength = 0.0f; _leftHapticDuration = 0.0f; } else { _leftHapticStrength = (duration > _leftHapticDuration) ? strength : _leftHapticStrength; if (ovr_SetControllerVibration(session, ovrControllerType_LTouch, 1.0f, _leftHapticStrength) != ovrSuccess) { toReturn = false; } _leftHapticDuration = std::max(duration, _leftHapticDuration); } } if (hand == controller::BOTH || hand == controller::RIGHT) { if (strength == 0.0f) { _rightHapticStrength = 0.0f; _rightHapticDuration = 0.0f; } else { _rightHapticStrength = (duration > _rightHapticDuration) ? strength : _rightHapticStrength; if (ovr_SetControllerVibration(session, ovrControllerType_RTouch, 1.0f, _rightHapticStrength) != ovrSuccess) { toReturn = false; } _rightHapticDuration = std::max(duration, _rightHapticDuration); } } }); return toReturn; } void OculusControllerManager::TouchDevice::stopHapticPulse(bool leftHand) { auto handType = (leftHand ? ovrControllerType_LTouch : ovrControllerType_RTouch); ovr::withSession([&](ovrSession session) { ovr_SetControllerVibration(session, handType, 0.0f, 0.0f); }); } /*@jsdoc * <p>The <code>Controller.Hardware.OculusTouch</code> object has properties representing the Oculus Rift. The property values * are integer IDs, uniquely identifying each output. <em>Read-only.</em></p> * <p>These outputs can be mapped to actions or functions or <code>Controller.Standard</code> items in a {@link RouteObject} * mapping.</p> * <table> * <thead> * <tr><th>Property</th><th>Type</th><th>Data</th><th>Description</th></tr> * </thead> * <tbody> * <tr><td colspan="4"><strong>Buttons</strong></td></tr> * <tr><td><code>A</code></td><td>number</td><td>number</td><td>"A" button pressed.</td></tr> * <tr><td><code>B</code></td><td>number</td><td>number</td><td>"B" button pressed.</td></tr> * <tr><td><code>X</code></td><td>number</td><td>number</td><td>"X" button pressed.</td></tr> * <tr><td><code>Y</code></td><td>number</td><td>number</td><td>"Y" button pressed.</td></tr> * <tr><td><code>LeftApplicationMenu</code></td><td>number</td><td>number</td><td>Left application menu button pressed. * </td></tr> * <tr><td><code>RightApplicationMenu</code></td><td>number</td><td>number</td><td>Right application menu button pressed. * </td></tr> * <tr><td colspan="4"><strong>Sticks</strong></td></tr> * <tr><td><code>LX</code></td><td>number</td><td>number</td><td>Left stick x-axis scale.</td></tr> * <tr><td><code>LY</code></td><td>number</td><td>number</td><td>Left stick y-axis scale.</td></tr> * <tr><td><code>RX</code></td><td>number</td><td>number</td><td>Right stick x-axis scale.</td></tr> * <tr><td><code>RY</code></td><td>number</td><td>number</td><td>Right stick y-axis scale.</td></tr> * <tr><td><code>LS</code></td><td>number</td><td>number</td><td>Left stick button pressed.</td></tr> * <tr><td><code>RS</code></td><td>number</td><td>number</td><td>Right stick button pressed.</td></tr> * <tr><td><code>LSTouch</code></td><td>number</td><td>number</td><td>Left stick is touched.</td></tr> * <tr><td><code>RSTouch</code></td><td>number</td><td>number</td><td>Right stick is touched.</td></tr> * <tr><td colspan="4"><strong>Triggers</strong></td></tr> * <tr><td><code>LT</code></td><td>number</td><td>number</td><td>Left trigger scale.</td></tr> * <tr><td><code>RT</code></td><td>number</td><td>number</td><td>Right trigger scale.</td></tr> * <tr><td><code>LeftGrip</code></td><td>number</td><td>number</td><td>Left grip scale.</td></tr> * <tr><td><code>RightGrip</code></td><td>number</td><td>number</td><td>Right grip scale.</td></tr> * <tr><td colspan="4"><strong>Finger Abstractions</strong></td></tr> * <tr><td><code>LeftPrimaryThumbTouch</code></td><td>number</td><td>number</td><td>Left thumb touching primary thumb * button.</td></tr> * <tr><td><code>LeftSecondaryThumbTouch</code></td><td>number</td><td>number</td><td>Left thumb touching secondary thumb * button.</td></tr> * <tr><td><code>LeftThumbUp</code></td><td>number</td><td>number</td><td>Left thumb not touching primary or secondary * thumb buttons.</td></tr> * <tr><td><code>RightPrimaryThumbTouch</code></td><td>number</td><td>number</td><td>Right thumb touching primary thumb * button.</td></tr> * <tr><td><code>RightSecondaryThumbTouch</code></td><td>number</td><td>number</td><td>Right thumb touching secondary thumb * button.</td></tr> * <tr><td><code>RightThumbUp</code></td><td>number</td><td>number</td><td>Right thumb not touching primary or secondary * thumb buttons.</td></tr> * <tr><td><code>LeftPrimaryIndexTouch</code></td><td>number</td><td>number</td><td>Left index finger is touching primary * index finger control.</td></tr> * <tr><td><code>LeftIndexPoint</code></td><td>number</td><td>number</td><td>Left index finger is pointing, not touching * primary or secondary index finger controls.</td></tr> * <tr><td><code>RightPrimaryIndexTouch</code></td><td>number</td><td>number</td><td>Right index finger is touching primary * index finger control.</td></tr> * <tr><td><code>RightIndexPoint</code></td><td>number</td><td>number</td><td>Right index finger is pointing, not touching * primary or secondary index finger controls.</td></tr> * <tr><td colspan="4"><strong>Avatar Skeleton</strong></td></tr> * <tr><td><code>Head</code></td><td>number</td><td>{@link Pose}</td><td>Head pose.</td></tr> * <tr><td><code>LeftHand</code></td><td>number</td><td>{@link Pose}</td><td>Left hand pose.</td></tr> * <tr><td><code>RightHand</code></td><td>number</td><td>{@link Pose}</td><td>right hand pose.</td></tr> * </tbody> * </table> * @typedef {object} Controller.Hardware-OculusTouch */ controller::Input::NamedVector OculusControllerManager::TouchDevice::getAvailableInputs() const { using namespace controller; QVector<Input::NamedPair> availableInputs{ // buttons makePair(A, "A"), makePair(B, "B"), makePair(X, "X"), makePair(Y, "Y"), // trackpad analogs makePair(LX, "LX"), makePair(LY, "LY"), makePair(RX, "RX"), makePair(RY, "RY"), // triggers makePair(LT, "LT"), makePair(RT, "RT"), // trigger buttons //makePair(LB, "LB"), //makePair(RB, "RB"),