diff --git a/cmake/macros/TargetLeapMotion.cmake b/cmake/macros/TargetLeapMotion.cmake new file mode 100644 index 0000000000..674ec8f62d --- /dev/null +++ b/cmake/macros/TargetLeapMotion.cmake @@ -0,0 +1,12 @@ +# +# Created by David Rowe on 16 Jun 2017. +# Copyright 2017 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 +# + +macro(TARGET_LEAPMOTION) + target_include_directories(${TARGET_NAME} PRIVATE ${LEAPMOTION_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${LEAPMOTION_LIBRARIES}) +endmacro() diff --git a/interface/external/leapmotion/readme.txt b/interface/external/leapmotion/readme.txt index 51a65caf22..97502d694c 100644 --- a/interface/external/leapmotion/readme.txt +++ b/interface/external/leapmotion/readme.txt @@ -10,7 +10,7 @@ Interface has been tested with SDK versions: 1. Copy the LeapSDK folders from the LeapDeveloperKit installation directory (Lib, Include) into the interface/externals/leapmotion folder. This readme.txt should be there as well. - The files neeeded in the folders are: + The files needed in the folders are: include/ - Leap.h @@ -21,8 +21,8 @@ Interface has been tested with SDK versions: x86/ - Leap.dll - Leap.lib - - mscvcp120.dll (optional if you already have the Msdev 2012 SDK redistriuable installed) - - mscvcr120.dll (optional if you already have the Msdev 2012 SDK redistriuable installed) + - mscvcp120.dll (optional if you already have the Msdev 2012 SDK redistributable installed) + - mscvcr120.dll (optional if you already have the Msdev 2012 SDK redistributable installed) - lipLeap.dylib libc++/ -libLeap.dylib @@ -30,4 +30,4 @@ Interface has been tested with SDK versions: You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different checkouts and different projects). If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder 'leapmotion' that contains the 2 folders mentioned above (Include, Lib). -2. Clear your build directory, run cmake and build, and you should be all set. \ No newline at end of file +2. Clear your build directory, run cmake and build, and you should be all set. diff --git a/interface/resources/controllers/leapmotion.json b/interface/resources/controllers/leapmotion.json new file mode 100644 index 0000000000..25cb575946 --- /dev/null +++ b/interface/resources/controllers/leapmotion.json @@ -0,0 +1,48 @@ +{ + "name": "Leap Motion to Standard", + "channels": [ + { "from": "LeapMotion.LeftHand", "to": "Standard.LeftHand" }, + { "from": "LeapMotion.LeftHandThumb1", "to": "Standard.LeftHandThumb1"}, + { "from": "LeapMotion.LeftHandThumb2", "to": "Standard.LeftHandThumb2"}, + { "from": "LeapMotion.LeftHandThumb3", "to": "Standard.LeftHandThumb3"}, + { "from": "LeapMotion.LeftHandThumb4", "to": "Standard.LeftHandThumb4"}, + { "from": "LeapMotion.LeftHandIndex1", "to": "Standard.LeftHandIndex1"}, + { "from": "LeapMotion.LeftHandIndex2", "to": "Standard.LeftHandIndex2"}, + { "from": "LeapMotion.LeftHandIndex3", "to": "Standard.LeftHandIndex3"}, + { "from": "LeapMotion.LeftHandIndex4", "to": "Standard.LeftHandIndex4"}, + { "from": "LeapMotion.LeftHandMiddle1", "to": "Standard.LeftHandMiddle1"}, + { "from": "LeapMotion.LeftHandMiddle2", "to": "Standard.LeftHandMiddle2"}, + { "from": "LeapMotion.LeftHandMiddle3", "to": "Standard.LeftHandMiddle3"}, + { "from": "LeapMotion.LeftHandMiddle4", "to": "Standard.LeftHandMiddle4"}, + { "from": "LeapMotion.LeftHandRing1", "to": "Standard.LeftHandRing1"}, + { "from": "LeapMotion.LeftHandRing2", "to": "Standard.LeftHandRing2"}, + { "from": "LeapMotion.LeftHandRing3", "to": "Standard.LeftHandRing3"}, + { "from": "LeapMotion.LeftHandRing4", "to": "Standard.LeftHandRing4"}, + { "from": "LeapMotion.LeftHandPinky1", "to": "Standard.LeftHandPinky1"}, + { "from": "LeapMotion.LeftHandPinky2", "to": "Standard.LeftHandPinky2"}, + { "from": "LeapMotion.LeftHandPinky3", "to": "Standard.LeftHandPinky3"}, + { "from": "LeapMotion.LeftHandPinky4", "to": "Standard.LeftHandPinky4"}, + + { "from": "LeapMotion.RightHand", "to": "Standard.RightHand" }, + { "from": "LeapMotion.RightHandThumb1", "to": "Standard.RightHandThumb1"}, + { "from": "LeapMotion.RightHandThumb2", "to": "Standard.RightHandThumb2"}, + { "from": "LeapMotion.RightHandThumb3", "to": "Standard.RightHandThumb3"}, + { "from": "LeapMotion.RightHandThumb4", "to": "Standard.RightHandThumb4"}, + { "from": "LeapMotion.RightHandIndex1", "to": "Standard.RightHandIndex1"}, + { "from": "LeapMotion.RightHandIndex2", "to": "Standard.RightHandIndex2"}, + { "from": "LeapMotion.RightHandIndex3", "to": "Standard.RightHandIndex3"}, + { "from": "LeapMotion.RightHandIndex4", "to": "Standard.RightHandIndex4"}, + { "from": "LeapMotion.RightHandMiddle1", "to": "Standard.RightHandMiddle1"}, + { "from": "LeapMotion.RightHandMiddle2", "to": "Standard.RightHandMiddle2"}, + { "from": "LeapMotion.RightHandMiddle3", "to": "Standard.RightHandMiddle3"}, + { "from": "LeapMotion.RightHandMiddle4", "to": "Standard.RightHandMiddle4"}, + { "from": "LeapMotion.RightHandRing1", "to": "Standard.RightHandRing1"}, + { "from": "LeapMotion.RightHandRing2", "to": "Standard.RightHandRing2"}, + { "from": "LeapMotion.RightHandRing3", "to": "Standard.RightHandRing3"}, + { "from": "LeapMotion.RightHandRing4", "to": "Standard.RightHandRing4"}, + { "from": "LeapMotion.RightHandPinky1", "to": "Standard.RightHandPinky1"}, + { "from": "LeapMotion.RightHandPinky2", "to": "Standard.RightHandPinky2"}, + { "from": "LeapMotion.RightHandPinky3", "to": "Standard.RightHandPinky3"}, + { "from": "LeapMotion.RightHandPinky4", "to": "Standard.RightHandPinky4"} + ] +} diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 75b4821118..166f1a6869 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -58,7 +58,48 @@ { "from": "Standard.RT", "to": "Actions.RightHandClick" }, { "from": "Standard.LeftHand", "to": "Actions.LeftHand" }, + { "from": "Standard.LeftHandThumb1", "to": "Actions.LeftHandThumb1"}, + { "from": "Standard.LeftHandThumb2", "to": "Actions.LeftHandThumb2"}, + { "from": "Standard.LeftHandThumb3", "to": "Actions.LeftHandThumb3"}, + { "from": "Standard.LeftHandThumb4", "to": "Actions.LeftHandThumb4"}, + { "from": "Standard.LeftHandIndex1", "to": "Actions.LeftHandIndex1"}, + { "from": "Standard.LeftHandIndex2", "to": "Actions.LeftHandIndex2"}, + { "from": "Standard.LeftHandIndex3", "to": "Actions.LeftHandIndex3"}, + { "from": "Standard.LeftHandIndex4", "to": "Actions.LeftHandIndex4"}, + { "from": "Standard.LeftHandMiddle1", "to": "Actions.LeftHandMiddle1"}, + { "from": "Standard.LeftHandMiddle2", "to": "Actions.LeftHandMiddle2"}, + { "from": "Standard.LeftHandMiddle3", "to": "Actions.LeftHandMiddle3"}, + { "from": "Standard.LeftHandMiddle4", "to": "Actions.LeftHandMiddle4"}, + { "from": "Standard.LeftHandRing1", "to": "Actions.LeftHandRing1"}, + { "from": "Standard.LeftHandRing2", "to": "Actions.LeftHandRing2"}, + { "from": "Standard.LeftHandRing3", "to": "Actions.LeftHandRing3"}, + { "from": "Standard.LeftHandRing4", "to": "Actions.LeftHandRing4"}, + { "from": "Standard.LeftHandPinky1", "to": "Actions.LeftHandPinky1"}, + { "from": "Standard.LeftHandPinky2", "to": "Actions.LeftHandPinky2"}, + { "from": "Standard.LeftHandPinky3", "to": "Actions.LeftHandPinky3"}, + { "from": "Standard.LeftHandPinky4", "to": "Actions.LeftHandPinky4"}, + { "from": "Standard.RightHand", "to": "Actions.RightHand" }, + { "from": "Standard.RightHandThumb1", "to": "Actions.RightHandThumb1"}, + { "from": "Standard.RightHandThumb2", "to": "Actions.RightHandThumb2"}, + { "from": "Standard.RightHandThumb3", "to": "Actions.RightHandThumb3"}, + { "from": "Standard.RightHandThumb4", "to": "Actions.RightHandThumb4"}, + { "from": "Standard.RightHandIndex1", "to": "Actions.RightHandIndex1"}, + { "from": "Standard.RightHandIndex2", "to": "Actions.RightHandIndex2"}, + { "from": "Standard.RightHandIndex3", "to": "Actions.RightHandIndex3"}, + { "from": "Standard.RightHandIndex4", "to": "Actions.RightHandIndex4"}, + { "from": "Standard.RightHandMiddle1", "to": "Actions.RightHandMiddle1"}, + { "from": "Standard.RightHandMiddle2", "to": "Actions.RightHandMiddle2"}, + { "from": "Standard.RightHandMiddle3", "to": "Actions.RightHandMiddle3"}, + { "from": "Standard.RightHandMiddle4", "to": "Actions.RightHandMiddle4"}, + { "from": "Standard.RightHandRing1", "to": "Actions.RightHandRing1"}, + { "from": "Standard.RightHandRing2", "to": "Actions.RightHandRing2"}, + { "from": "Standard.RightHandRing3", "to": "Actions.RightHandRing3"}, + { "from": "Standard.RightHandRing4", "to": "Actions.RightHandRing4"}, + { "from": "Standard.RightHandPinky1", "to": "Actions.RightHandPinky1"}, + { "from": "Standard.RightHandPinky2", "to": "Actions.RightHandPinky2"}, + { "from": "Standard.RightHandPinky3", "to": "Actions.RightHandPinky3"}, + { "from": "Standard.RightHandPinky4", "to": "Actions.RightHandPinky4"}, { "from": "Standard.LeftFoot", "to": "Actions.LeftFoot" }, { "from": "Standard.RightFoot", "to": "Actions.RightFoot" }, diff --git a/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml b/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml index 44cae95696..cabc09e49b 100644 --- a/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml +++ b/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml @@ -17,7 +17,7 @@ PreferencesDialog { id: root objectName: "GeneralPreferencesDialog" title: "General Settings" - showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect"] + showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect", "Leap Motion"] property var settings: Settings { category: root.objectName property alias x: root.x diff --git a/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml b/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml index fe043f6ac7..18e7898dd0 100644 --- a/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml @@ -32,6 +32,6 @@ StackView { TabletPreferencesDialog { id: root objectName: "TabletGeneralPreferences" - showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect", "Vive Pucks Configuration"] + showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect", "Vive Pucks Configuration", "Leap Motion"] } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 75bcee0703..d0845131f6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -145,7 +145,6 @@ #include "avatar/MyHead.h" #include "CrashHandler.h" #include "devices/DdeFaceTracker.h" -#include "devices/Leapmotion.h" #include "DiscoverabilityManager.h" #include "GLCanvas.h" #include "InterfaceDynamicFactory.h" @@ -1886,8 +1885,6 @@ Application::~Application() { // remove the NodeList from the DependencyManager DependencyManager::destroy(); - Leapmotion::destroy(); - if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { steamClient->shutdown(); } @@ -4055,8 +4052,6 @@ void Application::init() { qCDebug(interfaceapp) << "Loaded settings"; - Leapmotion::init(); - // fire off an immediate domain-server check in now that settings are loaded DependencyManager::get()->sendDomainServerCheckIn(); @@ -4520,7 +4515,6 @@ void Application::update(float deltaTime) { { PerformanceTimer perfTimer("devices"); - DeviceTracker::updateAll(); FaceTracker* tracker = getSelectedFaceTracker(); if (tracker && Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking) != tracker->isMuted()) { @@ -4589,8 +4583,6 @@ void Application::update(float deltaTime) { keyboardMousePlugin->pluginUpdate(deltaTime, calibrationData); } - _controllerScriptingInterface->updateInputControllers(); - // Transfer the user inputs to the driveKeys // FIXME can we drop drive keys and just have the avatar read the action states directly? myAvatar->clearDriveKeys(); @@ -4615,6 +4607,31 @@ void Application::update(float deltaTime) { auto avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix; myAvatar->setHandControllerPosesInSensorFrame(leftHandPose.transform(avatarToSensorMatrix), rightHandPose.transform(avatarToSensorMatrix)); + // If have previously done finger poses or there are new valid finger poses, update finger pose values. This so that if + // fingers are not being controlled, finger joints are not updated in MySkeletonModel. + // Assumption: Finger poses are either all present and valid or not present at all; thus can test just one joint. + MyAvatar::FingerPosesMap leftHandFingerPoses; + if (myAvatar->getLeftHandFingerControllerPosesInSensorFrame().size() > 0 + || userInputMapper->getPoseState(controller::Action::LEFT_HAND_THUMB1).isValid()) { + for (int i = (int)controller::Action::LEFT_HAND_THUMB1; i <= (int)controller::Action::LEFT_HAND_PINKY4; i++) { + leftHandFingerPoses[i] = { + userInputMapper->getPoseState((controller::Action)i).transform(avatarToSensorMatrix), + userInputMapper->getActionName((controller::Action)i) + }; + } + } + MyAvatar::FingerPosesMap rightHandFingerPoses; + if (myAvatar->getRightHandFingerControllerPosesInSensorFrame().size() > 0 + || userInputMapper->getPoseState(controller::Action::RIGHT_HAND_THUMB1).isValid()) { + for (int i = (int)controller::Action::RIGHT_HAND_THUMB1; i <= (int)controller::Action::RIGHT_HAND_PINKY4; i++) { + rightHandFingerPoses[i] = { + userInputMapper->getPoseState((controller::Action)i).transform(avatarToSensorMatrix), + userInputMapper->getActionName((controller::Action)i) + }; + } + } + myAvatar->setFingerControllerPosesInSensorFrame(leftHandFingerPoses, rightHandFingerPoses); + controller::Pose leftFootPose = userInputMapper->getPoseState(controller::Action::LEFT_FOOT); controller::Pose rightFootPose = userInputMapper->getPoseState(controller::Action::RIGHT_FOOT); myAvatar->setFootControllerPosesInSensorFrame(leftFootPose.transform(avatarToSensorMatrix), rightFootPose.transform(avatarToSensorMatrix)); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 7d4535c681..a746d72d91 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -566,9 +566,6 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false, avatar.get(), SLOT(setEnableDebugDrawHandControllers(bool))); - MenuWrapper* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion"); - addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false); - // Developer > Entities >>> MenuWrapper* entitiesOptionsMenu = developerMenu->addMenu("Entities"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index cc8e67c8f7..252fbcc142 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -114,7 +114,6 @@ namespace MenuOption { const QString IncreaseAvatarSize = "Increase Avatar Size"; const QString IndependentMode = "Independent Mode"; const QString ActionMotorControl = "Enable Default Motor Control"; - const QString LeapMotionOnHMD = "Leap Motion on HMD"; const QString LoadScript = "Open and Run Script File..."; const QString LoadScriptURL = "Open and Run Script from URL..."; const QString LodTools = "LOD Tools"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e5c4f4b972..2a7f000fd7 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1455,6 +1455,19 @@ controller::Pose MyAvatar::getRightHandControllerPoseInAvatarFrame() const { return getRightHandControllerPoseInWorldFrame().transform(invAvatarMatrix); } +void MyAvatar::setFingerControllerPosesInSensorFrame(const FingerPosesMap& left, const FingerPosesMap& right) { + _leftHandFingerPosesInSensorFramceCache.set(left); + _rightHandFingerPosesInSensorFramceCache.set(right); +} + +MyAvatar::FingerPosesMap MyAvatar::getLeftHandFingerControllerPosesInSensorFrame() const { + return _leftHandFingerPosesInSensorFramceCache.get(); +} + +MyAvatar::FingerPosesMap MyAvatar::getRightHandFingerControllerPosesInSensorFrame() const { + return _rightHandFingerPosesInSensorFramceCache.get(); +} + void MyAvatar::setFootControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) { _leftFootControllerPoseInSensorFrameCache.set(left); _rightFootControllerPoseInSensorFrameCache.set(right); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index e2ea745ed0..60fcf81a01 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -474,6 +474,11 @@ public: controller::Pose getLeftHandControllerPoseInAvatarFrame() const; controller::Pose getRightHandControllerPoseInAvatarFrame() const; + typedef std::map> FingerPosesMap; + void setFingerControllerPosesInSensorFrame(const FingerPosesMap& left, const FingerPosesMap& right); + FingerPosesMap getLeftHandFingerControllerPosesInSensorFrame() const; + FingerPosesMap getRightHandFingerControllerPosesInSensorFrame() const; + void setFootControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right); controller::Pose getLeftFootControllerPoseInSensorFrame() const; controller::Pose getRightFootControllerPoseInSensorFrame() const; @@ -788,13 +793,15 @@ private: // These are stored in SENSOR frame ThreadSafeValueCache _leftHandControllerPoseInSensorFrameCache { controller::Pose() }; ThreadSafeValueCache _rightHandControllerPoseInSensorFrameCache { controller::Pose() }; - ThreadSafeValueCache _leftFootControllerPoseInSensorFrameCache{ controller::Pose() }; - ThreadSafeValueCache _rightFootControllerPoseInSensorFrameCache{ controller::Pose() }; - ThreadSafeValueCache _hipsControllerPoseInSensorFrameCache{ controller::Pose() }; - ThreadSafeValueCache _spine2ControllerPoseInSensorFrameCache{ controller::Pose() }; - ThreadSafeValueCache _headControllerPoseInSensorFrameCache{ controller::Pose() }; - ThreadSafeValueCache _leftArmControllerPoseInSensorFrameCache{ controller::Pose() }; - ThreadSafeValueCache _rightArmControllerPoseInSensorFrameCache{ controller::Pose() }; + ThreadSafeValueCache _leftHandFingerPosesInSensorFramceCache { }; + ThreadSafeValueCache _rightHandFingerPosesInSensorFramceCache { }; + ThreadSafeValueCache _leftFootControllerPoseInSensorFrameCache { controller::Pose() }; + ThreadSafeValueCache _rightFootControllerPoseInSensorFrameCache { controller::Pose() }; + ThreadSafeValueCache _hipsControllerPoseInSensorFrameCache { controller::Pose() }; + ThreadSafeValueCache _spine2ControllerPoseInSensorFrameCache { controller::Pose() }; + ThreadSafeValueCache _headControllerPoseInSensorFrameCache { controller::Pose() }; + ThreadSafeValueCache _leftArmControllerPoseInSensorFrameCache { controller::Pose() }; + ThreadSafeValueCache _rightArmControllerPoseInSensorFrameCache { controller::Pose() }; bool _hmdLeanRecenterEnabled = true; AnimPose _prePhysicsRoomPose; diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 89b6c36c63..97309d9678 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -96,7 +96,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.controllerPoses[Rig::ControllerType_RightArm] = AnimPose::identity; params.controllerActiveFlags[Rig::ControllerType_RightArm] = false; } - + auto avatarLeftArmPose = myAvatar->getLeftArmControllerPoseInAvatarFrame(); if (avatarLeftArmPose.isValid()) { AnimPose pose(avatarLeftArmPose.getRotation(), avatarLeftArmPose.getTranslation()); @@ -174,5 +174,50 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; _rig.updateFromEyeParameters(eyeParams); + + updateFingers(myAvatar->getLeftHandFingerControllerPosesInSensorFrame()); + updateFingers(myAvatar->getRightHandFingerControllerPosesInSensorFrame()); } + +void MySkeletonModel::updateFingers(const MyAvatar::FingerPosesMap& fingerPoses) { + // Assumes that finger poses are kept in order in the poses map. + + if (fingerPoses.size() == 0) { + return; +} + + auto posesMapItr = fingerPoses.begin(); + + bool isLeftHand = posesMapItr->first < (int)controller::Action::RIGHT_HAND_THUMB1; + + MyAvatar* myAvatar = static_cast(_owningAvatar); + auto handPose = isLeftHand + ? myAvatar->getLeftHandControllerPoseInSensorFrame() + : myAvatar->getRightHandControllerPoseInSensorFrame(); + auto handJointRotation = handPose.getRotation(); + + bool isHandValid = handPose.isValid(); + bool isFingerValid = false; + glm::quat previousJointRotation; + + while (posesMapItr != fingerPoses.end()) { + auto jointName = posesMapItr->second.second; + if (isHandValid && jointName.right(1) == "1") { + isFingerValid = posesMapItr->second.first.isValid(); + previousJointRotation = handJointRotation; + } + + if (isHandValid && isFingerValid) { + auto thisJointRotation = posesMapItr->second.first.getRotation(); + const float CONTROLLER_PRIORITY = 2.0f; + _rig.setJointRotation(_rig.indexOfJoint(jointName), true, glm::inverse(previousJointRotation) * thisJointRotation, + CONTROLLER_PRIORITY); + previousJointRotation = thisJointRotation; + } else { + _rig.clearJointAnimationPriority(_rig.indexOfJoint(jointName)); + } + + posesMapItr++; + } +} diff --git a/interface/src/avatar/MySkeletonModel.h b/interface/src/avatar/MySkeletonModel.h index 12aba6b545..6867c596af 100644 --- a/interface/src/avatar/MySkeletonModel.h +++ b/interface/src/avatar/MySkeletonModel.h @@ -10,6 +10,7 @@ #define hifi_MySkeletonModel_h #include +#include "MyAvatar.h" /// A skeleton loaded from a model. class MySkeletonModel : public SkeletonModel { @@ -21,6 +22,9 @@ private: public: MySkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr); void updateRig(float deltaTime, glm::mat4 parentTransform) override; + +private: + void updateFingers(const MyAvatar::FingerPosesMap& fingerPoses); }; #endif // hifi_MySkeletonModel_h diff --git a/interface/src/devices/Leapmotion.cpp b/interface/src/devices/Leapmotion.cpp deleted file mode 100644 index c643042300..0000000000 --- a/interface/src/devices/Leapmotion.cpp +++ /dev/null @@ -1,246 +0,0 @@ -// -// Created by Sam Cake on 6/2/2014 -// Copyright 2014 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 "Leapmotion.h" - -#include - -#include "Menu.h" - -const int PALMROOT_NUM_JOINTS = 3; -const int FINGER_NUM_JOINTS = 4; -const int HAND_NUM_JOINTS = FINGER_NUM_JOINTS*5+PALMROOT_NUM_JOINTS; - -const DeviceTracker::Name Leapmotion::NAME = "Leapmotion"; - -// find the index of a joint from -// the side: true = right -// the finger & the bone: -// finger in [0..4] : bone in [0..3] a finger phalange -// [-1] up the hand branch : bone in [0..2] <=> [ hand, forearm, arm] -MotionTracker::Index evalJointIndex(bool isRightSide, int finger, int bone) { - - MotionTracker::Index offset = 1 // start after root - + (int(isRightSide) * HAND_NUM_JOINTS) // then offset for side - + PALMROOT_NUM_JOINTS; // then add the arm/forearm/hand chain - if (finger >= 0) { - // from there go down in the correct finger and bone - return offset + (finger * FINGER_NUM_JOINTS) + bone; - } else { - // or go back up for the correct root bone - return offset - 1 - bone; - } -} - -// static -void Leapmotion::init() { - DeviceTracker* device = DeviceTracker::getDevice(NAME); - - if (!device) { - // create a new Leapmotion and register it - Leapmotion* leap = new Leapmotion(); - DeviceTracker::registerDevice(NAME, leap); - } -} - -// static -void Leapmotion::destroy() { - DeviceTracker::destroyDevice(NAME); -} - -// static -Leapmotion* Leapmotion::getInstance() { - DeviceTracker* device = DeviceTracker::getDevice(NAME); - if (!device) { - // create a new Leapmotion and register it - device = new Leapmotion(); - DeviceTracker::registerDevice(NAME, device); - } - return dynamic_cast< Leapmotion* > (device); -} - -Leapmotion::Leapmotion() : - MotionTracker(), - _active(false) -{ - // Create the Leapmotion joint hierarchy - std::vector< Semantic > sides; - sides.push_back("joint_L_"); - sides.push_back("joint_R_"); - - std::vector< Semantic > rootBones; - rootBones.push_back("elbow"); - rootBones.push_back("wrist"); - rootBones.push_back("hand"); - - std::vector< Semantic > fingers; - fingers.push_back("thumb"); - fingers.push_back("index"); - fingers.push_back("middle"); - fingers.push_back("ring"); - fingers.push_back("pinky"); - - std::vector< Semantic > fingerBones; - fingerBones.push_back("1"); - fingerBones.push_back("2"); - fingerBones.push_back("3"); - fingerBones.push_back("4"); - - std::vector< Index > palms; - for (unsigned int s = 0; s < sides.size(); s++) { - Index rootJoint = 0; - for (unsigned int rb = 0; rb < rootBones.size(); rb++) { - rootJoint = addJoint(sides[s] + rootBones[rb], rootJoint); - } - - // capture the hand index for debug - palms.push_back(rootJoint); - - for (unsigned int f = 0; f < fingers.size(); f++) { - for (unsigned int b = 0; b < fingerBones.size(); b++) { - rootJoint = addJoint(sides[s] + fingers[f] + fingerBones[b], rootJoint); - } - } - } - -#ifdef HAVE_LEAPMOTION - if (Menu::getInstance()->isOptionChecked(MenuOption::LeapMotionOnHMD)) { - _controller.setPolicyFlags(Leap::Controller::POLICY_OPTIMIZE_HMD); - } -#endif -} - -Leapmotion::~Leapmotion() { -} - -#ifdef HAVE_LEAPMOTION -glm::quat quatFromLeapBase(float sideSign, const Leap::Matrix& basis) { - - // fix the handness to right and always... - glm::vec3 xAxis = glm::normalize(sideSign * glm::vec3(basis.xBasis.x, basis.xBasis.y, basis.xBasis.z)); - glm::vec3 yAxis = glm::normalize(glm::vec3(basis.yBasis.x, basis.yBasis.y, basis.yBasis.z)); - glm::vec3 zAxis = glm::normalize(glm::vec3(basis.zBasis.x, basis.zBasis.y, basis.zBasis.z)); - - xAxis = glm::normalize(glm::cross(yAxis, zAxis)); - - glm::quat orientation = (glm::quat_cast(glm::mat3(xAxis, yAxis, zAxis))); - return orientation; -} - -glm::vec3 vec3FromLeapVector(const Leap::Vector& vec) { - return glm::vec3(vec.x * METERS_PER_MILLIMETER, vec.y * METERS_PER_MILLIMETER, vec.z * METERS_PER_MILLIMETER); -} - -#endif - -void Leapmotion::update() { -#ifdef HAVE_LEAPMOTION - bool wasActive = _active; - _active = _controller.isConnected(); - - if (_active || wasActive) { - // Go through all the joints and increment their counter since last update. - // Increment all counters once after controller first becomes inactive so that each joint reports itself as inactive. - // TODO C++11 for (auto jointIt = _jointsArray.begin(); jointIt != _jointsArray.end(); jointIt++) { - for (JointTracker::Vector::iterator jointIt = _jointsArray.begin(); jointIt != _jointsArray.end(); jointIt++) { - (*jointIt).tickNewFrame(); - } - } - - if (!_active) { - return; - } - - // Get the most recent frame and report some basic information - const Leap::Frame frame = _controller.frame(); - static int64_t lastFrameID = -1; - int64_t newFrameID = frame.id(); - - // If too soon then exit - if (lastFrameID >= newFrameID) - return; - - glm::vec3 delta(0.0f); - glm::quat handOri; - if (!frame.hands().isEmpty()) { - for (int handNum = 0; handNum < frame.hands().count(); handNum++) { - - const Leap::Hand hand = frame.hands()[handNum]; - int side = (hand.isRight() ? 1 : -1); - - JointTracker* parentJointTracker = _jointsArray.data(); - - - int rootBranchIndex = -1; - - Leap::Arm arm = hand.arm(); - if (arm.isValid()) { - glm::quat ori = quatFromLeapBase(float(side), arm.basis()); - glm::vec3 pos = vec3FromLeapVector(arm.elbowPosition()); - JointTracker* elbow = editJointTracker(evalJointIndex((side > 0), rootBranchIndex, 2)); // 2 is the index of the elbow joint - elbow->editAbsFrame().setTranslation(pos); - elbow->editAbsFrame().setRotation(ori); - elbow->updateLocFromAbsTransform(parentJointTracker); - elbow->activeFrame(); - parentJointTracker = elbow; - - pos = vec3FromLeapVector(arm.wristPosition()); - JointTracker* wrist = editJointTracker(evalJointIndex((side > 0), rootBranchIndex, 1)); // 1 is the index of the wrist joint - wrist->editAbsFrame().setTranslation(pos); - wrist->editAbsFrame().setRotation(ori); - wrist->updateLocFromAbsTransform(parentJointTracker); - wrist->activeFrame(); - parentJointTracker = wrist; - } - - JointTracker* palmJoint = NULL; - { - glm::vec3 pos = vec3FromLeapVector(hand.palmPosition()); - glm::quat ori = quatFromLeapBase(float(side), hand.basis()); - - palmJoint = editJointTracker(evalJointIndex((side > 0), rootBranchIndex, 0)); // 0 is the index of the palm joint - palmJoint->editAbsFrame().setTranslation(pos); - palmJoint->editAbsFrame().setRotation(ori); - palmJoint->updateLocFromAbsTransform(parentJointTracker); - palmJoint->activeFrame(); - } - - // Check if the hand has any fingers - const Leap::FingerList fingers = hand.fingers(); - if (!fingers.isEmpty()) { - // For every fingers in the list - for (int i = 0; i < fingers.count(); ++i) { - // Reset the parent joint to the palmJoint for every finger traversal - parentJointTracker = palmJoint; - - // surprisingly, Leap::Finger::Type start at 0 for thumb a until 4 for the pinky - Index fingerIndex = evalJointIndex((side > 0), int(fingers[i].type()), 0); - - // let's update the finger's joints - for (int b = 0; b < FINGER_NUM_JOINTS; b++) { - Leap::Bone::Type type = Leap::Bone::Type(b + Leap::Bone::TYPE_METACARPAL); - Leap::Bone bone = fingers[i].bone(type); - JointTracker* ljointTracker = editJointTracker(fingerIndex + b); - if (bone.isValid()) { - Leap::Vector bp = bone.nextJoint(); - - ljointTracker->editAbsFrame().setTranslation(vec3FromLeapVector(bp)); - ljointTracker->editAbsFrame().setRotation(quatFromLeapBase(float(side), bone.basis())); - ljointTracker->updateLocFromAbsTransform(parentJointTracker); - ljointTracker->activeFrame(); - } - parentJointTracker = ljointTracker; - } - } - } - } - } - - lastFrameID = newFrameID; -#endif -} diff --git a/interface/src/devices/Leapmotion.h b/interface/src/devices/Leapmotion.h deleted file mode 100644 index a119821846..0000000000 --- a/interface/src/devices/Leapmotion.h +++ /dev/null @@ -1,48 +0,0 @@ -// -// Created by Sam Cake on 6/2/2014 -// Copyright 2014 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 -// - -#ifndef hifi_Leapmotion_h -#define hifi_Leapmotion_h - -#include - -#include - -#ifdef HAVE_LEAPMOTION -#include -#endif - -/// Handles interaction with the Leapmotion skeleton tracking suit. -class Leapmotion : public MotionTracker { -public: - static const Name NAME; - - static void init(); - static void destroy(); - - /// Leapmotion MotionTracker factory - static Leapmotion* getInstance(); - - bool isActive() const { return _active; } - - virtual void update() override; - -protected: - Leapmotion(); - virtual ~Leapmotion(); - -private: -#ifdef HAVE_LEAPMOTION - Leap::Listener _listener; - Leap::Controller _controller; -#endif - - bool _active; -}; - -#endif // hifi_Leapmotion_h diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index f3ec3cd79d..9fbd01817a 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -17,7 +17,6 @@ #include #include "Application.h" -#include void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { if (event->type() == HFActionEvent::startType()) { @@ -97,86 +96,6 @@ QVariant ControllerScriptingInterface::getRecommendedOverlayRect() const { return qRectToVariant(rect); } -controller::InputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) { - // This is where we retrieve the Device Tracker category and then the sub tracker within it - auto icIt = _inputControllers.find(0); - if (icIt != _inputControllers.end()) { - return (*icIt).second.get(); - } - - // Look for device - DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString()); - if (deviceID < 0) { - deviceID = 0; - } - // TODO in this current implementation, we just pick the device assuming there is one (normally the Leapmotion) - // in the near future we need to change that to a real mapping between the devices and the deviceName - // ALso we need to expand the spec so we can fall back on the "default" controller per categories - - if (deviceID >= 0) { - // TODO here again the assumption it's the LeapMotion and so it's a MOtionTracker, this would need to be changed to support different types of devices - MotionTracker* motionTracker = dynamic_cast< MotionTracker* > (DeviceTracker::getDevice(deviceID)); - if (motionTracker) { - MotionTracker::Index trackerID = motionTracker->findJointIndex(tracker.toStdString()); - if (trackerID >= 0) { - controller::InputController::Pointer inputController = std::make_shared(deviceID, trackerID, this); - controller::InputController::Key key = inputController->getKey(); - _inputControllers.insert(InputControllerMap::value_type(key, inputController)); - return inputController.get(); - } - } - } - - return nullptr; -} - -void ControllerScriptingInterface::releaseInputController(controller::InputController* input) { - _inputControllers.erase(input->getKey()); -} - -void ControllerScriptingInterface::updateInputControllers() { - for (auto it = _inputControllers.begin(); it != _inputControllers.end(); it++) { - (*it).second->update(); - } -} - -InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) : - _deviceTrackerId(deviceTrackerId), - _subTrackerId(subTrackerId), - _isActive(false) -{ -} - -void InputController::update() { - _isActive = false; - - // TODO for now the InputController is only supporting a JointTracker from a MotionTracker - MotionTracker* motionTracker = dynamic_cast< MotionTracker*> (DeviceTracker::getDevice(_deviceTrackerId)); - if (motionTracker) { - if ((int)_subTrackerId < motionTracker->numJointTrackers()) { - const MotionTracker::JointTracker* joint = motionTracker->getJointTracker(_subTrackerId); - - if (joint->isActive()) { - joint->getAbsFrame().getTranslation(_eventCache.absTranslation); - joint->getAbsFrame().getRotation(_eventCache.absRotation); - joint->getLocFrame().getTranslation(_eventCache.locTranslation); - joint->getLocFrame().getRotation(_eventCache.locRotation); - - _isActive = true; - //emit spatialEvent(_eventCache); - } - } - } -} - -const unsigned int INPUTCONTROLLER_KEY_DEVICE_OFFSET = 16; -const unsigned int INPUTCONTROLLER_KEY_DEVICE_MASK = 16; - -InputController::Key InputController::getKey() const { - return (((_deviceTrackerId & INPUTCONTROLLER_KEY_DEVICE_MASK) << INPUTCONTROLLER_KEY_DEVICE_OFFSET) | _subTrackerId); -} - - void ControllerScriptingInterface::emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); } void ControllerScriptingInterface::emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); } diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index 996ccabb20..6d197477bb 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -25,38 +25,6 @@ #include class ScriptEngine; -class PalmData; - -class InputController : public controller::InputController { - Q_OBJECT - -public: - InputController(int deviceTrackerId, int subTrackerId, QObject* parent = NULL); - - virtual void update() override; - virtual Key getKey() const override; - -public slots: - - virtual bool isActive() const override { return _isActive; } - virtual glm::vec3 getAbsTranslation() const override { return _eventCache.absTranslation; } - virtual glm::quat getAbsRotation() const override { return _eventCache.absRotation; } - virtual glm::vec3 getLocTranslation() const override { return _eventCache.locTranslation; } - virtual glm::quat getLocRotation() const override { return _eventCache.locRotation; } - -private: - - int _deviceTrackerId; - uint _subTrackerId; - - // cache for the spatial - SpatialEvent _eventCache; - bool _isActive; - -signals: -}; - - /// handles scripting of input controller commands from JS class ControllerScriptingInterface : public controller::ScriptingInterface { Q_OBJECT @@ -86,8 +54,6 @@ public: bool isJoystickCaptured(int joystickIndex) const; bool areEntityClicksCaptured() const; - void updateInputControllers(); - public slots: virtual void captureKeyEvents(const KeyEvent& event); @@ -102,10 +68,6 @@ public slots: virtual glm::vec2 getViewportDimensions() const; virtual QVariant getRecommendedOverlayRect() const; - /// Factory to create an InputController - virtual controller::InputController* createInputController(const QString& deviceName, const QString& tracker); - virtual void releaseInputController(controller::InputController* input); - signals: void keyPressEvent(const KeyEvent& event); void keyReleaseEvent(const KeyEvent& event); @@ -135,8 +97,6 @@ private: bool _captureEntityClicks; using InputKey = controller::InputController::Key; - using InputControllerMap = std::map; - InputControllerMap _inputControllers; }; const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index 96e433bcc8..b3c0ed3f05 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -59,6 +59,48 @@ namespace controller { makePosePair(Action::SPINE2, "Spine2"), makePosePair(Action::HEAD, "Head"), + makePosePair(Action::LEFT_HAND_THUMB1, "LeftHandThumb1"), + makePosePair(Action::LEFT_HAND_THUMB2, "LeftHandThumb2"), + makePosePair(Action::LEFT_HAND_THUMB3, "LeftHandThumb3"), + makePosePair(Action::LEFT_HAND_THUMB4, "LeftHandThumb4"), + makePosePair(Action::LEFT_HAND_INDEX1, "LeftHandIndex1"), + makePosePair(Action::LEFT_HAND_INDEX2, "LeftHandIndex2"), + makePosePair(Action::LEFT_HAND_INDEX3, "LeftHandIndex3"), + makePosePair(Action::LEFT_HAND_INDEX4, "LeftHandIndex4"), + makePosePair(Action::LEFT_HAND_MIDDLE1, "LeftHandMiddle1"), + makePosePair(Action::LEFT_HAND_MIDDLE2, "LeftHandMiddle2"), + makePosePair(Action::LEFT_HAND_MIDDLE3, "LeftHandMiddle3"), + makePosePair(Action::LEFT_HAND_MIDDLE4, "LeftHandMiddle4"), + makePosePair(Action::LEFT_HAND_RING1, "LeftHandRing1"), + makePosePair(Action::LEFT_HAND_RING2, "LeftHandRing2"), + makePosePair(Action::LEFT_HAND_RING3, "LeftHandRing3"), + makePosePair(Action::LEFT_HAND_RING4, "LeftHandRing4"), + makePosePair(Action::LEFT_HAND_PINKY1, "LeftHandPinky1"), + makePosePair(Action::LEFT_HAND_PINKY2, "LeftHandPinky2"), + makePosePair(Action::LEFT_HAND_PINKY3, "LeftHandPinky3"), + makePosePair(Action::LEFT_HAND_PINKY4, "LeftHandPinky4"), + + makePosePair(Action::RIGHT_HAND_THUMB1, "RightHandThumb1"), + makePosePair(Action::RIGHT_HAND_THUMB2, "RightHandThumb2"), + makePosePair(Action::RIGHT_HAND_THUMB3, "RightHandThumb3"), + makePosePair(Action::RIGHT_HAND_THUMB4, "RightHandThumb4"), + makePosePair(Action::RIGHT_HAND_INDEX1, "RightHandIndex1"), + makePosePair(Action::RIGHT_HAND_INDEX2, "RightHandIndex2"), + makePosePair(Action::RIGHT_HAND_INDEX3, "RightHandIndex3"), + makePosePair(Action::RIGHT_HAND_INDEX4, "RightHandIndex4"), + makePosePair(Action::RIGHT_HAND_MIDDLE1, "RightHandMiddle1"), + makePosePair(Action::RIGHT_HAND_MIDDLE2, "RightHandMiddle2"), + makePosePair(Action::RIGHT_HAND_MIDDLE3, "RightHandMiddle3"), + makePosePair(Action::RIGHT_HAND_MIDDLE4, "RightHandMiddle4"), + makePosePair(Action::RIGHT_HAND_RING1, "RightHandRing1"), + makePosePair(Action::RIGHT_HAND_RING2, "RightHandRing2"), + makePosePair(Action::RIGHT_HAND_RING3, "RightHandRing3"), + makePosePair(Action::RIGHT_HAND_RING4, "RightHandRing4"), + makePosePair(Action::RIGHT_HAND_PINKY1, "RightHandPinky1"), + makePosePair(Action::RIGHT_HAND_PINKY2, "RightHandPinky2"), + makePosePair(Action::RIGHT_HAND_PINKY3, "RightHandPinky3"), + makePosePair(Action::RIGHT_HAND_PINKY4, "RightHandPinky4"), + makeButtonPair(Action::LEFT_HAND_CLICK, "LeftHandClick"), makeButtonPair(Action::RIGHT_HAND_CLICK, "RightHandClick"), diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index 2cb500c42a..ec4800c9aa 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -104,6 +104,47 @@ enum class Action { LEFT_ARM, RIGHT_ARM, + LEFT_HAND_THUMB1, + LEFT_HAND_THUMB2, + LEFT_HAND_THUMB3, + LEFT_HAND_THUMB4, + LEFT_HAND_INDEX1, + LEFT_HAND_INDEX2, + LEFT_HAND_INDEX3, + LEFT_HAND_INDEX4, + LEFT_HAND_MIDDLE1, + LEFT_HAND_MIDDLE2, + LEFT_HAND_MIDDLE3, + LEFT_HAND_MIDDLE4, + LEFT_HAND_RING1, + LEFT_HAND_RING2, + LEFT_HAND_RING3, + LEFT_HAND_RING4, + LEFT_HAND_PINKY1, + LEFT_HAND_PINKY2, + LEFT_HAND_PINKY3, + LEFT_HAND_PINKY4, + + RIGHT_HAND_THUMB1, + RIGHT_HAND_THUMB2, + RIGHT_HAND_THUMB3, + RIGHT_HAND_THUMB4, + RIGHT_HAND_INDEX1, + RIGHT_HAND_INDEX2, + RIGHT_HAND_INDEX3, + RIGHT_HAND_INDEX4, + RIGHT_HAND_MIDDLE1, + RIGHT_HAND_MIDDLE2, + RIGHT_HAND_MIDDLE3, + RIGHT_HAND_MIDDLE4, + RIGHT_HAND_RING1, + RIGHT_HAND_RING2, + RIGHT_HAND_RING3, + RIGHT_HAND_RING4, + RIGHT_HAND_PINKY1, + RIGHT_HAND_PINKY2, + RIGHT_HAND_PINKY3, + RIGHT_HAND_PINKY4, NUM_ACTIONS, }; diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index 8e49bb0ebf..40b87bc6b2 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -101,7 +101,47 @@ Input::NamedVector StandardController::getAvailableInputs() const { // Poses makePair(LEFT_HAND, "LeftHand"), + makePair(LEFT_HAND_THUMB1, "LeftHandThumb1"), + makePair(LEFT_HAND_THUMB2, "LeftHandThumb2"), + makePair(LEFT_HAND_THUMB3, "LeftHandThumb3"), + makePair(LEFT_HAND_THUMB4, "LeftHandThumb4"), + makePair(LEFT_HAND_INDEX1, "LeftHandIndex1"), + makePair(LEFT_HAND_INDEX2, "LeftHandIndex2"), + makePair(LEFT_HAND_INDEX3, "LeftHandIndex3"), + makePair(LEFT_HAND_INDEX4, "LeftHandIndex4"), + makePair(LEFT_HAND_MIDDLE1, "LeftHandMiddle1"), + makePair(LEFT_HAND_MIDDLE2, "LeftHandMiddle2"), + makePair(LEFT_HAND_MIDDLE3, "LeftHandMiddle3"), + makePair(LEFT_HAND_MIDDLE4, "LeftHandMiddle4"), + makePair(LEFT_HAND_RING1, "LeftHandRing1"), + makePair(LEFT_HAND_RING2, "LeftHandRing2"), + makePair(LEFT_HAND_RING3, "LeftHandRing3"), + makePair(LEFT_HAND_RING4, "LeftHandRing4"), + makePair(LEFT_HAND_PINKY1, "LeftHandPinky1"), + makePair(LEFT_HAND_PINKY2, "LeftHandPinky2"), + makePair(LEFT_HAND_PINKY3, "LeftHandPinky3"), + makePair(LEFT_HAND_PINKY4, "LeftHandPinky4"), makePair(RIGHT_HAND, "RightHand"), + makePair(RIGHT_HAND_THUMB1, "RightHandThumb1"), + makePair(RIGHT_HAND_THUMB2, "RightHandThumb2"), + makePair(RIGHT_HAND_THUMB3, "RightHandThumb3"), + makePair(RIGHT_HAND_THUMB4, "RightHandThumb4"), + makePair(RIGHT_HAND_INDEX1, "RightHandIndex1"), + makePair(RIGHT_HAND_INDEX2, "RightHandIndex2"), + makePair(RIGHT_HAND_INDEX3, "RightHandIndex3"), + makePair(RIGHT_HAND_INDEX4, "RightHandIndex4"), + makePair(RIGHT_HAND_MIDDLE1, "RightHandMiddle1"), + makePair(RIGHT_HAND_MIDDLE2, "RightHandMiddle2"), + makePair(RIGHT_HAND_MIDDLE3, "RightHandMiddle3"), + makePair(RIGHT_HAND_MIDDLE4, "RightHandMiddle4"), + makePair(RIGHT_HAND_RING1, "RightHandRing1"), + makePair(RIGHT_HAND_RING2, "RightHandRing2"), + makePair(RIGHT_HAND_RING3, "RightHandRing3"), + makePair(RIGHT_HAND_RING4, "RightHandRing4"), + makePair(RIGHT_HAND_PINKY1, "RightHandPinky1"), + makePair(RIGHT_HAND_PINKY2, "RightHandPinky2"), + makePair(RIGHT_HAND_PINKY3, "RightHandPinky3"), + makePair(RIGHT_HAND_PINKY4, "RightHandPinky4"), makePair(LEFT_FOOT, "LeftFoot"), makePair(RIGHT_FOOT, "RightFoot"), makePair(RIGHT_ARM, "RightArm"), diff --git a/libraries/trackers/src/trackers/DeviceTracker.cpp b/libraries/trackers/src/trackers/DeviceTracker.cpp deleted file mode 100644 index 93aeb607bc..0000000000 --- a/libraries/trackers/src/trackers/DeviceTracker.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// -// Created by Sam Cake on 6/20/14. -// Copyright 2014 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 "DeviceTracker.h" - -DeviceTracker::SingletonData::~SingletonData() { - // Destroy all the device registered - //TODO C++11 for (auto device = _devicesVector.begin(); device != _devicesVector.end(); device++) { - for (Vector::iterator device = _devicesVector.begin(); device != _devicesVector.end(); device++) { - delete (*device); - } -} - -int DeviceTracker::getNumDevices() { - return (int)Singleton::get()->_devicesMap.size(); -} - -DeviceTracker::ID DeviceTracker::getDeviceID(const Name& name) { - //TODO C++11 auto deviceIt = Singleton::get()->_devicesMap.find(name); - Map::iterator deviceIt = Singleton::get()->_devicesMap.find(name); - if (deviceIt != Singleton::get()->_devicesMap.end()) { - return (*deviceIt).second; - } else { - return INVALID_DEVICE; - } -} - -DeviceTracker* DeviceTracker::getDevice(const Name& name) { - return getDevice(getDeviceID(name)); -} - -DeviceTracker* DeviceTracker::getDevice(DeviceTracker::ID deviceID) { - if ((deviceID >= 0) && (deviceID < (int)(Singleton::get()->_devicesVector.size()))) { - return Singleton::get()->_devicesVector[ deviceID ]; - } else { - return NULL; - } -} - -DeviceTracker::ID DeviceTracker::registerDevice(const Name& name, DeviceTracker* device) { - // Check that the device exists, if not exit - if (!device) { - return INVALID_DEVICE; - } - - // Look if the name is not already used - ID deviceID = getDeviceID(name); - if (deviceID >= 0) { - return INVALID_DEVICE_NAME; - } - - // Good to register the device - deviceID = (ID)Singleton::get()->_devicesVector.size(); - Singleton::get()->_devicesMap.insert(Map::value_type(name, deviceID)); - Singleton::get()->_devicesVector.push_back(device); - device->assignIDAndName(deviceID, name); - - return deviceID; -} - -void DeviceTracker::destroyDevice(const Name& name) { - DeviceTracker::ID deviceID = getDeviceID(name); - if (deviceID != INVALID_DEVICE) { - delete Singleton::get()->_devicesVector[getDeviceID(name)]; - Singleton::get()->_devicesVector[getDeviceID(name)] = nullptr; - } -} - -void DeviceTracker::updateAll() { - //TODO C++11 for (auto deviceIt = Singleton::get()->_devicesVector.begin(); deviceIt != Singleton::get()->_devicesVector.end(); deviceIt++) { - for (Vector::iterator deviceIt = Singleton::get()->_devicesVector.begin(); deviceIt != Singleton::get()->_devicesVector.end(); deviceIt++) { - if ((*deviceIt)) - (*deviceIt)->update(); - } -} - -// Core features of the Device Tracker -DeviceTracker::DeviceTracker() : - _ID(INVALID_DEVICE), - _name("Unkown") -{ -} - -DeviceTracker::~DeviceTracker() { -} - -void DeviceTracker::update() { -} diff --git a/libraries/trackers/src/trackers/DeviceTracker.h b/libraries/trackers/src/trackers/DeviceTracker.h deleted file mode 100644 index 8a7f509cb3..0000000000 --- a/libraries/trackers/src/trackers/DeviceTracker.h +++ /dev/null @@ -1,115 +0,0 @@ -// -// Created by Sam Cake on 6/20/14. -// Copyright 2014 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 -// - -#ifndef hifi_DeviceTracker_h -#define hifi_DeviceTracker_h - -#include -#include -#include - -// Singleton template class -template < typename T > -class TemplateSingleton { -public: - - static T* get() { - if ( !_singleton._one ) { - _singleton._one = new T(); - } - return _singleton._one; - } - - TemplateSingleton() : - _one(0) - { - } - ~TemplateSingleton() { - if ( _one ) { - delete _one; - _one = 0; - } - } -private: - static TemplateSingleton< T > _singleton; - T* _one; -}; -template -TemplateSingleton TemplateSingleton::_singleton; - -/// Base class for device trackers. -class DeviceTracker { -public: - - // THe ID and Name types used to manage the pool of devices - typedef std::string Name; - typedef int ID; - static const ID INVALID_DEVICE = -1; - static const ID INVALID_DEVICE_NAME = -2; - - // Singleton interface to register and query the devices currently connected - static int getNumDevices(); - static ID getDeviceID(const Name& name); - static DeviceTracker* getDevice(ID deviceID); - static DeviceTracker* getDevice(const Name& name); - - /// Update all the devices calling for their update() function - /// This should be called every frame by the main loop to update all the devices that pull their state - static void updateAll(); - - /// Register a device tracker to the factory - /// Right after creating a new DeviceTracker, it should be registered - /// This is why, it's recommended to use a factory static call in the specialized class - /// to create a new input device - /// - /// \param name The Name under wich registering the device - /// \param parent The DeviceTracker - /// - /// \return The Index of the newly registered device. - /// Valid if everything went well. - /// INVALID_DEVICE if the device is not valid (NULL) - /// INVALID_DEVICE_NAME if the name is already taken - static ID registerDevice(const Name& name, DeviceTracker* tracker); - - static void destroyDevice(const Name& name); - - // DeviceTracker interface - - virtual void update(); - - /// Get the ID assigned to the Device when registered after creation, or INVALID_DEVICE if it hasn't been registered which should not happen. - ID getID() const { return _ID; } - - /// Get the name assigned to the Device when registered after creation, or "Unknown" if it hasn't been registered which should not happen. - const Name& getName() const { return _name; } - - typedef std::map< Name, ID > Map; - static const Map& getDevices() { return Singleton::get()->_devicesMap; } - -protected: - DeviceTracker(); - virtual ~DeviceTracker(); - -private: - ID _ID; - Name _name; - - // this call is used by the singleton when the device tracker is currently beeing registered and beeing assigned an ID - void assignIDAndName( const ID id, const Name& name ) { _ID = id; _name = name; } - - typedef std::vector< DeviceTracker* > Vector; - struct SingletonData { - Map _devicesMap; - Vector _devicesVector; - - ~SingletonData(); - }; - typedef TemplateSingleton< SingletonData > Singleton; -}; - -#endif // hifi_DeviceTracker_h diff --git a/libraries/trackers/src/trackers/MotionTracker.cpp b/libraries/trackers/src/trackers/MotionTracker.cpp deleted file mode 100644 index c6012c0422..0000000000 --- a/libraries/trackers/src/trackers/MotionTracker.cpp +++ /dev/null @@ -1,186 +0,0 @@ -// -// Created by Sam Cake on 6/20/14. -// Copyright 2014 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 "MotionTracker.h" - -// glm::mult(mat43, mat43) just the composition of the 2 matrices assuming they are in fact mat44 with the last raw = { 0, 0, 0, 1 } -namespace glm { - mat4x3 mult(const mat4& lhs, const mat4x3& rhs) { - vec3 lrx(lhs[0].x, lhs[1].x, lhs[2].x); - vec3 lry(lhs[0].y, lhs[1].y, lhs[2].y); - vec3 lrz(lhs[0].z, lhs[1].z, lhs[2].z); - return mat4x3( - dot(lrx, rhs[0]), - dot(lry, rhs[0]), - dot(lrz, rhs[0]), - - dot(lrx, rhs[1]), - dot(lry, rhs[1]), - dot(lrz, rhs[1]), - - dot(lrx, rhs[2]), - dot(lry, rhs[2]), - dot(lrz, rhs[2]), - - dot(lrx, rhs[3]) + lhs[3].x, - dot(lry, rhs[3]) + lhs[3].y, - dot(lrz, rhs[3]) + lhs[3].z - ); - } - mat4x3 mult(const mat4x3& lhs, const mat4x3& rhs) { - vec3 lrx(lhs[0].x, lhs[1].x, lhs[2].x); - vec3 lry(lhs[0].y, lhs[1].y, lhs[2].y); - vec3 lrz(lhs[0].z, lhs[1].z, lhs[2].z); - return mat4x3( - dot(lrx, rhs[0]), - dot(lry, rhs[0]), - dot(lrz, rhs[0]), - - dot(lrx, rhs[1]), - dot(lry, rhs[1]), - dot(lrz, rhs[1]), - - dot(lrx, rhs[2]), - dot(lry, rhs[2]), - dot(lrz, rhs[2]), - - dot(lrx, rhs[3]) + lhs[3].x, - dot(lry, rhs[3]) + lhs[3].y, - dot(lrz, rhs[3]) + lhs[3].z - ); - } -} - -// MotionTracker -MotionTracker::MotionTracker() : - DeviceTracker() -{ - _jointsArray.resize(1); - _jointsMap.insert(JointTracker::Map::value_type(Semantic("Root"), 0)); -} - -MotionTracker::~MotionTracker() -{ -} - -bool MotionTracker::isConnected() const { - return false; -} - -MotionTracker::Index MotionTracker::addJoint(const Semantic& semantic, Index parent) { - // Check the parent - if (int(parent) < 0) - return INVALID_PARENT; - - // Check that the semantic is not already in use - Index foundIndex = findJointIndex(semantic); - if (foundIndex >= 0) { - return INVALID_SEMANTIC; - } - - - // All good then allocate the joint - Index newIndex = (Index)_jointsArray.size(); - _jointsArray.push_back(JointTracker(semantic, parent)); - _jointsMap.insert(JointTracker::Map::value_type(semantic, newIndex)); - - return newIndex; -} - -MotionTracker::Index MotionTracker::findJointIndex(const Semantic& semantic) const { - // TODO C++11 auto jointIt = _jointsMap.find(semantic); - JointTracker::Map::const_iterator jointIt = _jointsMap.find(semantic); - if (jointIt != _jointsMap.end()) { - return (*jointIt).second; - } - - return INVALID_SEMANTIC; -} - -void MotionTracker::updateAllAbsTransform() { - _jointsArray[0].updateAbsFromLocTransform(0); - - // Because we know the hierarchy is stored from root down the branches let's just traverse and update - for (Index i = 1; i < (Index)(_jointsArray.size()); i++) { - JointTracker* joint = _jointsArray.data() + i; - joint->updateAbsFromLocTransform(_jointsArray.data() + joint->getParent()); - } -} - - -// MotionTracker::JointTracker -MotionTracker::JointTracker::JointTracker() : - _locFrame(), - _absFrame(), - _semantic(""), - _parent(INVALID_PARENT), - _lastUpdate(1) // Joint inactive -{ -} - -MotionTracker::JointTracker::JointTracker(const Semantic& semantic, Index parent) : - _semantic(semantic), - _parent(parent), - _lastUpdate(1) // Joint inactive -{ -} - -MotionTracker::JointTracker::JointTracker(const JointTracker& tracker) : - _locFrame(tracker._locFrame), - _absFrame(tracker._absFrame), - _semantic(tracker._semantic), - _parent(tracker._parent), - _lastUpdate(tracker._lastUpdate) -{ -} - -void MotionTracker::JointTracker::updateAbsFromLocTransform(const JointTracker* parentJoint) { - if (parentJoint) { - editAbsFrame()._transform = (parentJoint->getAbsFrame()._transform * getLocFrame()._transform); - } else { - editAbsFrame()._transform = getLocFrame()._transform; - } -} - -void MotionTracker::JointTracker::updateLocFromAbsTransform(const JointTracker* parentJoint) { - if (parentJoint) { - glm::mat4 ip = glm::inverse(parentJoint->getAbsFrame()._transform); - editLocFrame()._transform = (ip * getAbsFrame()._transform); - } else { - editLocFrame()._transform = getAbsFrame()._transform; - } -} - -//-------------------------------------------------------------------------------------- -// MotionTracker::Frame -//-------------------------------------------------------------------------------------- - -MotionTracker::Frame::Frame() : - _transform() -{ -} - -void MotionTracker::Frame::setRotation(const glm::quat& rotation) { - glm::mat3x3 rot = glm::mat3_cast(rotation); - _transform[0] = glm::vec4(rot[0], 0.0f); - _transform[1] = glm::vec4(rot[1], 0.0f); - _transform[2] = glm::vec4(rot[2], 0.0f); -} - -void MotionTracker::Frame::getRotation(glm::quat& rotation) const { - rotation = glm::quat_cast(_transform); -} - -void MotionTracker::Frame::setTranslation(const glm::vec3& translation) { - _transform[3] = glm::vec4(translation, 1.0f); -} - -void MotionTracker::Frame::getTranslation(glm::vec3& translation) const { - translation = extractTranslation(_transform); -} - diff --git a/libraries/trackers/src/trackers/MotionTracker.h b/libraries/trackers/src/trackers/MotionTracker.h deleted file mode 100644 index 26c8dcea2c..0000000000 --- a/libraries/trackers/src/trackers/MotionTracker.h +++ /dev/null @@ -1,116 +0,0 @@ -// -// Created by Sam Cake on 6/20/14. -// Copyright 2014 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 -// - -#ifndef hifi_MotionTracker_h -#define hifi_MotionTracker_h - -#include "DeviceTracker.h" - -#include - -/// Base class for device trackers. -class MotionTracker : public DeviceTracker { -public: - - class Frame { - public: - Frame(); - - glm::mat4 _transform; - - void setRotation(const glm::quat& rotation); - void getRotation(glm::quat& rotatio) const; - - void setTranslation(const glm::vec3& translation); - void getTranslation(glm::vec3& translation) const; - }; - - // Semantic and Index types to retreive the JointTrackers of this MotionTracker - typedef std::string Semantic; - typedef int32_t Index; - static const Index INVALID_SEMANTIC = -1; - static const Index INVALID_PARENT = -2; - - class JointTracker { - public: - typedef std::vector< JointTracker > Vector; - typedef std::map< Semantic, Index > Map; - - JointTracker(); - JointTracker(const JointTracker& tracker); - JointTracker(const Semantic& semantic, Index parent); - - const Frame& getLocFrame() const { return _locFrame; } - Frame& editLocFrame() { return _locFrame; } - void setLocFrame(const Frame& frame) { editLocFrame() = frame; } - - const Frame& getAbsFrame() const { return _absFrame; } - Frame& editAbsFrame() { return _absFrame; } - void setAbsFrame(const Frame& frame) { editAbsFrame() = frame; } - - const Semantic& getSemantic() const { return _semantic; } - const Index& getParent() const { return _parent; } - - bool isActive() const { return (_lastUpdate <= 0); } - void tickNewFrame() { _lastUpdate++; } - void activeFrame() { _lastUpdate = 0; } - - /// Update the loc/abs transform for this joint from the current abs/loc value and the specified parent joint abs frame - void updateLocFromAbsTransform(const JointTracker* parentJoint); - void updateAbsFromLocTransform(const JointTracker* parentJoint); - - protected: - Frame _locFrame; - Frame _absFrame; - Semantic _semantic; - Index _parent; - int _lastUpdate; - }; - - virtual bool isConnected() const; - - Index numJointTrackers() const { return (Index)_jointsArray.size(); } - - /// Access a Joint from it's index. - /// Index 0 is always the "Root". - /// if the index is Invalid then returns NULL. - const JointTracker* getJointTracker(Index index) const { return ((index > 0) && (index < (Index)(_jointsArray.size())) ? _jointsArray.data() + index : NULL); } - JointTracker* editJointTracker(Index index) { return ((index > 0) && (index < (Index)(_jointsArray.size())) ? _jointsArray.data() + index : NULL); } - - /// From a semantic, find the Index of the Joint. - /// \return the index of the mapped Joint or INVALID_SEMANTIC if the semantic is not knowned. - Index findJointIndex(const Semantic& semantic) const; - -protected: - MotionTracker(); - virtual ~MotionTracker(); - - JointTracker::Vector _jointsArray; - JointTracker::Map _jointsMap; - - /// Adding joint is only done from the specialized Motion Tracker, hence this function is protected. - /// The hierarchy of joints must be created from the top down to the branches. - /// The "Root" node is at index 0 and exists at creation of the Motion Tracker. - /// - /// \param semantic A joint is defined by it's semantic, the unique name mapping to it - /// \param parent The parent's index, the parent must be valid and correspond to a Joint that has been previously created - /// - /// \return The Index of the newly created Joint. - /// Valid if everything went well. - /// INVALID_SEMANTIC if the semantic is already in use - /// INVALID_PARENT if the parent is not valid - Index addJoint(const Semantic& semantic, Index parent); - - /// Update the absolute transform stack traversing the hierarchy from the root down the branches - /// This is a generic way to update all the Joint's absFrame by combining the locFrame going down the hierarchy branch. - void updateAllAbsTransform(); -}; - - - -#endif // hifi_MotionTracker_h diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 82ca2a7d38..06cf929368 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -30,6 +30,8 @@ if (NOT SERVER_ONLY AND NOT ANDROID) add_subdirectory(${DIR}) set(DIR "steamClient") add_subdirectory(${DIR}) + set(DIR "hifiLeapMotion") + add_subdirectory(${DIR}) endif() # server-side plugins diff --git a/plugins/hifiLeapMotion/CMakeLists.txt b/plugins/hifiLeapMotion/CMakeLists.txt new file mode 100644 index 0000000000..14f9bbaa17 --- /dev/null +++ b/plugins/hifiLeapMotion/CMakeLists.txt @@ -0,0 +1,15 @@ +# +# Created by David Rowe on 15 Jun 2017. +# Copyright 2017 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 +# + +find_package(LEAPMOTION) +if (LEAPMOTION_FOUND) + set(TARGET_NAME hifiLeapMotion) + setup_hifi_plugin(Script Qml Widgets) + link_hifi_libraries(shared controllers ui plugins input-plugins) + target_leapmotion() +endif() diff --git a/plugins/hifiLeapMotion/src/LeapMotionPlugin.cpp b/plugins/hifiLeapMotion/src/LeapMotionPlugin.cpp new file mode 100644 index 0000000000..174dd02426 --- /dev/null +++ b/plugins/hifiLeapMotion/src/LeapMotionPlugin.cpp @@ -0,0 +1,493 @@ +// +// LeapMotionPlugin.cpp +// +// Created by David Rowe on 15 Jun 2017. +// Copyright 2017 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 "LeapMotionPlugin.h" + +#include + +#include +#include +#include +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(inputplugins) +Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") + +const char* LeapMotionPlugin::NAME = "Leap Motion"; +const char* LeapMotionPlugin::LEAPMOTION_ID_STRING = "Leap Motion"; + +const bool DEFAULT_ENABLED = false; +const char* SENSOR_ON_DESKTOP = "Desktop"; +const char* SENSOR_ON_HMD = "HMD"; +const char* DEFAULT_SENSOR_LOCATION = SENSOR_ON_DESKTOP; + +enum LeapMotionJointIndex { + LeftHand = 0, + LeftHandThumb1, + LeftHandThumb2, + LeftHandThumb3, + LeftHandThumb4, + LeftHandIndex1, + LeftHandIndex2, + LeftHandIndex3, + LeftHandIndex4, + LeftHandMiddle1, + LeftHandMiddle2, + LeftHandMiddle3, + LeftHandMiddle4, + LeftHandRing1, + LeftHandRing2, + LeftHandRing3, + LeftHandRing4, + LeftHandPinky1, + LeftHandPinky2, + LeftHandPinky3, + LeftHandPinky4, + RightHand, + RightHandThumb1, + RightHandThumb2, + RightHandThumb3, + RightHandThumb4, + RightHandIndex1, + RightHandIndex2, + RightHandIndex3, + RightHandIndex4, + RightHandMiddle1, + RightHandMiddle2, + RightHandMiddle3, + RightHandMiddle4, + RightHandRing1, + RightHandRing2, + RightHandRing3, + RightHandRing4, + RightHandPinky1, + RightHandPinky2, + RightHandPinky3, + RightHandPinky4, + + Size +}; + +static controller::StandardPoseChannel LeapMotionJointIndexToPoseIndexMap[LeapMotionJointIndex::Size] = { + controller::LEFT_HAND, + controller::LEFT_HAND_THUMB1, + controller::LEFT_HAND_THUMB2, + controller::LEFT_HAND_THUMB3, + controller::LEFT_HAND_THUMB4, + controller::LEFT_HAND_INDEX1, + controller::LEFT_HAND_INDEX2, + controller::LEFT_HAND_INDEX3, + controller::LEFT_HAND_INDEX4, + controller::LEFT_HAND_MIDDLE1, + controller::LEFT_HAND_MIDDLE2, + controller::LEFT_HAND_MIDDLE3, + controller::LEFT_HAND_MIDDLE4, + controller::LEFT_HAND_RING1, + controller::LEFT_HAND_RING2, + controller::LEFT_HAND_RING3, + controller::LEFT_HAND_RING4, + controller::LEFT_HAND_PINKY1, + controller::LEFT_HAND_PINKY2, + controller::LEFT_HAND_PINKY3, + controller::LEFT_HAND_PINKY4, + controller::RIGHT_HAND, + controller::RIGHT_HAND_THUMB1, + controller::RIGHT_HAND_THUMB2, + controller::RIGHT_HAND_THUMB3, + controller::RIGHT_HAND_THUMB4, + controller::RIGHT_HAND_INDEX1, + controller::RIGHT_HAND_INDEX2, + controller::RIGHT_HAND_INDEX3, + controller::RIGHT_HAND_INDEX4, + controller::RIGHT_HAND_MIDDLE1, + controller::RIGHT_HAND_MIDDLE2, + controller::RIGHT_HAND_MIDDLE3, + controller::RIGHT_HAND_MIDDLE4, + controller::RIGHT_HAND_RING1, + controller::RIGHT_HAND_RING2, + controller::RIGHT_HAND_RING3, + controller::RIGHT_HAND_RING4, + controller::RIGHT_HAND_PINKY1, + controller::RIGHT_HAND_PINKY2, + controller::RIGHT_HAND_PINKY3, + controller::RIGHT_HAND_PINKY4 +}; + +#define UNKNOWN_JOINT (controller::StandardPoseChannel)0 + +static controller::StandardPoseChannel LeapMotionJointIndexToPoseIndex(LeapMotionJointIndex i) { + assert(i >= 0 && i < LeapMotionJointIndex::Size); + if (i >= 0 && i < LeapMotionJointIndex::Size) { + return LeapMotionJointIndexToPoseIndexMap[i]; + } else { + return UNKNOWN_JOINT; + } +} + +QStringList controllerJointNames = { + "Hips", + "RightUpLeg", + "RightLeg", + "RightFoot", + "LeftUpLeg", + "LeftLeg", + "LeftFoot", + "Spine", + "Spine1", + "Spine2", + "Spine3", + "Neck", + "Head", + "RightShoulder", + "RightArm", + "RightForeArm", + "RightHand", + "RightHandThumb1", + "RightHandThumb2", + "RightHandThumb3", + "RightHandThumb4", + "RightHandIndex1", + "RightHandIndex2", + "RightHandIndex3", + "RightHandIndex4", + "RightHandMiddle1", + "RightHandMiddle2", + "RightHandMiddle3", + "RightHandMiddle4", + "RightHandRing1", + "RightHandRing2", + "RightHandRing3", + "RightHandRing4", + "RightHandPinky1", + "RightHandPinky2", + "RightHandPinky3", + "RightHandPinky4", + "LeftShoulder", + "LeftArm", + "LeftForeArm", + "LeftHand", + "LeftHandThumb1", + "LeftHandThumb2", + "LeftHandThumb3", + "LeftHandThumb4", + "LeftHandIndex1", + "LeftHandIndex2", + "LeftHandIndex3", + "LeftHandIndex4", + "LeftHandMiddle1", + "LeftHandMiddle2", + "LeftHandMiddle3", + "LeftHandMiddle4", + "LeftHandRing1", + "LeftHandRing2", + "LeftHandRing3", + "LeftHandRing4", + "LeftHandPinky1", + "LeftHandPinky2", + "LeftHandPinky3", + "LeftHandPinky4" +}; + +static const char* getControllerJointName(controller::StandardPoseChannel i) { + if (i >= 0 && i < controller::NUM_STANDARD_POSES) { + return qPrintable(controllerJointNames[i]); + } + return "unknown"; +} + + +void LeapMotionPlugin::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { + if (!_enabled) { + return; + } + + const auto frame = _controller.frame(); + const auto frameID = frame.id(); + if (_lastFrameID >= frameID) { + // Leap Motion not connected or duplicate frame. + return; + } + + if (!_hasLeapMotionBeenConnected) { + emit deviceConnected(getName()); + _hasLeapMotionBeenConnected = true; + } + + processFrame(frame); // Updates _joints. + + auto joints = _joints; + + auto userInputMapper = DependencyManager::get(); + userInputMapper->withLock([&, this]() { + _inputDevice->update(deltaTime, inputCalibrationData, joints, _prevJoints); + }); + + _prevJoints = joints; + + _lastFrameID = frameID; +} + +controller::Input::NamedVector LeapMotionPlugin::InputDevice::getAvailableInputs() const { + static controller::Input::NamedVector availableInputs; + if (availableInputs.size() == 0) { + for (int i = 0; i < LeapMotionJointIndex::Size; i++) { + auto channel = LeapMotionJointIndexToPoseIndex(static_cast(i)); + availableInputs.push_back(makePair(channel, getControllerJointName(channel))); + } + }; + return availableInputs; +} + +QString LeapMotionPlugin::InputDevice::getDefaultMappingConfig() const { + static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/leapmotion.json"; + return MAPPING_JSON; +} + +void LeapMotionPlugin::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, + const std::vector& joints, + const std::vector& prevJoints) { + + glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; + glm::quat controllerToAvatarRotation = glmExtractRotation(controllerToAvatar); + + glm::vec3 hmdSensorPosition; // HMD + glm::quat hmdSensorOrientation; // HMD + glm::vec3 leapMotionOffset; // Desktop + if (_isLeapOnHMD) { + hmdSensorPosition = extractTranslation(inputCalibrationData.hmdSensorMat); + hmdSensorOrientation = extractRotation(inputCalibrationData.hmdSensorMat); + } else { + // Desktop "zero" position is some distance above the Leap Motion sensor and half the avatar's shoulder-to-hand length + // in front of avatar. + float halfShouldToHandLength = fabsf(extractTranslation(inputCalibrationData.defaultLeftHand).x + - extractTranslation(inputCalibrationData.defaultLeftArm).x) / 2.0f; + leapMotionOffset = glm::vec3(0.0f, _desktopHeightOffset, halfShouldToHandLength); + } + + for (size_t i = 0; i < joints.size(); i++) { + int poseIndex = LeapMotionJointIndexToPoseIndex((LeapMotionJointIndex)i); + + if (joints[i].position == Vectors::ZERO) { + _poseStateMap[poseIndex] = controller::Pose(); + continue; + } + + glm::vec3 pos; + glm::quat rot; + if (_isLeapOnHMD) { + auto jointPosition = joints[i].position; + const glm::vec3 HMD_EYE_TO_LEAP_OFFSET = glm::vec3(0.0f, 0.0f, -0.09f); // Eyes to surface of Leap Motion. + jointPosition = glm::vec3(-jointPosition.x, -jointPosition.z, -jointPosition.y) + HMD_EYE_TO_LEAP_OFFSET; + jointPosition = hmdSensorPosition + hmdSensorOrientation * jointPosition; + pos = transformPoint(controllerToAvatar, jointPosition); + + glm::quat jointOrientation = joints[i].orientation; + jointOrientation = glm::quat(jointOrientation.w, -jointOrientation.x, -jointOrientation.z, -jointOrientation.y); + rot = controllerToAvatarRotation * hmdSensorOrientation * jointOrientation; + } else { + pos = controllerToAvatarRotation * (joints[i].position - leapMotionOffset); + const glm::quat ZERO_HAND_ORIENTATION = glm::quat(glm::vec3(PI_OVER_TWO, PI, 0.0f)); + rot = controllerToAvatarRotation * joints[i].orientation * ZERO_HAND_ORIENTATION; + } + + glm::vec3 linearVelocity, angularVelocity; + if (i < prevJoints.size()) { + linearVelocity = (pos - (prevJoints[i].position * METERS_PER_CENTIMETER)) / deltaTime; // m/s + // quat log imaginary part points along the axis of rotation, with length of one half the angle of rotation. + glm::quat d = glm::log(rot * glm::inverse(prevJoints[i].orientation)); + angularVelocity = glm::vec3(d.x, d.y, d.z) / (0.5f * deltaTime); // radians/s + } + + _poseStateMap[poseIndex] = controller::Pose(pos, rot, linearVelocity, angularVelocity); + } +} + +void LeapMotionPlugin::init() { + loadSettings(); + + auto preferences = DependencyManager::get(); + static const QString LEAPMOTION_PLUGIN { "Leap Motion" }; + { + auto getter = [this]()->bool { return _enabled; }; + auto setter = [this](bool value) { + _enabled = value; + saveSettings(); + if (!_enabled) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->withLock([&, this]() { + _inputDevice->clearState(); + }); + } + }; + auto preference = new CheckPreference(LEAPMOTION_PLUGIN, "Enabled", getter, setter); + preferences->addPreference(preference); + } + { + auto getter = [this]()->QString { return _sensorLocation; }; + auto setter = [this](QString value) { + _sensorLocation = value; + saveSettings(); + applySensorLocation(); + }; + auto preference = new ComboBoxPreference(LEAPMOTION_PLUGIN, "Sensor location", getter, setter); + QStringList list = { SENSOR_ON_DESKTOP, SENSOR_ON_HMD }; + preference->setItems(list); + preferences->addPreference(preference); + } + { + auto getter = [this]()->float { return _desktopHeightOffset; }; + auto setter = [this](float value) { + _desktopHeightOffset = value; + saveSettings(); + applyDesktopHeightOffset(); + }; + auto preference = new SpinnerPreference(LEAPMOTION_PLUGIN, "Desktop height for horizontal forearms", getter, setter); + float MIN_VALUE = 0.0f; + float MAX_VALUE = 1.0f; + float DECIMALS = 2.0f; + float STEP = 0.01f; + preference->setMin(MIN_VALUE); + preference->setMax(MAX_VALUE); + preference->setDecimals(DECIMALS); + preference->setStep(STEP); + preferences->addPreference(preference); + } +} + +bool LeapMotionPlugin::activate() { + InputPlugin::activate(); + + // Nothing required to be done to start up Leap Motion library. + + _joints.resize(LeapMotionJointIndex::Size, { glm::vec3(), glm::quat() }); + + auto userInputMapper = DependencyManager::get(); + userInputMapper->registerDevice(_inputDevice); + + return true; +} + +void LeapMotionPlugin::deactivate() { + if (_inputDevice->_deviceID != controller::Input::INVALID_DEVICE) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->removeDevice(_inputDevice->_deviceID); + } + + InputPlugin::deactivate(); +} + +const char* SETTINGS_ENABLED_KEY = "enabled"; +const char* SETTINGS_SENSOR_LOCATION_KEY = "sensorLocation"; +const char* SETTINGS_DESKTOP_HEIGHT_OFFSET_KEY = "desktopHeightOffset"; + +void LeapMotionPlugin::saveSettings() const { + Settings settings; + QString idString = getID(); + settings.beginGroup(idString); + { + settings.setValue(QString(SETTINGS_ENABLED_KEY), _enabled); + settings.setValue(QString(SETTINGS_SENSOR_LOCATION_KEY), _sensorLocation); + settings.setValue(QString(SETTINGS_DESKTOP_HEIGHT_OFFSET_KEY), _desktopHeightOffset); + } + settings.endGroup(); +} + +void LeapMotionPlugin::loadSettings() { + Settings settings; + QString idString = getID(); + settings.beginGroup(idString); + { + _enabled = settings.value(SETTINGS_ENABLED_KEY, QVariant(DEFAULT_ENABLED)).toBool(); + _sensorLocation = settings.value(SETTINGS_SENSOR_LOCATION_KEY, QVariant(DEFAULT_SENSOR_LOCATION)).toString(); + _desktopHeightOffset = + settings.value(SETTINGS_DESKTOP_HEIGHT_OFFSET_KEY, QVariant(DEFAULT_DESKTOP_HEIGHT_OFFSET)).toFloat(); + applySensorLocation(); + applyDesktopHeightOffset(); + } + settings.endGroup(); +} + +void LeapMotionPlugin::InputDevice::clearState() { + for (size_t i = 0; i < LeapMotionJointIndex::Size; i++) { + int poseIndex = LeapMotionJointIndexToPoseIndex((LeapMotionJointIndex)i); + _poseStateMap[poseIndex] = controller::Pose(); + } +} + +void LeapMotionPlugin::applySensorLocation() { + bool isLeapOnHMD = _sensorLocation == SENSOR_ON_HMD; + _controller.setPolicyFlags(isLeapOnHMD ? Leap::Controller::POLICY_OPTIMIZE_HMD : Leap::Controller::POLICY_DEFAULT); + _inputDevice->setIsLeapOnHMD(isLeapOnHMD); +} + +void LeapMotionPlugin::applyDesktopHeightOffset() { + _inputDevice->setDektopHeightOffset(_desktopHeightOffset); +} + +const float LEFT_SIDE_SIGN = -1.0f; +const float RIGHT_SIDE_SIGN = 1.0f; + +glm::quat LeapBasisToQuat(float sideSign, const Leap::Matrix& basis) { + glm::vec3 xAxis = sideSign * glm::vec3(basis.xBasis.x, basis.xBasis.y, basis.xBasis.z); + glm::vec3 yAxis = glm::vec3(basis.yBasis.x, basis.yBasis.y, basis.yBasis.z); + glm::vec3 zAxis = glm::vec3(basis.zBasis.x, basis.zBasis.y, basis.zBasis.z); + glm::quat orientation = (glm::quat_cast(glm::mat3(xAxis, yAxis, zAxis))); + return orientation; +} + +glm::vec3 LeapVectorToVec3(const Leap::Vector& vec) { + return glm::vec3(vec.x * METERS_PER_MILLIMETER, vec.y * METERS_PER_MILLIMETER, vec.z * METERS_PER_MILLIMETER); +} + +void LeapMotionPlugin::processFrame(const Leap::Frame& frame) { + // Default to uncontrolled. + for (int i = 0; i < _joints.size(); i++) { + _joints[i].position = glm::vec3(); + } + + auto hands = frame.hands(); + const int MAX_NUMBER_OF_HANDS = 2; + for (int i = 0; i < hands.count() && i < MAX_NUMBER_OF_HANDS; i++) { + auto hand = hands[i]; + + int sideSign = hand.isLeft() ? LEFT_SIDE_SIGN : RIGHT_SIDE_SIGN; + int jointIndex = hand.isLeft() ? LeapMotionJointIndex::LeftHand : LeapMotionJointIndex::RightHand; + + // Hand. + _joints[jointIndex].position = LeapVectorToVec3(hand.wristPosition()); + _joints[jointIndex].orientation = LeapBasisToQuat(sideSign, hand.basis()); + + // Fingers. + // Leap Motion SDK guarantees full set of fingers and finger joints so can straightforwardly process them all. + Leap::FingerList fingers = hand.fingers(); + for (int j = Leap::Finger::Type::TYPE_THUMB; j <= Leap::Finger::Type::TYPE_PINKY; j++) { + Leap::Finger finger; + finger = fingers[j]; + Leap::Bone bone; + bone = finger.bone(Leap::Bone::Type::TYPE_PROXIMAL); + jointIndex++; + _joints[jointIndex].position = LeapVectorToVec3(bone.prevJoint()); + _joints[jointIndex].orientation = LeapBasisToQuat(sideSign, bone.basis()); + bone = finger.bone(Leap::Bone::Type::TYPE_INTERMEDIATE); + jointIndex++; + _joints[jointIndex].position = LeapVectorToVec3(bone.prevJoint()); + _joints[jointIndex].orientation = LeapBasisToQuat(sideSign, bone.basis()); + bone = finger.bone(Leap::Bone::Type::TYPE_DISTAL); + jointIndex++; + _joints[jointIndex].position = LeapVectorToVec3(bone.prevJoint()); + _joints[jointIndex].orientation = LeapBasisToQuat(sideSign, bone.basis()); + jointIndex++; + _joints[jointIndex].position = LeapVectorToVec3(bone.nextJoint()); + _joints[jointIndex].orientation = LeapBasisToQuat(sideSign, bone.basis()); + } + } +} + diff --git a/plugins/hifiLeapMotion/src/LeapMotionPlugin.h b/plugins/hifiLeapMotion/src/LeapMotionPlugin.h new file mode 100644 index 0000000000..391d569567 --- /dev/null +++ b/plugins/hifiLeapMotion/src/LeapMotionPlugin.h @@ -0,0 +1,99 @@ +// +// LeapMotionPlugin.h +// +// Created by David Rowe on 15 Jun 2017. +// Copyright 2017 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 +// + +#ifndef hifi_LeapMotionPlugin_h +#define hifi_LeapMotionPlugin_h + +#include +#include + +// LeapMotion SDK +#include + +class LeapMotionPlugin : public InputPlugin { + Q_OBJECT + +public: + // InputPlugin methods + virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } + virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; + + bool isHandController() const override { return true; } + + // Plugin methods + virtual const QString getName() const override { return NAME; } + const QString getID() const override { return LEAPMOTION_ID_STRING; } + + virtual void init() override; + + virtual bool activate() override; + virtual void deactivate() override; + + virtual void saveSettings() const override; + virtual void loadSettings() override; + +protected: + static const char* NAME; + static const char* LEAPMOTION_ID_STRING; + const float DEFAULT_DESKTOP_HEIGHT_OFFSET = 0.2f; + + bool _enabled { false }; + QString _sensorLocation; + float _desktopHeightOffset { DEFAULT_DESKTOP_HEIGHT_OFFSET }; + + struct LeapMotionJoint { + glm::vec3 position; + glm::quat orientation; + }; + + std::vector _joints; + std::vector _prevJoints; + + class InputDevice : public controller::InputDevice { + public: + friend class LeapMotionPlugin; + + InputDevice() : controller::InputDevice("LeapMotion") {} + + // Device functions + virtual controller::Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; + virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override {}; + virtual void focusOutEvent() override {}; + + void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, + const std::vector& joints, + const std::vector& prevJoints); + + void clearState(); + + void setDektopHeightOffset(float desktopHeightOffset) { _desktopHeightOffset = desktopHeightOffset; }; + void setIsLeapOnHMD(bool isLeapOnHMD) { _isLeapOnHMD = isLeapOnHMD; }; + + private: + float _desktopHeightOffset { 0.0f }; + bool _isLeapOnHMD { false }; + }; + + std::shared_ptr _inputDevice { std::make_shared() }; + +private: + void applySensorLocation(); + void applyDesktopHeightOffset(); + + void processFrame(const Leap::Frame& frame); + + Leap::Controller _controller; + + bool _hasLeapMotionBeenConnected { false }; + int64_t _lastFrameID { -1 }; +}; + +#endif // hifi_LeapMotionPlugin_h diff --git a/plugins/hifiLeapMotion/src/LeapMotionProvider.cpp b/plugins/hifiLeapMotion/src/LeapMotionProvider.cpp new file mode 100644 index 0000000000..62517439c3 --- /dev/null +++ b/plugins/hifiLeapMotion/src/LeapMotionProvider.cpp @@ -0,0 +1,51 @@ +// +// LeapMotionProvider.cpp +// +// Created by David Rowe on 15 Jun 2017. +// Copyright 2017 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 + +#include +#include +#include + +#include +#include + +#include "LeapMotionPlugin.h" + +class LeapMotionProvider : public QObject, public InputProvider +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID InputProvider_iid FILE "plugin.json") + Q_INTERFACES(InputProvider) + +public: + LeapMotionProvider(QObject* parent = nullptr) : QObject(parent) {} + virtual ~LeapMotionProvider() {} + + virtual InputPluginList getInputPlugins() override { + static std::once_flag once; + std::call_once(once, [&] { + InputPluginPointer plugin(new LeapMotionPlugin()); + if (plugin->isSupported()) { + _inputPlugins.push_back(plugin); + } + }); + return _inputPlugins; + } + + virtual void destroyInputPlugins() override { + _inputPlugins.clear(); + } + +private: + InputPluginList _inputPlugins; +}; + +#include "LeapMotionProvider.moc" diff --git a/plugins/hifiLeapMotion/src/plugin.json b/plugins/hifiLeapMotion/src/plugin.json new file mode 100644 index 0000000000..2e867d96e4 --- /dev/null +++ b/plugins/hifiLeapMotion/src/plugin.json @@ -0,0 +1 @@ +{"name":"Leap Motion"} diff --git a/scripts/system/controllers/leapHands.js b/scripts/system/controllers/leapHands.js deleted file mode 100644 index 1be0b1e5f6..0000000000 --- a/scripts/system/controllers/leapHands.js +++ /dev/null @@ -1,527 +0,0 @@ -// -// leapHands.js -// examples -// -// Created by David Rowe on 8 Sep 2014. -// Copyright 2014 High Fidelity, Inc. -// -// This is an example script that uses the Leap Motion to make the avatar's hands replicate the user's hand actions. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -var leftTriggerValue = 0; -var rightTriggerValue = 0; - -var LEAP_TRIGGER_START_ANGLE = 15.0; -var LEAP_TRIGGER_END_ANGLE = 40.0; - -function getLeapMotionLeftTrigger() { - //print("left trigger = " + leftTriggerValue); - return leftTriggerValue; -} -function getLeapMotionRightTrigger() { - //print("right trigger = " + rightTriggerValue); - return rightTriggerValue; -} - -var leapHands = (function () { - - var isOnHMD, - LEAP_ON_HMD_MENU_ITEM = "Leap Motion on HMD", - LEAP_OFFSET = 0.019, // Thickness of Leap Motion plus HMD clip - HMD_OFFSET = 0.070, // Eyeballs to front surface of Oculus DK2 TODO: Confirm and make depend on device and eye relief - hasHandAndWristJoints, - handToWristOffset = [], // For avatars without a wrist joint we control an estimate of a proper hand joint position - HAND_OFFSET = 0.4, // Relative distance of wrist to hand versus wrist to index finger knuckle - handAnimationStateHandlers, - handAnimationStateFunctions, - handAnimationStateProperties, - hands, - wrists, - NUM_HANDS = 2, // 0 = left; 1 = right - fingers, - NUM_FINGERS = 5, // 0 = thumb; ...; 4 = pinky - THUMB = 0, - MIDDLE_FINGER = 2, - NUM_FINGER_JOINTS = 3, // 0 = metacarpal(hand)-proximal(finger) joint; ...; 2 = intermediate-distal joint - MAX_HAND_INACTIVE_COUNT = 20, - calibrationStatus, - UNCALIBRATED = 0, - CALIBRATING = 1, - CALIBRATED = 2, - CALIBRATION_TIME = 1000, // milliseconds - avatarScale, - avatarFaceModelURL, - avatarSkeletonModelURL, - settingsTimer, - HMD_CAMERA_TO_AVATAR_ROTATION = [ - Quat.angleAxis(180.0, { x: 0, y: 0, z: 1 }), - Quat.angleAxis(-180.0, { x: 0, y: 0, z: 1 }) - ], - DESKTOP_CAMERA_TO_AVATAR_ROTATION = - Quat.multiply(Quat.angleAxis(180.0, { x: 0, y: 1, z: 0 }), Quat.angleAxis(90.0, { x: 0, y: 0, z: 1 })), - LEAP_THUMB_ROOT_ADJUST = [Quat.fromPitchYawRollDegrees(0, 0, 20), Quat.fromPitchYawRollDegrees(0, 0, -20)]; - - function printSkeletonJointNames() { - var jointNames, - i; - - print(MyAvatar.skeletonModelURL); - - print("Skeleton joint names ..."); - jointNames = MyAvatar.getJointNames(); - for (i = 0; i < jointNames.length; i += 1) { - print(i + ": " + jointNames[i]); - } - print("... skeleton joint names"); - } - - function animateLeftHand() { - var ROTATION_AND_POSITION = 0; - - return { - leftHandType: ROTATION_AND_POSITION, - leftHandPosition: hands[0].position, - leftHandRotation: hands[0].rotation - }; - } - - function animateRightHand() { - var ROTATION_AND_POSITION = 0; - - return { - rightHandType: ROTATION_AND_POSITION, - rightHandPosition: hands[1].position, - rightHandRotation: hands[1].rotation - }; - } - - function finishCalibration() { - var avatarPosition, - handPosition, - middleFingerPosition, - leapHandHeight, - h; - - if (!isOnHMD) { - if (hands[0].controller.isActive() && hands[1].controller.isActive()) { - leapHandHeight = (hands[0].controller.getAbsTranslation().y + hands[1].controller.getAbsTranslation().y) / 2.0; - } else { - calibrationStatus = UNCALIBRATED; - return; - } - } - - avatarPosition = MyAvatar.position; - - for (h = 0; h < NUM_HANDS; h += 1) { - handPosition = MyAvatar.getJointPosition(hands[h].jointName); - if (!hasHandAndWristJoints) { - middleFingerPosition = MyAvatar.getJointPosition(fingers[h][MIDDLE_FINGER][0].jointName); - handToWristOffset[h] = Vec3.multiply(Vec3.subtract(handPosition, middleFingerPosition), 1.0 - HAND_OFFSET); - } - - if (isOnHMD) { - // Offset of Leap Motion origin from physical eye position - hands[h].zeroPosition = { x: 0.0, y: 0.0, z: HMD_OFFSET + LEAP_OFFSET }; - } else { - hands[h].zeroPosition = { - x: handPosition.x - avatarPosition.x, - y: handPosition.y - avatarPosition.y, - z: avatarPosition.z - handPosition.z - }; - hands[h].zeroPosition = Vec3.multiplyQbyV(MyAvatar.orientation, hands[h].zeroPosition); - hands[h].zeroPosition.y = hands[h].zeroPosition.y - leapHandHeight; - } - } - - MyAvatar.clearJointData("LeftHand"); - MyAvatar.clearJointData("LeftForeArm"); - MyAvatar.clearJointData("RightHand"); - MyAvatar.clearJointData("RightForeArm"); - - calibrationStatus = CALIBRATED; - print("Leap Motion: Calibrated"); - } - - function calibrate() { - var jointNames, - i; - - calibrationStatus = CALIBRATING; - - avatarScale = MyAvatar.scale; - avatarFaceModelURL = MyAvatar.faceModelURL; - avatarSkeletonModelURL = MyAvatar.skeletonModelURL; - - // Does this skeleton have both wrist and hand joints? - hasHandAndWristJoints = false; - jointNames = MyAvatar.getJointNames(); - for (i = 0; i < jointNames.length; i += 1) { - hasHandAndWristJoints = hasHandAndWristJoints || jointNames[i].toLowerCase() === "leftwrist"; - } - - // Set avatar arms vertical, forearms horizontal, as "zero" position for calibration - MyAvatar.setJointRotation("LeftForeArm", Quat.fromPitchYawRollDegrees(0.0, 0.0, 90.0)); - MyAvatar.setJointRotation("LeftHand", Quat.fromPitchYawRollDegrees(0.0, 90.0, 0.0)); - MyAvatar.setJointRotation("RightForeArm", Quat.fromPitchYawRollDegrees(0.0, 0.0, -90.0)); - MyAvatar.setJointRotation("RightHand", Quat.fromPitchYawRollDegrees(0.0, -90.0, 0.0)); - - // Wait for arms to assume their positions before calculating - Script.setTimeout(finishCalibration, CALIBRATION_TIME); - } - - function checkCalibration() { - - if (calibrationStatus === CALIBRATED) { - return true; - } - - if (calibrationStatus !== CALIBRATING) { - calibrate(); - } - - return false; - } - - function setIsOnHMD() { - isOnHMD = Menu.isOptionChecked(LEAP_ON_HMD_MENU_ITEM); - print("Leap Motion: " + (isOnHMD ? "Is on HMD" : "Is on desk")); - } - - function checkSettings() { - if (calibrationStatus > UNCALIBRATED && (MyAvatar.scale !== avatarScale - || MyAvatar.faceModelURL !== avatarFaceModelURL - || MyAvatar.skeletonModelURL !== avatarSkeletonModelURL - || Menu.isOptionChecked(LEAP_ON_HMD_MENU_ITEM) !== isOnHMD)) { - print("Leap Motion: Recalibrate..."); - calibrationStatus = UNCALIBRATED; - - setIsOnHMD(); - } - } - - function setUp() { - - wrists = [ - { - jointName: "LeftWrist", - controller: Controller.createInputController("Spatial", "joint_L_wrist") - }, - { - jointName: "RightWrist", - controller: Controller.createInputController("Spatial", "joint_R_wrist") - } - ]; - - hands = [ - { - jointName: "LeftHand", - controller: Controller.createInputController("Spatial", "joint_L_hand"), - inactiveCount: 0 - }, - { - jointName: "RightHand", - controller: Controller.createInputController("Spatial", "joint_R_hand"), - inactiveCount: 0 - } - ]; - - // The Leap controller's first joint is the hand-metacarpal joint but this joint's data is not used because it's too - // dependent on the model skeleton exactly matching the Leap skeleton; using just the second and subsequent joints - // seems to work better over all. - fingers = [{}, {}]; - fingers[0] = [ - [ - { jointName: "LeftHandThumb1", controller: Controller.createInputController("Spatial", "joint_L_thumb2") }, - { jointName: "LeftHandThumb2", controller: Controller.createInputController("Spatial", "joint_L_thumb3") }, - { jointName: "LeftHandThumb3", controller: Controller.createInputController("Spatial", "joint_L_thumb4") } - ], - [ - { jointName: "LeftHandIndex1", controller: Controller.createInputController("Spatial", "joint_L_index2") }, - { jointName: "LeftHandIndex2", controller: Controller.createInputController("Spatial", "joint_L_index3") }, - { jointName: "LeftHandIndex3", controller: Controller.createInputController("Spatial", "joint_L_index4") } - ], - [ - { jointName: "LeftHandMiddle1", controller: Controller.createInputController("Spatial", "joint_L_middle2") }, - { jointName: "LeftHandMiddle2", controller: Controller.createInputController("Spatial", "joint_L_middle3") }, - { jointName: "LeftHandMiddle3", controller: Controller.createInputController("Spatial", "joint_L_middle4") } - ], - [ - { jointName: "LeftHandRing1", controller: Controller.createInputController("Spatial", "joint_L_ring2") }, - { jointName: "LeftHandRing2", controller: Controller.createInputController("Spatial", "joint_L_ring3") }, - { jointName: "LeftHandRing3", controller: Controller.createInputController("Spatial", "joint_L_ring4") } - ], - [ - { jointName: "LeftHandPinky1", controller: Controller.createInputController("Spatial", "joint_L_pinky2") }, - { jointName: "LeftHandPinky2", controller: Controller.createInputController("Spatial", "joint_L_pinky3") }, - { jointName: "LeftHandPinky3", controller: Controller.createInputController("Spatial", "joint_L_pinky4") } - ] - ]; - fingers[1] = [ - [ - { jointName: "RightHandThumb1", controller: Controller.createInputController("Spatial", "joint_R_thumb2") }, - { jointName: "RightHandThumb2", controller: Controller.createInputController("Spatial", "joint_R_thumb3") }, - { jointName: "RightHandThumb3", controller: Controller.createInputController("Spatial", "joint_R_thumb4") } - ], - [ - { jointName: "RightHandIndex1", controller: Controller.createInputController("Spatial", "joint_R_index2") }, - { jointName: "RightHandIndex2", controller: Controller.createInputController("Spatial", "joint_R_index3") }, - { jointName: "RightHandIndex3", controller: Controller.createInputController("Spatial", "joint_R_index4") } - ], - [ - { jointName: "RightHandMiddle1", controller: Controller.createInputController("Spatial", "joint_R_middle2") }, - { jointName: "RightHandMiddle2", controller: Controller.createInputController("Spatial", "joint_R_middle3") }, - { jointName: "RightHandMiddle3", controller: Controller.createInputController("Spatial", "joint_R_middle4") } - ], - [ - { jointName: "RightHandRing1", controller: Controller.createInputController("Spatial", "joint_R_ring2") }, - { jointName: "RightHandRing2", controller: Controller.createInputController("Spatial", "joint_R_ring3") }, - { jointName: "RightHandRing3", controller: Controller.createInputController("Spatial", "joint_R_ring4") } - ], - [ - { jointName: "RightHandPinky1", controller: Controller.createInputController("Spatial", "joint_R_pinky2") }, - { jointName: "RightHandPinky2", controller: Controller.createInputController("Spatial", "joint_R_pinky3") }, - { jointName: "RightHandPinky3", controller: Controller.createInputController("Spatial", "joint_R_pinky4") } - ] - ]; - - handAnimationStateHandlers = [null, null]; - handAnimationStateFunctions = [animateLeftHand, animateRightHand]; - handAnimationStateProperties = [ - ["leftHandType", "leftHandPosition", "leftHandRotation"], - ["rightHandType", "rightHandPosition", "rightHandPosition"] - ]; - - setIsOnHMD(); - - settingsTimer = Script.setInterval(checkSettings, 2000); - - calibrationStatus = UNCALIBRATED; - - { - var mapping = Controller.newMapping("LeapmotionTrigger"); - mapping.from(getLeapMotionLeftTrigger).to(Controller.Standard.LT); - mapping.from(getLeapMotionRightTrigger).to(Controller.Standard.RT); - mapping.enable(); - } - } - - function moveHands() { - var h, - i, - j, - side, - handOffset, - wristOffset, - handRotation, - locRotation, - cameraOrientation, - inverseAvatarOrientation; - - for (h = 0; h < NUM_HANDS; h += 1) { - side = h === 0 ? -1.0 : 1.0; - - if (hands[h].controller.isActive()) { - - // Calibrate if necessary. - if (!checkCalibration()) { - return; - } - - // Hand animation handlers ... - if (handAnimationStateHandlers[h] === null) { - handAnimationStateHandlers[h] = MyAvatar.addAnimationStateHandler(handAnimationStateFunctions[h], - handAnimationStateProperties[h]); - } - - // Hand position ... - handOffset = hands[h].controller.getAbsTranslation(); - handRotation = hands[h].controller.getAbsRotation(); - - if (isOnHMD) { - - // Adjust to control wrist position if "hand" joint is at wrist ... - if (!hasHandAndWristJoints) { - wristOffset = Vec3.multiplyQbyV(handRotation, handToWristOffset[h]); - handOffset = Vec3.sum(handOffset, wristOffset); - } - - // Hand offset in camera coordinates ... - handOffset = { - x: -handOffset.x, - y: -handOffset.z, - z: -handOffset.y - hands[h].zeroPosition.z - }; - - // Hand offset in world coordinates ... - cameraOrientation = Camera.getOrientation(); - handOffset = Vec3.sum(Camera.getPosition(), Vec3.multiplyQbyV(cameraOrientation, handOffset)); - - // Hand offset in avatar coordinates ... - inverseAvatarOrientation = Quat.inverse(MyAvatar.orientation); - handOffset = Vec3.subtract(handOffset, MyAvatar.position); - handOffset = Vec3.multiplyQbyV(inverseAvatarOrientation, handOffset); - handOffset.z = -handOffset.z; - handOffset.x = -handOffset.x; - - - // Hand rotation in camera coordinates ... - handRotation = { - x: -handRotation.y, - y: -handRotation.z, - z: -handRotation.x, - w: handRotation.w - }; - - // Hand rotation in avatar coordinates ... - handRotation = Quat.multiply(HMD_CAMERA_TO_AVATAR_ROTATION[h], handRotation); - cameraOrientation = { - x: cameraOrientation.z, - y: cameraOrientation.y, - z: cameraOrientation.x, - w: cameraOrientation.w - }; - cameraOrientation = Quat.multiply(cameraOrientation, Quat.inverse(MyAvatar.orientation)); - handRotation = Quat.multiply(handRotation, cameraOrientation); // Works!!! - - } else { - - // Adjust to control wrist position if "hand" joint is at wrist ... - if (!hasHandAndWristJoints) { - wristOffset = Vec3.multiplyQbyV(handRotation, handToWristOffset[h]); - handOffset = Vec3.sum(handOffset, wristOffset); - } - - // Hand offset in camera coordinates ... - handOffset = { - x: -handOffset.x, - y: hands[h].zeroPosition.y + handOffset.y, - z: hands[h].zeroPosition.z - handOffset.z - }; - - // Hand rotation in camera coordinates ... - handRotation = { - x: handRotation.z, - y: handRotation.y, - z: handRotation.x, - w: handRotation.w - }; - - // Hand rotation in avatar coordinates ... - handRotation = Quat.multiply(DESKTOP_CAMERA_TO_AVATAR_ROTATION, handRotation); - } - - // Set hand position and orientation for animation state handler ... - hands[h].position = handOffset; - hands[h].rotation = handRotation; - - // Set finger joints ... - var summed = 0; - var closeAngle = 0; - for (i = 0; i < NUM_FINGERS; i += 1) { - for (j = 0; j < NUM_FINGER_JOINTS; j += 1) { - if (fingers[h][i][j].controller !== null) { - locRotation = fingers[h][i][j].controller.getLocRotation(); - var eulers = Quat.safeEulerAngles(locRotation); - closeAngle += eulers.x; - - summed++; - - if (i === THUMB) { - locRotation = { - x: side * locRotation.y, - y: side * -locRotation.z, - z: side * -locRotation.x, - w: locRotation.w - }; - if (j === 0) { - // Adjust avatar thumb root joint rotation to make avatar hands look better - locRotation = Quat.multiply(LEAP_THUMB_ROOT_ADJUST[h], locRotation); - } - } else { - locRotation = { - x: -locRotation.x, - y: -locRotation.z, - z: -locRotation.y, - w: locRotation.w - }; - } - MyAvatar.setJointRotation(fingers[h][i][j].jointName, locRotation); - } - } - } - - hands[h].inactiveCount = 0; - if (summed > 0) { - closeAngle /= summed; - } - - var triggerValue = (-closeAngle - LEAP_TRIGGER_START_ANGLE) / (LEAP_TRIGGER_END_ANGLE - LEAP_TRIGGER_START_ANGLE); - triggerValue = Math.max(0.0, Math.min(triggerValue, 1.0)); - - if (h == 0) { - leftTriggerValue = triggerValue; - } else { - rightTriggerValue = triggerValue; - - } - - } else { - - if (hands[h].inactiveCount < MAX_HAND_INACTIVE_COUNT) { - - hands[h].inactiveCount += 1; - - if (hands[h].inactiveCount === MAX_HAND_INACTIVE_COUNT) { - if (handAnimationStateHandlers[h] !== null) { - MyAvatar.removeAnimationStateHandler(handAnimationStateHandlers[h]); - handAnimationStateHandlers[h] = null; - leftTriggerValue = 0.0; - rightTriggerValue = 0.0; - } - } - } - } - } - } - - function tearDown() { - var h, - i, - j; - - Script.clearInterval(settingsTimer); - - for (h = 0; h < NUM_HANDS; h += 1) { - Controller.releaseInputController(hands[h].controller); - Controller.releaseInputController(wrists[h].controller); - if (handAnimationStateHandlers[h] !== null) { - MyAvatar.removeAnimationStateHandler(handAnimationStateHandlers[h]); - } - for (i = 0; i < NUM_FINGERS; i += 1) { - for (j = 0; j < NUM_FINGER_JOINTS; j += 1) { - if (fingers[h][i][j].controller !== null) { - Controller.releaseInputController(fingers[h][i][j].controller); - } - } - } - } - } - - return { - printSkeletonJointNames: printSkeletonJointNames, - setUp : setUp, - moveHands : moveHands, - tearDown : tearDown - }; -}()); - - -//leapHands.printSkeletonJointNames(); - -leapHands.setUp(); -Script.update.connect(leapHands.moveHands); -Script.scriptEnding.connect(leapHands.tearDown);