From ac3c9655ec105a72a5b5575d1b2838f52f903cab Mon Sep 17 00:00:00 2001
From: Dante Ruiz <dante@highfidelity.io>
Date: Wed, 24 May 2017 01:28:08 +0100
Subject: [PATCH] added arms

---
 .../resources/avatar/avatar-animation.json    |  18 +++
 interface/resources/controllers/standard.json |   4 +-
 interface/resources/controllers/vive.json     |   4 +-
 interface/src/Application.cpp                 |   4 +
 interface/src/avatar/MyAvatar.cpp             |  42 +++++--
 interface/src/avatar/MyAvatar.h               |  10 ++
 interface/src/avatar/MySkeletonModel.cpp      |  20 ++++
 libraries/animation/src/Rig.cpp               |  11 ++
 libraries/animation/src/Rig.h                 |   6 +
 libraries/controllers/src/controllers/Input.h |   2 +-
 plugins/openvr/src/ViveControllerManager.cpp  | 112 ++++++++++++++----
 plugins/openvr/src/ViveControllerManager.h    |  17 ++-
 12 files changed, 215 insertions(+), 35 deletions(-)

diff --git a/interface/resources/avatar/avatar-animation.json b/interface/resources/avatar/avatar-animation.json
index f88e322d99..7171d4c223 100644
--- a/interface/resources/avatar/avatar-animation.json
+++ b/interface/resources/avatar/avatar-animation.json
@@ -114,6 +114,24 @@
                                     "weightVar": "headWeight",
                                     "weight": 4.0,
                                     "flexCoefficients": [1, 0.05, 0.25, 0.25, 0.25]
+                                },
+                                {
+                                    "jointName": "LeftArm",
+                                    "positionVar": "leftArmPosition",
+                                    "rotationVar": "leftArmRotation",
+                                    "typeVar": "leftArmType",
+                                    "weightVar": "leftArmWeight",
+                                    "weight": 1.0,
+                                    "flexCoefficients": [1, 0.5, 0.25, 0.0, 0.0, 0.0]
+                                },
+                                {
+                                    "jointName": "RightArm",
+                                    "positionVar": "rightArmPosition",
+                                    "rotationVar": "rightArmRotation",
+                                    "typeVar": "rightArmType",
+                                    "weightVar": "rightArmWeight",
+                                    "weight": 1.0,
+                                    "flexCoefficients": [1, 0.5, 0.25, 0.0, 0.0, 0.0]
                                 }
                             ]
                         },
diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json
index 62eec9bc3c..75b4821118 100644
--- a/interface/resources/controllers/standard.json
+++ b/interface/resources/controllers/standard.json
@@ -66,6 +66,8 @@
         { "from": "Standard.Hips", "to": "Actions.Hips" },
         { "from": "Standard.Spine2", "to": "Actions.Spine2" },
 
-        { "from": "Standard.Head", "to": "Actions.Head" }
+        { "from": "Standard.Head", "to": "Actions.Head" },
+        { "from": "Standard.LeftArm", "to": "Actions.LeftArm" },
+        { "from": "Standard.RightArm", "to": "Actions.RightArm" }
     ]
 }
diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json
index 4491507a9c..803914670c 100644
--- a/interface/resources/controllers/vive.json
+++ b/interface/resources/controllers/vive.json
@@ -40,6 +40,8 @@
         { "from": "Vive.RightFoot", "to" : "Standard.RightFoot",  "when": [ "Application.InHMD"] },
         { "from": "Vive.Hips", "to" : "Standard.Hips",  "when": [ "Application.InHMD"] },
         { "from": "Vive.Spine2", "to" : "Standard.Spine2",  "when": [ "Application.InHMD"] },
