From 22d8adcb9951ab22c3b10693b692c6edb4ee7c23 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 13 Jul 2017 10:39:48 -0700 Subject: [PATCH 01/45] Better shoulder/arm puck calibration. --- .../resources/avatar/avatar-animation.json | 36 +++++----- plugins/openvr/src/ViveControllerManager.cpp | 66 ++++++++++++++----- plugins/openvr/src/ViveControllerManager.h | 3 +- 3 files changed, 71 insertions(+), 34 deletions(-) diff --git a/interface/resources/avatar/avatar-animation.json b/interface/resources/avatar/avatar-animation.json index a493d8e9af..a179f452c1 100644 --- a/interface/resources/avatar/avatar-animation.json +++ b/interface/resources/avatar/avatar-animation.json @@ -61,6 +61,24 @@ "weight": 1.0, "flexCoefficients": [1] }, + { + "jointName": "LeftArm", + "positionVar": "leftArmPosition", + "rotationVar": "leftArmRotation", + "typeVar": "leftArmType", + "weightVar": "leftArmWeight", + "weight": 0.75, + "flexCoefficients": [1.0, 0.35, 0.2, 0.1, 0.05, 0.0, 0.0, 0.0] + }, + { + "jointName": "RightArm", + "positionVar": "rightArmPosition", + "rotationVar": "rightArmRotation", + "typeVar": "rightArmType", + "weightVar": "rightArmWeight", + "weight": 0.75, + "flexCoefficients": [1.0, 0.35, 0.2, 0.1, 0.05, 0.0, 0.0, 0.0] + }, { "jointName": "RightHand", "positionVar": "rightHandPosition", @@ -126,24 +144,6 @@ "weightVar": "headWeight", "weight": 4.0, "flexCoefficients": [1, 0.5, 0.25, 0.2, 0.1] - }, - { - "jointName": "LeftArm", - "positionVar": "leftArmPosition", - "rotationVar": "leftArmRotation", - "typeVar": "leftArmType", - "weightVar": "leftArmWeight", - "weight": 0.75, - "flexCoefficients": [1.0, 0.35, 0.2, 0.1, 0.05, 0.0, 0.0, 0.0] - }, - { - "jointName": "RightArm", - "positionVar": "rightArmPosition", - "rotationVar": "rightArmRotation", - "typeVar": "rightArmType", - "weightVar": "rightArmWeight", - "weight": 0.75, - "flexCoefficients": [1.0, 0.35, 0.2, 0.1, 0.05, 0.0, 0.0, 0.0] } ] }, diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 07b3b2f73d..216d50a4af 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -624,7 +624,8 @@ bool ViveControllerManager::InputDevice::configureBody(glm::mat4& defaultToRefer void ViveControllerManager::InputDevice::uncalibrate() { _config = Config::None; - _pucksOffset.clear(); + _pucksPostOffset.clear(); + _pucksPreOffset.clear(); _jointToPuckMap.clear(); _calibrated = false; _overrideHead = false; @@ -654,10 +655,17 @@ controller::Pose ViveControllerManager::InputDevice::addOffsetToPuckPose(int joi if (puck != _jointToPuckMap.end()) { uint32_t puckIndex = puck->second; auto puckPose = _poseStateMap.find(puckIndex); - auto puckOffset = _pucksOffset.find(puckIndex); + auto puckPostOffset = _pucksPostOffset.find(puckIndex); + auto puckPreOffset = _pucksPreOffset.find(puckIndex); - if ((puckPose != _poseStateMap.end()) && (puckOffset != _pucksOffset.end())) { - return puckPose->second.postTransform(puckOffset->second); + if (puckPose != _poseStateMap.end()) { + if (puckPreOffset != _pucksPreOffset.end() && puckPostOffset != _pucksPostOffset.end()) { + return puckPose->second.postTransform(puckPostOffset->second).transform(puckPreOffset->second); + } else if (puckPostOffset != _pucksPostOffset.end()) { + return puckPose->second.postTransform(puckPostOffset->second); + } else if (puckPreOffset != _pucksPreOffset.end()) { + return puckPose->second.transform(puckPreOffset->second); + } } } return controller::Pose(); @@ -970,7 +978,7 @@ void ViveControllerManager::InputDevice::calibrateLeftHand(glm::mat4& defaultToR glm::mat4 offsetMat = createMatFromQuatAndPos(rotationOffset, translationOffset); _jointToPuckMap[controller::LEFT_HAND] = handPair.first; - _pucksOffset[handPair.first] = offsetMat; + _pucksPostOffset[handPair.first] = offsetMat; } void ViveControllerManager::InputDevice::calibrateRightHand(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair) { @@ -1001,7 +1009,7 @@ void ViveControllerManager::InputDevice::calibrateRightHand(glm::mat4& defaultTo glm::mat4 offsetMat = createMatFromQuatAndPos(rotationOffset, translationOffset); _jointToPuckMap[controller::RIGHT_HAND] = handPair.first; - _pucksOffset[handPair.first] = offsetMat; + _pucksPostOffset[handPair.first] = offsetMat; } @@ -1037,21 +1045,21 @@ void ViveControllerManager::InputDevice::calibrateFoot(glm::mat4& defaultToRefer if (isLeftFoot) { _jointToPuckMap[controller::LEFT_FOOT] = footPair.first; - _pucksOffset[footPair.first] = finalOffset; + _pucksPostOffset[footPair.first] = finalOffset; } else { _jointToPuckMap[controller::RIGHT_FOOT] = footPair.first; - _pucksOffset[footPair.first] = finalOffset; + _pucksPostOffset[footPair.first] = finalOffset; } } 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); + _pucksPostOffset[_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); + _pucksPostOffset[_validTrackedObjects[CHEST].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultSpine2, _validTrackedObjects[CHEST].second); } void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, @@ -1061,16 +1069,44 @@ void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultTo const controller::Pose& firstShoulderPose = firstShoulder.second; const controller::Pose& secondShoulderPose = secondShoulder.second; + glm::mat4 refLeftArm = defaultToReferenceMat * inputCalibration.defaultLeftArm; + glm::mat4 refRightArm = defaultToReferenceMat * inputCalibration.defaultRightArm; + glm::mat4 userRefLeftArm = refLeftArm; + glm::mat4 userRefRightArm = refRightArm; + const float PUCK_TO_USER_REF_ARM_Y_OFFSET = -0.06; + 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); + + // move the userRefArm to the same height as the puck. + userRefLeftArm[3][1] = firstShoulderPose.translation.y + PUCK_TO_USER_REF_ARM_Y_OFFSET; + userRefRightArm[3][1] = secondShoulderPose.translation.y + PUCK_TO_USER_REF_ARM_Y_OFFSET; + + // compute the post offset from the userRefArm + _pucksPostOffset[firstShoulder.first] = computeOffset(Matrices::IDENTITY, userRefLeftArm, firstShoulderPose); + _pucksPostOffset[secondShoulder.first] = computeOffset(Matrices::IDENTITY, userRefRightArm, secondShoulderPose); + + // compute the pre offset from the diff between userRefArm and refArm transforms. + // as an optimization we don't do a full inverse, but subtract the translations. + _pucksPreOffset[firstShoulder.first] = createMatFromQuatAndPos(glm::quat(), extractTranslation(userRefLeftArm) - extractTranslation(refLeftArm)); + _pucksPreOffset[secondShoulder.first] = createMatFromQuatAndPos(glm::quat(), extractTranslation(userRefRightArm) - extractTranslation(refRightArm)); } 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); + + // move the userRefArm to the same height as the puck + userRefLeftArm[3][1] = secondShoulderPose.translation.y + PUCK_TO_USER_REF_ARM_Y_OFFSET; + userRefRightArm[3][1] = firstShoulderPose.translation.y + PUCK_TO_USER_REF_ARM_Y_OFFSET; + + // compute the post offset from the userRefArm + _pucksPostOffset[secondShoulder.first] = computeOffset(Matrices::IDENTITY, userRefLeftArm, secondShoulderPose); + _pucksPostOffset[firstShoulder.first] = computeOffset(Matrices::IDENTITY, userRefRightArm, firstShoulderPose); + + // compute the pre offset from the diff between userRefArm and refArm transforms. + // as an optimization we don't do a full inverse, but subtract the translations. + _pucksPreOffset[secondShoulder.first] = createMatFromQuatAndPos(glm::quat(), extractTranslation(userRefLeftArm) - extractTranslation(refLeftArm)); + _pucksPreOffset[firstShoulder.first] = createMatFromQuatAndPos(glm::quat(), extractTranslation(userRefRightArm) - extractTranslation(refRightArm)); } } @@ -1078,7 +1114,7 @@ void ViveControllerManager::InputDevice::calibrateHead(glm::mat4& defaultToRefer size_t headIndex = _validTrackedObjects.size() - 1; const PuckPosePair& head = _validTrackedObjects[headIndex]; _jointToPuckMap[controller::HEAD] = head.first; - _pucksOffset[head.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHeadMat, head.second); + _pucksPostOffset[head.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHeadMat, head.second); } QString ViveControllerManager::InputDevice::configToString(Config config) { diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index a9bcc7e4e2..d82b895579 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -161,7 +161,8 @@ private: FilteredStick _filteredRightStick; std::vector _validTrackedObjects; - std::map _pucksOffset; + std::map _pucksPostOffset; + std::map _pucksPreOffset; std::map _jointToPuckMap; std::map _configStringMap; PoseData _lastSimPoseData; From fe711ccbe9044fe6b3aaf4e026f472a15feab949 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 13 Jul 2017 15:01:14 -0700 Subject: [PATCH 02/45] Refactored how controller poses are stored in MyAvatar. --- interface/src/Application.cpp | 105 +++++----- interface/src/avatar/AvatarActionHold.cpp | 4 +- interface/src/avatar/MyAvatar.cpp | 228 ++++++---------------- interface/src/avatar/MyAvatar.h | 60 +----- interface/src/avatar/MyHead.cpp | 2 +- interface/src/avatar/MySkeletonModel.cpp | 148 +++++++++----- interface/src/avatar/MySkeletonModel.h | 2 +- 7 files changed, 228 insertions(+), 321 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ddd1870723..9497456677 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4335,10 +4335,9 @@ void Application::updateMyAvatarLookAtPosition() { } } else { // I am not looking at anyone else, so just look forward - auto headPose = myAvatar->getHeadControllerPoseInSensorFrame(); + auto headPose = myAvatar->getControllerPoseInWorldFrame(controller::Action::HEAD); if (headPose.isValid()) { - glm::mat4 worldHeadMat = myAvatar->getSensorToWorldMatrix() * headPose.getMatrix(); - lookAtSpot = transformPoint(worldHeadMat, glm::vec3(0.0f, 0.0f, TREE_SCALE)); + lookAtSpot = transformPoint(headPose.getMatrix(), glm::vec3(0.0f, 0.0f, TREE_SCALE)); } else { lookAtSpot = myAvatar->getHead()->getEyePosition() + (myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); @@ -4750,52 +4749,64 @@ void Application::update(float deltaTime) { myAvatar->setDriveKey(MyAvatar::ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z)); } - controller::Pose leftHandPose = userInputMapper->getPoseState(controller::Action::LEFT_HAND); - controller::Pose rightHandPose = userInputMapper->getPoseState(controller::Action::RIGHT_HAND); - auto myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()); - auto worldToSensorMatrix = glm::inverse(myAvatar->getSensorToWorldMatrix()); - auto avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix; - myAvatar->setHandControllerPosesInSensorFrame(leftHandPose.transform(avatarToSensorMatrix), rightHandPose.transform(avatarToSensorMatrix)); + static std::vector avatarControllerActions = { + controller::Action::LEFT_HAND, + controller::Action::RIGHT_HAND, + controller::Action::LEFT_FOOT, + controller::Action::RIGHT_FOOT, + controller::Action::HIPS, + controller::Action::SPINE2, + controller::Action::HEAD, + controller::Action::LEFT_HAND_THUMB1, + controller::Action::LEFT_HAND_THUMB2, + controller::Action::LEFT_HAND_THUMB3, + controller::Action::LEFT_HAND_THUMB4, + controller::Action::LEFT_HAND_INDEX1, + controller::Action::LEFT_HAND_INDEX2, + controller::Action::LEFT_HAND_INDEX3, + controller::Action::LEFT_HAND_INDEX4, + controller::Action::LEFT_HAND_MIDDLE1, + controller::Action::LEFT_HAND_MIDDLE2, + controller::Action::LEFT_HAND_MIDDLE3, + controller::Action::LEFT_HAND_MIDDLE4, + controller::Action::LEFT_HAND_RING1, + controller::Action::LEFT_HAND_RING2, + controller::Action::LEFT_HAND_RING3, + controller::Action::LEFT_HAND_RING4, + controller::Action::LEFT_HAND_PINKY1, + controller::Action::LEFT_HAND_PINKY2, + controller::Action::LEFT_HAND_PINKY3, + controller::Action::LEFT_HAND_PINKY4, + controller::Action::RIGHT_HAND_THUMB1, + controller::Action::RIGHT_HAND_THUMB2, + controller::Action::RIGHT_HAND_THUMB3, + controller::Action::RIGHT_HAND_THUMB4, + controller::Action::RIGHT_HAND_INDEX1, + controller::Action::RIGHT_HAND_INDEX2, + controller::Action::RIGHT_HAND_INDEX3, + controller::Action::RIGHT_HAND_INDEX4, + controller::Action::RIGHT_HAND_MIDDLE1, + controller::Action::RIGHT_HAND_MIDDLE2, + controller::Action::RIGHT_HAND_MIDDLE3, + controller::Action::RIGHT_HAND_MIDDLE4, + controller::Action::RIGHT_HAND_RING1, + controller::Action::RIGHT_HAND_RING2, + controller::Action::RIGHT_HAND_RING3, + controller::Action::RIGHT_HAND_RING4, + controller::Action::RIGHT_HAND_PINKY1, + controller::Action::RIGHT_HAND_PINKY2, + controller::Action::RIGHT_HAND_PINKY3, + controller::Action::RIGHT_HAND_PINKY4 + }; - // 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) - }; - } + glm::mat4 myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()); + glm::mat4 worldToSensorMatrix = glm::inverse(myAvatar->getSensorToWorldMatrix()); + glm::mat4 avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix; + + for (auto& action : avatarControllerActions) { + controller::Pose pose = userInputMapper->getPoseState(action); + myAvatar->setControllerPoseInSensorFrame(action, pose.transform(avatarToSensorMatrix)); } - 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)); - - controller::Pose hipsPose = userInputMapper->getPoseState(controller::Action::HIPS); - controller::Pose spine2Pose = userInputMapper->getPoseState(controller::Action::SPINE2); - myAvatar->setSpineControllerPosesInSensorFrame(hipsPose.transform(avatarToSensorMatrix), spine2Pose.transform(avatarToSensorMatrix)); - - 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/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index c1d2f903f3..fe5355ff2e 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -134,9 +134,9 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm:: // fetch the hand controller pose controller::Pose pose; if (isRightHand) { - pose = myAvatar->getRightHandControllerPoseInWorldFrame(); + pose = myAvatar->getControllerPoseInWorldFrame(controller::Action::RIGHT_HAND); } else { - pose = myAvatar->getLeftHandControllerPoseInWorldFrame(); + pose = myAvatar->getControllerPoseInWorldFrame(controller::Action::LEFT_HAND); } if (pose.isValid()) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index f6fb87dad9..3b480ac9bf 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -422,7 +422,7 @@ void MyAvatar::update(float deltaTime) { } #ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE - glm::vec3 p = transformPoint(getSensorToWorldMatrix(), getHeadControllerPoseInAvatarFrame() * + glm::vec3 p = transformPoint(getSensorToWorldMatrix(), getControllerPoseInAvatarFrame(controller::Pose::HEAD) * glm::vec3(_headControllerFacingMovingAverage.x, 0.0f, _headControllerFacingMovingAverage.y)); DebugDraw::getInstance().addMarker("facing-avg", getOrientation(), p, glm::vec4(1.0f)); p = transformPoint(getSensorToWorldMatrix(), getHMDSensorPosition() + @@ -657,7 +657,7 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { _hmdSensorPosition = newHmdSensorPosition; _hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix); - auto headPose = _headControllerPoseInSensorFrameCache.get(); + auto headPose = getControllerPoseInSensorFrame(controller::Action::HEAD); if (headPose.isValid()) { _headControllerFacing = getFacingDir2D(headPose.rotation); } else { @@ -753,37 +753,37 @@ void MyAvatar::updateFromTrackers(float deltaTime) { } glm::vec3 MyAvatar::getLeftHandPosition() const { - auto pose = getLeftHandControllerPoseInAvatarFrame(); + auto pose = getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND); return pose.isValid() ? pose.getTranslation() : glm::vec3(0.0f); } glm::vec3 MyAvatar::getRightHandPosition() const { - auto pose = getRightHandControllerPoseInAvatarFrame(); + auto pose = getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND); return pose.isValid() ? pose.getTranslation() : glm::vec3(0.0f); } glm::vec3 MyAvatar::getLeftHandTipPosition() const { const float TIP_LENGTH = 0.3f; - auto pose = getLeftHandControllerPoseInAvatarFrame(); + auto pose = getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND); return pose.isValid() ? pose.getTranslation() * pose.getRotation() + glm::vec3(0.0f, TIP_LENGTH, 0.0f) : glm::vec3(0.0f); } glm::vec3 MyAvatar::getRightHandTipPosition() const { const float TIP_LENGTH = 0.3f; - auto pose = getRightHandControllerPoseInAvatarFrame(); + auto pose = getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND); return pose.isValid() ? pose.getTranslation() * pose.getRotation() + glm::vec3(0.0f, TIP_LENGTH, 0.0f) : glm::vec3(0.0f); } controller::Pose MyAvatar::getLeftHandPose() const { - return getLeftHandControllerPoseInAvatarFrame(); + return getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND); } controller::Pose MyAvatar::getRightHandPose() const { - return getRightHandControllerPoseInAvatarFrame(); + return getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND); } controller::Pose MyAvatar::getLeftHandTipPose() const { - auto pose = getLeftHandControllerPoseInAvatarFrame(); + auto pose = getLeftHandPose(); glm::vec3 tipTrans = getLeftHandTipPosition(); pose.velocity += glm::cross(pose.getAngularVelocity(), pose.getTranslation() - tipTrans); pose.translation = tipTrans; @@ -791,7 +791,7 @@ controller::Pose MyAvatar::getLeftHandTipPose() const { } controller::Pose MyAvatar::getRightHandTipPose() const { - auto pose = getRightHandControllerPoseInAvatarFrame(); + auto pose = getRightHandPose(); glm::vec3 tipTrans = getRightHandTipPosition(); pose.velocity += glm::cross(pose.getAngularVelocity(), pose.getTranslation() - tipTrans); pose.translation = tipTrans; @@ -1425,159 +1425,43 @@ void MyAvatar::rebuildCollisionShape() { _characterController.setLocalBoundingBox(corner, diagonal); } - -void MyAvatar::setHandControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) { - _leftHandControllerPoseInSensorFrameCache.set(left); - _rightHandControllerPoseInSensorFrameCache.set(right); +void MyAvatar::setControllerPoseInSensorFrame(controller::Action action, const controller::Pose& pose) { + std::lock_guard guard(_controllerPoseMapMutex); + auto iter = _controllerPoseMap.find(action); + if (iter != _controllerPoseMap.end()) { + iter->second = pose; + } else { + _controllerPoseMap.insert({ action, pose }); + } } -controller::Pose MyAvatar::getLeftHandControllerPoseInSensorFrame() const { - return _leftHandControllerPoseInSensorFrameCache.get(); +controller::Pose MyAvatar::getControllerPoseInSensorFrame(controller::Action action) const { + std::lock_guard guard(_controllerPoseMapMutex); + auto iter = _controllerPoseMap.find(action); + if (iter != _controllerPoseMap.end()) { + return iter->second; + } else { + return controller::Pose(); // invalid pose + } } -controller::Pose MyAvatar::getRightHandControllerPoseInSensorFrame() const { - return _rightHandControllerPoseInSensorFrameCache.get(); +controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action action) const { + auto pose = getControllerPoseInSensorFrame(action); + if (pose.valid) { + return pose.transform(getSensorToWorldMatrix()); + } else { + return controller::Pose(); // invalid pose + } } -controller::Pose MyAvatar::getLeftHandControllerPoseInWorldFrame() const { - return _leftHandControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix()); -} - -controller::Pose MyAvatar::getRightHandControllerPoseInWorldFrame() const { - return _rightHandControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix()); -} - -controller::Pose MyAvatar::getLeftHandControllerPoseInAvatarFrame() const { - glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); - return getLeftHandControllerPoseInWorldFrame().transform(invAvatarMatrix); -} - -controller::Pose MyAvatar::getRightHandControllerPoseInAvatarFrame() const { - glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); - 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); -} - -controller::Pose MyAvatar::getLeftFootControllerPoseInSensorFrame() const { - return _leftFootControllerPoseInSensorFrameCache.get(); -} - -controller::Pose MyAvatar::getRightFootControllerPoseInSensorFrame() const { - return _rightFootControllerPoseInSensorFrameCache.get(); -} - -controller::Pose MyAvatar::getLeftFootControllerPoseInWorldFrame() const { - return _leftFootControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix()); -} - -controller::Pose MyAvatar::getRightFootControllerPoseInWorldFrame() const { - return _rightFootControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix()); -} - -controller::Pose MyAvatar::getLeftFootControllerPoseInAvatarFrame() const { - glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); - return getLeftFootControllerPoseInWorldFrame().transform(invAvatarMatrix); -} - -controller::Pose MyAvatar::getRightFootControllerPoseInAvatarFrame() const { - glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); - return getRightFootControllerPoseInWorldFrame().transform(invAvatarMatrix); -} - -void MyAvatar::setSpineControllerPosesInSensorFrame(const controller::Pose& hips, const controller::Pose& spine2) { - _hipsControllerPoseInSensorFrameCache.set(hips); - _spine2ControllerPoseInSensorFrameCache.set(spine2); -} - -controller::Pose MyAvatar::getHipsControllerPoseInSensorFrame() const { - return _hipsControllerPoseInSensorFrameCache.get(); -} - -controller::Pose MyAvatar::getSpine2ControllerPoseInSensorFrame() const { - return _spine2ControllerPoseInSensorFrameCache.get(); -} - -controller::Pose MyAvatar::getHipsControllerPoseInWorldFrame() const { - return _hipsControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix()); -} - -controller::Pose MyAvatar::getSpine2ControllerPoseInWorldFrame() const { - return _spine2ControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix()); -} - -controller::Pose MyAvatar::getHipsControllerPoseInAvatarFrame() const { - glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); - return getHipsControllerPoseInWorldFrame().transform(invAvatarMatrix); -} - -controller::Pose MyAvatar::getSpine2ControllerPoseInAvatarFrame() const { - glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); - return getSpine2ControllerPoseInWorldFrame().transform(invAvatarMatrix); -} - -void MyAvatar::setHeadControllerPoseInSensorFrame(const controller::Pose& head) { - _headControllerPoseInSensorFrameCache.set(head); -} - -controller::Pose MyAvatar::getHeadControllerPoseInSensorFrame() const { - return _headControllerPoseInSensorFrameCache.get(); -} - -controller::Pose MyAvatar::getHeadControllerPoseInWorldFrame() const { - return _headControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix()); -} - -controller::Pose MyAvatar::getHeadControllerPoseInAvatarFrame() const { - glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); - return getHeadControllerPoseInWorldFrame().transform(invAvatarMatrix); -} - -void MyAvatar::setArmControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) { - _leftArmControllerPoseInSensorFrameCache.set(left); - _rightArmControllerPoseInSensorFrameCache.set(right); -} - -controller::Pose MyAvatar::getLeftArmControllerPoseInSensorFrame() const { - return _leftArmControllerPoseInSensorFrameCache.get(); -} - -controller::Pose MyAvatar::getRightArmControllerPoseInSensorFrame() const { - return _rightArmControllerPoseInSensorFrameCache.get(); -} - -controller::Pose MyAvatar::getLeftArmControllerPoseInWorldFrame() const { - return getLeftArmControllerPoseInSensorFrame().transform(getSensorToWorldMatrix()); -} - -controller::Pose MyAvatar::getRightArmControllerPoseInWorldFrame() const { - return getRightArmControllerPoseInSensorFrame().transform(getSensorToWorldMatrix()); -} - -controller::Pose MyAvatar::getLeftArmControllerPoseInAvatarFrame() const { - glm::mat4 worldToAvatarMat = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); - return getLeftArmControllerPoseInWorldFrame().transform(worldToAvatarMat); -} - -controller::Pose MyAvatar::getRightArmControllerPoseInAvatarFrame() const { - glm::mat4 worldToAvatarMat = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); - return getRightArmControllerPoseInWorldFrame().transform(worldToAvatarMat); +controller::Pose MyAvatar::getControllerPoseInAvatarFrame(controller::Action action) const { + auto pose = getControllerPoseInWorldFrame(action); + if (pose.valid) { + glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); + return pose.transform(invAvatarMatrix); + } else { + return controller::Pose(); // invalid pose + } } void MyAvatar::updateMotors() { @@ -1635,7 +1519,7 @@ void MyAvatar::prepareForPhysicsSimulation() { _characterController.setParentVelocity(parentVelocity); _characterController.setPositionAndOrientation(getPosition(), getOrientation()); - auto headPose = getHeadControllerPoseInAvatarFrame(); + auto headPose = getControllerPoseInAvatarFrame(controller::Action::HEAD); if (headPose.isValid()) { _follow.prePhysicsUpdate(*this, deriveBodyFromHMDSensor(), _bodySensorMatrix, hasDriveInput()); } else { @@ -1869,8 +1753,8 @@ void MyAvatar::postUpdate(float deltaTime) { } if (_enableDebugDrawHandControllers) { - auto leftHandPose = getLeftHandControllerPoseInWorldFrame(); - auto rightHandPose = getRightHandControllerPoseInWorldFrame(); + auto leftHandPose = getControllerPoseInWorldFrame(controller::Action::LEFT_HAND); + auto rightHandPose = getControllerPoseInWorldFrame(controller::Action::RIGHT_HAND); if (leftHandPose.isValid()) { DebugDraw::getInstance().addMarker("leftHandController", leftHandPose.getRotation(), leftHandPose.getTranslation(), glm::vec4(1)); @@ -2023,7 +1907,7 @@ void MyAvatar::updateOrientation(float deltaTime) { getHead()->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime); - auto headPose = getHeadControllerPoseInAvatarFrame(); + auto headPose = getControllerPoseInAvatarFrame(controller::Action::HEAD); if (headPose.isValid()) { glm::quat localOrientation = headPose.rotation * Quaternions::Y_180; // these angles will be in radians @@ -2641,10 +2525,10 @@ bool MyAvatar::isDriveKeyDisabled(DriveKeys key) const { glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { glm::vec3 headPosition; glm::quat headOrientation; - auto headPose = getHeadControllerPoseInSensorFrame(); + auto headPose = getControllerPoseInSensorFrame(controller::Action::HEAD); if (headPose.isValid()) { - headPosition = getHeadControllerPoseInSensorFrame().translation; - headOrientation = getHeadControllerPoseInSensorFrame().rotation * Quaternions::Y_180; + headPosition = headPose.translation; + headOrientation = headPose.rotation * Quaternions::Y_180; } const glm::quat headOrientationYawOnly = cancelOutRollAndPitch(headOrientation); @@ -2953,19 +2837,19 @@ glm::quat MyAvatar::getAbsoluteJointRotationInObjectFrame(int index) const { switch (index) { case CONTROLLER_LEFTHAND_INDEX: { - return getLeftHandControllerPoseInAvatarFrame().getRotation(); + return getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).getRotation(); } case CONTROLLER_RIGHTHAND_INDEX: { - return getRightHandControllerPoseInAvatarFrame().getRotation(); + return getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).getRotation(); } case CAMERA_RELATIVE_CONTROLLER_LEFTHAND_INDEX: { - auto pose = _leftHandControllerPoseInSensorFrameCache.get(); + auto pose = getControllerPoseInSensorFrame(controller::Action::LEFT_HAND); glm::mat4 controllerSensorMatrix = createMatFromQuatAndPos(pose.rotation, pose.translation); glm::mat4 result = computeCameraRelativeHandControllerMatrix(controllerSensorMatrix); return glmExtractRotation(result); } case CAMERA_RELATIVE_CONTROLLER_RIGHTHAND_INDEX: { - auto pose = _rightHandControllerPoseInSensorFrameCache.get(); + auto pose = getControllerPoseInSensorFrame(controller::Action::RIGHT_HAND); glm::mat4 controllerSensorMatrix = createMatFromQuatAndPos(pose.rotation, pose.translation); glm::mat4 result = computeCameraRelativeHandControllerMatrix(controllerSensorMatrix); return glmExtractRotation(result); @@ -2990,19 +2874,19 @@ glm::vec3 MyAvatar::getAbsoluteJointTranslationInObjectFrame(int index) const { switch (index) { case CONTROLLER_LEFTHAND_INDEX: { - return getLeftHandControllerPoseInAvatarFrame().getTranslation(); + return getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).getTranslation(); } case CONTROLLER_RIGHTHAND_INDEX: { - return getRightHandControllerPoseInAvatarFrame().getTranslation(); + return getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).getTranslation(); } case CAMERA_RELATIVE_CONTROLLER_LEFTHAND_INDEX: { - auto pose = _leftHandControllerPoseInSensorFrameCache.get(); + auto pose = getControllerPoseInSensorFrame(controller::Action::LEFT_HAND); glm::mat4 controllerSensorMatrix = createMatFromQuatAndPos(pose.rotation, pose.translation); glm::mat4 result = computeCameraRelativeHandControllerMatrix(controllerSensorMatrix); return extractTranslation(result); } case CAMERA_RELATIVE_CONTROLLER_RIGHTHAND_INDEX: { - auto pose = _rightHandControllerPoseInSensorFrameCache.get(); + auto pose = getControllerPoseInSensorFrame(controller::Action::RIGHT_HAND); glm::mat4 controllerSensorMatrix = createMatFromQuatAndPos(pose.rotation, pose.translation); glm::mat4 result = computeCameraRelativeHandControllerMatrix(controllerSensorMatrix); return extractTranslation(result); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 4d599230fb..9b9f841ff0 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -466,49 +466,12 @@ public: virtual void rebuildCollisionShape() override; - void setHandControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right); - controller::Pose getLeftHandControllerPoseInSensorFrame() const; - controller::Pose getRightHandControllerPoseInSensorFrame() const; - controller::Pose getLeftHandControllerPoseInWorldFrame() const; - controller::Pose getRightHandControllerPoseInWorldFrame() const; - 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; - controller::Pose getLeftFootControllerPoseInWorldFrame() const; - controller::Pose getRightFootControllerPoseInWorldFrame() const; - controller::Pose getLeftFootControllerPoseInAvatarFrame() const; - controller::Pose getRightFootControllerPoseInAvatarFrame() const; - - void setSpineControllerPosesInSensorFrame(const controller::Pose& hips, const controller::Pose& spine2); - controller::Pose getHipsControllerPoseInSensorFrame() const; - controller::Pose getSpine2ControllerPoseInSensorFrame() const; - controller::Pose getHipsControllerPoseInWorldFrame() const; - controller::Pose getSpine2ControllerPoseInWorldFrame() const; - controller::Pose getHipsControllerPoseInAvatarFrame() const; - controller::Pose getSpine2ControllerPoseInAvatarFrame() const; - - void setHeadControllerPoseInSensorFrame(const controller::Pose& head); - controller::Pose getHeadControllerPoseInSensorFrame() const; - controller::Pose getHeadControllerPoseInWorldFrame() const; - controller::Pose getHeadControllerPoseInAvatarFrame() const; const glm::vec2& getHeadControllerFacingMovingAverage() const { return _headControllerFacingMovingAverage; } - - void setArmControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right); - controller::Pose getLeftArmControllerPoseInSensorFrame() const; - controller::Pose getRightArmControllerPoseInSensorFrame() const; - controller::Pose getLeftArmControllerPoseInWorldFrame() const; - controller::Pose getRightArmControllerPoseInWorldFrame() const; - controller::Pose getLeftArmControllerPoseInAvatarFrame() const; - controller::Pose getRightArmControllerPoseInAvatarFrame() const; + void setControllerPoseInSensorFrame(controller::Action action, const controller::Pose& pose); + controller::Pose getControllerPoseInSensorFrame(controller::Action action) const; + controller::Pose getControllerPoseInWorldFrame(controller::Action action) const; + controller::Pose getControllerPoseInAvatarFrame(controller::Action action) const; bool hasDriveInput() const; @@ -794,18 +757,9 @@ private: bool _hoverReferenceCameraFacingIsCaptured { false }; glm::vec3 _hoverReferenceCameraFacing { 0.0f, 0.0f, -1.0f }; // hmd sensor space - // These are stored in SENSOR frame - ThreadSafeValueCache _leftHandControllerPoseInSensorFrameCache { controller::Pose() }; - ThreadSafeValueCache _rightHandControllerPoseInSensorFrameCache { 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() }; + // all poses are in sensor-frame + std::unordered_map _controllerPoseMap; + mutable std::mutex _controllerPoseMapMutex; bool _hmdLeanRecenterEnabled = true; AnimPose _prePhysicsRoomPose; diff --git a/interface/src/avatar/MyHead.cpp b/interface/src/avatar/MyHead.cpp index 9f2d080cd6..40d1fa3ffc 100644 --- a/interface/src/avatar/MyHead.cpp +++ b/interface/src/avatar/MyHead.cpp @@ -34,7 +34,7 @@ glm::quat MyHead::getHeadOrientation() const { // always the same. MyAvatar* myAvatar = static_cast(_owningAvatar); - auto headPose = myAvatar->getHeadControllerPoseInWorldFrame(); + auto headPose = myAvatar->getControllerPoseInWorldFrame(controller::Action::HEAD); if (headPose.isValid()) { return headPose.rotation * Quaternions::Y_180; } diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 97309d9678..f97b20dcca 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -46,13 +46,14 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { } MyAvatar* myAvatar = static_cast(_owningAvatar); + assert(myAvatar); Rig::ControllerParameters params; AnimPose avatarToRigPose(glm::vec3(1.0f), Quaternions::Y_180, glm::vec3(0.0f)); // input action is the highest priority source for head orientation. - auto avatarHeadPose = myAvatar->getHeadControllerPoseInAvatarFrame(); + auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); if (avatarHeadPose.isValid()) { AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); params.controllerPoses[Rig::ControllerType_Head] = avatarToRigPose * pose; @@ -67,7 +68,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.controllerActiveFlags[Rig::ControllerType_Head] = false; } - auto avatarHipsPose = myAvatar->getHipsControllerPoseInAvatarFrame(); + auto avatarHipsPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HIPS); if (avatarHipsPose.isValid()) { AnimPose pose(avatarHipsPose.getRotation(), avatarHipsPose.getTranslation()); params.controllerPoses[Rig::ControllerType_Hips] = avatarToRigPose * pose; @@ -77,7 +78,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.controllerActiveFlags[Rig::ControllerType_Hips] = false; } - auto avatarSpine2Pose = myAvatar->getSpine2ControllerPoseInAvatarFrame(); + auto avatarSpine2Pose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::SPINE2); if (avatarSpine2Pose.isValid()) { AnimPose pose(avatarSpine2Pose.getRotation(), avatarSpine2Pose.getTranslation()); params.controllerPoses[Rig::ControllerType_Spine2] = avatarToRigPose * pose; @@ -87,7 +88,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.controllerActiveFlags[Rig::ControllerType_Spine2] = false; } - auto avatarRightArmPose = myAvatar->getRightArmControllerPoseInAvatarFrame(); + auto avatarRightArmPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_ARM); if (avatarRightArmPose.isValid()) { AnimPose pose(avatarRightArmPose.getRotation(), avatarRightArmPose.getTranslation()); params.controllerPoses[Rig::ControllerType_RightArm] = avatarToRigPose * pose; @@ -96,8 +97,8 @@ 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(); + + auto avatarLeftArmPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_ARM); if (avatarLeftArmPose.isValid()) { AnimPose pose(avatarLeftArmPose.getRotation(), avatarLeftArmPose.getTranslation()); params.controllerPoses[Rig::ControllerType_LeftArm] = avatarToRigPose * pose; @@ -107,7 +108,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.controllerActiveFlags[Rig::ControllerType_LeftArm] = false; } - auto avatarLeftHandPose = myAvatar->getLeftHandControllerPoseInAvatarFrame(); + auto avatarLeftHandPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND); if (avatarLeftHandPose.isValid()) { AnimPose pose(avatarLeftHandPose.getRotation(), avatarLeftHandPose.getTranslation()); params.controllerPoses[Rig::ControllerType_LeftHand] = avatarToRigPose * pose; @@ -117,7 +118,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.controllerActiveFlags[Rig::ControllerType_LeftHand] = false; } - auto avatarRightHandPose = myAvatar->getRightHandControllerPoseInAvatarFrame(); + auto avatarRightHandPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND); if (avatarRightHandPose.isValid()) { AnimPose pose(avatarRightHandPose.getRotation(), avatarRightHandPose.getTranslation()); params.controllerPoses[Rig::ControllerType_RightHand] = avatarToRigPose * pose; @@ -127,7 +128,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.controllerActiveFlags[Rig::ControllerType_RightHand] = false; } - auto avatarLeftFootPose = myAvatar->getLeftFootControllerPoseInAvatarFrame(); + auto avatarLeftFootPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_FOOT); if (avatarLeftFootPose.isValid()) { AnimPose pose(avatarLeftFootPose.getRotation(), avatarLeftFootPose.getTranslation()); params.controllerPoses[Rig::ControllerType_LeftFoot] = avatarToRigPose * pose; @@ -137,7 +138,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.controllerActiveFlags[Rig::ControllerType_LeftFoot] = false; } - auto avatarRightFootPose = myAvatar->getRightFootControllerPoseInAvatarFrame(); + auto avatarRightFootPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_FOOT); if (avatarRightFootPose.isValid()) { AnimPose pose(avatarRightFootPose.getRotation(), avatarRightFootPose.getTranslation()); params.controllerPoses[Rig::ControllerType_RightFoot] = avatarToRigPose * pose; @@ -175,49 +176,106 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig.updateFromEyeParameters(eyeParams); - updateFingers(myAvatar->getLeftHandFingerControllerPosesInSensorFrame()); - updateFingers(myAvatar->getRightHandFingerControllerPosesInSensorFrame()); + updateFingers(); } -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; +void MySkeletonModel::updateFingers() { 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; + static std::vector>> fingerChains = { + { + { controller::Action::LEFT_HAND, "LeftHand" }, + { controller::Action::LEFT_HAND_THUMB1, "LeftHandThumb1" }, + { controller::Action::LEFT_HAND_THUMB2, "LeftHandThumb2" }, + { controller::Action::LEFT_HAND_THUMB3, "LeftHandThumb3" }, + { controller::Action::LEFT_HAND_THUMB4, "LeftHandThumb4" } + }, + { + { controller::Action::LEFT_HAND, "LeftHand" }, + { controller::Action::LEFT_HAND_INDEX1, "LeftHandIndex1" }, + { controller::Action::LEFT_HAND_INDEX2, "LeftHandIndex2" }, + { controller::Action::LEFT_HAND_INDEX3, "LeftHandIndex3" }, + { controller::Action::LEFT_HAND_INDEX4, "LeftHandIndex4" } + }, + { + { controller::Action::LEFT_HAND, "LeftHand" }, + { controller::Action::LEFT_HAND_MIDDLE1, "LeftHandMiddle1" }, + { controller::Action::LEFT_HAND_MIDDLE2, "LeftHandMiddle2" }, + { controller::Action::LEFT_HAND_MIDDLE3, "LeftHandMiddle3" }, + { controller::Action::LEFT_HAND_MIDDLE4, "LeftHandMiddle4" } + }, + { + { controller::Action::LEFT_HAND, "LeftHand" }, + { controller::Action::LEFT_HAND_RING1, "LeftHandRing1" }, + { controller::Action::LEFT_HAND_RING2, "LeftHandRing2" }, + { controller::Action::LEFT_HAND_RING3, "LeftHandRing3" }, + { controller::Action::LEFT_HAND_RING4, "LeftHandRing4" } + }, + { + { controller::Action::LEFT_HAND, "LeftHand" }, + { controller::Action::LEFT_HAND_PINKY1, "LeftHandPinky1" }, + { controller::Action::LEFT_HAND_PINKY2, "LeftHandPinky2" }, + { controller::Action::LEFT_HAND_PINKY3, "LeftHandPinky3" }, + { controller::Action::LEFT_HAND_PINKY4, "LeftHandPinky4" } + }, + { + { controller::Action::RIGHT_HAND, "RightHand" }, + { controller::Action::RIGHT_HAND_THUMB1, "RightHandThumb1" }, + { controller::Action::RIGHT_HAND_THUMB2, "RightHandThumb2" }, + { controller::Action::RIGHT_HAND_THUMB3, "RightHandThumb3" }, + { controller::Action::RIGHT_HAND_THUMB4, "RightHandThumb4" } + }, + { + { controller::Action::RIGHT_HAND, "RightHand" }, + { controller::Action::RIGHT_HAND_INDEX1, "RightHandIndex1" }, + { controller::Action::RIGHT_HAND_INDEX2, "RightHandIndex2" }, + { controller::Action::RIGHT_HAND_INDEX3, "RightHandIndex3" }, + { controller::Action::RIGHT_HAND_INDEX4, "RightHandIndex4" } + }, + { + { controller::Action::RIGHT_HAND, "RightHand" }, + { controller::Action::RIGHT_HAND_MIDDLE1, "RightHandMiddle1" }, + { controller::Action::RIGHT_HAND_MIDDLE2, "RightHandMiddle2" }, + { controller::Action::RIGHT_HAND_MIDDLE3, "RightHandMiddle3" }, + { controller::Action::RIGHT_HAND_MIDDLE4, "RightHandMiddle4" } + }, + { + { controller::Action::RIGHT_HAND, "RightHand" }, + { controller::Action::RIGHT_HAND_RING1, "RightHandRing1" }, + { controller::Action::RIGHT_HAND_RING2, "RightHandRing2" }, + { controller::Action::RIGHT_HAND_RING3, "RightHandRing3" }, + { controller::Action::RIGHT_HAND_RING4, "RightHandRing4" } + }, + { + { controller::Action::RIGHT_HAND, "RightHand" }, + { controller::Action::RIGHT_HAND_PINKY1, "RightHandPinky1" }, + { controller::Action::RIGHT_HAND_PINKY2, "RightHandPinky2" }, + { controller::Action::RIGHT_HAND_PINKY3, "RightHandPinky3" }, + { controller::Action::RIGHT_HAND_PINKY4, "RightHandPinky4" } } + }; - 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)); + const float CONTROLLER_PRIORITY = 2.0f; + + for (auto& chain : fingerChains) { + glm::quat prevAbsRot = Quaternions::IDENTITY; + for (auto& link : chain) { + int index = _rig.indexOfJoint(link.second); + if (index >= 0) { + auto pose = myAvatar->getControllerPoseInSensorFrame(link.first); + if (pose.valid) { + glm::quat relRot = glm::inverse(prevAbsRot) * pose.getRotation(); + // only set the rotation for the finger joints, not the hands. + if (link.first != controller::Action::LEFT_HAND && link.first != controller::Action::RIGHT_HAND) { + _rig.setJointRotation(index, true, relRot, CONTROLLER_PRIORITY); + } + prevAbsRot = pose.getRotation(); + } else { + _rig.clearJointAnimationPriority(index); + } + } } - - posesMapItr++; } } diff --git a/interface/src/avatar/MySkeletonModel.h b/interface/src/avatar/MySkeletonModel.h index 6867c596af..ad0ae1b8e9 100644 --- a/interface/src/avatar/MySkeletonModel.h +++ b/interface/src/avatar/MySkeletonModel.h @@ -24,7 +24,7 @@ public: void updateRig(float deltaTime, glm::mat4 parentTransform) override; private: - void updateFingers(const MyAvatar::FingerPosesMap& fingerPoses); + void updateFingers(); }; #endif // hifi_MySkeletonModel_h From 2e0bc36cfda23a12cc22691ae0e68b425cd1ed1a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 13 Jul 2017 15:02:05 -0700 Subject: [PATCH 03/45] warning fix + whitespace --- plugins/openvr/src/ViveControllerManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 216d50a4af..ddafa5cb5b 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -716,7 +716,7 @@ void ViveControllerManager::InputDevice::handleHandController(float deltaTime, u // pseudo buttons the depend on both of the above for-loops partitionTouchpad(controller::LS, controller::LX, controller::LY, controller::LS_CENTER, controller::LS_X, controller::LS_Y); partitionTouchpad(controller::RS, controller::RX, controller::RY, controller::RS_CENTER, controller::RS_X, controller::RS_Y); - } + } } } @@ -1073,7 +1073,7 @@ void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultTo glm::mat4 refRightArm = defaultToReferenceMat * inputCalibration.defaultRightArm; glm::mat4 userRefLeftArm = refLeftArm; glm::mat4 userRefRightArm = refRightArm; - const float PUCK_TO_USER_REF_ARM_Y_OFFSET = -0.06; + const float PUCK_TO_USER_REF_ARM_Y_OFFSET = -0.06f; if (firstShoulderPose.translation.x < secondShoulderPose.translation.x) { _jointToPuckMap[controller::LEFT_ARM] = firstShoulder.first; From c85e187c6174c9894a660b433464519db1e0ccad Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 13 Jul 2017 18:12:33 -0700 Subject: [PATCH 04/45] first stab at secondary target pose support --- interface/src/Application.cpp | 16 ++- interface/src/avatar/MySkeletonModel.cpp | 132 +++++++----------- .../animation/src/AnimInverseKinematics.cpp | 31 ++++ .../animation/src/AnimInverseKinematics.h | 6 + libraries/animation/src/Rig.cpp | 71 ++++++---- libraries/animation/src/Rig.h | 43 ++++-- .../controllers/src/controllers/Actions.h | 14 +- 7 files changed, 190 insertions(+), 123 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9497456677..37001b55bb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4749,7 +4749,7 @@ void Application::update(float deltaTime) { myAvatar->setDriveKey(MyAvatar::ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z)); } - static std::vector avatarControllerActions = { + static const std::vector avatarControllerActions = { controller::Action::LEFT_HAND, controller::Action::RIGHT_HAND, controller::Action::LEFT_FOOT, @@ -4796,7 +4796,19 @@ void Application::update(float deltaTime) { controller::Action::RIGHT_HAND_PINKY1, controller::Action::RIGHT_HAND_PINKY2, controller::Action::RIGHT_HAND_PINKY3, - controller::Action::RIGHT_HAND_PINKY4 + controller::Action::RIGHT_HAND_PINKY4, + controller::Action::LEFT_ARM, + controller::Action::RIGHT_ARM, + controller::Action::LEFT_SHOULDER, + controller::Action::RIGHT_SHOULDER, + controller::Action::LEFT_FORE_ARM, + controller::Action::RIGHT_FORE_ARM, + controller::Action::LEFT_LEG, + controller::Action::RIGHT_LEG, + controller::Action::LEFT_UP_LEG, + controller::Action::RIGHT_UP_LEG, + controller::Action::LEFT_TOE_BASE, + controller::Action::RIGHT_TOE_BASE }; glm::mat4 myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()); diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index f97b20dcca..4a86e0ce0a 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -56,96 +56,72 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); if (avatarHeadPose.isValid()) { AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); - params.controllerPoses[Rig::ControllerType_Head] = avatarToRigPose * pose; - params.controllerActiveFlags[Rig::ControllerType_Head] = true; + params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = avatarToRigPose * pose; + params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Head] = true; } else { // even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and // down in desktop mode. // preMult 180 is necessary to convert from avatar to rig coordinates. // postMult 180 is necessary to convert head from -z forward to z forward. glm::quat headRot = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180; - params.controllerPoses[Rig::ControllerType_Head] = AnimPose(glm::vec3(1.0f), headRot, glm::vec3(0.0f)); - params.controllerActiveFlags[Rig::ControllerType_Head] = false; + params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = AnimPose(glm::vec3(1.0f), headRot, glm::vec3(0.0f)); + params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Head] = false; } - auto avatarHipsPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HIPS); - if (avatarHipsPose.isValid()) { - AnimPose pose(avatarHipsPose.getRotation(), avatarHipsPose.getTranslation()); - params.controllerPoses[Rig::ControllerType_Hips] = avatarToRigPose * pose; - params.controllerActiveFlags[Rig::ControllerType_Hips] = true; - } else { - params.controllerPoses[Rig::ControllerType_Hips] = AnimPose::identity; - params.controllerActiveFlags[Rig::ControllerType_Hips] = false; + // + // primary controller poses, control IK targets directly. + // + + static const std::vector> primaryControllers = { + { controller::Action::LEFT_HAND, Rig::PrimaryControllerType_LeftHand }, + { controller::Action::RIGHT_HAND, Rig::PrimaryControllerType_RightHand }, + { controller::Action::HIPS, Rig::PrimaryControllerType_Hips }, + { controller::Action::LEFT_FOOT, Rig::PrimaryControllerType_LeftFoot }, + { controller::Action::RIGHT_FOOT, Rig::PrimaryControllerType_RightFoot }, + { controller::Action::SPINE2, Rig::PrimaryControllerType_Spine2 } + }; + + for (auto pair : primaryControllers) { + auto pose = myAvatar->getControllerPoseInAvatarFrame(pair.first); + if (pose.isValid()) { + AnimPose pose(pose.getRotation(), pose.getTranslation()); + params.primaryControllerPoses[pair.second] = avatarToRigPose * pose; + params.primaryControllerActiveFlags[pair.second] = true; + } else { + params.primaryControllerPoses[pair.second] = AnimPose::identity; + params.primaryControllerActiveFlags[pair.second] = false; + } } - auto avatarSpine2Pose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::SPINE2); - if (avatarSpine2Pose.isValid()) { - AnimPose pose(avatarSpine2Pose.getRotation(), avatarSpine2Pose.getTranslation()); - params.controllerPoses[Rig::ControllerType_Spine2] = avatarToRigPose * pose; - params.controllerActiveFlags[Rig::ControllerType_Spine2] = true; - } else { - params.controllerPoses[Rig::ControllerType_Spine2] = AnimPose::identity; - params.controllerActiveFlags[Rig::ControllerType_Spine2] = false; - } + // + // secondary controller poses, influence the pose of the skeleton indirectly. + // - auto avatarRightArmPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_ARM); - if (avatarRightArmPose.isValid()) { - AnimPose pose(avatarRightArmPose.getRotation(), avatarRightArmPose.getTranslation()); - params.controllerPoses[Rig::ControllerType_RightArm] = avatarToRigPose * pose; - params.controllerActiveFlags[Rig::ControllerType_RightArm] = true; - } else { - params.controllerPoses[Rig::ControllerType_RightArm] = AnimPose::identity; - params.controllerActiveFlags[Rig::ControllerType_RightArm] = false; - } + static const std::vector> secondaryControllers = { + { controller::Action::LEFT_SHOULDER, Rig::SecondaryControllerType_LeftShoulder }, + { controller::Action::RIGHT_SHOULDER, Rig::SecondaryControllerType_RightShoulder }, + { controller::Action::LEFT_ARM, Rig::SecondaryControllerType_LeftArm }, + { controller::Action::RIGHT_ARM, Rig::SecondaryControllerType_RightArm }, + { controller::Action::LEFT_FORE_ARM, Rig::SecondaryControllerType_LeftForeArm }, + { controller::Action::RIGHT_FORE_ARM, Rig::SecondaryControllerType_RightForeArm }, + { controller::Action::LEFT_UP_LEG, Rig::SecondaryControllerType_LeftUpLeg }, + { controller::Action::RIGHT_UP_LEG, Rig::SecondaryControllerType_RightUpLeg }, + { controller::Action::LEFT_LEG, Rig::SecondaryControllerType_LeftLeg }, + { controller::Action::RIGHT_LEG, Rig::SecondaryControllerType_RightLeg }, + { controller::Action::LEFT_TOE_BASE, Rig::SecondaryControllerType_LeftToeBase }, + { controller::Action::RIGHT_TOE_BASE, Rig::SecondaryControllerType_RightToeBase } + }; - auto avatarLeftArmPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_ARM); - if (avatarLeftArmPose.isValid()) { - AnimPose pose(avatarLeftArmPose.getRotation(), avatarLeftArmPose.getTranslation()); - params.controllerPoses[Rig::ControllerType_LeftArm] = avatarToRigPose * pose; - params.controllerActiveFlags[Rig::ControllerType_LeftArm] = true; - } else { - params.controllerPoses[Rig::ControllerType_LeftArm] = AnimPose::identity; - params.controllerActiveFlags[Rig::ControllerType_LeftArm] = false; - } - - auto avatarLeftHandPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND); - if (avatarLeftHandPose.isValid()) { - AnimPose pose(avatarLeftHandPose.getRotation(), avatarLeftHandPose.getTranslation()); - params.controllerPoses[Rig::ControllerType_LeftHand] = avatarToRigPose * pose; - params.controllerActiveFlags[Rig::ControllerType_LeftHand] = true; - } else { - params.controllerPoses[Rig::ControllerType_LeftHand] = AnimPose::identity; - params.controllerActiveFlags[Rig::ControllerType_LeftHand] = false; - } - - auto avatarRightHandPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND); - if (avatarRightHandPose.isValid()) { - AnimPose pose(avatarRightHandPose.getRotation(), avatarRightHandPose.getTranslation()); - params.controllerPoses[Rig::ControllerType_RightHand] = avatarToRigPose * pose; - params.controllerActiveFlags[Rig::ControllerType_RightHand] = true; - } else { - params.controllerPoses[Rig::ControllerType_RightHand] = AnimPose::identity; - params.controllerActiveFlags[Rig::ControllerType_RightHand] = false; - } - - auto avatarLeftFootPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_FOOT); - if (avatarLeftFootPose.isValid()) { - AnimPose pose(avatarLeftFootPose.getRotation(), avatarLeftFootPose.getTranslation()); - params.controllerPoses[Rig::ControllerType_LeftFoot] = avatarToRigPose * pose; - params.controllerActiveFlags[Rig::ControllerType_LeftFoot] = true; - } else { - params.controllerPoses[Rig::ControllerType_LeftFoot] = AnimPose::identity; - params.controllerActiveFlags[Rig::ControllerType_LeftFoot] = false; - } - - auto avatarRightFootPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_FOOT); - if (avatarRightFootPose.isValid()) { - AnimPose pose(avatarRightFootPose.getRotation(), avatarRightFootPose.getTranslation()); - params.controllerPoses[Rig::ControllerType_RightFoot] = avatarToRigPose * pose; - params.controllerActiveFlags[Rig::ControllerType_RightFoot] = true; - } else { - params.controllerPoses[Rig::ControllerType_RightFoot] = AnimPose::identity; - params.controllerActiveFlags[Rig::ControllerType_RightFoot] = false; + for (auto pair : secondaryControllers) { + auto pose = myAvatar->getControllerPoseInAvatarFrame(pair.first); + if (pose.isValid()) { + AnimPose pose(pose.getRotation(), pose.getTranslation()); + params.secondaryControllerPoses[pair.second] = avatarToRigPose * pose; + params.secondaryControllerActiveFlags[pair.second] = true; + } else { + params.secondaryControllerPoses[pair.second] = AnimPose::identity; + params.secondaryControllerActiveFlags[pair.second] = false; + } } params.bodyCapsuleRadius = myAvatar->getCharacterController()->getCapsuleRadius(); diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index d7076a443e..86ebe5fc91 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -921,6 +921,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars { PROFILE_RANGE_EX(simulation_animation, "ik/ccd", 0xffff00ff, 0); preconditionRelativePosesToAvoidLimbLock(context, targets); + setSecondaryTargets(context); solve(context, targets); } @@ -1015,6 +1016,22 @@ void AnimInverseKinematics::clearIKJointLimitHistory() { } } +void AnimInverseKinematics::setSecondaryTargetInRigFrame(int jointIndex, const AnimPose& pose) { + auto iter = _secondaryTargetsInRigFrame.find(jointIndex); + if (iter != _secondaryTargetsInRigFrame.end()) { + iter->second = pose; + } else { + _secondaryTargetsInRigFrame.insert({ jointIndex, pose }); + } +} + +void AnimInverseKinematics::clearSecondaryTarget(int jointIndex) { + auto iter = _secondaryTargetsInRigFrame.find(jointIndex); + if (iter != _secondaryTargetsInRigFrame.end()) { + _secondaryTargetsInRigFrame.erase(iter); + } +} + RotationConstraint* AnimInverseKinematics::getConstraint(int index) const { RotationConstraint* constraint = nullptr; std::map::const_iterator constraintItr = _constraints.find(index); @@ -1722,6 +1739,20 @@ void AnimInverseKinematics::preconditionRelativePosesToAvoidLimbLock(const AnimC } } +// overwrites _relativePoses with secondary poses. +void AnimInverseKinematics::setSecondaryTargets(const AnimContext& context) { + AnimPose rigToGeometryPose = AnimPose(glm::inverse(context.getGeometryToRigMatrix())); + for (auto& iter : _secondaryTargetsInRigFrame) { + AnimPose absPose = rigToGeometryPose * iter.second; + AnimPose parentAbsPose; + int parentIndex = _skeleton->getParentIndex(iter.first); + if (parentIndex >= 0) { + parentAbsPose = _skeleton->getAbsolutePose(parentIndex, _relativePoses); + } + _relativePoses[iter.first] = parentAbsPose.inverse() * absPose; + } +} + void AnimInverseKinematics::initRelativePosesFromSolutionSource(SolutionSource solutionSource, const AnimPoseVec& underPoses) { const float RELAX_BLEND_FACTOR = (1.0f / 16.0f); const float COPY_BLEND_FACTOR = 1.0f; diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index d473ae3698..2ca333323a 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -63,6 +63,9 @@ public: NumSolutionSources, }; + void setSecondaryTargetInRigFrame(int jointIndex, const AnimPose& pose); + void clearSecondaryTarget(int jointIndex); + void setSolutionSource(SolutionSource solutionSource) { _solutionSource = solutionSource; } void setSolutionSourceVar(const QString& solutionSourceVar) { _solutionSourceVar = solutionSourceVar; } @@ -83,6 +86,7 @@ protected: void initRelativePosesFromSolutionSource(SolutionSource solutionSource, const AnimPoseVec& underPose); void blendToPoses(const AnimPoseVec& targetPoses, const AnimPoseVec& underPose, float blendFactor); void preconditionRelativePosesToAvoidLimbLock(const AnimContext& context, const std::vector& targets); + void setSecondaryTargets(const AnimContext& context); // used to pre-compute information about each joint influeced by a spline IK target. struct SplineJointInfo { @@ -136,6 +140,8 @@ protected: AnimPoseVec _relativePoses; // current relative poses AnimPoseVec _limitCenterPoses; // relative + std::map _secondaryTargetsInRigFrame; + std::map> _splineJointInfoMap; // experimental data for moving hips during IK diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 6ebb68773f..9db8b187b6 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1474,24 +1474,25 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo _animVars.set("isTalking", params.isTalking); _animVars.set("notIsTalking", !params.isTalking); - bool headEnabled = params.controllerActiveFlags[ControllerType_Head]; - bool leftHandEnabled = params.controllerActiveFlags[ControllerType_LeftHand]; - bool rightHandEnabled = params.controllerActiveFlags[ControllerType_RightHand]; - bool hipsEnabled = params.controllerActiveFlags[ControllerType_Hips]; - bool leftFootEnabled = params.controllerActiveFlags[ControllerType_LeftFoot]; - bool rightFootEnabled = params.controllerActiveFlags[ControllerType_RightFoot]; - bool leftArmEnabled = params.controllerActiveFlags[ControllerType_LeftArm]; - bool rightArmEnabled = params.controllerActiveFlags[ControllerType_RightArm]; - bool spine2Enabled = params.controllerActiveFlags[ControllerType_Spine2]; + bool headEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_Head]; + bool leftHandEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_LeftHand]; + bool rightHandEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_RightHand]; + bool hipsEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_Hips]; + bool leftFootEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_LeftFoot]; + bool rightFootEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_RightFoot]; + bool spine2Enabled = params.primaryControllerActiveFlags[PrimaryControllerType_Spine2]; - updateHead(headEnabled, hipsEnabled, params.controllerPoses[ControllerType_Head]); + bool leftArmEnabled = params.secondaryControllerActiveFlags[SecondaryControllerType_LeftArm]; + bool rightArmEnabled = params.secondaryControllerActiveFlags[SecondaryControllerType_RightArm]; + + updateHead(headEnabled, hipsEnabled, params.primaryControllerPoses[PrimaryControllerType_Head]); updateHands(leftHandEnabled, rightHandEnabled, hipsEnabled, leftArmEnabled, rightArmEnabled, dt, - params.controllerPoses[ControllerType_LeftHand], params.controllerPoses[ControllerType_RightHand], + params.primaryControllerPoses[PrimaryControllerType_LeftHand], params.primaryControllerPoses[PrimaryControllerType_RightHand], params.bodyCapsuleRadius, params.bodyCapsuleHalfHeight, params.bodyCapsuleLocalOffset); updateFeet(leftFootEnabled, rightFootEnabled, - params.controllerPoses[ControllerType_LeftFoot], params.controllerPoses[ControllerType_RightFoot]); + params.primaryControllerPoses[PrimaryControllerType_LeftFoot], params.primaryControllerPoses[PrimaryControllerType_RightFoot]); // if the hips or the feet are being controlled. if (hipsEnabled || rightFootEnabled || leftFootEnabled) { @@ -1512,34 +1513,46 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo if (hipsEnabled) { _animVars.set("hipsType", (int)IKTarget::Type::RotationAndPosition); - _animVars.set("hipsPosition", params.controllerPoses[ControllerType_Hips].trans()); - _animVars.set("hipsRotation", params.controllerPoses[ControllerType_Hips].rot()); + _animVars.set("hipsPosition", params.primaryControllerPoses[PrimaryControllerType_Hips].trans()); + _animVars.set("hipsRotation", params.primaryControllerPoses[PrimaryControllerType_Hips].rot()); } else { _animVars.set("hipsType", (int)IKTarget::Type::Unknown); } if (hipsEnabled && spine2Enabled) { _animVars.set("spine2Type", (int)IKTarget::Type::Spline); - _animVars.set("spine2Position", params.controllerPoses[ControllerType_Spine2].trans()); - _animVars.set("spine2Rotation", params.controllerPoses[ControllerType_Spine2].rot()); + _animVars.set("spine2Position", params.primaryControllerPoses[PrimaryControllerType_Spine2].trans()); + _animVars.set("spine2Rotation", params.primaryControllerPoses[PrimaryControllerType_Spine2].rot()); } else { _animVars.set("spine2Type", (int)IKTarget::Type::Unknown); } - if (leftArmEnabled) { - _animVars.set("leftArmType", (int)IKTarget::Type::RotationAndPosition); - _animVars.set("leftArmPosition", params.controllerPoses[ControllerType_LeftArm].trans()); - _animVars.set("leftArmRotation", params.controllerPoses[ControllerType_LeftArm].rot()); - } else { - _animVars.set("leftArmType", (int)IKTarget::Type::Unknown); - } + // set secondary targets + static const std::vector secondaryControllerJointNames = { + "LeftShoulder", + "RightShoulder", + "LeftArm", + "RightArm", + "LeftForeArm", + "RightForeArm", + "LeftUpLeg", + "RightUpLeg", + "LeftLeg", + "RightLeg", + "LeftToeBase", + "RightToeBase" + }; - if (rightArmEnabled) { - _animVars.set("rightArmType", (int)IKTarget::Type::RotationAndPosition); - _animVars.set("rightArmPosition", params.controllerPoses[ControllerType_RightArm].trans()); - _animVars.set("rightArmRotation", params.controllerPoses[ControllerType_RightArm].rot()); - } else { - _animVars.set("rightArmType", (int)IKTarget::Type::Unknown); + std::shared_ptr ikNode = getAnimInverseKinematicsNode(); + for (int i = 0; i < (int)NumSecondaryControllerTypes; i++) { + int index = indexOfJoint(secondaryControllerJointNames[i]); + if (index >= 0) { + if (params.secondaryControllerActiveFlags[i]) { + ikNode->setSecondaryTargetInRigFrame(index, params.secondaryControllerPoses[i]); + } else { + ikNode->clearSecondaryTarget(index); + } + } } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index c17a7b9c8f..5718c62c26 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -41,22 +41,39 @@ public: bool useNames; }; - enum ControllerType { - ControllerType_Head = 0, - ControllerType_LeftHand, - ControllerType_RightHand, - ControllerType_Hips, - ControllerType_LeftFoot, - ControllerType_RightFoot, - ControllerType_LeftArm, - ControllerType_RightArm, - ControllerType_Spine2, - NumControllerTypes + enum PrimaryControllerType { + PrimaryControllerType_Head = 0, + PrimaryControllerType_LeftHand, + PrimaryControllerType_RightHand, + PrimaryControllerType_Hips, + PrimaryControllerType_LeftFoot, + PrimaryControllerType_RightFoot, + PrimaryControllerType_Spine2, + NumPrimaryControllerTypes + }; + + // NOTE: These should ordered such that joint parents appear before their children. + enum SecondaryControllerType { + SecondaryControllerType_LeftShoulder = 0, + SecondaryControllerType_RightShoulder, + SecondaryControllerType_LeftArm, + SecondaryControllerType_RightArm, + SecondaryControllerType_LeftForeArm, + SecondaryControllerType_RightForeArm, + SecondaryControllerType_LeftUpLeg, + SecondaryControllerType_RightUpLeg, + SecondaryControllerType_LeftLeg, + SecondaryControllerType_RightLeg, + SecondaryControllerType_LeftToeBase, + SecondaryControllerType_RightToeBase, + NumSecondaryControllerTypes }; struct ControllerParameters { - AnimPose controllerPoses[NumControllerTypes]; // rig space - bool controllerActiveFlags[NumControllerTypes]; + AnimPose primaryControllerPoses[NumPrimaryControllerTypes]; // rig space + bool primaryControllerActiveFlags[NumPrimaryControllerTypes]; + AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space + bool secondaryControllerActiveFlags[NumSecondaryControllerTypes]; bool isTalking; float bodyCapsuleRadius; float bodyCapsuleHalfHeight; diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index ec4800c9aa..9aa9bbdb29 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -101,6 +101,7 @@ enum class Action { // Bisected aliases for TRANSLATE_CAMERA_Z BOOM_IN, BOOM_OUT, + LEFT_ARM, RIGHT_ARM, @@ -146,7 +147,18 @@ enum class Action { RIGHT_HAND_PINKY3, RIGHT_HAND_PINKY4, - NUM_ACTIONS, + LEFT_SHOULDER, + RIGHT_SHOULDER, + LEFT_FORE_ARM, + RIGHT_FORE_ARM, + LEFT_LEG, + RIGHT_LEG, + LEFT_UP_LEG, + RIGHT_UP_LEG, + LEFT_TOE_BASE, + RIGHT_TOE_BASE, + + NUM_ACTIONS }; template From 9f6641ed1087d5926ea4d95378af20ab8501b60a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 24 Jul 2017 17:22:48 -0700 Subject: [PATCH 05/45] Shoulder puck calibration work in progress * AnimInverseKinematics: debug draw for secondary targets * AnimInverseKienmatics: better clean up of ik target debug draw * GeometryUtil: added findPlaneFromPoints() * ViveControllerManager: external dependency on eigen * ViveControllerManager: record history of left/right hand controllers * ViveControllerManager: use history to determine user shoulder location for better calibration * ViveControllerManager: pass defaultToReferenceMat by const ref to calibrate functions. * CMake: added external depenency to eigen linear algebra library. --- cmake/externals/eigen/CMakeLists.txt | 20 +++ cmake/modules/FindEigen.cmake | 26 ++++ .../resources/avatar/avatar-animation.json | 18 --- interface/src/Application.cpp | 2 +- .../animation/src/AnimInverseKinematics.cpp | 29 +++- libraries/shared/src/GeometryUtil.cpp | 52 ++++++++ libraries/shared/src/GeometryUtil.h | 2 + plugins/openvr/CMakeLists.txt | 6 + plugins/openvr/src/ViveControllerManager.cpp | 126 ++++++++++++++---- plugins/openvr/src/ViveControllerManager.h | 27 ++-- 10 files changed, 252 insertions(+), 56 deletions(-) create mode 100644 cmake/externals/eigen/CMakeLists.txt create mode 100644 cmake/modules/FindEigen.cmake diff --git a/cmake/externals/eigen/CMakeLists.txt b/cmake/externals/eigen/CMakeLists.txt new file mode 100644 index 0000000000..15d94576cc --- /dev/null +++ b/cmake/externals/eigen/CMakeLists.txt @@ -0,0 +1,20 @@ +set(EXTERNAL_NAME eigen) + +include(ExternalProject) +ExternalProject_Add( + ${EXTERNAL_NAME} + URL http://bitbucket.org/eigen/eigen/get/3.3.4.zip + URL_MD5 e337acc279874bc6a56da4d973a723fb + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 +) + +# Hide this external target (for ide users) +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") + +ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) + +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR} CACHE PATH "List of eigen include directories") \ No newline at end of file diff --git a/cmake/modules/FindEigen.cmake b/cmake/modules/FindEigen.cmake new file mode 100644 index 0000000000..4b09ed8f2b --- /dev/null +++ b/cmake/modules/FindEigen.cmake @@ -0,0 +1,26 @@ +# +# FindEigen.cmake +# +# Try to find Eigen include path. +# Once done this will define +# +# EIGEN_INCLUDE_DIRS +# +# Created on 7/14/2017 by Anthony Thibault +# 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 +# + +# setup hints for Eigen search +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("eigen") + +# locate dir +string(CONCAT EIGEN_INCLUDE_DIRS ${EIGEN_INCLUDE_DIRS} "/src/eigen") + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(EIGEN DEFAULT_MSG EIGEN_INCLUDE_DIRS) + +mark_as_advanced(EIGEN_INCLUDE_DIRS EIGEN_SEARCH_DIRS) \ No newline at end of file diff --git a/interface/resources/avatar/avatar-animation.json b/interface/resources/avatar/avatar-animation.json index a179f452c1..ee2b916d1e 100644 --- a/interface/resources/avatar/avatar-animation.json +++ b/interface/resources/avatar/avatar-animation.json @@ -61,24 +61,6 @@ "weight": 1.0, "flexCoefficients": [1] }, - { - "jointName": "LeftArm", - "positionVar": "leftArmPosition", - "rotationVar": "leftArmRotation", - "typeVar": "leftArmType", - "weightVar": "leftArmWeight", - "weight": 0.75, - "flexCoefficients": [1.0, 0.35, 0.2, 0.1, 0.05, 0.0, 0.0, 0.0] - }, - { - "jointName": "RightArm", - "positionVar": "rightArmPosition", - "rotationVar": "rightArmRotation", - "typeVar": "rightArmType", - "weightVar": "rightArmWeight", - "weight": 0.75, - "flexCoefficients": [1.0, 0.35, 0.2, 0.1, 0.05, 0.0, 0.0, 0.0] - }, { "jointName": "RightHand", "positionVar": "rightHandPosition", diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 37001b55bb..7b10c9a364 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4811,10 +4811,10 @@ void Application::update(float deltaTime) { controller::Action::RIGHT_TOE_BASE }; + // copy controller poses from userInputMapper to myAvatar. glm::mat4 myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()); glm::mat4 worldToSensorMatrix = glm::inverse(myAvatar->getSensorToWorldMatrix()); glm::mat4 avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix; - for (auto& action : avatarControllerActions) { controller::Pose pose = userInputMapper->getPoseState(action); myAvatar->setControllerPoseInSensorFrame(action, pose.transform(avatarToSensorMatrix)); diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 86ebe5fc91..908519880d 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -23,6 +23,8 @@ #include "CubicHermiteSpline.h" #include "AnimUtil.h" +static const int MAX_TARGET_MARKERS = 30; + static void lookupJointChainInfo(AnimInverseKinematics::JointChainInfo* jointChainInfos, size_t numJointChainInfos, int indexA, int indexB, AnimInverseKinematics::JointChainInfo** jointChainInfoA, @@ -94,6 +96,12 @@ AnimInverseKinematics::~AnimInverseKinematics() { _rotationAccumulators.clear(); _translationAccumulators.clear(); _targetVarVec.clear(); + + // remove markers + for (int i = 0; i < MAX_TARGET_MARKERS; i++) { + QString name = QString("ikTarget%1").arg(i); + DebugDraw::getInstance().removeMyAvatarMarker(name); + } } void AnimInverseKinematics::loadDefaultPoses(const AnimPoseVec& poses) { @@ -898,19 +906,30 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars // debug render ik targets if (context.getEnableDebugDrawIKTargets()) { const vec4 WHITE(1.0f); + const vec4 GREEN(0.0f, 1.0f, 0.0f, 1.0f); glm::mat4 rigToAvatarMat = createMatFromQuatAndPos(Quaternions::Y_180, glm::vec3()); + int targetNum = 0; for (auto& target : targets) { glm::mat4 geomTargetMat = createMatFromQuatAndPos(target.getRotation(), target.getTranslation()); glm::mat4 avatarTargetMat = rigToAvatarMat * context.getGeometryToRigMatrix() * geomTargetMat; - QString name = QString("ikTarget%1").arg(target.getIndex()); + QString name = QString("ikTarget%1").arg(targetNum); DebugDraw::getInstance().addMyAvatarMarker(name, glmExtractRotation(avatarTargetMat), extractTranslation(avatarTargetMat), WHITE); + targetNum++; + } + + // draw secondary ik targets + for (auto& iter : _secondaryTargetsInRigFrame) { + glm::mat4 avatarTargetMat = rigToAvatarMat * (glm::mat4)iter.second; + QString name = QString("ikTarget%1").arg(targetNum); + DebugDraw::getInstance().addMyAvatarMarker(name, glmExtractRotation(avatarTargetMat), extractTranslation(avatarTargetMat), GREEN); + targetNum++; } } else if (context.getEnableDebugDrawIKTargets() != _previousEnableDebugIKTargets) { // remove markers if they were added last frame. - for (auto& target : targets) { - QString name = QString("ikTarget%1").arg(target.getIndex()); + for (int i = 0; i < MAX_TARGET_MARKERS; i++) { + QString name = QString("ikTarget%1").arg(i); DebugDraw::getInstance().removeMyAvatarMarker(name); } } @@ -1744,12 +1763,16 @@ void AnimInverseKinematics::setSecondaryTargets(const AnimContext& context) { AnimPose rigToGeometryPose = AnimPose(glm::inverse(context.getGeometryToRigMatrix())); for (auto& iter : _secondaryTargetsInRigFrame) { AnimPose absPose = rigToGeometryPose * iter.second; + absPose.scale() = glm::vec3(1.0f); AnimPose parentAbsPose; int parentIndex = _skeleton->getParentIndex(iter.first); if (parentIndex >= 0) { parentAbsPose = _skeleton->getAbsolutePose(parentIndex, _relativePoses); } + // AJT: for now ignore translation on secondary poses. + glm::vec3 origTrans = _relativePoses[iter.first].trans(); _relativePoses[iter.first] = parentAbsPose.inverse() * absPose; + _relativePoses[iter.first].trans() = origTrans; } } diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index f853240fe3..4ae907eb3b 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -605,3 +605,55 @@ float coneSphereAngle(const glm::vec3& coneCenter, const glm::vec3& coneDirectio return glm::max(0.0f, theta - phi); } + +// given a set of points, compute a best fit plane that passes as close as possible through all the points. +// http://www.ilikebigbits.com/blog/2015/3/2/plane-from-points +bool findPlaneFromPoints(const glm::vec3* points, size_t numPoints, glm::vec3& planeNormalOut, glm::vec3& pointOnPlaneOut) { + if (numPoints < 3) { + return false; + } + glm::vec3 sum; + for (size_t i = 0; i < numPoints; i++) { + sum += points[i]; + } + glm::vec3 centroid = sum * (1.0f / (float)numPoints); + float xx = 0.0f, xy = 0.0f, xz = 0.0f; + float yy = 0.0f, yz = 0.0f, zz = 0.0f; + + for (size_t i = 0; i < numPoints; i++) { + glm::vec3 r = points[i] - centroid; + xx += r.x * r.x; + xy += r.x * r.y; + xz += r.x * r.z; + yy += r.y * r.y; + yz += r.y * r.z; + zz += r.z * r.z; + } + + float det_x = yy * zz - yz * yz; + float det_y = xx * zz - xz * xz; + float det_z = xx * yy - xy * xy; + float det_max = std::max(std::max(det_x, det_y), det_z); + + if (det_max == 0.0f) { + return false; // The points don't span a plane + } + + glm::vec3 dir; + if (det_max == det_x) { + float a = (xz * yz - xy * zz) / det_x; + float b = (xy * yz - xz * yy) / det_x; + dir = glm::vec3(1.0f, a, b); + } else if (det_max == det_y) { + float a = (yz * xz - xy * zz) / det_y; + float b = (xy * xz - yz * xx) / det_y; + dir = glm::vec3(a, 1.0f, b); + } else { + float a = (yz * xy - xz * yy) / det_z; + float b = (xz * xy - yz * xx) / det_z; + dir = glm::vec3(a, b, 1.0f); + } + pointOnPlaneOut = centroid; + planeNormalOut = glm::normalize(dir); + return true; +} diff --git a/libraries/shared/src/GeometryUtil.h b/libraries/shared/src/GeometryUtil.h index 857d423896..a5ee67748b 100644 --- a/libraries/shared/src/GeometryUtil.h +++ b/libraries/shared/src/GeometryUtil.h @@ -163,5 +163,7 @@ private: static void copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& lengthB, glm::vec2* vertexArrayB); }; +// given a set of points, compute a best fit plane that passes as close as possible through all the points. +bool findPlaneFromPoints(const glm::vec3* points, size_t numPoints, glm::vec3& planeNormalOut, glm::vec3& pointOnPlaneOut); #endif // hifi_GeometryUtil_h diff --git a/plugins/openvr/CMakeLists.txt b/plugins/openvr/CMakeLists.txt index 6a95ef6d76..6633b99b31 100644 --- a/plugins/openvr/CMakeLists.txt +++ b/plugins/openvr/CMakeLists.txt @@ -18,8 +18,14 @@ if (WIN32) include_hifi_library_headers(octree) add_dependency_external_projects(OpenVR) + add_dependency_external_projects(eigen) + find_package(OpenVR REQUIRED) target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) target_link_libraries(${TARGET_NAME} Winmm.lib) + + # header only library + find_package(eigen REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE ${EIGEN_INCLUDE_DIRS}) endif() diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index ddafa5cb5b..02d3afd713 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -35,6 +35,7 @@ #include #include +#include extern PoseData _nextSimPoseData; @@ -282,7 +283,13 @@ void ViveControllerManager::pluginUpdate(float deltaTime, const controller::Inpu } } -ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : controller::InputDevice("Vive"), _system(system) { +static const size_t CONTROLLER_HISTORY_SIZE = 90 * 3; + +ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : + controller::InputDevice("Vive"), + _system(system), + _leftControllerHistory(CONTROLLER_HISTORY_SIZE), + _rightControllerHistory(CONTROLLER_HISTORY_SIZE) { _configStringMap[Config::None] = QString("None"); _configStringMap[Config::Feet] = QString("Feet"); @@ -534,7 +541,7 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr } } -bool ViveControllerManager::InputDevice::configureHands(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { +bool ViveControllerManager::InputDevice::configureHands(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { std::sort(_validTrackedObjects.begin(), _validTrackedObjects.end(), sortPucksXPosition); int puckCount = (int)_validTrackedObjects.size(); if (_handConfig == HandConfig::Pucks && puckCount >= MIN_PUCK_COUNT) { @@ -569,7 +576,7 @@ bool ViveControllerManager::InputDevice::configureHands(glm::mat4& defaultToRefe return false; } -bool ViveControllerManager::InputDevice::configureHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { +bool ViveControllerManager::InputDevice::configureHead(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { std::sort(_validTrackedObjects.begin(), _validTrackedObjects.end(), sortPucksYPosition); int puckCount = (int)_validTrackedObjects.size(); if (_headConfig == HeadConfig::Puck && puckCount >= MIN_HEAD) { @@ -583,7 +590,7 @@ bool ViveControllerManager::InputDevice::configureHead(glm::mat4& defaultToRefer return false; } -bool ViveControllerManager::InputDevice::configureBody(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { +bool ViveControllerManager::InputDevice::configureBody(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { std::sort(_validTrackedObjects.begin(), _validTrackedObjects.end(), sortPucksYPosition); int puckCount = (int)_validTrackedObjects.size(); glm::vec3 headXAxis = getReferenceHeadXAxis(defaultToReferenceMat, inputCalibration.defaultHeadMat); @@ -637,8 +644,17 @@ void ViveControllerManager::InputDevice::updateCalibratedLimbs() { _poseStateMap[controller::RIGHT_FOOT] = addOffsetToPuckPose(controller::RIGHT_FOOT); _poseStateMap[controller::HIPS] = addOffsetToPuckPose(controller::HIPS); _poseStateMap[controller::SPINE2] = addOffsetToPuckPose(controller::SPINE2); - _poseStateMap[controller::RIGHT_ARM] = addOffsetToPuckPose(controller::RIGHT_ARM); - _poseStateMap[controller::LEFT_ARM] = addOffsetToPuckPose(controller::LEFT_ARM); + controller::Pose rightArmPose = addOffsetToPuckPose(controller::RIGHT_ARM); + _poseStateMap[controller::RIGHT_ARM] = rightArmPose; + if (rightArmPose.isValid()) { + // AJT: TODO: compute rotation for RIGHT_SHOULDER AS WELL. + } + + controller::Pose leftArmPose = addOffsetToPuckPose(controller::LEFT_ARM); + _poseStateMap[controller::LEFT_ARM] = leftArmPose; + if (leftArmPose.isValid()) { + // AJT: TODO: compute rotation for LEFT_SHOULDER AS WELL. + } if (_overrideHead) { _poseStateMap[controller::HEAD] = addOffsetToPuckPose(controller::HEAD); @@ -899,6 +915,16 @@ void ViveControllerManager::InputDevice::handlePoseEvent(float deltaTime, const // transform into avatar frame glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; _poseStateMap[isLeftHand ? controller::LEFT_HAND : controller::RIGHT_HAND] = pose.transform(controllerToAvatar); + + // AJT: TODO ONLY DO THIS IF CALIBRATING! + // record controller history + if (isLeftHand) { + _leftControllerHistory[_leftControllerHistoryCursor] = pose.translation; + _leftControllerHistoryCursor = (_leftControllerHistoryCursor + 1) % CONTROLLER_HISTORY_SIZE; + } else { + _rightControllerHistory[_rightControllerHistoryCursor] = pose.translation; + _rightControllerHistoryCursor = (_rightControllerHistoryCursor + 1) % CONTROLLER_HISTORY_SIZE; + } } bool ViveControllerManager::InputDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) { @@ -950,7 +976,7 @@ void ViveControllerManager::InputDevice::hapticsHelper(float deltaTime, bool lef } } -void ViveControllerManager::InputDevice::calibrateLeftHand(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair) { +void ViveControllerManager::InputDevice::calibrateLeftHand(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair) { controller::Pose& handPose = handPair.second; glm::mat4 handPoseAvatarMat = createMatFromQuatAndPos(handPose.getRotation(), handPose.getTranslation()); glm::vec3 handPoseTranslation = extractTranslation(handPoseAvatarMat); @@ -981,7 +1007,7 @@ void ViveControllerManager::InputDevice::calibrateLeftHand(glm::mat4& defaultToR _pucksPostOffset[handPair.first] = offsetMat; } -void ViveControllerManager::InputDevice::calibrateRightHand(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair) { +void ViveControllerManager::InputDevice::calibrateRightHand(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair) { controller::Pose& handPose = handPair.second; glm::mat4 handPoseAvatarMat = createMatFromQuatAndPos(handPose.getRotation(), handPose.getTranslation()); glm::vec3 handPoseTranslation = extractTranslation(handPoseAvatarMat); @@ -1013,7 +1039,7 @@ void ViveControllerManager::InputDevice::calibrateRightHand(glm::mat4& defaultTo } -void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { +void ViveControllerManager::InputDevice::calibrateFeet(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { glm::vec3 headXAxis = getReferenceHeadXAxis(defaultToReferenceMat, inputCalibration.defaultHeadMat); glm::vec3 headPosition = getReferenceHeadPosition(defaultToReferenceMat, inputCalibration.defaultHeadMat); auto& firstFoot = _validTrackedObjects[FIRST_FOOT]; @@ -1030,7 +1056,7 @@ void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToRefer } } -void ViveControllerManager::InputDevice::calibrateFoot(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& footPair, bool isLeftFoot){ +void ViveControllerManager::InputDevice::calibrateFoot(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& footPair, bool isLeftFoot){ controller::Pose footPose = footPair.second; glm::mat4 puckPoseAvatarMat = createMatFromQuatAndPos(footPose.getRotation(), footPose.getTranslation()); glm::mat4 defaultFoot = isLeftFoot ? inputCalibration.defaultLeftFoot : inputCalibration.defaultRightFoot; @@ -1052,17 +1078,75 @@ void ViveControllerManager::InputDevice::calibrateFoot(glm::mat4& defaultToRefer } } -void ViveControllerManager::InputDevice::calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { +void ViveControllerManager::InputDevice::calibrateHips(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { _jointToPuckMap[controller::HIPS] = _validTrackedObjects[HIP].first; _pucksPostOffset[_validTrackedObjects[HIP].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHips, _validTrackedObjects[HIP].second); } -void ViveControllerManager::InputDevice::calibrateChest(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { +void ViveControllerManager::InputDevice::calibrateChest(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { _jointToPuckMap[controller::SPINE2] = _validTrackedObjects[CHEST].first; _pucksPostOffset[_validTrackedObjects[CHEST].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultSpine2, _validTrackedObjects[CHEST].second); } -void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, +// assuming the user has kept his arms straight to his sides and has rotated his hand controllers +// up and down laterally about his shoulders. This will attempt to locate the users actual shoulders +// by calculating a best fit circle around the points, in the plane of the head's forward normal. +static glm::vec3 computeUserShoulderPositionFromHistory(const glm::mat4& headMat, const std::vector& history) { + glm::mat4 invHeadMat = glm::inverse(headMat); + + // AJT: TODO: we could perhaps reject the approximation if the radius is larger or smaller then we expect. + // AJT: TODO: abort if history is too small or invalid... + + // Take equation for a circle: (x - h)^2 + (y - k)^2 = r^2 + // And put it into the linear form: A * x = b + // + // where A = | (2 * x0) (2 * y0) 1 | + // | (2 * x1) (2 * y1) 1 | + // | . . . | + // | (2 * xn) (2 * yn) 1 | + // + // and b = | x0^2 + y0^2 | + // | x1^2 + y1^2 | + // | . | + // | xn^2 + yn^2 | + // + // and x = | h | + // | k | + // | r^2 - h^2 - k^2 | + // + + // Build aMat and bMat + Eigen::MatrixXf aMat(CONTROLLER_HISTORY_SIZE, 3); + Eigen::MatrixXf bMat(CONTROLLER_HISTORY_SIZE, 1); + for (int r = 0; r < CONTROLLER_HISTORY_SIZE; r++) { + // transform history[r] into local head coordinates + glm::vec3 p = transformPoint(invHeadMat, history[r]); + + // update the aMat with the appropriate 2d history values + aMat(r, 0) = 2.0f * p.x; + aMat(r, 1) = 2.0f * p.y; + aMat(r, 2) = 1.0f; + + // update the bMat with the appropriate 2d history values + bMat(r, 0) = p.x * p.x + p.y * p.y; + } + + // Then use least squares approximation to solve for the best x. + // http://math.mit.edu/~gs/linearalgebra/ila0403.pdf + Eigen::MatrixXf aTransMat = aMat.transpose(); + Eigen::MatrixXf xMat = (aTransMat * aMat).inverse() * aTransMat * bMat; + + // extract the 2d center of the circle from the xMat + glm::vec3 center(xMat(0, 0), xMat(1, 0), 0.0f); + + // we don't need the radius, but it's included here for completeness. + // float radius = center.x * center.x + center.y * center.y - xMat(2, 0); + + // transform center back into sensor coordinates + return transformPoint(headMat, center); +} + +void ViveControllerManager::InputDevice::calibrateShoulders(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, int firstShoulderIndex, int secondShoulderIndex) { const PuckPosePair& firstShoulder = _validTrackedObjects[firstShoulderIndex]; const PuckPosePair& secondShoulder = _validTrackedObjects[secondShoulderIndex]; @@ -1071,18 +1155,18 @@ void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultTo glm::mat4 refLeftArm = defaultToReferenceMat * inputCalibration.defaultLeftArm; glm::mat4 refRightArm = defaultToReferenceMat * inputCalibration.defaultRightArm; + glm::mat4 userRefLeftArm = refLeftArm; glm::mat4 userRefRightArm = refRightArm; - const float PUCK_TO_USER_REF_ARM_Y_OFFSET = -0.06f; + + glm::mat4 headMat = defaultToReferenceMat * inputCalibration.defaultHeadMat; + userRefLeftArm[3] = glm::vec4(computeUserShoulderPositionFromHistory(headMat, _leftControllerHistory), 1.0f); + userRefRightArm[3] = glm::vec4(computeUserShoulderPositionFromHistory(headMat, _rightControllerHistory), 1.0f); if (firstShoulderPose.translation.x < secondShoulderPose.translation.x) { _jointToPuckMap[controller::LEFT_ARM] = firstShoulder.first; _jointToPuckMap[controller::RIGHT_ARM] = secondShoulder.first; - // move the userRefArm to the same height as the puck. - userRefLeftArm[3][1] = firstShoulderPose.translation.y + PUCK_TO_USER_REF_ARM_Y_OFFSET; - userRefRightArm[3][1] = secondShoulderPose.translation.y + PUCK_TO_USER_REF_ARM_Y_OFFSET; - // compute the post offset from the userRefArm _pucksPostOffset[firstShoulder.first] = computeOffset(Matrices::IDENTITY, userRefLeftArm, firstShoulderPose); _pucksPostOffset[secondShoulder.first] = computeOffset(Matrices::IDENTITY, userRefRightArm, secondShoulderPose); @@ -1095,10 +1179,6 @@ void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultTo _jointToPuckMap[controller::LEFT_ARM] = secondShoulder.first; _jointToPuckMap[controller::RIGHT_ARM] = firstShoulder.first; - // move the userRefArm to the same height as the puck - userRefLeftArm[3][1] = secondShoulderPose.translation.y + PUCK_TO_USER_REF_ARM_Y_OFFSET; - userRefRightArm[3][1] = firstShoulderPose.translation.y + PUCK_TO_USER_REF_ARM_Y_OFFSET; - // compute the post offset from the userRefArm _pucksPostOffset[secondShoulder.first] = computeOffset(Matrices::IDENTITY, userRefLeftArm, secondShoulderPose); _pucksPostOffset[firstShoulder.first] = computeOffset(Matrices::IDENTITY, userRefRightArm, firstShoulderPose); @@ -1110,7 +1190,7 @@ void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultTo } } -void ViveControllerManager::InputDevice::calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { +void ViveControllerManager::InputDevice::calibrateHead(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { size_t headIndex = _validTrackedObjects.size() - 1; const PuckPosePair& head = _validTrackedObjects[headIndex]; _jointToPuckMap[controller::HEAD] = head.first; diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index d82b895579..329901cd25 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -93,18 +93,18 @@ private: void partitionTouchpad(int sButton, int xAxis, int yAxis, int centerPsuedoButton, int xPseudoButton, int yPseudoButton); void printDeviceTrackingResultChange(uint32_t deviceIndex); void setConfigFromString(const QString& value); - bool configureHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); - bool configureHands(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); - bool configureBody(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); - void calibrateLeftHand(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair); - void calibrateRightHand(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair); - void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); - void calibrateFoot(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& footPair, bool isLeftFoot); - void calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); - void calibrateChest(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); - void calibrateShoulders(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, + bool configureHead(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + bool configureHands(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + bool configureBody(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + void calibrateLeftHand(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair); + void calibrateRightHand(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair); + void calibrateFeet(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + void calibrateFoot(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& footPair, bool isLeftFoot); + void calibrateHips(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + void calibrateChest(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + void calibrateShoulders(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, int firstShoulderIndex, int secondShoulderIndex); - void calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + void calibrateHead(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); void calibrateFromHandController(const controller::InputCalibrationData& inputCalibrationData); void calibrateFromUI(const controller::InputCalibrationData& inputCalibrationData); void emitCalibrationStatus(); @@ -190,6 +190,11 @@ private: bool _overrideHands { false }; mutable std::recursive_mutex _lock; + std::vector _leftControllerHistory; + size_t _leftControllerHistoryCursor { 0 }; + std::vector _rightControllerHistory; + size_t _rightControllerHistoryCursor { 0 }; + QString configToString(Config config); friend class ViveControllerManager; }; From c81875a2808ada23429daa191aef5789842097ed Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 25 Jul 2017 15:24:09 -0700 Subject: [PATCH 06/45] Improved shoulder calibration using hard-coded measured shoulder width --- .../animation/src/AnimInverseKinematics.cpp | 25 ++++++++++++ libraries/animation/src/AnimUtil.cpp | 11 +++++ libraries/animation/src/AnimUtil.h | 2 + plugins/openvr/src/ViveControllerManager.cpp | 40 ++++++++++++++----- 4 files changed, 67 insertions(+), 11 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index b9eb1c2912..10c4011f52 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -1862,15 +1862,40 @@ void AnimInverseKinematics::preconditionRelativePosesToAvoidLimbLock(const AnimC // overwrites _relativePoses with secondary poses. void AnimInverseKinematics::setSecondaryTargets(const AnimContext& context) { + + if (_secondaryTargetsInRigFrame.empty()) { + return; + } + + // shoulder joint should look-at position of arm joint. + const int leftArmIndex = _skeleton->nameToJointIndex("LeftArm"); + const int rightArmIndex = _skeleton->nameToJointIndex("RightArm"); + AnimPose rigToGeometryPose = AnimPose(glm::inverse(context.getGeometryToRigMatrix())); for (auto& iter : _secondaryTargetsInRigFrame) { AnimPose absPose = rigToGeometryPose * iter.second; absPose.scale() = glm::vec3(1.0f); + AnimPose parentAbsPose; int parentIndex = _skeleton->getParentIndex(iter.first); if (parentIndex >= 0) { parentAbsPose = _skeleton->getAbsolutePose(parentIndex, _relativePoses); } + + // if parent should "look-at" child joint position. + if (iter.first == leftArmIndex || iter.first == rightArmIndex) { + + AnimPose grandParentAbsPose; + int grandParentIndex = _skeleton->getParentIndex(parentIndex); + if (parentIndex >= 0) { + grandParentAbsPose = _skeleton->getAbsolutePose(grandParentIndex, _relativePoses); + } + + // the shoulder should rotate toward the arm joint via "look-at" constraint + parentAbsPose = boneLookAt(absPose.trans(), parentAbsPose); + _relativePoses[parentIndex] = grandParentAbsPose.inverse() * parentAbsPose; + } + // AJT: for now ignore translation on secondary poses. glm::vec3 origTrans = _relativePoses[iter.first].trans(); _relativePoses[iter.first] = parentAbsPose.inverse() * absPose; diff --git a/libraries/animation/src/AnimUtil.cpp b/libraries/animation/src/AnimUtil.cpp index bcf30642e8..65c605b5ba 100644 --- a/libraries/animation/src/AnimUtil.cpp +++ b/libraries/animation/src/AnimUtil.cpp @@ -96,3 +96,14 @@ float accumulateTime(float startFrame, float endFrame, float timeScale, float cu return frame; } +// rotate bone's y-axis with target. +AnimPose boneLookAt(const glm::vec3& target, const AnimPose& bone) { + glm::vec3 u, v, w; + generateBasisVectors(target - bone.trans(), bone.rot() * Vectors::UNIT_X, u, v, w); + glm::mat4 lookAt(glm::vec4(v, 0.0f), + glm::vec4(u, 0.0f), + // AJT: TODO REVISIT THIS, this could be -w. + glm::vec4(glm::normalize(glm::cross(v, u)), 0.0f), + glm::vec4(bone.trans(), 1.0f)); + return AnimPose(lookAt); +} diff --git a/libraries/animation/src/AnimUtil.h b/libraries/animation/src/AnimUtil.h index d215fdc654..f2cceb361b 100644 --- a/libraries/animation/src/AnimUtil.h +++ b/libraries/animation/src/AnimUtil.h @@ -31,4 +31,6 @@ inline glm::quat safeLerp(const glm::quat& a, const glm::quat& b, float alpha) { return glm::normalize(glm::lerp(a, bTemp, alpha)); } +AnimPose boneLookAt(const glm::vec3& target, const AnimPose& bone); + #endif diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 02d3afd713..4c34a4cd27 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -644,17 +644,8 @@ void ViveControllerManager::InputDevice::updateCalibratedLimbs() { _poseStateMap[controller::RIGHT_FOOT] = addOffsetToPuckPose(controller::RIGHT_FOOT); _poseStateMap[controller::HIPS] = addOffsetToPuckPose(controller::HIPS); _poseStateMap[controller::SPINE2] = addOffsetToPuckPose(controller::SPINE2); - controller::Pose rightArmPose = addOffsetToPuckPose(controller::RIGHT_ARM); - _poseStateMap[controller::RIGHT_ARM] = rightArmPose; - if (rightArmPose.isValid()) { - // AJT: TODO: compute rotation for RIGHT_SHOULDER AS WELL. - } - - controller::Pose leftArmPose = addOffsetToPuckPose(controller::LEFT_ARM); - _poseStateMap[controller::LEFT_ARM] = leftArmPose; - if (leftArmPose.isValid()) { - // AJT: TODO: compute rotation for LEFT_SHOULDER AS WELL. - } + _poseStateMap[controller::RIGHT_ARM] = addOffsetToPuckPose(controller::RIGHT_ARM); + _poseStateMap[controller::LEFT_ARM] = addOffsetToPuckPose(controller::LEFT_ARM); if (_overrideHead) { _poseStateMap[controller::HEAD] = addOffsetToPuckPose(controller::HEAD); @@ -1146,6 +1137,25 @@ static glm::vec3 computeUserShoulderPositionFromHistory(const glm::mat4& headMat return transformPoint(headMat, center); } +// y axis comes out of puck usb port/green light +// -z axis comes out of puck center/vive logo +static glm::vec3 computeUserShoulderPositionFromMeasurements(const glm::mat4& headMat, const controller::Pose& armPuck, bool isLeftHand) { + // AJT: TODO measurments are hard coded! + const float ARM_CIRC = 0.33f; // 13 inches + const float SHOULDER_SPAN = 0.4826; // 19 inches + + float armRadius = ARM_CIRC / TWO_PI; + + float sign = isLeftHand ? 1.0f : -1.0f; + float localArmX = sign * SHOULDER_SPAN / 2.0f; + + controller::Pose localPuck = armPuck.transform(glm::inverse(headMat)); + glm::mat4 localPuckMat = localPuck.getMatrix(); + glm::vec3 localArmCenter = extractTranslation(localPuckMat) + armRadius * transformVectorFast(localPuckMat, Vectors::UNIT_Z); + + return transformPoint(headMat, glm::vec3(localArmX, localArmCenter.y, localArmCenter.z)); +} + void ViveControllerManager::InputDevice::calibrateShoulders(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, int firstShoulderIndex, int secondShoulderIndex) { const PuckPosePair& firstShoulder = _validTrackedObjects[firstShoulderIndex]; @@ -1160,13 +1170,18 @@ void ViveControllerManager::InputDevice::calibrateShoulders(const glm::mat4& def glm::mat4 userRefRightArm = refRightArm; glm::mat4 headMat = defaultToReferenceMat * inputCalibration.defaultHeadMat; + /* AJT: TODO REMOVE userRefLeftArm[3] = glm::vec4(computeUserShoulderPositionFromHistory(headMat, _leftControllerHistory), 1.0f); userRefRightArm[3] = glm::vec4(computeUserShoulderPositionFromHistory(headMat, _rightControllerHistory), 1.0f); + */ if (firstShoulderPose.translation.x < secondShoulderPose.translation.x) { _jointToPuckMap[controller::LEFT_ARM] = firstShoulder.first; _jointToPuckMap[controller::RIGHT_ARM] = secondShoulder.first; + userRefLeftArm[3] = glm::vec4(computeUserShoulderPositionFromMeasurements(headMat, firstShoulderPose, true), 1.0f); + userRefRightArm[3] = glm::vec4(computeUserShoulderPositionFromMeasurements(headMat, secondShoulderPose, false), 1.0f); + // compute the post offset from the userRefArm _pucksPostOffset[firstShoulder.first] = computeOffset(Matrices::IDENTITY, userRefLeftArm, firstShoulderPose); _pucksPostOffset[secondShoulder.first] = computeOffset(Matrices::IDENTITY, userRefRightArm, secondShoulderPose); @@ -1179,6 +1194,9 @@ void ViveControllerManager::InputDevice::calibrateShoulders(const glm::mat4& def _jointToPuckMap[controller::LEFT_ARM] = secondShoulder.first; _jointToPuckMap[controller::RIGHT_ARM] = firstShoulder.first; + userRefLeftArm[3] = glm::vec4(computeUserShoulderPositionFromMeasurements(headMat, secondShoulderPose, true), 1.0f); + userRefRightArm[3] = glm::vec4(computeUserShoulderPositionFromMeasurements(headMat, firstShoulderPose, false), 1.0f); + // compute the post offset from the userRefArm _pucksPostOffset[secondShoulder.first] = computeOffset(Matrices::IDENTITY, userRefLeftArm, secondShoulderPose); _pucksPostOffset[firstShoulder.first] = computeOffset(Matrices::IDENTITY, userRefRightArm, firstShoulderPose); From 572213daaf6ac3c4a90c87f51d95ab82a63614cf Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 25 Jul 2017 17:05:07 -0700 Subject: [PATCH 07/45] Added shoulder config to Controller Settings UI All offset values in Controller Settings UI are now in cm. --- .../qml/hifi/tablet/OpenVrConfiguration.qml | 88 +++++++++++++++---- plugins/openvr/src/ViveControllerManager.cpp | 38 +++++--- plugins/openvr/src/ViveControllerManager.h | 2 + 3 files changed, 95 insertions(+), 33 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index 90d6ba7022..36424e22b4 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -145,12 +145,13 @@ Rectangle { visible: headPuckBox.checked HifiControls.SpinBox { id: headYOffset - decimals: 4 + decimals: 1 width: 112 - label: "Y: offset" + label: "Y Offset" + suffix: " cm" minimumValue: -10 - stepSize: 0.0254 - value: -0.05 + stepSize: 1 + value: -5 colorScheme: hifi.colorSchemes.dark onEditingFinished: { @@ -162,11 +163,12 @@ Rectangle { HifiControls.SpinBox { id: headZOffset width: 112 - label: "Z: offset" + label: "Z Offset" minimumValue: -10 - stepSize: 0.0254 - decimals: 4 - value: -0.05 + stepSize: 1 + decimals: 1 + suffix: " cm" + value: -5 colorScheme: hifi.colorSchemes.dark onEditingFinished: { @@ -175,7 +177,6 @@ Rectangle { } } - RalewayBold { id: hands @@ -254,11 +255,12 @@ Rectangle { HifiControls.SpinBox { id: handYOffset - decimals: 4 + decimals: 1 width: 112 - label: "Y: offset" + suffix: " cm" + label: "Y Offset" minimumValue: -10 - stepSize: 0.0254 + stepSize: 1 colorScheme: hifi.colorSchemes.dark onEditingFinished: { @@ -270,10 +272,11 @@ Rectangle { HifiControls.SpinBox { id: handZOffset width: 112 - label: "Z: offset" + label: "Z Offset" + suffix: " cm" minimumValue: -10 - stepSize: 0.0254 - decimals: 4 + stepSize: 1 + decimals: 1 colorScheme: hifi.colorSchemes.dark onEditingFinished: { @@ -488,15 +491,55 @@ Rectangle { } } + Row { + id: shoulderAdditionalConfig + visible: shoulderBox.checked + anchors.top: shoulderConfig.bottom + anchors.topMargin: 5 + anchors.left: openVrConfiguration.left + anchors.leftMargin: leftMargin + 20 + spacing: 10 + + HifiControls.SpinBox { + id: armCircumference + decimals: 1 + width: 160 + suffix: " cm" + label: "Arm Circumference" + minimumValue: 0 + stepSize: 1.0 + colorScheme: hifi.colorSchemes.dark + value: 33.0 + + onEditingFinished: { + sendConfigurationSettings(); + } + } + + HifiControls.SpinBox { + id: shoulderWidth + width: 160 + label: "Shoulder Width" + suffix: " cm" + minimumValue: 0 + stepSize: 1.0 + decimals: 1 + colorScheme: hifi.colorSchemes.dark + value: 48 + + onEditingFinished: { + sendConfigurationSettings(); + } + } + } + Separator { id: bottomSeperator width: parent.width - anchors.top: shoulderConfig.bottom - anchors.topMargin: 10 + anchors.top: shoulderAdditionalConfig.visible ? shoulderAdditionalConfig.bottom : shoulderConfig.bottom + anchors.topMargin: (shoulderAdditionalConfig.visible ? 25 : 10) } - - Rectangle { id: calibrationButton property int color: hifi.buttons.blue @@ -1006,10 +1049,17 @@ Rectangle { "Z": handZOffset.value } + var shoulderObject = { + "override": shouldersChecked, + "armCircumference": armCircumference.value, + "shoulderWidth": shoulderWidth.value + } + var settingsObject = { "bodyConfiguration": trackerConfiguration, "headConfiguration": headObject, "handConfiguration": handObject, + "shoulderConfiguration": shoulderObject, "desktopMode": viveInDesktop.checked } diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 4c34a4cd27..395e131124 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -378,6 +378,8 @@ void ViveControllerManager::InputDevice::calibrateFromUI(const controller::Input } } +static const float CM_TO_M = 0.01f; + void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJsonObject configurationSettings) { Locker locker(_lock); if (!configurationSettings.empty()) { @@ -391,8 +393,8 @@ void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJso bool overrideHead = headObject["override"].toBool(); if (overrideHead) { _headConfig = HeadConfig::Puck; - _headPuckYOffset = headObject["Y"].toDouble(); - _headPuckZOffset = headObject["Z"].toDouble(); + _headPuckYOffset = headObject["Y"].toDouble() * CM_TO_M; + _headPuckZOffset = headObject["Z"].toDouble() * CM_TO_M; } else { _headConfig = HeadConfig::HMD; } @@ -401,11 +403,18 @@ void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJso bool overrideHands = handsObject["override"].toBool(); if (overrideHands) { _handConfig = HandConfig::Pucks; - _handPuckYOffset = handsObject["Y"].toDouble(); - _handPuckZOffset = handsObject["Z"].toDouble(); + _handPuckYOffset = handsObject["Y"].toDouble() * CM_TO_M; + _handPuckZOffset = handsObject["Z"].toDouble() * CM_TO_M; } else { _handConfig = HandConfig::HandController; } + } else if (iter.key() == "shoulderConfiguration") { + QJsonObject shoulderObj = iter.value().toObject(); + bool shouldersChecked = shoulderObj["override"].toBool(); + if (shouldersChecked) { + _armCircumference = shoulderObj["armCircumference"].toDouble() * CM_TO_M; + _shoulderWidth = shoulderObj["shoulderWidth"].toDouble() * CM_TO_M; + } } iter++; } @@ -1139,15 +1148,12 @@ static glm::vec3 computeUserShoulderPositionFromHistory(const glm::mat4& headMat // y axis comes out of puck usb port/green light // -z axis comes out of puck center/vive logo -static glm::vec3 computeUserShoulderPositionFromMeasurements(const glm::mat4& headMat, const controller::Pose& armPuck, bool isLeftHand) { - // AJT: TODO measurments are hard coded! - const float ARM_CIRC = 0.33f; // 13 inches - const float SHOULDER_SPAN = 0.4826; // 19 inches +static glm::vec3 computeUserShoulderPositionFromMeasurements(float armCirc, float shoulderSpan, const glm::mat4& headMat, const controller::Pose& armPuck, bool isLeftHand) { - float armRadius = ARM_CIRC / TWO_PI; + float armRadius = armCirc / TWO_PI; float sign = isLeftHand ? 1.0f : -1.0f; - float localArmX = sign * SHOULDER_SPAN / 2.0f; + float localArmX = sign * shoulderSpan / 2.0f; controller::Pose localPuck = armPuck.transform(glm::inverse(headMat)); glm::mat4 localPuckMat = localPuck.getMatrix(); @@ -1179,8 +1185,10 @@ void ViveControllerManager::InputDevice::calibrateShoulders(const glm::mat4& def _jointToPuckMap[controller::LEFT_ARM] = firstShoulder.first; _jointToPuckMap[controller::RIGHT_ARM] = secondShoulder.first; - userRefLeftArm[3] = glm::vec4(computeUserShoulderPositionFromMeasurements(headMat, firstShoulderPose, true), 1.0f); - userRefRightArm[3] = glm::vec4(computeUserShoulderPositionFromMeasurements(headMat, secondShoulderPose, false), 1.0f); + glm::vec3 leftPos = computeUserShoulderPositionFromMeasurements(_armCircumference, _shoulderWidth, headMat, firstShoulderPose, true); + userRefLeftArm[3] = glm::vec4(leftPos, 1.0f); + glm::vec3 rightPos = computeUserShoulderPositionFromMeasurements(_armCircumference, _shoulderWidth, headMat, secondShoulderPose, false); + userRefRightArm[3] = glm::vec4(rightPos, 1.0f); // compute the post offset from the userRefArm _pucksPostOffset[firstShoulder.first] = computeOffset(Matrices::IDENTITY, userRefLeftArm, firstShoulderPose); @@ -1194,8 +1202,10 @@ void ViveControllerManager::InputDevice::calibrateShoulders(const glm::mat4& def _jointToPuckMap[controller::LEFT_ARM] = secondShoulder.first; _jointToPuckMap[controller::RIGHT_ARM] = firstShoulder.first; - userRefLeftArm[3] = glm::vec4(computeUserShoulderPositionFromMeasurements(headMat, secondShoulderPose, true), 1.0f); - userRefRightArm[3] = glm::vec4(computeUserShoulderPositionFromMeasurements(headMat, firstShoulderPose, false), 1.0f); + glm::vec3 leftPos = computeUserShoulderPositionFromMeasurements(_armCircumference, _shoulderWidth, headMat, secondShoulderPose, true); + userRefLeftArm[3] = glm::vec4(leftPos, 1.0f); + glm::vec3 rightPos = computeUserShoulderPositionFromMeasurements(_armCircumference, _shoulderWidth, headMat, firstShoulderPose, false); + userRefRightArm[3] = glm::vec4(rightPos, 1.0f); // compute the post offset from the userRefArm _pucksPostOffset[secondShoulder.first] = computeOffset(Matrices::IDENTITY, userRefLeftArm, secondShoulderPose); diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 329901cd25..41ed494ffb 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -182,6 +182,8 @@ private: float _headPuckZOffset { -0.05f }; float _handPuckYOffset { 0.0f }; float _handPuckZOffset { 0.0f }; + float _armCircumference { 0.33f }; + float _shoulderWidth { 0.48f }; bool _triggersPressedHandled { false }; bool _calibrated { false }; bool _timeTilCalibrationSet { false }; From 2a45c6d3dc170b913cd2c5519c97de4ea2f70aae Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 26 Jul 2017 16:53:18 -0700 Subject: [PATCH 08/45] Removed eigen dependency, fixed Debug Draw IK Chains Also, clarified for special case for secondary shoulder joint look-at constraint. --- cmake/externals/eigen/CMakeLists.txt | 20 ----- cmake/modules/FindEigen.cmake | 26 ------- .../animation/src/AnimInverseKinematics.cpp | 28 +++++-- plugins/openvr/CMakeLists.txt | 5 -- plugins/openvr/src/ViveControllerManager.cpp | 78 +------------------ plugins/openvr/src/ViveControllerManager.h | 5 -- 6 files changed, 21 insertions(+), 141 deletions(-) delete mode 100644 cmake/externals/eigen/CMakeLists.txt delete mode 100644 cmake/modules/FindEigen.cmake diff --git a/cmake/externals/eigen/CMakeLists.txt b/cmake/externals/eigen/CMakeLists.txt deleted file mode 100644 index 15d94576cc..0000000000 --- a/cmake/externals/eigen/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -set(EXTERNAL_NAME eigen) - -include(ExternalProject) -ExternalProject_Add( - ${EXTERNAL_NAME} - URL http://bitbucket.org/eigen/eigen/get/3.3.4.zip - URL_MD5 e337acc279874bc6a56da4d973a723fb - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 -) - -# Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR} CACHE PATH "List of eigen include directories") \ No newline at end of file diff --git a/cmake/modules/FindEigen.cmake b/cmake/modules/FindEigen.cmake deleted file mode 100644 index 4b09ed8f2b..0000000000 --- a/cmake/modules/FindEigen.cmake +++ /dev/null @@ -1,26 +0,0 @@ -# -# FindEigen.cmake -# -# Try to find Eigen include path. -# Once done this will define -# -# EIGEN_INCLUDE_DIRS -# -# Created on 7/14/2017 by Anthony Thibault -# 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 -# - -# setup hints for Eigen search -include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") -hifi_library_search_hints("eigen") - -# locate dir -string(CONCAT EIGEN_INCLUDE_DIRS ${EIGEN_INCLUDE_DIRS} "/src/eigen") - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(EIGEN DEFAULT_MSG EIGEN_INCLUDE_DIRS) - -mark_as_advanced(EIGEN_INCLUDE_DIRS EIGEN_SEARCH_DIRS) \ No newline at end of file diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 10c4011f52..74fbf00067 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -27,9 +27,9 @@ static const int MAX_TARGET_MARKERS = 30; static const float JOINT_CHAIN_INTERP_TIME = 0.25f; static void lookupJointInfo(const AnimInverseKinematics::JointChainInfo& jointChainInfo, - int indexA, int indexB, - const AnimInverseKinematics::JointInfo** jointInfoA, - const AnimInverseKinematics::JointInfo** jointInfoB) { + int indexA, int indexB, + const AnimInverseKinematics::JointInfo** jointInfoA, + const AnimInverseKinematics::JointInfo** jointInfoB) { *jointInfoA = nullptr; *jointInfoB = nullptr; for (size_t i = 0; i < jointChainInfo.jointInfoVec.size(); i++) { @@ -1640,8 +1640,10 @@ void AnimInverseKinematics::debugDrawIKChain(const JointChainInfo& jointChainInf // copy debug joint rotations into the relative poses for (size_t i = 0; i < jointChainInfo.jointInfoVec.size(); i++) { const JointInfo& info = jointChainInfo.jointInfoVec[i]; - poses[info.jointIndex].rot() = info.rot; - poses[info.jointIndex].trans() = info.trans; + if (info.jointIndex != _hipsIndex) { + poses[info.jointIndex].rot() = info.rot; + poses[info.jointIndex].trans() = info.trans; + } } // convert relative poses to absolute @@ -1867,9 +1869,19 @@ void AnimInverseKinematics::setSecondaryTargets(const AnimContext& context) { return; } - // shoulder joint should look-at position of arm joint. + // special case for arm secondary poses. + // determine if shoulder joint should look-at position of arm joint. + bool shoulderShouldLookAtArm = false; const int leftArmIndex = _skeleton->nameToJointIndex("LeftArm"); const int rightArmIndex = _skeleton->nameToJointIndex("RightArm"); + const int leftShoulderIndex = _skeleton->nameToJointIndex("LeftShoulder"); + const int rightShoulderIndex = _skeleton->nameToJointIndex("RightShoulder"); + for (auto& iter : _secondaryTargetsInRigFrame) { + if (iter.first == leftShoulderIndex || iter.first == rightShoulderIndex) { + shoulderShouldLookAtArm = true; + break; + } + } AnimPose rigToGeometryPose = AnimPose(glm::inverse(context.getGeometryToRigMatrix())); for (auto& iter : _secondaryTargetsInRigFrame) { @@ -1883,7 +1895,7 @@ void AnimInverseKinematics::setSecondaryTargets(const AnimContext& context) { } // if parent should "look-at" child joint position. - if (iter.first == leftArmIndex || iter.first == rightArmIndex) { + if (shoulderShouldLookAtArm && (iter.first == leftArmIndex || iter.first == rightArmIndex)) { AnimPose grandParentAbsPose; int grandParentIndex = _skeleton->getParentIndex(parentIndex); @@ -1896,7 +1908,7 @@ void AnimInverseKinematics::setSecondaryTargets(const AnimContext& context) { _relativePoses[parentIndex] = grandParentAbsPose.inverse() * parentAbsPose; } - // AJT: for now ignore translation on secondary poses. + // Ignore translation on secondary poses, to prevent them from distorting the skeleton. glm::vec3 origTrans = _relativePoses[iter.first].trans(); _relativePoses[iter.first] = parentAbsPose.inverse() * absPose; _relativePoses[iter.first].trans() = origTrans; diff --git a/plugins/openvr/CMakeLists.txt b/plugins/openvr/CMakeLists.txt index 6633b99b31..7878ae2d7e 100644 --- a/plugins/openvr/CMakeLists.txt +++ b/plugins/openvr/CMakeLists.txt @@ -18,14 +18,9 @@ if (WIN32) include_hifi_library_headers(octree) add_dependency_external_projects(OpenVR) - add_dependency_external_projects(eigen) find_package(OpenVR REQUIRED) target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) target_link_libraries(${TARGET_NAME} Winmm.lib) - - # header only library - find_package(eigen REQUIRED) - target_include_directories(${TARGET_NAME} PRIVATE ${EIGEN_INCLUDE_DIRS}) endif() diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 395e131124..61a0b33255 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -35,8 +35,6 @@ #include #include -#include - extern PoseData _nextSimPoseData; vr::IVRSystem* acquireOpenVrSystem(); @@ -287,9 +285,7 @@ static const size_t CONTROLLER_HISTORY_SIZE = 90 * 3; ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : controller::InputDevice("Vive"), - _system(system), - _leftControllerHistory(CONTROLLER_HISTORY_SIZE), - _rightControllerHistory(CONTROLLER_HISTORY_SIZE) { + _system(system) { _configStringMap[Config::None] = QString("None"); _configStringMap[Config::Feet] = QString("Feet"); @@ -915,16 +911,6 @@ void ViveControllerManager::InputDevice::handlePoseEvent(float deltaTime, const // transform into avatar frame glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; _poseStateMap[isLeftHand ? controller::LEFT_HAND : controller::RIGHT_HAND] = pose.transform(controllerToAvatar); - - // AJT: TODO ONLY DO THIS IF CALIBRATING! - // record controller history - if (isLeftHand) { - _leftControllerHistory[_leftControllerHistoryCursor] = pose.translation; - _leftControllerHistoryCursor = (_leftControllerHistoryCursor + 1) % CONTROLLER_HISTORY_SIZE; - } else { - _rightControllerHistory[_rightControllerHistoryCursor] = pose.translation; - _rightControllerHistoryCursor = (_rightControllerHistoryCursor + 1) % CONTROLLER_HISTORY_SIZE; - } } bool ViveControllerManager::InputDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) { @@ -1088,64 +1074,6 @@ void ViveControllerManager::InputDevice::calibrateChest(const glm::mat4& default _pucksPostOffset[_validTrackedObjects[CHEST].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultSpine2, _validTrackedObjects[CHEST].second); } -// assuming the user has kept his arms straight to his sides and has rotated his hand controllers -// up and down laterally about his shoulders. This will attempt to locate the users actual shoulders -// by calculating a best fit circle around the points, in the plane of the head's forward normal. -static glm::vec3 computeUserShoulderPositionFromHistory(const glm::mat4& headMat, const std::vector& history) { - glm::mat4 invHeadMat = glm::inverse(headMat); - - // AJT: TODO: we could perhaps reject the approximation if the radius is larger or smaller then we expect. - // AJT: TODO: abort if history is too small or invalid... - - // Take equation for a circle: (x - h)^2 + (y - k)^2 = r^2 - // And put it into the linear form: A * x = b - // - // where A = | (2 * x0) (2 * y0) 1 | - // | (2 * x1) (2 * y1) 1 | - // | . . . | - // | (2 * xn) (2 * yn) 1 | - // - // and b = | x0^2 + y0^2 | - // | x1^2 + y1^2 | - // | . | - // | xn^2 + yn^2 | - // - // and x = | h | - // | k | - // | r^2 - h^2 - k^2 | - // - - // Build aMat and bMat - Eigen::MatrixXf aMat(CONTROLLER_HISTORY_SIZE, 3); - Eigen::MatrixXf bMat(CONTROLLER_HISTORY_SIZE, 1); - for (int r = 0; r < CONTROLLER_HISTORY_SIZE; r++) { - // transform history[r] into local head coordinates - glm::vec3 p = transformPoint(invHeadMat, history[r]); - - // update the aMat with the appropriate 2d history values - aMat(r, 0) = 2.0f * p.x; - aMat(r, 1) = 2.0f * p.y; - aMat(r, 2) = 1.0f; - - // update the bMat with the appropriate 2d history values - bMat(r, 0) = p.x * p.x + p.y * p.y; - } - - // Then use least squares approximation to solve for the best x. - // http://math.mit.edu/~gs/linearalgebra/ila0403.pdf - Eigen::MatrixXf aTransMat = aMat.transpose(); - Eigen::MatrixXf xMat = (aTransMat * aMat).inverse() * aTransMat * bMat; - - // extract the 2d center of the circle from the xMat - glm::vec3 center(xMat(0, 0), xMat(1, 0), 0.0f); - - // we don't need the radius, but it's included here for completeness. - // float radius = center.x * center.x + center.y * center.y - xMat(2, 0); - - // transform center back into sensor coordinates - return transformPoint(headMat, center); -} - // y axis comes out of puck usb port/green light // -z axis comes out of puck center/vive logo static glm::vec3 computeUserShoulderPositionFromMeasurements(float armCirc, float shoulderSpan, const glm::mat4& headMat, const controller::Pose& armPuck, bool isLeftHand) { @@ -1176,10 +1104,6 @@ void ViveControllerManager::InputDevice::calibrateShoulders(const glm::mat4& def glm::mat4 userRefRightArm = refRightArm; glm::mat4 headMat = defaultToReferenceMat * inputCalibration.defaultHeadMat; - /* AJT: TODO REMOVE - userRefLeftArm[3] = glm::vec4(computeUserShoulderPositionFromHistory(headMat, _leftControllerHistory), 1.0f); - userRefRightArm[3] = glm::vec4(computeUserShoulderPositionFromHistory(headMat, _rightControllerHistory), 1.0f); - */ if (firstShoulderPose.translation.x < secondShoulderPose.translation.x) { _jointToPuckMap[controller::LEFT_ARM] = firstShoulder.first; diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 41ed494ffb..1b466d7329 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -192,11 +192,6 @@ private: bool _overrideHands { false }; mutable std::recursive_mutex _lock; - std::vector _leftControllerHistory; - size_t _leftControllerHistoryCursor { 0 }; - std::vector _rightControllerHistory; - size_t _rightControllerHistoryCursor { 0 }; - QString configToString(Config config); friend class ViveControllerManager; }; From 8dc97142827ec1e9fb1f72af5a3711239cbfa58b Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Jul 2017 09:08:34 -0700 Subject: [PATCH 09/45] Pass armCircumference and shoulderWidth to qml. --- .../resources/qml/hifi/tablet/OpenVrConfiguration.qml | 3 +++ plugins/openvr/src/ViveControllerManager.cpp | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index 36424e22b4..e3aff97557 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -878,6 +878,9 @@ Rectangle { var viveController = settings["handController"]; var desktopMode = settings["desktopMode"]; + armCircumference.value = settings.armCircumference; + shoulderWidth.value = settings.shoulderWidth; + if (HmdHead) { headBox.checked = true; headPuckBox.checked = false; diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 61a0b33255..5aaf92ed0b 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -375,6 +375,7 @@ void ViveControllerManager::InputDevice::calibrateFromUI(const controller::Input } static const float CM_TO_M = 0.01f; +static const float M_TO_CM = 100.0f; void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJsonObject configurationSettings) { Locker locker(_lock); @@ -411,6 +412,10 @@ void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJso _armCircumference = shoulderObj["armCircumference"].toDouble() * CM_TO_M; _shoulderWidth = shoulderObj["shoulderWidth"].toDouble() * CM_TO_M; } + } else if (iter.key() == "armCircumference") { + _armCircumference = (float)iter.value().toDouble() * CM_TO_M; + } else if (iter.key() == "shoulderWidth") { + _shoulderWidth = (float)iter.value().toDouble() * CM_TO_M; } iter++; } @@ -429,6 +434,8 @@ QJsonObject ViveControllerManager::InputDevice::configurationSettings() { configurationSettings["HMDHead"] = (_headConfig == HeadConfig::HMD); configurationSettings["handController"] = (_handConfig == HandConfig::HandController); configurationSettings["puckCount"] = (int)_validTrackedObjects.size(); + configurationSettings["armCircumference"] = (double)_armCircumference * M_TO_CM; + configurationSettings["shoulderWidth"] = (double)_shoulderWidth * M_TO_CM; return configurationSettings; } From 8f5c41af2dac88a6e9f3ddb6b07225c0807e47d0 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Jul 2017 09:44:37 -0700 Subject: [PATCH 10/45] changed _controllerPoseMap from unordered_map to map --- interface/src/avatar/MyAvatar.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 5983c6eb0e..298452cad8 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -767,7 +767,7 @@ private: glm::vec3 _hoverReferenceCameraFacing { 0.0f, 0.0f, -1.0f }; // hmd sensor space // all poses are in sensor-frame - std::unordered_map _controllerPoseMap; + std::map _controllerPoseMap; mutable std::mutex _controllerPoseMapMutex; bool _hmdLeanRecenterEnabled = true; From 778c8bf9a749e76fa479c7d45f4f9aa657fb4264 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Jul 2017 10:08:35 -0700 Subject: [PATCH 11/45] clang build error fix --- interface/src/avatar/MySkeletonModel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 4a86e0ce0a..6e29af0d33 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -82,9 +82,9 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { }; for (auto pair : primaryControllers) { - auto pose = myAvatar->getControllerPoseInAvatarFrame(pair.first); - if (pose.isValid()) { - AnimPose pose(pose.getRotation(), pose.getTranslation()); + auto controllerPose = myAvatar->getControllerPoseInAvatarFrame(pair.first); + if (controllerPose.isValid()) { + AnimPose pose(controllerPose.getRotation(), controllerPose.getTranslation()); params.primaryControllerPoses[pair.second] = avatarToRigPose * pose; params.primaryControllerActiveFlags[pair.second] = true; } else { From bcb4cc32d26718ee9c622202cc7339252dc44e87 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Jul 2017 10:26:10 -0700 Subject: [PATCH 12/45] another clang error fix --- interface/src/avatar/MySkeletonModel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 6e29af0d33..6d468c3f30 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -113,9 +113,9 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { }; for (auto pair : secondaryControllers) { - auto pose = myAvatar->getControllerPoseInAvatarFrame(pair.first); - if (pose.isValid()) { - AnimPose pose(pose.getRotation(), pose.getTranslation()); + auto controllerPose = myAvatar->getControllerPoseInAvatarFrame(pair.first); + if (controllerPose.isValid()) { + AnimPose pose(controllerPose.getRotation(), controllerPose.getTranslation()); params.secondaryControllerPoses[pair.second] = avatarToRigPose * pose; params.secondaryControllerActiveFlags[pair.second] = true; } else { From 6c2b6674bb22336a83d95b8830f51248b82b004b Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Jul 2017 17:33:54 -0700 Subject: [PATCH 13/45] load and save shoulderConfig to settings. --- .../qml/hifi/tablet/OpenVrConfiguration.qml | 9 +--- plugins/openvr/src/ViveControllerManager.cpp | 41 +++++++++++++++---- plugins/openvr/src/ViveControllerManager.h | 3 ++ 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index e3aff97557..78c10e2ffa 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -1052,17 +1052,12 @@ Rectangle { "Z": handZOffset.value } - var shoulderObject = { - "override": shouldersChecked, - "armCircumference": armCircumference.value, - "shoulderWidth": shoulderWidth.value - } - var settingsObject = { "bodyConfiguration": trackerConfiguration, "headConfiguration": headObject, "handConfiguration": handObject, - "shoulderConfiguration": shoulderObject, + "armCircumference": armCircumference.value, + "shoulderWidth": shoulderWidth.value, "desktopMode": viveInDesktop.checked } diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 5aaf92ed0b..5a1c23839e 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -167,6 +167,7 @@ void ViveControllerManager::setConfigurationSettings(const QJsonObject configura } } _inputDevice->configureCalibrationSettings(configurationSettings); + saveSettings(); } } @@ -187,6 +188,8 @@ QString ViveControllerManager::configurationLayout() { bool ViveControllerManager::activate() { InputPlugin::activate(); + loadSettings(); + if (!_system) { _system = acquireOpenVrSystem(); } @@ -229,6 +232,8 @@ void ViveControllerManager::deactivate() { auto userInputMapper = DependencyManager::get(); userInputMapper->removeDevice(_inputDevice->_deviceID); _registeredWithInputMapper = false; + + saveSettings(); } bool ViveControllerManager::isHeadControllerMounted() const { @@ -281,7 +286,34 @@ void ViveControllerManager::pluginUpdate(float deltaTime, const controller::Inpu } } -static const size_t CONTROLLER_HISTORY_SIZE = 90 * 3; +void ViveControllerManager::loadSettings() { + Settings settings; + QString nameString = getName(); + settings.beginGroup(nameString); + { + if (_inputDevice) { + const double DEFAULT_ARM_CIRCUMFERENCE = 0.33; + const double DEFAULT_SHOULDER_WIDTH = 0.48; + _inputDevice->_armCircumference = settings.value("armCircumference", QVariant(DEFAULT_ARM_CIRCUMFERENCE)).toDouble(); + _inputDevice->_shoulderWidth = settings.value("shoulderWidth", QVariant(DEFAULT_SHOULDER_WIDTH)).toDouble(); + } + } + settings.endGroup(); +} + +void ViveControllerManager::saveSettings() const { + Settings settings; + QString nameString = getName(); + settings.beginGroup(nameString); + { + if (_inputDevice) { + settings.setValue(QString("armCircumference"), _inputDevice->_armCircumference); + settings.setValue(QString("shoulderWidth"), _inputDevice->_shoulderWidth); + } + } + settings.endGroup(); +} + ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : controller::InputDevice("Vive"), @@ -405,13 +437,6 @@ void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJso } else { _handConfig = HandConfig::HandController; } - } else if (iter.key() == "shoulderConfiguration") { - QJsonObject shoulderObj = iter.value().toObject(); - bool shouldersChecked = shoulderObj["override"].toBool(); - if (shouldersChecked) { - _armCircumference = shoulderObj["armCircumference"].toDouble() * CM_TO_M; - _shoulderWidth = shoulderObj["shoulderWidth"].toDouble() * CM_TO_M; - } } else if (iter.key() == "armCircumference") { _armCircumference = (float)iter.value().toDouble() * CM_TO_M; } else if (iter.key() == "shoulderWidth") { diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 1b466d7329..9a7b2cbc93 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -57,6 +57,9 @@ public: void setRenderControllers(bool renderControllers) { _renderControllers = renderControllers; } + virtual void saveSettings() const override; + virtual void loadSettings() override; + private: class InputDevice : public controller::InputDevice { public: From 25b5cb4762a0969609bfcc9ce474edabaf4f1d02 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 1 Aug 2017 13:17:33 -0700 Subject: [PATCH 14/45] Switch order of precondition and setSecondaryTargets --- libraries/animation/src/AnimInverseKinematics.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 74fbf00067..d17fbebf3a 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -1056,8 +1056,9 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars { PROFILE_RANGE_EX(simulation_animation, "ik/ccd", 0xffff00ff, 0); - preconditionRelativePosesToAvoidLimbLock(context, targets); setSecondaryTargets(context); + preconditionRelativePosesToAvoidLimbLock(context, targets); + solve(context, targets, dt, jointChainInfoVec); } @@ -1610,7 +1611,7 @@ void AnimInverseKinematics::debugDrawRelativePoses(const AnimContext& context) c const vec4 GREEN(0.0f, 1.0f, 0.0f, 1.0f); const vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f); const vec4 GRAY(0.2f, 0.2f, 0.2f, 1.0f); - const float AXIS_LENGTH = 2.0f; // cm + const float AXIS_LENGTH = 10.0f; // cm // draw each pose for (int i = 0; i < (int)poses.size(); i++) { From 24718e242405f422e2fcfc1a64a55be1725611c6 Mon Sep 17 00:00:00 2001 From: Armads Date: Thu, 3 Aug 2017 09:03:23 -0400 Subject: [PATCH 15/45] WL21423 Camera mode on startup is determined by user settings and HMD status --- interface/src/Application.cpp | 44 +++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ceded99f40..8debcbd538 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4146,6 +4146,7 @@ void Application::loadSettings() { //DependencyManager::get()->setAutomaticLODAdjust(false); Menu::getInstance()->loadSettings(); + // If there is a preferred plugin, we probably messed it up with the menu settings, so fix it. auto pluginManager = PluginManager::getInstance(); auto plugins = pluginManager->getPreferredDisplayPlugins(); @@ -4159,24 +4160,44 @@ void Application::loadSettings() { break; } } - } else { + } + + Setting::Handle firstRun { Settings::firstRun, true }; + bool isFirstPerson = false; + if (firstRun.get()) { // If this is our first run, and no preferred devices were set, default to // an HMD device if available. - Setting::Handle firstRun { Settings::firstRun, true }; - if (firstRun.get()) { - auto displayPlugins = pluginManager->getDisplayPlugins(); - for (auto& plugin : displayPlugins) { - if (plugin->isHmd()) { - if (auto action = menu->getActionForOption(plugin->getName())) { - action->setChecked(true); - action->trigger(); - break; - } + auto displayPlugins = pluginManager->getDisplayPlugins(); + for (auto& plugin : displayPlugins) { + if (plugin->isHmd()) { + if (auto action = menu->getActionForOption(plugin->getName())) { + action->setChecked(true); + action->trigger(); + break; } } } + + isFirstPerson = (qApp->isHMDMode()); + } else { + // if this is not the first run, the camera will be initialized differently depending on user settings + + if (qApp->isHMDMode()) { + // if the HMD is active, use first-person camera, unless the appropriate setting is checked + isFirstPerson = menu->isOptionChecked(MenuOption::FirstPersonHMD); + } else { + // if HMD is not active, only use first person if the menu option is checked + isFirstPerson = menu->isOptionChecked(MenuOption::FirstPerson); + } } + // finish initializing the camera, based on everything we checked above. Third person camera will be used if no settings + // dictated that we should be in first person + _myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_THIRD_PERSON); + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, isFirstPerson); + Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !isFirstPerson); + cameraMenuChanged(); + auto inputs = pluginManager->getInputPlugins(); for (auto plugin : inputs) { if (!plugin->isActive()) { @@ -4225,7 +4246,6 @@ void Application::init() { DependencyManager::get()->init(); DependencyManager::get()->init(); - _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); _timerStart.start(); _lastTimeUpdated.start(); From dff4e6e32c2d6af0834139cc26150ffb715240c8 Mon Sep 17 00:00:00 2001 From: Armads Date: Thu, 3 Aug 2017 15:40:38 -0400 Subject: [PATCH 16/45] WL21423 Changing order of camera setup steps --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8debcbd538..7728015d70 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4193,9 +4193,9 @@ void Application::loadSettings() { // finish initializing the camera, based on everything we checked above. Third person camera will be used if no settings // dictated that we should be in first person - _myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_THIRD_PERSON); Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, isFirstPerson); Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !isFirstPerson); + _myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_THIRD_PERSON); cameraMenuChanged(); auto inputs = pluginManager->getInputPlugins(); From 9058a8e2ca73481570ff0198ed90747d2cb82f85 Mon Sep 17 00:00:00 2001 From: vladest Date: Fri, 4 Aug 2017 16:36:00 +0200 Subject: [PATCH 17/45] Cleanup top menu on menu reinstantiation --- interface/resources/qml/hifi/tablet/TabletMenuStack.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml index 2fd33e9cbc..9076cd6c48 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml @@ -114,6 +114,7 @@ Item { } function clearMenus() { + topMenu = null d.clear() } From 85500841fb5ba0b361411aae13713bb9d9e99ccf Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 4 Aug 2017 11:19:09 -0700 Subject: [PATCH 18/45] Crash fix for avatars that are missing "Hips", "Head" and other required joints. --- interface/src/avatar/MyCharacterController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyCharacterController.cpp b/interface/src/avatar/MyCharacterController.cpp index 3d98a0e604..761e2e5bff 100755 --- a/interface/src/avatar/MyCharacterController.cpp +++ b/interface/src/avatar/MyCharacterController.cpp @@ -36,7 +36,7 @@ MyCharacterController::~MyCharacterController() { void MyCharacterController::setDynamicsWorld(btDynamicsWorld* world) { CharacterController::setDynamicsWorld(world); - if (world) { + if (world && _rigidBody) { initRayShotgun(world); } } From 9fdc044ae10d4ae34175169240a4c70bedc5a485 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 4 Aug 2017 11:19:59 -0700 Subject: [PATCH 19/45] Crash fix for web3d overlays that are not the tablet. --- interface/src/ui/overlays/Web3DOverlay.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index d61370151c..e904ff4e4c 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -127,8 +127,9 @@ QString Web3DOverlay::pickURL() { QUrl sourceUrl(_url); if (sourceUrl.scheme() == "http" || sourceUrl.scheme() == "https" || _url.toLower().endsWith(".htm") || _url.toLower().endsWith(".html")) { - - _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); + if (_webSurface) { + _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); + } return "Web3DOverlay.qml"; } else { return QUrl::fromLocalFile(PathUtils::resourcesPath()).toString() + "/" + _url; From d05e1677c3a58272b14c655bbd940a9d73200edd Mon Sep 17 00:00:00 2001 From: beholder Date: Fri, 4 Aug 2017 23:04:15 +0300 Subject: [PATCH 20/45] 6677: Change "Desktop" App to Say "Exit VR" --- scripts/system/hmd.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index 3598b461a4..c95d85ebeb 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -43,17 +43,20 @@ var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); // Independent and Entity mode make people sick; disable them in hmd. var desktopOnlyViews = ['Independent Mode', 'Entity Mode']; +var switchToVR = "Enter VR"; +var switchToDesktop = "Exit VR"; + function onHmdChanged(isHmd) { HMD.closeTablet(); if (isHmd) { button.editProperties({ icon: "icons/tablet-icons/switch-desk-i.svg", - text: "DESKTOP" + text: switchToDesktop }); } else { button.editProperties({ icon: "icons/tablet-icons/switch-vr-i.svg", - text: "VR" + text: switchToVR }); } desktopOnlyViews.forEach(function (view) { @@ -70,7 +73,7 @@ function onClicked() { if (headset) { button = tablet.addButton({ icon: HMD.active ? "icons/tablet-icons/switch-desk-i.svg" : "icons/tablet-icons/switch-vr-i.svg", - text: HMD.active ? "DESKTOP" : "VR", + text: HMD.active ? switchToDesktop : switchToVR, sortOrder: 2 }); onHmdChanged(HMD.active); From 86e348916724fc8dd290569acd9fd44ee0c37e02 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 25 Jul 2017 09:42:42 -0700 Subject: [PATCH 21/45] Updates from Threaded Rendering project --- interface/src/Application.cpp | 49 +++++------- interface/src/Application.h | 9 ++- interface/src/scripting/AudioDevices.cpp | 1 + interface/src/ui/ResourceImageItem.cpp | 3 +- interface/src/ui/SnapshotAnimated.cpp | 3 +- .../src/RenderableWebEntityItem.cpp | 7 +- .../src/RenderableWebEntityItem.h | 2 +- .../plugins/src/plugins/DisplayPlugin.cpp | 17 +++- libraries/plugins/src/plugins/DisplayPlugin.h | 11 ++- libraries/render/src/render/Args.h | 3 - libraries/shared/src/ThreadHelpers.cpp | 77 ++++++++++++++----- libraries/shared/src/ThreadHelpers.h | 13 +++- libraries/shared/src/shared/QtHelpers.cpp | 29 ++++++- libraries/shared/src/shared/QtHelpers.h | 1 + tests/render-perf/src/main.cpp | 2 +- 15 files changed, 155 insertions(+), 72 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d016c9d020..549e5338a0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2256,7 +2256,7 @@ void Application::paintGL() { QMutexLocker viewLocker(&_viewMutex); _viewFrustum.calculate(); } - renderArgs = RenderArgs(_gpuContext, getEntities(), lodManager->getOctreeSizeScale(), + renderArgs = RenderArgs(_gpuContext, lodManager->getOctreeSizeScale(), lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); { @@ -2772,7 +2772,12 @@ bool Application::importSVOFromURL(const QString& urlString) { return true; } -bool _renderRequested { false }; +void Application::onPresent(quint32 frameCount) { + if (shouldPaint()) { + postEvent(this, new QEvent(static_cast(Idle)), Qt::HighEventPriority); + postEvent(this, new QEvent(static_cast(Paint)), Qt::HighEventPriority); + } +} bool Application::event(QEvent* event) { if (!Menu::getInstance()) { @@ -2788,23 +2793,9 @@ bool Application::event(QEvent* event) { // Explicit idle keeps the idle running at a lower interval, but without any rendering // see (windowMinimizedChanged) case Event::Idle: - { - float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed(); - _lastTimeUpdated.start(); - idle(nsecsElapsed); - } - return true; - - case Event::Present: - if (!_renderRequested) { - float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed(); - if (shouldPaint(nsecsElapsed)) { - _renderRequested = true; - _lastTimeUpdated.start(); - idle(nsecsElapsed); - postEvent(this, new QEvent(static_cast(Paint)), Qt::HighEventPriority); - } - } + idle(); + // Clear the event queue of pending idle calls + removePostedEvents(this, Idle); return true; case Event::Paint: @@ -2812,9 +2803,8 @@ bool Application::event(QEvent* event) { // or AvatarInputs will mysteriously move to the bottom-right AvatarInputs::getInstance()->update(); paintGL(); - // wait for the next present event before starting idle / paint again - removePostedEvents(this, Present); - _renderRequested = false; + // Clear the event queue of pending paint calls + removePostedEvents(this, Paint); return true; default: @@ -3633,7 +3623,7 @@ bool Application::acceptSnapshot(const QString& urlString) { static uint32_t _renderedFrameIndex { INVALID_FRAME }; -bool Application::shouldPaint(float nsecsElapsed) { +bool Application::shouldPaint() { if (_aboutToQuit) { return false; } @@ -3653,11 +3643,9 @@ bool Application::shouldPaint(float nsecsElapsed) { (float)paintDelaySamples / paintDelayUsecs << "us"; } #endif - - float msecondsSinceLastUpdate = nsecsElapsed / NSECS_PER_USEC / USECS_PER_MSEC; - + // Throttle if requested - if (displayPlugin->isThrottled() && (msecondsSinceLastUpdate < THROTTLED_SIM_FRAME_PERIOD_MS)) { + if (displayPlugin->isThrottled() && (_lastTimeUpdated.elapsed() < THROTTLED_SIM_FRAME_PERIOD_MS)) { return false; } @@ -3874,7 +3862,7 @@ void setupCpuMonitorThread() { #endif -void Application::idle(float nsecsElapsed) { +void Application::idle() { PerformanceTimer perfTimer("idle"); // Update the deadlock watchdog @@ -3931,7 +3919,8 @@ void Application::idle(float nsecsElapsed) { steamClient->runCallbacks(); } - float secondsSinceLastUpdate = nsecsElapsed / NSECS_PER_MSEC / MSECS_PER_SECOND; + float secondsSinceLastUpdate = (float)_lastTimeUpdated.nsecsElapsed() / NSECS_PER_MSEC / MSECS_PER_SECOND; + _lastTimeUpdated.start(); // If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus. if (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) { @@ -7106,6 +7095,7 @@ void Application::updateDisplayMode() { auto oldDisplayPlugin = _displayPlugin; if (_displayPlugin) { + disconnect(_displayPluginPresentConnection); _displayPlugin->deactivate(); } @@ -7146,6 +7136,7 @@ void Application::updateDisplayMode() { _offscreenContext->makeCurrent(); getApplicationCompositor().setDisplayPlugin(newDisplayPlugin); _displayPlugin = newDisplayPlugin; + _displayPluginPresentConnection = connect(_displayPlugin.get(), &DisplayPlugin::presented, this, &Application::onPresent); offscreenUi->getDesktop()->setProperty("repositionLocked", wasRepositionLocked); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 123aa85e2e..300bd4ac02 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -129,8 +129,7 @@ public: virtual DisplayPluginPointer getActiveDisplayPlugin() const override; enum Event { - Present = DisplayPlugin::Present, - Paint, + Paint = QEvent::User + 1, Idle, Lambda }; @@ -409,6 +408,7 @@ private slots: void clearDomainOctreeDetails(); void clearDomainAvatars(); void onAboutToQuit(); + void onPresent(quint32 frameCount); void resettingDomain(); @@ -455,8 +455,8 @@ private: void cleanupBeforeQuit(); - bool shouldPaint(float nsecsElapsed); - void idle(float nsecsElapsed); + bool shouldPaint(); + void idle(); void update(float deltaTime); // Various helper functions called during update() @@ -518,6 +518,7 @@ private: OffscreenGLCanvas* _offscreenContext { nullptr }; DisplayPluginPointer _displayPlugin; + QMetaObject::Connection _displayPluginPresentConnection; mutable std::mutex _displayPluginLock; InputPluginList _activeInputPlugins; diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index d02f4d8fcf..f2e6dbf4d7 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -12,6 +12,7 @@ #include #include +#include #include "AudioDevices.h" diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index 7b9592fa4c..5b7c1896fe 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -8,7 +8,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -//#include "Application.h" #include "ResourceImageItem.h" #include @@ -16,6 +15,8 @@ #include #include +#include + ResourceImageItem::ResourceImageItem() : QQuickFramebufferObject() { auto textureCache = DependencyManager::get(); connect(textureCache.data(), SIGNAL(spectatorCameraFramebufferReset()), this, SLOT(update())); diff --git a/interface/src/ui/SnapshotAnimated.cpp b/interface/src/ui/SnapshotAnimated.cpp index 70767b007d..3c00be8358 100644 --- a/interface/src/ui/SnapshotAnimated.cpp +++ b/interface/src/ui/SnapshotAnimated.cpp @@ -13,8 +13,9 @@ #include #include #include +#include -#include +#include #include "SnapshotAnimated.h" QTimer* SnapshotAnimated::snapshotAnimatedTimer = NULL; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 9a898b4071..ba8e0c18e7 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -69,7 +69,7 @@ RenderableWebEntityItem::~RenderableWebEntityItem() { } } -bool RenderableWebEntityItem::buildWebSurface(QSharedPointer renderer) { +bool RenderableWebEntityItem::buildWebSurface() { if (_currentWebCount >= MAX_CONCURRENT_WEB_VIEWS) { qWarning() << "Too many concurrent web views to create new view"; return false; @@ -132,6 +132,8 @@ bool RenderableWebEntityItem::buildWebSurface(QSharedPointer handlePointerEvent(event); } }; + + auto renderer = DependencyManager::get(); _mousePressConnection = QObject::connect(renderer.data(), &EntityTreeRenderer::mousePressOnEntity, forwardPointerEvent); _mouseReleaseConnection = QObject::connect(renderer.data(), &EntityTreeRenderer::mouseReleaseOnEntity, forwardPointerEvent); _mouseMoveConnection = QObject::connect(renderer.data(), &EntityTreeRenderer::mouseMoveOnEntity, forwardPointerEvent); @@ -185,8 +187,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { #endif if (!_webSurface) { - auto renderer = qSharedPointerCast(args->_renderData); - if (!buildWebSurface(renderer)) { + if (!buildWebSurface()) { return; } _fadeStartTime = usecTimestampNow(); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 0f5d307766..a2318081b6 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -57,7 +57,7 @@ public: virtual QObject* getRootItem() override; private: - bool buildWebSurface(QSharedPointer renderer); + bool buildWebSurface(); void destroyWebSurface(); glm::vec2 getWindowSize() const; diff --git a/libraries/plugins/src/plugins/DisplayPlugin.cpp b/libraries/plugins/src/plugins/DisplayPlugin.cpp index 747c72c08e..20c72159c4 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.cpp +++ b/libraries/plugins/src/plugins/DisplayPlugin.cpp @@ -18,6 +18,19 @@ void DisplayPlugin::incrementPresentCount() { ++_presentedFrameIndex; - // Alert the app that it needs to paint a new presentation frame - qApp->postEvent(qApp, new QEvent(static_cast(Present)), Qt::HighEventPriority); + { + QMutexLocker locker(&_presentMutex); + _presentCondition.wakeAll(); + } + + emit presented(_presentedFrameIndex); } + +void DisplayPlugin::waitForPresent() { + QMutexLocker locker(&_presentMutex); + while (isActive()) { + if (_presentCondition.wait(&_presentMutex, MSECS_PER_SECOND)) { + break; + } + } +} \ No newline at end of file diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 481a2609fc..9e18ee534d 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include @@ -134,10 +136,6 @@ class DisplayPlugin : public Plugin, public HmdDisplay { Q_OBJECT using Parent = Plugin; public: - enum Event { - Present = QEvent::User + 1 - }; - virtual int getRequiredThreadCount() const { return 0; } virtual bool isHmd() const { return false; } virtual int getHmdScreen() const { return -1; } @@ -221,12 +219,15 @@ public: virtual void cycleDebugOutput() {} + void waitForPresent(); + static const QString& MENU_PATH(); signals: void recommendedFramebufferSizeChanged(const QSize& size); void resetSensorsRequested(); + void presented(quint32 frame); protected: void incrementPresentCount(); @@ -234,6 +235,8 @@ protected: gpu::ContextPointer _gpuContext; private: + QMutex _presentMutex; + QWaitCondition _presentCondition; std::atomic _presentedFrameIndex; mutable std::mutex _paintDelayMutex; QElapsedTimer _paintDelayTimer; diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index d5b5440c32..6a91081c95 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -77,7 +77,6 @@ namespace render { Args() {} Args(const gpu::ContextPointer& context, - QSharedPointer renderData = QSharedPointer(nullptr), float sizeScale = 1.0f, int boundaryLevelAdjust = 0, RenderMode renderMode = DEFAULT_RENDER_MODE, @@ -85,7 +84,6 @@ namespace render { DebugFlags debugFlags = RENDER_DEBUG_NONE, gpu::Batch* batch = nullptr) : _context(context), - _renderData(renderData), _sizeScale(sizeScale), _boundaryLevelAdjust(boundaryLevelAdjust), _renderMode(renderMode), @@ -110,7 +108,6 @@ namespace render { std::shared_ptr _context; std::shared_ptr _blitFramebuffer; std::shared_ptr _shapePipeline; - QSharedPointer _renderData; std::stack _viewFrustums; glm::ivec4 _viewport { 0.0f, 0.0f, 1.0f, 1.0f }; glm::vec3 _boomOffset { 0.0f, 0.0f, 1.0f }; diff --git a/libraries/shared/src/ThreadHelpers.cpp b/libraries/shared/src/ThreadHelpers.cpp index 8f3d16a577..8cf8b85284 100644 --- a/libraries/shared/src/ThreadHelpers.cpp +++ b/libraries/shared/src/ThreadHelpers.cpp @@ -10,29 +10,66 @@ #include +// Support for viewing the thread name in the debugger. +// Note, Qt actually does this for you but only in debug builds +// Code from https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx +// and matches logic in `qt_set_thread_name` in qthread_win.cpp +#ifdef Q_OS_WIN +#include +#pragma pack(push,8) +struct THREADNAME_INFO { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. +}; +#pragma pack(pop) +#endif + +void setThreadName(const std::string& name) { +#ifdef Q_OS_WIN + static const DWORD MS_VC_EXCEPTION = 0x406D1388; + THREADNAME_INFO info{ 0x1000, name.c_str(), (DWORD)-1, 0 }; + __try { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); + } __except (EXCEPTION_EXECUTE_HANDLER) { } +#endif +} + +void moveToNewNamedThread(QObject* object, const QString& name, std::function preStartCallback, std::function startCallback, QThread::Priority priority) { + Q_ASSERT(QThread::currentThread() == object->thread()); + // setup a thread for the NodeList and its PacketReceiver + QThread* thread = new QThread(); + thread->setObjectName(name); + + // Execute any additional work to do before the thread starts (like moving members to the target thread + preStartCallback(thread); + + // Link the in-thread initialization code + QObject::connect(thread, &QThread::started, [name, startCallback] { + if (!name.isEmpty()) { + // Make it easy to spot our thread processes inside the debugger + setThreadName("Hifi_" + name.toStdString()); + } + startCallback(); + }); + + // Make sure the thread will be destroyed and cleaned up + QObject::connect(object, &QObject::destroyed, thread, &QThread::quit); + QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater); + + // put the object on the thread + object->moveToThread(thread); + thread->start(); + if (priority != QThread::InheritPriority) { + thread->setPriority(priority); + } +} void moveToNewNamedThread(QObject* object, const QString& name, std::function startCallback, QThread::Priority priority) { - Q_ASSERT(QThread::currentThread() == object->thread()); - // setup a thread for the NodeList and its PacketReceiver - QThread* thread = new QThread(); - thread->setObjectName(name); - - QString tempName = name; - QObject::connect(thread, &QThread::started, [startCallback] { - startCallback(); - }); - // Make sure the thread will be destroyed and cleaned up - QObject::connect(object, &QObject::destroyed, thread, &QThread::quit); - QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater); - - // put the object on the thread - object->moveToThread(thread); - thread->start(); - if (priority != QThread::InheritPriority) { - thread->setPriority(priority); - } + moveToNewNamedThread(object, name, [](QThread*){}, startCallback, priority); } void moveToNewNamedThread(QObject* object, const QString& name, QThread::Priority priority) { - moveToNewNamedThread(object, name, [] {}, priority); + moveToNewNamedThread(object, name, [](QThread*){}, []{}, priority); } diff --git a/libraries/shared/src/ThreadHelpers.h b/libraries/shared/src/ThreadHelpers.h index 6e024f787a..d236344dc5 100644 --- a/libraries/shared/src/ThreadHelpers.h +++ b/libraries/shared/src/ThreadHelpers.h @@ -32,8 +32,17 @@ void withLock(QMutex& lock, F function) { function(); } -void moveToNewNamedThread(QObject* object, const QString& name, std::function startCallback, QThread::Priority priority = QThread::InheritPriority); -void moveToNewNamedThread(QObject* object, const QString& name, QThread::Priority priority = QThread::InheritPriority); +void moveToNewNamedThread(QObject* object, const QString& name, + std::function preStartCallback, + std::function startCallback, + QThread::Priority priority = QThread::InheritPriority); + +void moveToNewNamedThread(QObject* object, const QString& name, + std::function startCallback, + QThread::Priority priority = QThread::InheritPriority); + +void moveToNewNamedThread(QObject* object, const QString& name, + QThread::Priority priority = QThread::InheritPriority); class ConditionalGuard { public: diff --git a/libraries/shared/src/shared/QtHelpers.cpp b/libraries/shared/src/shared/QtHelpers.cpp index 1ce1c3e07c..3e8c6d57ed 100644 --- a/libraries/shared/src/shared/QtHelpers.cpp +++ b/libraries/shared/src/shared/QtHelpers.cpp @@ -11,11 +11,24 @@ #include #include #include +#include +#include "../Profile.h" Q_LOGGING_CATEGORY(thread_safety, "hifi.thread_safety") namespace hifi { namespace qt { +static QHash threadHash; +static QReadWriteLock threadHashLock; + +void addBlockingForbiddenThread(const QString& name, QThread* thread) { + if (!thread) { + thread = QThread::currentThread(); + } + QWriteLocker locker(&threadHashLock); + threadHash[thread] = name; +} + bool blockingInvokeMethod( const char* function, QObject *obj, const char *member, @@ -30,9 +43,23 @@ bool blockingInvokeMethod( QGenericArgument val7, QGenericArgument val8, QGenericArgument val9) { - if (QThread::currentThread() == qApp->thread()) { + auto currentThread = QThread::currentThread(); + if (currentThread == qApp->thread()) { qCWarning(thread_safety) << "BlockingQueuedConnection invoked on main thread from " << function; + return QMetaObject::invokeMethod(obj, member, + Qt::BlockingQueuedConnection, ret, val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); + } + + { + QReadLocker locker(&threadHashLock); + for (const auto& thread : threadHash.keys()) { + if (currentThread == thread) { + qCWarning(thread_safety) << "BlockingQueuedConnection invoked on forbidden thread " << threadHash[thread]; + } + } } + + PROFILE_RANGE(app, function); return QMetaObject::invokeMethod(obj, member, Qt::BlockingQueuedConnection, ret, val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); } diff --git a/libraries/shared/src/shared/QtHelpers.h b/libraries/shared/src/shared/QtHelpers.h index 5da65a378f..2133119324 100644 --- a/libraries/shared/src/shared/QtHelpers.h +++ b/libraries/shared/src/shared/QtHelpers.h @@ -14,6 +14,7 @@ namespace hifi { namespace qt { +void addBlockingForbiddenThread(const QString& name, QThread* thread = nullptr); bool blockingInvokeMethod( const char* function, diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index ce47a896aa..dbb315a9ae 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -681,7 +681,7 @@ private: _renderCount = _renderThread._presentCount.load(); update(); - RenderArgs renderArgs(_renderThread._gpuContext, _octree, DEFAULT_OCTREE_SIZE_SCALE, + RenderArgs renderArgs(_renderThread._gpuContext, DEFAULT_OCTREE_SIZE_SCALE, 0, RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); From c8ebc299f5aab37ba8db0965b21c12bb837d4a16 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Fri, 4 Aug 2017 23:25:26 +0100 Subject: [PATCH 22/45] Limited Max Length of the Chat Box to 256 - as this exceeds Message. max. --- scripts/system/html/ChatPage.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/html/ChatPage.html b/scripts/system/html/ChatPage.html index e1a3776dd5..9606eeab3e 100644 --- a/scripts/system/html/ChatPage.html +++ b/scripts/system/html/ChatPage.html @@ -195,7 +195,7 @@
- +
From 0e508432c4c745d08d890502d7279c062fc7b105 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 4 Aug 2017 15:27:33 -0700 Subject: [PATCH 23/45] PR comments --- libraries/shared/src/ThreadHelpers.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/libraries/shared/src/ThreadHelpers.cpp b/libraries/shared/src/ThreadHelpers.cpp index 8cf8b85284..654b8e0252 100644 --- a/libraries/shared/src/ThreadHelpers.cpp +++ b/libraries/shared/src/ThreadHelpers.cpp @@ -38,11 +38,14 @@ void setThreadName(const std::string& name) { void moveToNewNamedThread(QObject* object, const QString& name, std::function preStartCallback, std::function startCallback, QThread::Priority priority) { Q_ASSERT(QThread::currentThread() == object->thread()); - // setup a thread for the NodeList and its PacketReceiver + + // Create the target thread QThread* thread = new QThread(); thread->setObjectName(name); - // Execute any additional work to do before the thread starts (like moving members to the target thread + // Execute any additional work to do before the thread starts like moving members to the target thread. + // This is required as QObject::moveToThread isn't virutal, so we can't override it on objects that contain + // an OpenGLContext and ensure that the context moves to the target thread as well. preStartCallback(thread); // Link the in-thread initialization code @@ -54,8 +57,10 @@ void moveToNewNamedThread(QObject* object, const QString& name, std::function Date: Mon, 24 Jul 2017 17:10:45 -0700 Subject: [PATCH 24/45] Changed marketplaces message to be more discoverable --- scripts/system/html/js/marketplacesInject.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 3b3d4b4937..45b2e99018 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -57,7 +57,7 @@ $("body").append( '
' + (!isInitialHiFiPage ? '' : '') + - (isInitialHiFiPage ? '🛈 See also other marketplaces.' : '') + + (isInitialHiFiPage ? '🛈 Get items from Blocks and Clara.io!' : '') + (!isDirectoryPage ? '' : '') + (isDirectoryPage ? '🛈 Select a marketplace to explore.' : '') + '
' From 4570814145dc54c600eaf3b6bcc7b1209f091d81 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Tue, 25 Jul 2017 18:36:39 -0700 Subject: [PATCH 25/45] Added Blocks to marketplace window --- scripts/system/html/img/blocks-tile.png | Bin 0 -> 35130 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 scripts/system/html/img/blocks-tile.png diff --git a/scripts/system/html/img/blocks-tile.png b/scripts/system/html/img/blocks-tile.png new file mode 100644 index 0000000000000000000000000000000000000000..49de535c1c2e908165b7902e562daef419cbc4a2 GIT binary patch literal 35130 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 literal 0 HcmV?d00001 From ca6a323d541dc7311054090acf4df784ebbc05d3 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Wed, 26 Jul 2017 15:21:39 -0700 Subject: [PATCH 26/45] Can drag blocks zip folder into hifi and upload the .obj --- interface/src/Application.cpp | 19 ++++++- interface/src/Application.h | 3 ++ .../src/FileScriptingInterface.cpp | 50 +++++++++++-------- .../src/FileScriptingInterface.h | 2 +- scripts/system/html/js/marketplacesInject.js | 24 +++++++-- scripts/system/html/marketplaces.html | 36 ++++++++----- 6 files changed, 96 insertions(+), 38 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 549e5338a0..4fa973f68c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -230,6 +230,9 @@ static const QString OBJ_EXTENSION = ".obj"; static const QString AVA_JSON_EXTENSION = ".ava.json"; static const QString WEB_VIEW_TAG = "noDownload=true"; +// temporary zip handling for Emily +static const QString ZIP_EXTENSION = ".zip"; + static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND; @@ -260,7 +263,10 @@ const QHash Application::_acceptedExtensi { AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl }, { JSON_EXTENSION, &Application::importJSONFromURL }, { JS_EXTENSION, &Application::askToLoadScript }, - { FST_EXTENSION, &Application::askToSetAvatarUrl } + { FST_EXTENSION, &Application::askToSetAvatarUrl }, + + // temporary zip handling for Emily + { ZIP_EXTENSION, &Application::importFromZIP } }; class DeadlockWatchdogThread : public QThread { @@ -2779,6 +2785,15 @@ void Application::onPresent(quint32 frameCount) { } } +bool Application::importFromZIP(const QString& filePath) { + qDebug() << "A zip file has been dropped in: " << filePath; + QUrl empty = ""; + qApp->getFileDownloadInterface()->runUnzip(filePath, empty, false, true); + return true; +} + +bool _renderRequested { false }; + bool Application::event(QEvent* event) { if (!Menu::getInstance()) { return false; @@ -6218,7 +6233,7 @@ void Application::addAssetToWorldFromURLRequestFinished() { if (tempFile.open(QIODevice::WriteOnly)) { tempFile.write(request->getData()); addAssetToWorldInfoClear(filename); // Remove message from list; next one added will have a different key. - qApp->getFileDownloadInterface()->runUnzip(downloadPath, url, true); + qApp->getFileDownloadInterface()->runUnzip(downloadPath, url, true, false); } else { QString errorInfo = "Couldn't open temporary file for download"; qWarning(interfaceapp) << errorInfo; diff --git a/interface/src/Application.h b/interface/src/Application.h index 300bd4ac02..dcd1a9a532 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -482,6 +482,9 @@ private: bool importJSONFromURL(const QString& urlString); bool importSVOFromURL(const QString& urlString); + // temporary zip handling for Emily + bool importFromZIP(const QString& filePath); + bool nearbyEntitiesAreReadyForPhysics(); int processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode); void trackIncomingOctreePacket(ReceivedMessage& message, SharedNodePointer sendingNode, bool wasStatsPacket); diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index 30d0a3a201..7d8458598d 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -32,12 +32,19 @@ FileScriptingInterface::FileScriptingInterface(QObject* parent) : QObject(parent // nothing for now } -void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd) { +void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd, bool isBlocks) { qCDebug(scriptengine) << "Url that was downloaded: " + url.toString(); qCDebug(scriptengine) << "Path where download is saved: " + path; QString fileName = "/" + path.section("/", -1); QString tempDir = path; - tempDir.remove(fileName); + if (!isBlocks) { + tempDir.remove(fileName); + } else { + QTemporaryDir blocks; + tempDir = blocks.path(); + path.remove("file:///"); + } + qCDebug(scriptengine) << "Temporary directory at: " + tempDir; if (!isTempDir(tempDir)) { qCDebug(scriptengine) << "Temporary directory mismatch; risk of losing files"; @@ -45,6 +52,7 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd) { } QString file = unzipFile(path, tempDir); + qCDebug(scriptengine) << "Unzipped file: " << file; QString filename = QUrl::fromLocalFile(file).toString(); if (file != "") { qCDebug(scriptengine) << "File to upload: " + filename; @@ -54,6 +62,26 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd) { emit unzipResult(path, filename, autoAdd); } +QString FileScriptingInterface::unzipFile(QString path, QString tempDir) { + + QDir dir(path); + QString dirName = dir.path(); + qCDebug(scriptengine) << "Directory to unzip: " << dirName; + QString target = tempDir + "/model_repo"; + QStringList list = JlCompress::extractDir(dirName, target); + + qCDebug(scriptengine) << list; + + if (!list.isEmpty()) { + return list.front(); + } + else { + qCDebug(scriptengine) << "Extraction failed"; + return ""; + } + +} + // fix to check that we are only referring to a temporary directory bool FileScriptingInterface::isTempDir(QString tempDir) { QString folderName = "/" + tempDir.section("/", -1); @@ -92,24 +120,6 @@ void FileScriptingInterface::downloadZip(QString path, const QString link) { request->send(); } -QString FileScriptingInterface::unzipFile(QString path, QString tempDir) { - - QDir dir(path); - QString dirName = dir.path(); - QString target = tempDir + "/model_repo"; - QStringList list = JlCompress::extractDir(dirName, target); - - qCDebug(scriptengine) << list; - - if (!list.isEmpty()) { - return list.front(); - } else { - qCDebug(scriptengine) << "Extraction failed"; - return ""; - } - -} - // this function is not in use void FileScriptingInterface::recursiveFileScan(QFileInfo file, QString* dirName) { /*if (!file.isDir()) { diff --git a/libraries/script-engine/src/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h index 5e9a6029e8..dc5ffdace8 100644 --- a/libraries/script-engine/src/FileScriptingInterface.h +++ b/libraries/script-engine/src/FileScriptingInterface.h @@ -24,7 +24,7 @@ public: public slots: QString convertUrlToPath(QUrl url); - void runUnzip(QString path, QUrl url, bool autoAdd); + void runUnzip(QString path, QUrl url, bool autoAdd, bool isBlocks); QString getTempDir(); signals: diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 45b2e99018..0ee337a2ef 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -33,8 +33,8 @@ $("head").append( '' + ); + } + function updateClaraCode() { // Have to repeatedly update Clara page because its content can change dynamically without location.href changing. @@ -322,10 +335,12 @@ var DIRECTORY = 0; var HIFI = 1; - var CLARA = 2; + var BLOCKS = 2; + var CLARA = 3; var pageType = DIRECTORY; if (location.href.indexOf("highfidelity.com/") !== -1) { pageType = HIFI; } + if (location.href.indexOf("google.com/") !== -1) { pageType = BLOCKS; } if (location.href.indexOf("clara.io/") !== -1) { pageType = CLARA; } injectCommonCode(pageType === DIRECTORY); @@ -336,6 +351,9 @@ case HIFI: injectHiFiCode(); break; + case BLOCKS: + injectBlocksCode(); + break; case CLARA: injectClaraCode(); break; diff --git a/scripts/system/html/marketplaces.html b/scripts/system/html/marketplaces.html index 6051a9df96..8c5fe15429 100644 --- a/scripts/system/html/marketplaces.html +++ b/scripts/system/html/marketplaces.html @@ -30,19 +30,31 @@
-
-
- +
+
+
+ +
+
+

Blocks, released by Google, allows anyone to create 3D models using just a few simple tools. Browse through other users' creations for low-poly assets to add to your world.

+
+
-
-

Clara.io has thousands of models available for importing into High Fidelity. Follow these steps for the best experience:

-
    -
  1. Create an account here or log in as an existing user.
  2. -
  3. Choose a model from the list and click “Download to High Fidelity”.
  4. -
-
- -
+
+
+
+
+
+ +
+
+

Clara.io has thousands of models available for importing into High Fidelity. Follow these steps for the best experience:

+
    +
  1. Create an account here or log in as an existing user.
  2. +
  3. Choose a model from the list and click “Download to High Fidelity”.
  4. +
+
+
From ed1ed4f3e0e16b6abc65d185cfa675f1460a6a04 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Wed, 26 Jul 2017 16:58:23 -0700 Subject: [PATCH 27/45] Blocks drag does not work yet --- interface/src/Application.cpp | 23 +++++++++++++++---- interface/src/Application.h | 4 ++-- .../src/FileScriptingInterface.cpp | 21 +++++++++-------- .../src/FileScriptingInterface.h | 4 ++-- 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4fa973f68c..be926cd7ac 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6263,12 +6263,21 @@ void Application::addAssetToWorldUnzipFailure(QString filePath) { addAssetToWorldError(filename, "Couldn't unzip file " + filename + "."); } -void Application::addAssetToWorld(QString filePath) { +void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBlocks) { // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). + qCDebug(interfaceapp) << "File about to be uploaded: " << filePath; + qCDebug(interfaceapp) << "Original zip folder: " << zipFile; QString path = QUrl(filePath).toLocalFile(); QString filename = filenameFromPath(path); - QString mapping = "/" + filename; + QString mapping; + if (isBlocks) { + QString assetFolder = zipFile.section("/", -1); + assetFolder.remove(".zip"); + mapping = "/" + assetFolder + "/" + filename; + } else { + mapping = "/" + filename; + } // Test repeated because possibly different code paths. if (!DependencyManager::get()->getThisNodeCanWriteAssets()) { @@ -6639,15 +6648,19 @@ void Application::onAssetToWorldMessageBoxClosed() { } -void Application::handleUnzip(QString zipFile, QString unzipFile, bool autoAdd) { +void Application::handleUnzip(QString zipFile, QStringList unzipFile, bool autoAdd, bool isBlocks) { if (autoAdd) { if (!unzipFile.isEmpty()) { - addAssetToWorld(unzipFile); + qCDebug(interfaceapp) << "My folder contents: " << unzipFile; + for (int i = 0; i < unzipFile.length(); i++) { + qCDebug(interfaceapp) << "Preparing file for asset server: " << unzipFile.at(i); + addAssetToWorld(unzipFile.at(i), zipFile, isBlocks); + } } else { addAssetToWorldUnzipFailure(zipFile); } } else { - showAssetServerWidget(unzipFile); + showAssetServerWidget(unzipFile.first()); } } diff --git a/interface/src/Application.h b/interface/src/Application.h index dcd1a9a532..033cca8452 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -331,14 +331,14 @@ public slots: // FIXME: Move addAssetToWorld* methods to own class? void addAssetToWorldFromURL(QString url); void addAssetToWorldFromURLRequestFinished(); - void addAssetToWorld(QString filePath); + void addAssetToWorld(QString filePath, QString zipFile, bool isBlocks); void addAssetToWorldUnzipFailure(QString filePath); void addAssetToWorldWithNewMapping(QString filePath, QString mapping, int copy); void addAssetToWorldUpload(QString filePath, QString mapping); void addAssetToWorldSetMapping(QString filePath, QString mapping, QString hash); void addAssetToWorldAddEntity(QString filePath, QString mapping); - void handleUnzip(QString sourceFile, QString destinationFile, bool autoAdd); + void handleUnzip(QString sourceFile, QStringList destinationFile, bool autoAdd, bool isBlocks); FileScriptingInterface* getFileDownloadInterface() { return _fileDownload; } diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index 7d8458598d..5ba2868365 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -51,18 +51,21 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd, bool return; } - QString file = unzipFile(path, tempDir); - qCDebug(scriptengine) << "Unzipped file: " << file; - QString filename = QUrl::fromLocalFile(file).toString(); - if (file != "") { + QStringList fileList = unzipFile(path, tempDir); + qCDebug(scriptengine) << "Unzipped file list: " << fileList; + QString filename = QUrl::fromLocalFile(fileList.first()).toString(); + + if (filename != "") { qCDebug(scriptengine) << "File to upload: " + filename; - } else { + } + else { qCDebug(scriptengine) << "Unzip failed"; } - emit unzipResult(path, filename, autoAdd); + emit unzipResult(path, fileList, autoAdd, isBlocks); + } -QString FileScriptingInterface::unzipFile(QString path, QString tempDir) { +QStringList FileScriptingInterface::unzipFile(QString path, QString tempDir) { QDir dir(path); QString dirName = dir.path(); @@ -73,11 +76,11 @@ QString FileScriptingInterface::unzipFile(QString path, QString tempDir) { qCDebug(scriptengine) << list; if (!list.isEmpty()) { - return list.front(); + return list; } else { qCDebug(scriptengine) << "Extraction failed"; - return ""; + return list; } } diff --git a/libraries/script-engine/src/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h index dc5ffdace8..806fa7fd9b 100644 --- a/libraries/script-engine/src/FileScriptingInterface.h +++ b/libraries/script-engine/src/FileScriptingInterface.h @@ -28,11 +28,11 @@ public slots: QString getTempDir(); signals: - void unzipResult(QString zipFile, QString unzipFile, bool autoAdd); + void unzipResult(QString zipFile, QStringList unzipFile, bool autoAdd, bool isBlocks); private: bool isTempDir(QString tempDir); - QString unzipFile(QString path, QString tempDir); + QStringList unzipFile(QString path, QString tempDir); void recursiveFileScan(QFileInfo file, QString* dirName); void downloadZip(QString path, const QString link); From c3d943ff72a2aea26e5dec70249496b9328b9a66 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Wed, 26 Jul 2017 18:57:19 -0700 Subject: [PATCH 28/45] Drag zip folder works --- interface/src/Application.cpp | 36 +++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index be926cd7ac..b52c3321b6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2788,7 +2788,7 @@ void Application::onPresent(quint32 frameCount) { bool Application::importFromZIP(const QString& filePath) { qDebug() << "A zip file has been dropped in: " << filePath; QUrl empty = ""; - qApp->getFileDownloadInterface()->runUnzip(filePath, empty, false, true); + qApp->getFileDownloadInterface()->runUnzip(filePath, empty, true, true); return true; } @@ -6266,6 +6266,38 @@ void Application::addAssetToWorldUnzipFailure(QString filePath) { void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBlocks) { // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). + qCDebug(interfaceapp) << "File about to be uploaded: " << filePath; + qCDebug(interfaceapp) << "Original zip folder: " << zipFile; + QString mapping; + QString path = filePath; + QString filename = filenameFromPath(filePath); + if (isBlocks) { + QString assetFolder = zipFile.section("/", -1); + assetFolder.remove(".zip"); + mapping = "/" + assetFolder + "/" + filenameFromPath(filename); + } + else { + path = QUrl(filePath).toLocalFile(); + filename = filenameFromPath(path); + mapping = "/" + filename; + } + + // Test repeated because possibly different code paths. + if (!DependencyManager::get()->getThisNodeCanWriteAssets()) { + QString errorInfo = "You do not have permissions to write to the Asset Server."; + qWarning(interfaceapp) << "Error downloading model: " + errorInfo; + addAssetToWorldError(filename, errorInfo); + return; + } + + addAssetToWorldInfo(filename, "Adding " + mapping.mid(1) + " to the Asset Server."); + + addAssetToWorldWithNewMapping(path, mapping, 0); +} + +/**void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBlocks) { + // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). + qCDebug(interfaceapp) << "File about to be uploaded: " << filePath; qCDebug(interfaceapp) << "Original zip folder: " << zipFile; QString path = QUrl(filePath).toLocalFile(); @@ -6290,7 +6322,7 @@ void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBloc addAssetToWorldInfo(filename, "Adding " + mapping.mid(1) + " to the Asset Server."); addAssetToWorldWithNewMapping(path, mapping, 0); -} +}*/ void Application::addAssetToWorldWithNewMapping(QString filePath, QString mapping, int copy) { auto request = DependencyManager::get()->createGetMappingRequest(mapping); From c6d5b1a5e627b285d09c2ead61ec8471a8c062d8 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Thu, 27 Jul 2017 13:33:53 -0700 Subject: [PATCH 29/45] Removed extraneous print statements --- interface/src/Application.cpp | 36 +------------------ .../src/FileScriptingInterface.cpp | 1 - 2 files changed, 1 insertion(+), 36 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b52c3321b6..004b1e91e0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6265,9 +6265,6 @@ void Application::addAssetToWorldUnzipFailure(QString filePath) { void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBlocks) { // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). - - qCDebug(interfaceapp) << "File about to be uploaded: " << filePath; - qCDebug(interfaceapp) << "Original zip folder: " << zipFile; QString mapping; QString path = filePath; QString filename = filenameFromPath(filePath); @@ -6275,8 +6272,7 @@ void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBloc QString assetFolder = zipFile.section("/", -1); assetFolder.remove(".zip"); mapping = "/" + assetFolder + "/" + filenameFromPath(filename); - } - else { + } else { path = QUrl(filePath).toLocalFile(); filename = filenameFromPath(path); mapping = "/" + filename; @@ -6295,35 +6291,6 @@ void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBloc addAssetToWorldWithNewMapping(path, mapping, 0); } -/**void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBlocks) { - // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). - - qCDebug(interfaceapp) << "File about to be uploaded: " << filePath; - qCDebug(interfaceapp) << "Original zip folder: " << zipFile; - QString path = QUrl(filePath).toLocalFile(); - QString filename = filenameFromPath(path); - QString mapping; - if (isBlocks) { - QString assetFolder = zipFile.section("/", -1); - assetFolder.remove(".zip"); - mapping = "/" + assetFolder + "/" + filename; - } else { - mapping = "/" + filename; - } - - // Test repeated because possibly different code paths. - if (!DependencyManager::get()->getThisNodeCanWriteAssets()) { - QString errorInfo = "You do not have permissions to write to the Asset Server."; - qWarning(interfaceapp) << "Error downloading model: " + errorInfo; - addAssetToWorldError(filename, errorInfo); - return; - } - - addAssetToWorldInfo(filename, "Adding " + mapping.mid(1) + " to the Asset Server."); - - addAssetToWorldWithNewMapping(path, mapping, 0); -}*/ - void Application::addAssetToWorldWithNewMapping(QString filePath, QString mapping, int copy) { auto request = DependencyManager::get()->createGetMappingRequest(mapping); @@ -6683,7 +6650,6 @@ void Application::onAssetToWorldMessageBoxClosed() { void Application::handleUnzip(QString zipFile, QStringList unzipFile, bool autoAdd, bool isBlocks) { if (autoAdd) { if (!unzipFile.isEmpty()) { - qCDebug(interfaceapp) << "My folder contents: " << unzipFile; for (int i = 0; i < unzipFile.length(); i++) { qCDebug(interfaceapp) << "Preparing file for asset server: " << unzipFile.at(i); addAssetToWorld(unzipFile.at(i), zipFile, isBlocks); diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index 5ba2868365..af1cf7994a 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -52,7 +52,6 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd, bool } QStringList fileList = unzipFile(path, tempDir); - qCDebug(scriptengine) << "Unzipped file list: " << fileList; QString filename = QUrl::fromLocalFile(fileList.first()).toString(); if (filename != "") { From 9d2be28a8a2c4d0cff2d1737fd090535e5db49bd Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Fri, 28 Jul 2017 10:21:03 -0700 Subject: [PATCH 30/45] troubleshooting Blocks injection --- scripts/system/html/js/marketplacesInject.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 0ee337a2ef..0fbd9a778e 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -96,11 +96,13 @@ function injectBlocksCode() { // Make space for marketplaces footer in Blocks pages. - $("head").append( - '' + /*$("body").append( + '
' + + '' + + '
' + );*/ + $("body").append( + 'style= "bottom: 135px" ' ); } From 692aa4c4b9f05fced7f2110fa1d377d2eb048a9b Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Fri, 28 Jul 2017 15:13:24 -0700 Subject: [PATCH 31/45] Clara download working again, still can drag in Blocks zip --- interface/src/Application.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 004b1e91e0..963ef22482 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6267,14 +6267,12 @@ void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBloc // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). QString mapping; QString path = filePath; - QString filename = filenameFromPath(filePath); + QString filename = filenameFromPath(path); if (isBlocks) { QString assetFolder = zipFile.section("/", -1); assetFolder.remove(".zip"); - mapping = "/" + assetFolder + "/" + filenameFromPath(filename); + mapping = "/" + assetFolder + "/" + filename; } else { - path = QUrl(filePath).toLocalFile(); - filename = filenameFromPath(path); mapping = "/" + filename; } From 06ff76695288009a5f04241fc2df801001c59d91 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Wed, 2 Aug 2017 10:51:40 -0700 Subject: [PATCH 32/45] Drag zip cleaned up, marketplaces stylized, back button fixed --- interface/src/Application.cpp | 11 +++++---- interface/src/Application.h | 2 -- scripts/system/html/js/marketplacesInject.js | 16 +++++++++---- scripts/system/html/marketplaces.html | 24 +++++++++++--------- 4 files changed, 31 insertions(+), 22 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 963ef22482..90f4198383 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -229,8 +229,6 @@ static const QString FBX_EXTENSION = ".fbx"; static const QString OBJ_EXTENSION = ".obj"; static const QString AVA_JSON_EXTENSION = ".ava.json"; static const QString WEB_VIEW_TAG = "noDownload=true"; - -// temporary zip handling for Emily static const QString ZIP_EXTENSION = ".zip"; static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; @@ -264,8 +262,6 @@ const QHash Application::_acceptedExtensi { JSON_EXTENSION, &Application::importJSONFromURL }, { JS_EXTENSION, &Application::askToLoadScript }, { FST_EXTENSION, &Application::askToSetAvatarUrl }, - - // temporary zip handling for Emily { ZIP_EXTENSION, &Application::importFromZIP } }; @@ -6355,7 +6351,12 @@ void Application::addAssetToWorldSetMapping(QString filePath, QString mapping, Q qWarning(interfaceapp) << "Error downloading model: " + errorInfo; addAssetToWorldError(filenameFromPath(filePath), errorInfo); } else { - addAssetToWorldAddEntity(filePath, mapping); + // to prevent files that aren't models from being loaded into world automatically + if (filePath.endsWith(".obj") || filePath.endsWith(".fbx")) { + addAssetToWorldAddEntity(filePath, mapping); + } else { + addAssetToWorldInfoDone(filenameFromPath(filePath)); + } } request->deleteLater(); }); diff --git a/interface/src/Application.h b/interface/src/Application.h index 033cca8452..6731cd8611 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -481,8 +481,6 @@ private: bool importJSONFromURL(const QString& urlString); bool importSVOFromURL(const QString& urlString); - - // temporary zip handling for Emily bool importFromZIP(const QString& filePath); bool nearbyEntitiesAreReadyForPhysics(); diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 0fbd9a778e..92fa869242 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -65,7 +65,10 @@ // Footer actions. $("#back-button").on("click", function () { - window.history.back(); + if (window.history.state != null) window.history.back(); + // to fix back button issue when in directory + //else window.location = "http://www.highfidelity.com/marketplace"; + else window.location = "https://metaverse.highfidelity.com/marketplace?"; }); $("#all-markets").on("click", function () { EventBridge.emitWebEvent(GOTO_DIRECTORY); @@ -79,9 +82,11 @@ letUsKnow.replaceWith(letUsKnow.html()); // Add button links. + + /* Blocks not yet implemented $('#exploreBlocksMarketplace').on('click', function () { window.location = "https://vr.google.com/objects"; - }); + });*/ $('#exploreClaraMarketplace').on('click', function () { window.location = "https://clara.io/library?gameCheck=true&public=true"; }); @@ -102,7 +107,7 @@ '
' );*/ $("body").append( - 'style= "bottom: 135px" ' + '

hello

' ); } @@ -345,6 +350,7 @@ if (location.href.indexOf("google.com/") !== -1) { pageType = BLOCKS; } if (location.href.indexOf("clara.io/") !== -1) { pageType = CLARA; } + //if (pageType != BLOCKS) injectCommonCode(pageType === DIRECTORY); switch (pageType) { case DIRECTORY: @@ -354,7 +360,9 @@ injectHiFiCode(); break; case BLOCKS: - injectBlocksCode(); + console.log("in Blocks"); + //injectBlocksCode(); + console.log("blocks injection"); break; case CLARA: injectClaraCode(); diff --git a/scripts/system/html/marketplaces.html b/scripts/system/html/marketplaces.html index 8c5fe15429..11c9e208ca 100644 --- a/scripts/system/html/marketplaces.html +++ b/scripts/system/html/marketplaces.html @@ -25,24 +25,26 @@

This is the default High Fidelity marketplace. Viewing and downloading content from here is fully supported in Interface.

-
- -
-
+
+ +
+
+
@@ -53,9 +55,9 @@
  • Create an account here or log in as an existing user.
  • Choose a model from the list and click “Download to High Fidelity”.
  • -
    - -
    +
    +
    +
    From ebc28a655797da21af274f033ed43da7f1fd5158 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Wed, 2 Aug 2017 12:24:14 -0700 Subject: [PATCH 33/45] Renamed isBlocks to isZip, added debug print for non-entity files --- interface/src/Application.cpp | 9 +++++---- interface/src/Application.h | 4 ++-- libraries/script-engine/src/FileScriptingInterface.cpp | 6 +++--- libraries/script-engine/src/FileScriptingInterface.h | 4 ++-- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 90f4198383..ff4eb87dce 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6259,12 +6259,12 @@ void Application::addAssetToWorldUnzipFailure(QString filePath) { addAssetToWorldError(filename, "Couldn't unzip file " + filename + "."); } -void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBlocks) { +void Application::addAssetToWorld(QString filePath, QString zipFile, bool isZip) { // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). QString mapping; QString path = filePath; QString filename = filenameFromPath(path); - if (isBlocks) { + if (isZip) { QString assetFolder = zipFile.section("/", -1); assetFolder.remove(".zip"); mapping = "/" + assetFolder + "/" + filename; @@ -6355,6 +6355,7 @@ void Application::addAssetToWorldSetMapping(QString filePath, QString mapping, Q if (filePath.endsWith(".obj") || filePath.endsWith(".fbx")) { addAssetToWorldAddEntity(filePath, mapping); } else { + qCDebug(interfaceapp) << "Zipped contents are not valid entity files"; addAssetToWorldInfoDone(filenameFromPath(filePath)); } } @@ -6646,12 +6647,12 @@ void Application::onAssetToWorldMessageBoxClosed() { } -void Application::handleUnzip(QString zipFile, QStringList unzipFile, bool autoAdd, bool isBlocks) { +void Application::handleUnzip(QString zipFile, QStringList unzipFile, bool autoAdd, bool isZip) { if (autoAdd) { if (!unzipFile.isEmpty()) { for (int i = 0; i < unzipFile.length(); i++) { qCDebug(interfaceapp) << "Preparing file for asset server: " << unzipFile.at(i); - addAssetToWorld(unzipFile.at(i), zipFile, isBlocks); + addAssetToWorld(unzipFile.at(i), zipFile, isZip); } } else { addAssetToWorldUnzipFailure(zipFile); diff --git a/interface/src/Application.h b/interface/src/Application.h index 6731cd8611..f8eb393f9e 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -331,14 +331,14 @@ public slots: // FIXME: Move addAssetToWorld* methods to own class? void addAssetToWorldFromURL(QString url); void addAssetToWorldFromURLRequestFinished(); - void addAssetToWorld(QString filePath, QString zipFile, bool isBlocks); + void addAssetToWorld(QString filePath, QString zipFile, bool isZip); void addAssetToWorldUnzipFailure(QString filePath); void addAssetToWorldWithNewMapping(QString filePath, QString mapping, int copy); void addAssetToWorldUpload(QString filePath, QString mapping); void addAssetToWorldSetMapping(QString filePath, QString mapping, QString hash); void addAssetToWorldAddEntity(QString filePath, QString mapping); - void handleUnzip(QString sourceFile, QStringList destinationFile, bool autoAdd, bool isBlocks); + void handleUnzip(QString sourceFile, QStringList destinationFile, bool autoAdd, bool isZip); FileScriptingInterface* getFileDownloadInterface() { return _fileDownload; } diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index af1cf7994a..0baf3034e8 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -32,12 +32,12 @@ FileScriptingInterface::FileScriptingInterface(QObject* parent) : QObject(parent // nothing for now } -void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd, bool isBlocks) { +void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd, bool isZip) { qCDebug(scriptengine) << "Url that was downloaded: " + url.toString(); qCDebug(scriptengine) << "Path where download is saved: " + path; QString fileName = "/" + path.section("/", -1); QString tempDir = path; - if (!isBlocks) { + if (!isZip) { tempDir.remove(fileName); } else { QTemporaryDir blocks; @@ -60,7 +60,7 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd, bool else { qCDebug(scriptengine) << "Unzip failed"; } - emit unzipResult(path, fileList, autoAdd, isBlocks); + emit unzipResult(path, fileList, autoAdd, isZip); } diff --git a/libraries/script-engine/src/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h index 806fa7fd9b..4069e7cc78 100644 --- a/libraries/script-engine/src/FileScriptingInterface.h +++ b/libraries/script-engine/src/FileScriptingInterface.h @@ -24,11 +24,11 @@ public: public slots: QString convertUrlToPath(QUrl url); - void runUnzip(QString path, QUrl url, bool autoAdd, bool isBlocks); + void runUnzip(QString path, QUrl url, bool autoAdd, bool isZip); QString getTempDir(); signals: - void unzipResult(QString zipFile, QStringList unzipFile, bool autoAdd, bool isBlocks); + void unzipResult(QString zipFile, QStringList unzipFile, bool autoAdd, bool isZip); private: bool isTempDir(QString tempDir); From c2cb7573c5678e3c7d21100a922c7515a6183bbc Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Thu, 3 Aug 2017 09:42:39 -0700 Subject: [PATCH 34/45] Removed commented out blocks code --- scripts/system/html/js/marketplacesInject.js | 34 ++------------------ scripts/system/html/marketplaces.html | 14 -------- 2 files changed, 3 insertions(+), 45 deletions(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 92fa869242..8a8cf62008 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -57,7 +57,7 @@ $("body").append( '
    ' + (!isInitialHiFiPage ? '' : '') + - (isInitialHiFiPage ? '🛈 Get items from Blocks and Clara.io!' : '') + + (isInitialHiFiPage ? '🛈 Get items from Clara.io!' : '') + (!isDirectoryPage ? '' : '') + (isDirectoryPage ? '🛈 Select a marketplace to explore.' : '') + '
    ' @@ -65,10 +65,7 @@ // Footer actions. $("#back-button").on("click", function () { - if (window.history.state != null) window.history.back(); - // to fix back button issue when in directory - //else window.location = "http://www.highfidelity.com/marketplace"; - else window.location = "https://metaverse.highfidelity.com/marketplace?"; + (window.history.state != null) ? window.history.back() : window.location = "https://metaverse.highfidelity.com/marketplace?"; }); $("#all-markets").on("click", function () { EventBridge.emitWebEvent(GOTO_DIRECTORY); @@ -82,11 +79,6 @@ letUsKnow.replaceWith(letUsKnow.html()); // Add button links. - - /* Blocks not yet implemented - $('#exploreBlocksMarketplace').on('click', function () { - window.location = "https://vr.google.com/objects"; - });*/ $('#exploreClaraMarketplace').on('click', function () { window.location = "https://clara.io/library?gameCheck=true&public=true"; }); @@ -99,18 +91,6 @@ // Nothing to do. } - function injectBlocksCode() { - // Make space for marketplaces footer in Blocks pages. - /*$("body").append( - '
    ' + - '' + - '
    ' - );*/ - $("body").append( - '

    hello

    ' - ); - } - function updateClaraCode() { // Have to repeatedly update Clara page because its content can change dynamically without location.href changing. @@ -342,15 +322,12 @@ var DIRECTORY = 0; var HIFI = 1; - var BLOCKS = 2; - var CLARA = 3; + var CLARA = 2; var pageType = DIRECTORY; if (location.href.indexOf("highfidelity.com/") !== -1) { pageType = HIFI; } - if (location.href.indexOf("google.com/") !== -1) { pageType = BLOCKS; } if (location.href.indexOf("clara.io/") !== -1) { pageType = CLARA; } - //if (pageType != BLOCKS) injectCommonCode(pageType === DIRECTORY); switch (pageType) { case DIRECTORY: @@ -359,11 +336,6 @@ case HIFI: injectHiFiCode(); break; - case BLOCKS: - console.log("in Blocks"); - //injectBlocksCode(); - console.log("blocks injection"); - break; case CLARA: injectClaraCode(); break; diff --git a/scripts/system/html/marketplaces.html b/scripts/system/html/marketplaces.html index 11c9e208ca..9e001c064f 100644 --- a/scripts/system/html/marketplaces.html +++ b/scripts/system/html/marketplaces.html @@ -31,20 +31,6 @@
    -
    From 8c9ce829867d8e520d829e6fbbee61a9720ad45a Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Thu, 3 Aug 2017 10:37:48 -0700 Subject: [PATCH 35/45] Fixed Mac and Ubuntu platform build issue (hopefully) --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ff4eb87dce..18c9dabfb0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2783,7 +2783,7 @@ void Application::onPresent(quint32 frameCount) { bool Application::importFromZIP(const QString& filePath) { qDebug() << "A zip file has been dropped in: " << filePath; - QUrl empty = ""; + QUrl empty; qApp->getFileDownloadInterface()->runUnzip(filePath, empty, true, true); return true; } From af432087e337a987390501d735b5310231738316 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Thu, 3 Aug 2017 11:49:02 -0700 Subject: [PATCH 36/45] small revisions to bracketing/variable name --- libraries/script-engine/src/FileScriptingInterface.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index 0baf3034e8..5f2460be78 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -40,8 +40,8 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd, bool if (!isZip) { tempDir.remove(fileName); } else { - QTemporaryDir blocks; - tempDir = blocks.path(); + QTemporaryDir zipTemp; + tempDir = zipTemp.path(); path.remove("file:///"); } @@ -56,8 +56,7 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd, bool if (filename != "") { qCDebug(scriptengine) << "File to upload: " + filename; - } - else { + } else { qCDebug(scriptengine) << "Unzip failed"; } emit unzipResult(path, fileList, autoAdd, isZip); @@ -76,8 +75,7 @@ QStringList FileScriptingInterface::unzipFile(QString path, QString tempDir) { if (!list.isEmpty()) { return list; - } - else { + } else { qCDebug(scriptengine) << "Extraction failed"; return list; } From a246c592775f16f554c0b7cfb048f1260349d20a Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Fri, 4 Aug 2017 16:59:21 -0700 Subject: [PATCH 37/45] changed debug print for unsupported entity files --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 18c9dabfb0..2bc1d1e20e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6355,7 +6355,7 @@ void Application::addAssetToWorldSetMapping(QString filePath, QString mapping, Q if (filePath.endsWith(".obj") || filePath.endsWith(".fbx")) { addAssetToWorldAddEntity(filePath, mapping); } else { - qCDebug(interfaceapp) << "Zipped contents are not valid entity files"; + qCDebug(interfaceapp) << "Zipped contents are not supported entity files"; addAssetToWorldInfoDone(filenameFromPath(filePath)); } } From 22afb25d44a425d4780de32805f0057f4a0e5920 Mon Sep 17 00:00:00 2001 From: anshuman64 Date: Fri, 4 Aug 2017 17:19:26 -0700 Subject: [PATCH 38/45] Capitalize "ENTER VR" --- scripts/system/hmd.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index c95d85ebeb..c9c3dbe493 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -43,8 +43,8 @@ var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); // Independent and Entity mode make people sick; disable them in hmd. var desktopOnlyViews = ['Independent Mode', 'Entity Mode']; -var switchToVR = "Enter VR"; -var switchToDesktop = "Exit VR"; +var switchToVR = "ENTER VR"; +var switchToDesktop = "EXIT VR"; function onHmdChanged(isHmd) { HMD.closeTablet(); From 06c9378b0345092805fa2f90065974c953207298 Mon Sep 17 00:00:00 2001 From: beholder Date: Sat, 5 Aug 2017 04:07:04 +0300 Subject: [PATCH 39/45] fix for 6760: Create Mode turns to Portrait when Reopening tablet --- scripts/system/libraries/WebTablet.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 53f88ea62d..c3d55d5875 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -42,7 +42,7 @@ var LOCAL_TABLET_MODEL_PATH = Script.resourcesPath() + "meshes/tablet-with-home- // returns object with two fields: // * position - position in front of the user // * rotation - rotation of entity so it faces the user. -function calcSpawnInfo(hand, tabletHeight) { +function calcSpawnInfo(hand, tabletHeight, landscape) { var finalPosition; var headPos = (HMD.active && Camera.mode === "first person") ? HMD.position : Camera.position; @@ -81,7 +81,7 @@ function calcSpawnInfo(hand, tabletHeight) { return { position: position, - rotation: rotation + rotation: landscape ? Quat.multiply(rotation, { x: 0.0, y: 0.0, z: 0.707, w: 0.707 }) : rotation }; } else { var forward = Quat.getForward(headRot); @@ -89,7 +89,7 @@ function calcSpawnInfo(hand, tabletHeight) { var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, forward, {x: 0, y: 1, z: 0}); return { position: finalPosition, - rotation: Quat.multiply(orientation, {x: 0, y: 1, z: 0, w: 0}) + rotation: landscape ? Quat.multiply(orientation, ROT_LANDSCAPE) : Quat.multiply(orientation, ROT_Y_180) }; } } @@ -424,7 +424,7 @@ WebTablet.prototype.calculateTabletAttachmentProperties = function (hand, useMou tabletProperties.parentJointIndex = SENSOR_TO_ROOM_MATRIX; // compute the appropriate position of the tablet, near the hand controller that was used to spawn it. - var spawnInfo = calcSpawnInfo(hand, this.height); + var spawnInfo = calcSpawnInfo(hand, this.height, this.landscape); tabletProperties.position = spawnInfo.position; tabletProperties.rotation = spawnInfo.rotation; } else { From 3a013bf86e6071ddaa1a70d428fc084fe5f54781 Mon Sep 17 00:00:00 2001 From: vladest Date: Sat, 5 Aug 2017 13:28:08 +0200 Subject: [PATCH 40/45] Remove snap to item. Add caching for scrolling speedup --- .../resources/qml/hifi/dialogs/content/AttachmentsContent.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml b/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml index 4adb485c2b..62cd581e3b 100644 --- a/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml +++ b/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml @@ -50,7 +50,7 @@ Item { margins: 4 } clip: true - snapMode: ListView.SnapToItem + cacheBuffer: 4000 model: ListModel {} delegate: Item { From 134bad95341af905dd01423b1eec359b12d8a1ba Mon Sep 17 00:00:00 2001 From: vladest Date: Sun, 6 Aug 2017 19:09:40 +0200 Subject: [PATCH 41/45] fixed typo leads to lost touch or mouse release events --- interface/src/ui/overlays/Web3DOverlay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index d61370151c..8dc5ae2ca2 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -252,7 +252,7 @@ void Web3DOverlay::render(RenderArgs* args) { if (overlayID == selfOverlayID && (self->_pressed || (!self->_activeTouchPoints.empty() && self->_touchBeginAccepted))) { PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(), event.getButton(), event.getButtons(), event.getKeyboardModifiers()); - forwardPointerEvent(overlayID, event); + forwardPointerEvent(overlayID, endEvent); } }); From 186035dc5d1b1a5f9a36f0d18cb7662e73469df3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 6 Aug 2017 11:57:55 -0700 Subject: [PATCH 42/45] adjust various things that cause a crash-on-exit on Linux --- assignment-client/src/audio/AudioMixer.cpp | 2 +- .../src/audio/AudioMixerClientData.cpp | 4 +- assignment-client/src/avatars/AvatarMixer.cpp | 4 +- libraries/networking/src/LimitedNodeList.cpp | 14 +++--- libraries/networking/src/NLPacket.cpp | 11 ++-- libraries/networking/src/Node.cpp | 8 +-- libraries/networking/src/Node.h | 5 ++ libraries/networking/src/PacketReceiver.cpp | 4 +- .../networking/src/udt/PacketHeaders.cpp | 32 ------------ libraries/networking/src/udt/PacketHeaders.h | 50 +++++++++++++++++-- .../ui/src/ui/TabletScriptingInterface.cpp | 4 -- tests/render-texture-load/src/GLIHelpers.h | 1 + 12 files changed, 75 insertions(+), 64 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 93b9b10eb7..9ed6c7fdbc 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -127,7 +127,7 @@ void AudioMixer::queueReplicatedAudioPacket(QSharedPointer mess // construct a "fake" audio received message from the byte array and packet list information auto audioData = message->getMessage().mid(NUM_BYTES_RFC4122_UUID); - PacketType rewrittenType = REPLICATED_PACKET_MAPPING.key(message->getType()); + PacketType rewrittenType = PacketTypeEnum::getReplicatedPacketMapping().key(message->getType()); if (rewrittenType == PacketType::Unknown) { qDebug() << "Cannot unwrap replicated packet type not present in REPLICATED_PACKET_WRAPPING"; diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 408ddf038c..9bba9c7f30 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -125,11 +125,11 @@ void AudioMixerClientData::optionallyReplicatePacket(ReceivedMessage& message, c // now make sure it's a packet type that we want to replicate // first check if it is an original type that we should replicate - PacketType mirroredType = REPLICATED_PACKET_MAPPING.value(message.getType()); + PacketType mirroredType = PacketTypeEnum::getReplicatedPacketMapping().value(message.getType()); if (mirroredType == PacketType::Unknown) { // if it wasn't check if it is a replicated type that we should re-replicate - if (REPLICATED_PACKET_MAPPING.key(message.getType()) != PacketType::Unknown) { + if (PacketTypeEnum::getReplicatedPacketMapping().key(message.getType()) != PacketType::Unknown) { mirroredType = message.getType(); } else { qDebug() << "Packet passed to optionallyReplicatePacket was not a replicatable type - returning"; diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index eea44f031e..c67e998dd4 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -144,10 +144,10 @@ void AvatarMixer::optionallyReplicatePacket(ReceivedMessage& message, const Node // check if this is a packet type we replicate // which means it must be a packet type present in REPLICATED_PACKET_MAPPING or must be the // replicated version of one of those packet types - PacketType replicatedType = REPLICATED_PACKET_MAPPING.value(message.getType()); + PacketType replicatedType = PacketTypeEnum::getReplicatedPacketMapping().value(message.getType()); if (replicatedType == PacketType::Unknown) { - if (REPLICATED_PACKET_MAPPING.key(message.getType()) != PacketType::Unknown) { + if (PacketTypeEnum::getReplicatedPacketMapping().key(message.getType()) != PacketType::Unknown) { replicatedType = message.getType(); } else { qDebug() << __FUNCTION__ << "called without replicatable packet type - returning"; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index eab0e5e41f..e03ec5e771 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -220,7 +220,7 @@ bool LimitedNodeList::packetVersionMatch(const udt::Packet& packet) { const HifiSockAddr& senderSockAddr = packet.getSenderSockAddr(); QUuid sourceID; - if (NON_SOURCED_PACKETS.contains(headerType)) { + if (PacketTypeEnum::getNonSourcedPackets().contains(headerType)) { hasBeenOutput = versionDebugSuppressMap.contains(senderSockAddr, headerType); if (!hasBeenOutput) { @@ -256,8 +256,8 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe PacketType headerType = NLPacket::typeInHeader(packet); - if (NON_SOURCED_PACKETS.contains(headerType)) { - if (REPLICATED_PACKET_MAPPING.key(headerType) != PacketType::Unknown) { + if (PacketTypeEnum::getNonSourcedPackets().contains(headerType)) { + if (PacketTypeEnum::getReplicatedPacketMapping().key(headerType) != PacketType::Unknown) { // this is a replicated packet type - make sure the socket that sent it to us matches // one from one of our current upstream nodes @@ -298,7 +298,7 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe SharedNodePointer matchingNode = nodeWithUUID(sourceID); if (matchingNode) { - if (!NON_VERIFIED_PACKETS.contains(headerType)) { + if (!PacketTypeEnum::getNonVerifiedPackets().contains(headerType)) { QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet); QByteArray expectedHash = NLPacket::hashForPacketAndSecret(packet, matchingNode->getConnectionSecret()); @@ -345,13 +345,13 @@ void LimitedNodeList::collectPacketStats(const NLPacket& packet) { } void LimitedNodeList::fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret) { - if (!NON_SOURCED_PACKETS.contains(packet.getType())) { + if (!PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())) { packet.writeSourceID(getSessionUUID()); } if (!connectionSecret.isNull() - && !NON_SOURCED_PACKETS.contains(packet.getType()) - && !NON_VERIFIED_PACKETS.contains(packet.getType())) { + && !PacketTypeEnum::getNonSourcedPackets().contains(packet.getType()) + && !PacketTypeEnum::getNonVerifiedPackets().contains(packet.getType())) { packet.writeVerificationHashGivenSecret(connectionSecret); } } diff --git a/libraries/networking/src/NLPacket.cpp b/libraries/networking/src/NLPacket.cpp index a11dd69753..5c5077691b 100644 --- a/libraries/networking/src/NLPacket.cpp +++ b/libraries/networking/src/NLPacket.cpp @@ -12,8 +12,8 @@ #include "NLPacket.h" int NLPacket::localHeaderSize(PacketType type) { - bool nonSourced = NON_SOURCED_PACKETS.contains(type); - bool nonVerified = NON_VERIFIED_PACKETS.contains(type); + bool nonSourced = PacketTypeEnum::getNonSourcedPackets().contains(type); + bool nonVerified = PacketTypeEnum::getNonVerifiedPackets().contains(type); qint64 optionalSize = (nonSourced ? 0 : NUM_BYTES_RFC4122_UUID) + ((nonSourced || nonVerified) ? 0 : NUM_BYTES_MD5_HASH); return sizeof(PacketType) + sizeof(PacketVersion) + optionalSize; } @@ -198,13 +198,13 @@ void NLPacket::readVersion() { } void NLPacket::readSourceID() { - if (!NON_SOURCED_PACKETS.contains(_type)) { + if (!PacketTypeEnum::getNonSourcedPackets().contains(_type)) { _sourceID = sourceIDInHeader(*this); } } void NLPacket::writeSourceID(const QUuid& sourceID) const { - Q_ASSERT(!NON_SOURCED_PACKETS.contains(_type)); + Q_ASSERT(!PacketTypeEnum::getNonSourcedPackets().contains(_type)); auto offset = Packet::totalHeaderSize(isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion); memcpy(_packet.get() + offset, sourceID.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID); @@ -213,7 +213,8 @@ void NLPacket::writeSourceID(const QUuid& sourceID) const { } void NLPacket::writeVerificationHashGivenSecret(const QUuid& connectionSecret) const { - Q_ASSERT(!NON_SOURCED_PACKETS.contains(_type) && !NON_VERIFIED_PACKETS.contains(_type)); + Q_ASSERT(!PacketTypeEnum::getNonSourcedPackets().contains(_type) && + !PacketTypeEnum::getNonVerifiedPackets().contains(_type)); auto offset = Packet::totalHeaderSize(isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID; diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index d7f8f404e6..25eef38dbd 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -29,11 +29,9 @@ int NodePtrMetaTypeId = qRegisterMetaType("Node*"); int sharedPtrNodeMetaTypeId = qRegisterMetaType>("QSharedPointer"); int sharedNodePtrMetaTypeId = qRegisterMetaType("SharedNodePointer"); -namespace NodeType { - QHash TypeNameHash; -} - void NodeType::init() { + QHash& TypeNameHash = Node::getTypeNameHash(); + TypeNameHash.insert(NodeType::DomainServer, "Domain Server"); TypeNameHash.insert(NodeType::EntityServer, "Entity Server"); TypeNameHash.insert(NodeType::Agent, "Agent"); @@ -50,6 +48,7 @@ void NodeType::init() { } const QString& NodeType::getNodeTypeName(NodeType_t nodeType) { + QHash& TypeNameHash = Node::getTypeNameHash(); QHash::iterator matchedTypeName = TypeNameHash.find(nodeType); return matchedTypeName != TypeNameHash.end() ? matchedTypeName.value() : UNKNOWN_NodeType_t_NAME; } @@ -85,6 +84,7 @@ NodeType_t NodeType::downstreamType(NodeType_t primaryType) { } NodeType_t NodeType::fromString(QString type) { + QHash& TypeNameHash = Node::getTypeNameHash(); return TypeNameHash.key(type, NodeType::Unassigned); } diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index c20ff5a395..4d09f077bd 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -86,6 +86,11 @@ public: bool isIgnoreRadiusEnabled() const { return _ignoreRadiusEnabled; } + static QHash& getTypeNameHash() { + static QHash TypeNameHash; + return TypeNameHash; + } + private: // privatize copy and assignment operator to disallow Node copying Node(const Node &otherNode); diff --git a/libraries/networking/src/PacketReceiver.cpp b/libraries/networking/src/PacketReceiver.cpp index 21db207375..556e55beb2 100644 --- a/libraries/networking/src/PacketReceiver.cpp +++ b/libraries/networking/src/PacketReceiver.cpp @@ -32,7 +32,7 @@ bool PacketReceiver::registerListenerForTypes(PacketTypeList types, QObject* lis // Partition types based on whether they are sourced or not (non sourced in front) auto middle = std::partition(std::begin(types), std::end(types), [](PacketType type) { - return NON_SOURCED_PACKETS.contains(type); + return PacketTypeEnum::getNonSourcedPackets().contains(type); }); QMetaMethod nonSourcedMethod, sourcedMethod; @@ -123,7 +123,7 @@ QMetaMethod PacketReceiver::matchingMethodForListener(PacketType type, QObject* SIGNATURE_TEMPLATE.arg(slot, NON_SOURCED_MESSAGE_LISTENER_PARAMETERS) }; - if (!NON_SOURCED_PACKETS.contains(type)) { + if (!PacketTypeEnum::getNonSourcedPackets().contains(type)) { static const QString SOURCED_MESSAGE_LISTENER_PARAMETERS = "QSharedPointer,QSharedPointer"; static const QString TYPEDEF_SOURCED_MESSAGE_LISTENER_PARAMETERS = "QSharedPointer,SharedNodePointer"; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index d2500196d9..241ccaf5d6 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -22,38 +22,6 @@ Q_DECLARE_METATYPE(PacketType); int packetTypeMetaTypeId = qRegisterMetaType(); -const QSet NON_VERIFIED_PACKETS = QSet() - << PacketType::NodeJsonStats << PacketType::EntityQuery - << PacketType::OctreeDataNack << PacketType::EntityEditNack - << PacketType::DomainListRequest << PacketType::StopNode - << PacketType::DomainDisconnectRequest << PacketType::UsernameFromIDRequest - << PacketType::NodeKickRequest << PacketType::NodeMuteRequest; - -const QSet NON_SOURCED_PACKETS = QSet() - << PacketType::StunResponse << PacketType::CreateAssignment << PacketType::RequestAssignment - << PacketType::DomainServerRequireDTLS << PacketType::DomainConnectRequest - << PacketType::DomainList << PacketType::DomainConnectionDenied - << PacketType::DomainServerPathQuery << PacketType::DomainServerPathResponse - << PacketType::DomainServerAddedNode << PacketType::DomainServerConnectionToken - << PacketType::DomainSettingsRequest << PacketType::DomainSettings - << PacketType::ICEServerPeerInformation << PacketType::ICEServerQuery << PacketType::ICEServerHeartbeat - << PacketType::ICEServerHeartbeatACK << PacketType::ICEPing << PacketType::ICEPingReply - << PacketType::ICEServerHeartbeatDenied << PacketType::AssignmentClientStatus << PacketType::StopNode - << PacketType::DomainServerRemovedNode << PacketType::UsernameFromIDReply << PacketType::OctreeFileReplacement - << PacketType::ReplicatedMicrophoneAudioNoEcho << PacketType::ReplicatedMicrophoneAudioWithEcho - << PacketType::ReplicatedInjectAudio << PacketType::ReplicatedSilentAudioFrame - << PacketType::ReplicatedAvatarIdentity << PacketType::ReplicatedKillAvatar << PacketType::ReplicatedBulkAvatarData; - -const QHash REPLICATED_PACKET_MAPPING { - { PacketType::MicrophoneAudioNoEcho, PacketType::ReplicatedMicrophoneAudioNoEcho }, - { PacketType::MicrophoneAudioWithEcho, PacketType::ReplicatedMicrophoneAudioWithEcho }, - { PacketType::InjectAudio, PacketType::ReplicatedInjectAudio }, - { PacketType::SilentAudioFrame, PacketType::ReplicatedSilentAudioFrame }, - { PacketType::AvatarIdentity, PacketType::ReplicatedAvatarIdentity }, - { PacketType::KillAvatar, PacketType::ReplicatedKillAvatar }, - { PacketType::BulkAvatarData, PacketType::ReplicatedBulkAvatarData } -}; - PacketVersion versionForPacketType(PacketType packetType) { switch (packetType) { case PacketType::DomainList: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index cb3db791b4..8ed0966291 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -123,19 +123,59 @@ public: ReplicatedBulkAvatarData, NUM_PACKET_TYPE }; + + const static QHash getReplicatedPacketMapping() { + const QHash REPLICATED_PACKET_MAPPING { + { PacketTypeEnum::Value::MicrophoneAudioNoEcho, PacketTypeEnum::Value::ReplicatedMicrophoneAudioNoEcho }, + { PacketTypeEnum::Value::MicrophoneAudioWithEcho, PacketTypeEnum::Value::ReplicatedMicrophoneAudioWithEcho }, + { PacketTypeEnum::Value::InjectAudio, PacketTypeEnum::Value::ReplicatedInjectAudio }, + { PacketTypeEnum::Value::SilentAudioFrame, PacketTypeEnum::Value::ReplicatedSilentAudioFrame }, + { PacketTypeEnum::Value::AvatarIdentity, PacketTypeEnum::Value::ReplicatedAvatarIdentity }, + { PacketTypeEnum::Value::KillAvatar, PacketTypeEnum::Value::ReplicatedKillAvatar }, + { PacketTypeEnum::Value::BulkAvatarData, PacketTypeEnum::Value::ReplicatedBulkAvatarData } + }; + + return REPLICATED_PACKET_MAPPING; + } + + const static QSet getNonVerifiedPackets() { + const QSet NON_VERIFIED_PACKETS = QSet() + << PacketTypeEnum::Value::NodeJsonStats << PacketTypeEnum::Value::EntityQuery + << PacketTypeEnum::Value::OctreeDataNack << PacketTypeEnum::Value::EntityEditNack + << PacketTypeEnum::Value::DomainListRequest << PacketTypeEnum::Value::StopNode + << PacketTypeEnum::Value::DomainDisconnectRequest << PacketTypeEnum::Value::UsernameFromIDRequest + << PacketTypeEnum::Value::NodeKickRequest << PacketTypeEnum::Value::NodeMuteRequest; + return NON_VERIFIED_PACKETS; + } + + const static QSet getNonSourcedPackets() { + const QSet NON_SOURCED_PACKETS = QSet() + << PacketTypeEnum::Value::StunResponse << PacketTypeEnum::Value::CreateAssignment + << PacketTypeEnum::Value::RequestAssignment << PacketTypeEnum::Value::DomainServerRequireDTLS + << PacketTypeEnum::Value::DomainConnectRequest << PacketTypeEnum::Value::DomainList + << PacketTypeEnum::Value::DomainConnectionDenied << PacketTypeEnum::Value::DomainServerPathQuery + << PacketTypeEnum::Value::DomainServerPathResponse << PacketTypeEnum::Value::DomainServerAddedNode + << PacketTypeEnum::Value::DomainServerConnectionToken << PacketTypeEnum::Value::DomainSettingsRequest + << PacketTypeEnum::Value::DomainSettings << PacketTypeEnum::Value::ICEServerPeerInformation + << PacketTypeEnum::Value::ICEServerQuery << PacketTypeEnum::Value::ICEServerHeartbeat + << PacketTypeEnum::Value::ICEServerHeartbeatACK << PacketTypeEnum::Value::ICEPing + << PacketTypeEnum::Value::ICEPingReply << PacketTypeEnum::Value::ICEServerHeartbeatDenied + << PacketTypeEnum::Value::AssignmentClientStatus << PacketTypeEnum::Value::StopNode + << PacketTypeEnum::Value::DomainServerRemovedNode << PacketTypeEnum::Value::UsernameFromIDReply + << PacketTypeEnum::Value::OctreeFileReplacement << PacketTypeEnum::Value::ReplicatedMicrophoneAudioNoEcho + << PacketTypeEnum::Value::ReplicatedMicrophoneAudioWithEcho << PacketTypeEnum::Value::ReplicatedInjectAudio + << PacketTypeEnum::Value::ReplicatedSilentAudioFrame << PacketTypeEnum::Value::ReplicatedAvatarIdentity + << PacketTypeEnum::Value::ReplicatedKillAvatar << PacketTypeEnum::Value::ReplicatedBulkAvatarData; + return NON_SOURCED_PACKETS; + } }; using PacketType = PacketTypeEnum::Value; -extern const QHash REPLICATED_PACKET_MAPPING; - const int NUM_BYTES_MD5_HASH = 16; typedef char PacketVersion; -extern const QSet NON_VERIFIED_PACKETS; -extern const QSet NON_SOURCED_PACKETS; - PacketVersion versionForPacketType(PacketType packetType); QByteArray protocolVersionsSignature(); /// returns a unqiue signature for all the current protocols QString protocolVersionsSignatureBase64(); diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 0fd32b42e6..984d743ebf 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -28,11 +28,9 @@ const QString SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system"; const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; TabletScriptingInterface::TabletScriptingInterface() { - qCDebug(uiLogging) << "Building tablet scripting interface"; } TabletScriptingInterface::~TabletScriptingInterface() { - qCDebug(uiLogging) << "Destroying tablet scripting interface"; } ToolbarProxy* TabletScriptingInterface::getSystemToolbarProxy() { @@ -191,7 +189,6 @@ TabletProxy::TabletProxy(QObject* parent, const QString& name) : QObject(parent) } TabletProxy::~TabletProxy() { - qCDebug(uiLogging) << "Destroying tablet proxy " << _name; if (QThread::currentThread() != thread()) { qCWarning(uiLogging) << "Destroying tablet proxy on wrong thread" << _name; } @@ -846,7 +843,6 @@ TabletButtonProxy::TabletButtonProxy(const QVariantMap& properties) : } TabletButtonProxy::~TabletButtonProxy() { - qCDebug(uiLogging) << "Destroying tablet button proxy " ; if (QThread::currentThread() != thread()) { qCWarning(uiLogging) << "Destroying tablet button proxy on wrong thread"; } diff --git a/tests/render-texture-load/src/GLIHelpers.h b/tests/render-texture-load/src/GLIHelpers.h index c2841311a9..886176e874 100644 --- a/tests/render-texture-load/src/GLIHelpers.h +++ b/tests/render-texture-load/src/GLIHelpers.h @@ -28,6 +28,7 @@ #pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-result" #pragma GCC diagnostic ignored "-Wignored-qualifiers" +#pragma GCC diagnostic ignored "-Wtype-limits" #endif #include From 6f9460162d3d93ee5bfde7137b4a1aacf3558dd7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 6 Aug 2017 12:47:45 -0700 Subject: [PATCH 43/45] avoid calling a pure virtual function from GLBackend dtor --- libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 2 -- libraries/gpu-gl/src/gpu/gl/GLBackend.h | 2 +- libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 5 +++++ libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 5 +++++ 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index 11e67811b6..eac74fbdf9 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -196,8 +196,6 @@ GLBackend::GLBackend() { GLBackend::~GLBackend() { - resetStages(); - killInput(); killTransform(); } diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index 88aecda617..1908db614d 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -66,7 +66,7 @@ protected: public: static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet()); - ~GLBackend(); + virtual ~GLBackend(); void setCameraCorrection(const Mat4& correction); void render(const Batch& batch) final override; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 2c64b9d23d..42926fdb1c 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -43,6 +43,11 @@ public: explicit GL41Backend(bool syncCache) : Parent(syncCache) {} GL41Backend() : Parent() {} + virtual ~GL41Backend() { + // call resetStages here rather than in ~GLBackend dtor because it will call releaseResourceBuffer + // which is pure virtual from GLBackend's dtor. + resetStages(); + } static const std::string GL41_VERSION; const std::string& getVersion() const override { return GL41_VERSION; } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index d5ff1a3485..1a4b63d35f 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -39,6 +39,11 @@ public: explicit GL45Backend(bool syncCache) : Parent(syncCache) {} GL45Backend() : Parent() {} + virtual ~GL45Backend() { + // call resetStages here rather than in ~GLBackend dtor because it will call releaseResourceBuffer + // which is pure virtual from GLBackend's dtor. + resetStages(); + } static const std::string GL45_VERSION; const std::string& getVersion() const override { return GL45_VERSION; } From 83a30e2f350ed9d9811c21bcee381c1730fbf280 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 6 Aug 2017 13:12:27 -0700 Subject: [PATCH 44/45] missed some statics --- libraries/networking/src/udt/PacketHeaders.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 8ed0966291..e2304e62f7 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -125,7 +125,7 @@ public: }; const static QHash getReplicatedPacketMapping() { - const QHash REPLICATED_PACKET_MAPPING { + const static QHash REPLICATED_PACKET_MAPPING { { PacketTypeEnum::Value::MicrophoneAudioNoEcho, PacketTypeEnum::Value::ReplicatedMicrophoneAudioNoEcho }, { PacketTypeEnum::Value::MicrophoneAudioWithEcho, PacketTypeEnum::Value::ReplicatedMicrophoneAudioWithEcho }, { PacketTypeEnum::Value::InjectAudio, PacketTypeEnum::Value::ReplicatedInjectAudio }, @@ -134,12 +134,11 @@ public: { PacketTypeEnum::Value::KillAvatar, PacketTypeEnum::Value::ReplicatedKillAvatar }, { PacketTypeEnum::Value::BulkAvatarData, PacketTypeEnum::Value::ReplicatedBulkAvatarData } }; - return REPLICATED_PACKET_MAPPING; } const static QSet getNonVerifiedPackets() { - const QSet NON_VERIFIED_PACKETS = QSet() + const static QSet NON_VERIFIED_PACKETS = QSet() << PacketTypeEnum::Value::NodeJsonStats << PacketTypeEnum::Value::EntityQuery << PacketTypeEnum::Value::OctreeDataNack << PacketTypeEnum::Value::EntityEditNack << PacketTypeEnum::Value::DomainListRequest << PacketTypeEnum::Value::StopNode @@ -149,7 +148,7 @@ public: } const static QSet getNonSourcedPackets() { - const QSet NON_SOURCED_PACKETS = QSet() + const static QSet NON_SOURCED_PACKETS = QSet() << PacketTypeEnum::Value::StunResponse << PacketTypeEnum::Value::CreateAssignment << PacketTypeEnum::Value::RequestAssignment << PacketTypeEnum::Value::DomainServerRequireDTLS << PacketTypeEnum::Value::DomainConnectRequest << PacketTypeEnum::Value::DomainList From 07a4beac73ed8c789148816a52ed7d5ee00a5d02 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 7 Aug 2017 10:28:36 -0700 Subject: [PATCH 45/45] Fix the crash --- .../ui/overlays/ContextOverlayInterface.cpp | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index e406d139d0..77b9424d2f 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -257,15 +257,23 @@ void ContextOverlayInterface::openMarketplace() { } void ContextOverlayInterface::enableEntityHighlight(const EntityItemID& entityItemID) { - if (!qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->getShouldHighlight()) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(true); - } + auto entityTree = qApp->getEntities()->getTree(); + entityTree->withReadLock([&] { + auto entityItem = entityTree->findEntityByEntityItemID(entityItemID); + if ((entityItem != NULL) && !entityItem->getShouldHighlight()) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID; + entityItem->setShouldHighlight(true); + } + }); } void ContextOverlayInterface::disableEntityHighlight(const EntityItemID& entityItemID) { - if (qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->getShouldHighlight()) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityItemID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(false); - } + auto entityTree = qApp->getEntities()->getTree(); + entityTree->withReadLock([&] { + auto entityItem = entityTree->findEntityByEntityItemID(entityItemID); + if ((entityItem != NULL) && entityItem->getShouldHighlight()) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityItemID; + entityItem->setShouldHighlight(false); + } + }); }