-        { "from": "Vive.Head", "to" : "Standard.Head", "when" : [ "Application.InHMD"] }
+        { "from": "Vive.Head", "to" : "Standard.Head", "when" : [ "Application.InHMD"] },
+        { "from": "Vive.RightArm", "to" : "Standard.RightArm", "when" : [ "Application.InHMD"] },
+        { "from": "Vive.LeftArm", "to" : "Standard.LeftArm", "when" : [ "Application.InHMD"] }
     ]
 }
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 3dfcbbe2c6..4f38877477 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -4427,6 +4427,10 @@ void Application::update(float deltaTime) {
     controller::Pose headPose = userInputMapper->getPoseState(controller::Action::HEAD);
     myAvatar->setHeadControllerPoseInSensorFrame(headPose.transform(avatarToSensorMatrix));
 
+    controller::Pose leftArmPose = userInputMapper->getPoseState(controller::Action::LEFT_ARM);
+    controller::Pose rightArmPose = userInputMapper->getPoseState(controller::Action::RIGHT_ARM);
+    myAvatar->setArmControllerPosesInSensorFrame(leftArmPose.transform(avatarToSensorMatrix), rightArmPose.transform(avatarToSensorMatrix));
+
     updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
     updateDialogs(deltaTime); // update various stats dialogs if present
 
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 647b7f6f05..9104111712 100755
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -1479,6 +1479,37 @@ controller::Pose MyAvatar::getHeadControllerPoseInAvatarFrame() const {
     return getHeadControllerPoseInWorldFrame().transform(invAvatarMatrix);
 }
 
+void MyAvatar::setArmControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) {
+    _leftArmControllerPoseInSensorFrameCache.set(left);
+    _rightArmControllerPoseInSensorFrameCache.set(right);
+}
+
+controller::Pose MyAvatar::getLeftArmControllerInSensorFrame() const {
+    return _leftArmControllerPoseInSensorFrameCache.get();
+}
+
+controller::Pose MyAvatar::getRightArmControllerInSensorFrame() const {
+    return _rightArmControllerPoseInSensorFrameCache.get();
+}
+
+controller::Pose MyAvatar::getLeftArmControllerInWorldFrame() const {
+    return getLeftArmControllerInSensorFrame().transform(getSensorToWorldMatrix());
+}
+
+controller::Pose MyAvatar::getRightArmControllerInWorldFrame() const {
+    return getRightArmControllerInSensorFrame().transform(getSensorToWorldMatrix());
+}
+
+controller::Pose MyAvatar::getLeftArmControllerInAvatarFrame() const {
+    glm::mat4 worldToAvatarMat = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
+    return getLeftArmControllerInWorldFrame().transform(worldToAvatarMat);
+}
+
+controller::Pose MyAvatar::getRightArmControllerInAvatarFrame() const {
+    glm::mat4 worldToAvatarMat = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
+    return getRightArmControllerInWorldFrame().transform(worldToAvatarMat);
+}
+
 void MyAvatar::updateMotors() {
     _characterController.clearMotors();
     glm::quat motorRotation;
@@ -2794,7 +2825,7 @@ glm::mat4 MyAvatar::getRightFootCalibrationMat() const {
 
 
 glm::mat4 MyAvatar::getRightArmCalibrationMat() const {
-    int rightArmIndex = _rig->indexOfJoint("RightArm");
+    int rightArmIndex = _skeletonModel->getRig().indexOfJoint("RightArm");
     if (rightArmIndex >= 0) {
         auto rightArmPos = getAbsoluteDefaultJointTranslationInObjectFrame(rightArmIndex);
         auto rightArmRot = getAbsoluteDefaultJointRotationInObjectFrame(rightArmIndex);
@@ -2805,8 +2836,7 @@ glm::mat4 MyAvatar::getRightArmCalibrationMat() const {
 }
 
 glm::mat4 MyAvatar::getLeftArmCalibrationMat() const {
-    int leftArmIndex = _rig->indexOfJoint("LeftArm");
-    auto skeleton = _rig->getAnimSkeleton();
+    int leftArmIndex = _skeletonModel->getRig().indexOfJoint("LeftArm");
     if (leftArmIndex >= 0) {
         auto leftArmPos = getAbsoluteDefaultJointTranslationInObjectFrame(leftArmIndex);
         auto leftArmRot = getAbsoluteDefaultJointRotationInObjectFrame(leftArmIndex);
@@ -2817,8 +2847,7 @@ glm::mat4 MyAvatar::getLeftArmCalibrationMat() const {
 }
 
 glm::mat4 MyAvatar::getRightHandCalibrationMat() const {
-    int rightHandIndex = _rig->indexOfJoint("RightHand");
-    auto skeleton = _rig->getAnimSkeleton();
+    int rightHandIndex = _skeletonModel->getRig().indexOfJoint("RightHand");
     if (rightHandIndex >= 0) {
         auto rightHandPos = getAbsoluteDefaultJointTranslationInObjectFrame(rightHandIndex);
         auto rightHandRot = getAbsoluteDefaultJointRotationInObjectFrame(rightHandIndex);
@@ -2829,8 +2858,7 @@ glm::mat4 MyAvatar::getRightHandCalibrationMat() const {
 }
 
 glm::mat4 MyAvatar::getLeftHandCalibrationMat() const {
-    int leftHandIndex = _rig->indexOfJoint("LeftHand");
-    auto skeleton = _rig->getAnimSkeleton();
+    int leftHandIndex = _skeletonModel->getRig().indexOfJoint("LeftHand");
     if (leftHandIndex >= 0) {
         auto leftHandPos = getAbsoluteDefaultJointTranslationInObjectFrame(leftHandIndex);
         auto leftHandRot = getAbsoluteDefaultJointRotationInObjectFrame(leftHandIndex);
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index f896518b08..9bbdbd7b23 100644
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -471,6 +471,14 @@ public:
     controller::Pose getHeadControllerPoseInWorldFrame() const;
     controller::Pose getHeadControllerPoseInAvatarFrame() const;
 
+    void setArmControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right);
+    controller::Pose getLeftArmControllerInSensorFrame() const;
+    controller::Pose getRightArmControllerInSensorFrame() const;
+    controller::Pose getLeftArmControllerInWorldFrame() const;
+    controller::Pose getRightArmControllerInWorldFrame() const;
+    controller::Pose getLeftArmControllerInAvatarFrame() const;
+    controller::Pose getRightArmControllerInAvatarFrame() const;
+
     bool hasDriveInput() const;
 
     Q_INVOKABLE void setCollisionsEnabled(bool enabled);
@@ -741,6 +749,8 @@ private:
     ThreadSafeValueCache<controller::Pose> _hipsControllerPoseInSensorFrameCache{ controller::Pose() };
     ThreadSafeValueCache<controller::Pose> _spine2ControllerPoseInSensorFrameCache{ controller::Pose() };
     ThreadSafeValueCache<controller::Pose> _headControllerPoseInSensorFrameCache{ controller::Pose() };
+    ThreadSafeValueCache<controller::Pose> _leftArmControllerPoseInSensorFrameCache{ controller::Pose() };
+    ThreadSafeValueCache<controller::Pose> _rightArmControllerPoseInSensorFrameCache{ controller::Pose() };
 
     bool _hmdLeanRecenterEnabled = true;
 
diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp
index 828a5f8a01..ea97e3d0fe 100644
--- a/interface/src/avatar/MySkeletonModel.cpp
+++ b/interface/src/avatar/MySkeletonModel.cpp
@@ -92,6 +92,26 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
         headParams.spine2Enabled = false;
     }
 
+    auto avatarRightArmPose = myAvatar->getRightArmControllerInAvatarFrame();
+    if (avatarRightArmPose.isValid()) {
+        glm::mat4 rightArmMat = Matrices::Y_180 * createMatFromQuatAndPos(avatarRightArmPose.getRotation(), avatarRightArmPose.getTranslation());
+        headParams.rightArmPosition = extractTranslation(rightArmMat);
+        headParams.rightArmRotation = glmExtractRotation(rightArmMat);
+        headParams.rightArmEnabled = true;
+    } else {
+        headParams.rightArmEnabled = false;
+    }
+    
+    auto avatarLeftArmPose = myAvatar->getLeftArmControllerInAvatarFrame();
+    if (avatarLeftArmPose.isValid()) {
+        glm::mat4 leftArmMat = Matrices::Y_180 * createMatFromQuatAndPos(avatarLeftArmPose.getRotation(), avatarLeftArmPose.getTranslation());
+        headParams.leftArmPosition = extractTranslation(leftArmMat);
+        headParams.leftArmRotation = glmExtractRotation(leftArmMat);
+        headParams.leftArmEnabled = true;
+    } else {
+        headParams.leftArmEnabled = false;
+    }
+
     headParams.isTalking = head->getTimeWithoutTalking() <= 1.5f;
 
     _rig.updateFromHeadParameters(headParams, deltaTime);
diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp
index 99d2deb323..c74b8c478b 100644
--- a/libraries/animation/src/Rig.cpp
+++ b/libraries/animation/src/Rig.cpp
@@ -1047,6 +1047,17 @@ void Rig::updateFromHeadParameters(const HeadParameters& params, float dt) {
     } else {
         _animVars.set("spine2Type", (int)IKTarget::Type::Unknown);
     }
+
+    if (params.leftArmEnabled) {
+        
+    } else {
+        _animVars.set("leftArmType", (int)IKTarget::Type::Unknown);
+    }
+
+    if (params.rightArmEnabled) {
+    } else {
+        _animVars.set("rightArmType", (int)IKTarget::Type::Unknown);
+    }
 }
 
 void Rig::updateFromEyeParameters(const EyeParameters& params) {
diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h
index 18780d6e64..c9d52d8c72 100644
--- a/libraries/animation/src/Rig.h
+++ b/libraries/animation/src/Rig.h
@@ -46,9 +46,15 @@ public:
         glm::mat4 spine2Matrix = glm::mat4();         // rig space
         glm::quat rigHeadOrientation = glm::quat();   // rig space (-z forward)
         glm::vec3 rigHeadPosition = glm::vec3();      // rig space
+        glm::vec3 rightArmPosition = glm::vec3();     // rig space
+        glm::quat rightArmRotation = glm::quat();     // rig space
+        glm::vec3 leftArmPosition = glm::vec3();      // rig space
+        glm::quat leftArmRotation = glm::quat();      // rig space
         bool hipsEnabled = false;
         bool headEnabled = false;
         bool spine2Enabled = false;
+        bool leftArmEnabled = false;
+        bool rightArmEnabled = false;
         bool isTalking = false;
     };
 
diff --git a/libraries/controllers/src/controllers/Input.h b/libraries/controllers/src/controllers/Input.h
index 7780e91769..b74ad48c6f 100644
--- a/libraries/controllers/src/controllers/Input.h
+++ b/libraries/controllers/src/controllers/Input.h
@@ -25,7 +25,7 @@ struct InputCalibrationData {
     glm::mat4 defaultHips;         // default pose for hips joint in avatar space
     glm::mat4 defaultLeftFoot;     // default pose for leftFoot joint in avatar space
     glm::mat4 defaultRightFoot;    // default pose for rightFoot joint in avatar space
-    glm::mat4 defaultRigtArm;      // default pose for rightArm joint in avatar space
+    glm::mat4 defaultRightArm;     // default pose for rightArm joint in avatar space
     glm::mat4 defaultLeftArm;      // default pose for leftArm joint in avatar space
     glm::mat4 defaultRightHand;    // default pose for rightHand joint in avatar space
     glm::mat4 defaultLeftHand;     // default pose for leftHand joint in avatar space
diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp
index 8fa6f8b9f6..5e509f43b8 100644
--- a/plugins/openvr/src/ViveControllerManager.cpp
+++ b/plugins/openvr/src/ViveControllerManager.cpp
@@ -33,6 +33,7 @@
 
 #include <controllers/StandardControls.h>
 
+
 extern PoseData _nextSimPoseData;
 
 vr::IVRSystem* acquireOpenVrSystem();
@@ -73,10 +74,14 @@ static glm::mat4 computeOffset(glm::mat4 defaultToReferenceMat, glm::mat4 defaul
     return glm::inverse(poseMat) * referenceJointMat;
 }
 
-static bool sortPucksYPosition(std::pair<uint32_t, controller::Pose> firstPuck, std::pair<uint32_t, controller::Pose> secondPuck) {
+static bool sortPucksYPosition(PuckPosePair firstPuck, PuckPosePair secondPuck) {
     return (firstPuck.second.translation.y < secondPuck.second.translation.y);
 }
 
+static bool sortPucksXPosition(PuckPosePair firstPuck, PuckPosePair secondPuck) {
+    return (firstPuck.second.translation.x < secondPuck.second.translation.x);
+}
+
 static QString deviceTrackingResultToString(vr::ETrackingResult trackingResult) {
     QString result;
     auto iterator = TRACKING_RESULT_TO_STRING.find(trackingResult);
@@ -172,6 +177,8 @@ ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : contro
     _configStringMap[Config::Feet] =  QString("Feet");
     _configStringMap[Config::FeetAndHips] =  QString("FeetAndHips");
     _configStringMap[Config::FeetHipsAndChest] =  QString("FeetHipsAndChest");
+    _configStringMap[Config::FeetHipsAndShoulders] = QString("FeetHipsAndShoulders");
+    _configStringMap[Config::FeetAndShoulders] = QString("FeetAndShoulders");
 }
 
 void ViveControllerManager::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
@@ -334,31 +341,25 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr
     std::sort(_validTrackedObjects.begin(), _validTrackedObjects.end(), sortPucksYPosition);
 
     if (_config == Config::Feet) {
-        auto& firstFoot = _validTrackedObjects[FIRST_FOOT];
-        auto& secondFoot = _validTrackedObjects[SECOND_FOOT];
-        controller::Pose& firstFootPose = firstFoot.second;
-        controller::Pose& secondFootPose = secondFoot.second;
-        
-        if (firstFootPose.translation.x < secondFootPose.translation.x) {
-            _jointToPuckMap[controller::LEFT_FOOT] = firstFoot.first;
-            _pucksOffset[firstFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftFoot, firstFootPose);
-            _jointToPuckMap[controller::RIGHT_FOOT] = secondFoot.first;
-            _pucksOffset[secondFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightFoot, secondFootPose);   
-        } else {
-            _jointToPuckMap[controller::LEFT_FOOT] = secondFoot.first;
-            _pucksOffset[secondFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftFoot, secondFootPose);
-            _jointToPuckMap[controller::RIGHT_FOOT] = firstFoot.first;
-            _pucksOffset[firstFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightFoot, firstFootPose);
-        }
+        calibrateFeet(defaultToReferenceMat, inputCalibration);
+    } else if (_config == Config::FeetAndShoulders && puckCount >= MIN_FEET_HIPS_CHEST) {
+        calibrateFeet(defaultToReferenceMat, inputCalibration);
+        int firstShoulder = 2;
+        int secondShoulder = 3;
+        calibrateShoulders(defaultToReferenceMat, inputCalibration, firstShoulder, secondShoulder);
     } else if (_config == Config::FeetAndHips && puckCount >= MIN_FEET_AND_HIPS) {
-        _jointToPuckMap[controller::HIPS] = _validTrackedObjects[HIP].first;
-        _pucksOffset[_validTrackedObjects[HIP].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHips, _validTrackedObjects[HIP].second);
+        calibrateFeet(defaultToReferenceMat, inputCalibration);
+        calibrateHips(defaultToReferenceMat, inputCalibration);
     } else if (_config == Config::FeetHipsAndChest && puckCount >= MIN_FEET_HIPS_CHEST) {
-        _jointToPuckMap[controller::HIPS] = _validTrackedObjects[HIP].first;
-        _pucksOffset[_validTrackedObjects[HIP].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHips, _validTrackedObjects[HIP].second);
-        _jointToPuckMap[controller::SPINE2] = _validTrackedObjects[CHEST].first;
-        _pucksOffset[_validTrackedObjects[CHEST].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultSpine2, _validTrackedObjects[CHEST].second);
+        calibrateFeet(defaultToReferenceMat, inputCalibration);
+        calibrateHips(defaultToReferenceMat, inputCalibration);
+        calibrateChest(defaultToReferenceMat, inputCalibration);
     } else if (_config == Config::FeetHipsAndShoulders && puckCount >= MIN_FEET_HIPS_SHOULDERS){
+        calibrateFeet(defaultToReferenceMat, inputCalibration);
+        calibrateHips(defaultToReferenceMat, inputCalibration);
+        int firstShoulderIndex = 3;
+        int secondShoulderIndex = 4;
+        calibrateShoulders(defaultToReferenceMat, inputCalibration, firstShoulderIndex, secondShoulderIndex);
     } else {
         qDebug() << "Puck Calibration: " << configToString(_config) << " Config Failed: Could not meet the minimal # of pucks";
         uncalibrate();
@@ -615,6 +616,65 @@ void ViveControllerManager::InputDevice::hapticsHelper(float deltaTime, bool lef
     }
 }
 
+void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) {
+    auto& firstFoot = _validTrackedObjects[FIRST_FOOT];
+    auto& secondFoot = _validTrackedObjects[SECOND_FOOT];
+    controller::Pose& firstFootPose = firstFoot.second;
+    controller::Pose& secondFootPose = secondFoot.second;
+    
+    if (firstFootPose.translation.x < secondFootPose.translation.x) {
+        _jointToPuckMap[controller::LEFT_FOOT] = firstFoot.first;
+        _pucksOffset[firstFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftFoot, firstFootPose);
+        _jointToPuckMap[controller::RIGHT_FOOT] = secondFoot.first;
+            _pucksOffset[secondFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightFoot, secondFootPose);   
+    } else {
+        _jointToPuckMap[controller::LEFT_FOOT] = secondFoot.first;
+        _pucksOffset[secondFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftFoot, secondFootPose);
+        _jointToPuckMap[controller::RIGHT_FOOT] = firstFoot.first;
+        _pucksOffset[firstFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightFoot, firstFootPose);
+    }
+}
+
+void ViveControllerManager::InputDevice::calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) {
+    _jointToPuckMap[controller::HIPS] = _validTrackedObjects[HIP].first;
+    _pucksOffset[_validTrackedObjects[HIP].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHips, _validTrackedObjects[HIP].second);
+}
+
+void ViveControllerManager::InputDevice::calibrateChest(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) {
+    _jointToPuckMap[controller::SPINE2] = _validTrackedObjects[CHEST].first;
+    _pucksOffset[_validTrackedObjects[CHEST].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultSpine2, _validTrackedObjects[CHEST].second);
+}
+
+void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration,
+                                                            int firstShoulderIndex, int secondShoulderIndex) {
+    qDebug() << "calibrate shoulders";
+    const PuckPosePair& firstShoulder = _validTrackedObjects[firstShoulderIndex];
+    const PuckPosePair& secondShoulder = _validTrackedObjects[secondShoulderIndex];
+    const controller::Pose& firstShoulderPose = firstShoulder.second;
+    const controller::Pose& secondShoulderPose = secondShoulder.second;
+
+    if (firstShoulderPose.translation.x < secondShoulderPose.translation.x) {
+        _jointToPuckMap[controller::LEFT_ARM] = firstShoulder.first;
+        _pucksOffset[firstShoulder.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftArm, firstShoulder.second);
+        _jointToPuckMap[controller::RIGHT_ARM] = secondShoulder.first;
+        _pucksOffset[secondShoulder.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightArm, secondShoulder.second);
+    } else {
+        _jointToPuckMap[controller::LEFT_ARM] = secondShoulder.first;
+        _pucksOffset[secondShoulder.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftArm, secondShoulder.second);
+        _jointToPuckMap[controller::RIGHT_ARM] = firstShoulder.first;
+        _pucksOffset[firstShoulder.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightArm, firstShoulder.second);
+    }
+}
+
+void ViveControllerManager::InputDevice::calibrateHands(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration,
+                                                        PuckPosePair firstHand, PuckPosePair secondHand) {
+}
+
+void ViveControllerManager::InputDevice::calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) {
+    int headIndex = _validTrackedObjects.size() - 1;
+}
+
+
 void ViveControllerManager::InputDevice::loadSettings() {
     Settings settings;
     settings.beginGroup("PUCK_CONFIG");
@@ -646,6 +706,10 @@ void ViveControllerManager::InputDevice::setConfigFromString(const QString& valu
         _preferedConfig = Config::FeetAndHips;
     } else if (value == "FeetHipsAndChest") {
         _preferedConfig = Config::FeetHipsAndChest;
+    } else if (value == "FeetHipsAndShoulders") {
+        _preferedConfig = Config::FeetHipsAndShoulders;
+    } else if (value == "FeetAndShoulders") {
+        _preferedConfig = Config::FeetAndShoulders;
     }
 }
 
@@ -658,7 +722,7 @@ void ViveControllerManager::InputDevice::createPreferences() {
         auto getter = [this]()->QString { return _configStringMap[_preferedConfig]; };
         auto setter = [this](const QString& value) { setConfigFromString(value); saveSettings(); };
         auto preference = new ComboBoxPreference(VIVE_PUCKS_CONFIG, "Configuration", getter, setter);
-        QStringList list = (QStringList() << "Auto" << "Feet" << "FeetAndHips" << "FeetHipsAndChest");
+        QStringList list = (QStringList() << "Auto" << "Feet" << "FeetAndHips" << "FeetHipsAndChest" << "FeetAndShoulders");
         preference->setItems(list);
         preferences->addPreference(preference);
 
diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h
index 186917de51..1fd11ccfe6 100644
--- a/plugins/openvr/src/ViveControllerManager.h
+++ b/plugins/openvr/src/ViveControllerManager.h
@@ -27,6 +27,8 @@
 #include <render/Scene.h>
 #include "OpenVrHelpers.h"
 
+using PuckPosePair = std::pair<uint32_t, controller::Pose>;
+
 namespace vr {
     class IVRSystem;
 }
@@ -104,10 +106,12 @@ private:
         };
         enum class Config {
             Auto,
+            Head,
             Feet,
             Hands,
             Shoulders,
             FeetAndHips,
+            FeetAndShoulders,
             FeetHipsAndChest,
             FeetHipsAndShoulders,
             FeetHipsChestAndHands,
@@ -118,7 +122,7 @@ private:
         FilteredStick _filteredLeftStick;
         FilteredStick _filteredRightStick;
 
-        std::vector<std::pair<uint32_t, controller::Pose>> _validTrackedObjects;
+        std::vector<PuckPosePair> _validTrackedObjects;
         std::map<uint32_t, glm::mat4> _pucksOffset;
         std::map<int, uint32_t> _jointToPuckMap;
         std::map<Config, QString> _configStringMap;
@@ -145,6 +149,17 @@ private:
         void setConfigFromString(const QString& value);
         void loadSettings();
         void saveSettings() const;
+        void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
+        void calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
+        void calibrateChest(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
+        
+        void calibrateHands(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration,
+                            PuckPosePair firstHand, PuckPosePair secondHand);
+        
+        void calibrateShoulders(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration,
+                                int firstShoulderIndex, int secondShoulderIndex);
+
+        void calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
         friend class ViveControllerManager;
     };