From 61edd83987dbe2c5629f82b4387439d2f33af42d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 12 Dec 2017 17:02:07 -0800 Subject: [PATCH] Improve stability for HMD third person avatars Previously there was a hack where the sensor to world matrix was NOT updated in HMD mode if the camera was third person. The reason the hack existed was to make third person camera less nauseating for users. However this had a major drawbacks. It led to avatar hip spasms when the user turned around in the tracked volume, while in third person camera. To fix this issue properly, the hack which prevented updating the sensor to world matrix was removed, and now the third person camera is now computed in sensor space relative to the hmd. --- interface/src/Application.cpp | 30 ++++++++++++++++++------ interface/src/Application.h | 5 +++- interface/src/avatar/MyAvatar.cpp | 11 +++------ interface/src/avatar/MySkeletonModel.cpp | 1 - 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f3c41565f8..2d5ec4c62c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2441,7 +2441,7 @@ void Application::initializeUi() { offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2); } -void Application::updateCamera(RenderArgs& renderArgs) { +void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { PROFILE_RANGE(render, __FUNCTION__); PerformanceTimer perfTimer("updateCamera"); @@ -2456,6 +2456,7 @@ void Application::updateCamera(RenderArgs& renderArgs) { // Using the latter will cause the camera to wobble with idle animations, // or with changes from the face tracker if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { + _thirdPersonHMDCameraBoomValid= false; if (isHMDMode()) { mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); _myCamera.setPosition(extractTranslation(camMat)); @@ -2468,12 +2469,25 @@ void Application::updateCamera(RenderArgs& renderArgs) { } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { if (isHMDMode()) { - auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); - _myCamera.setOrientation(glm::normalize(glmExtractRotation(hmdWorldMat))); - _myCamera.setPosition(extractTranslation(hmdWorldMat) + - myAvatar->getWorldOrientation() * boomOffset); + + if (!_thirdPersonHMDCameraBoomValid) { + const glm::vec3 CAMERA_OFFSET = glm::vec3(0.0f, 0.0f, 0.7f); + _thirdPersonHMDCameraBoom = cancelOutRollAndPitch(myAvatar->getHMDSensorOrientation()) * CAMERA_OFFSET; + _thirdPersonHMDCameraBoomValid = true; + } + + glm::mat4 thirdPersonCameraSensorToWorldMatrix = myAvatar->getSensorToWorldMatrix(); + + const glm::vec3 cameraPos = myAvatar->getHMDSensorPosition() + _thirdPersonHMDCameraBoom * myAvatar->getBoomLength(); + glm::mat4 sensorCameraMat = createMatFromQuatAndPos(myAvatar->getHMDSensorOrientation(), cameraPos); + glm::mat4 worldCameraMat = thirdPersonCameraSensorToWorldMatrix * sensorCameraMat; + + _myCamera.setOrientation(glm::normalize(glmExtractRotation(worldCameraMat))); + _myCamera.setPosition(extractTranslation(worldCameraMat)); } else { + _thirdPersonHMDCameraBoomValid = false; + _myCamera.setOrientation(myAvatar->getHead()->getOrientation()); if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { _myCamera.setPosition(myAvatar->getDefaultEyePosition() @@ -2486,6 +2500,7 @@ void Application::updateCamera(RenderArgs& renderArgs) { } } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { + _thirdPersonHMDCameraBoomValid= false; if (isHMDMode()) { auto mirrorBodyOrientation = myAvatar->getWorldOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)); @@ -2520,6 +2535,7 @@ void Application::updateCamera(RenderArgs& renderArgs) { renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; } else if (_myCamera.getMode() == CAMERA_MODE_ENTITY) { + _thirdPersonHMDCameraBoomValid= false; EntityItemPointer cameraEntity = _myCamera.getCameraEntityPointer(); if (cameraEntity != nullptr) { if (isHMDMode()) { @@ -5094,7 +5110,7 @@ void Application::update(float deltaTime) { _postUpdateLambdas.clear(); } - editRenderArgs([this](AppRenderArgs& appRenderArgs) { + editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) { appRenderArgs._headPose= getHMDSensorPose(); auto myAvatar = getMyAvatar(); @@ -5140,7 +5156,7 @@ void Application::update(float deltaTime) { resizeGL(); } - this->updateCamera(appRenderArgs._renderArgs); + this->updateCamera(appRenderArgs._renderArgs, deltaTime); appRenderArgs._eyeToWorld = _myCamera.getTransform(); appRenderArgs._isStereo = false; diff --git a/interface/src/Application.h b/interface/src/Application.h index 479ce919a3..b01e998a21 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -146,7 +146,7 @@ public: void initializeGL(); void initializeUi(); - void updateCamera(RenderArgs& renderArgs); + void updateCamera(RenderArgs& renderArgs, float deltaTime); void paintGL(); void resizeGL(); @@ -695,6 +695,9 @@ private: void startHMDStandBySession(); void endHMDSession(); + glm::vec3 _thirdPersonHMDCameraBoom { 0.0f, 0.0f, -1.0f }; + bool _thirdPersonHMDCameraBoomValid { true }; + QUrl _avatarOverrideUrl; bool _saveAvatarOverrideUrl { false }; QObject* _renderEventHandler{ nullptr }; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 68e417ba1d..e863a58e14 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2799,14 +2799,9 @@ void MyAvatar::FollowHelper::decrementTimeRemaining(float dt) { } bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { - auto cameraMode = qApp->getCamera().getMode(); - if (cameraMode == CAMERA_MODE_THIRD_PERSON) { - return false; - } else { - const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees - glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix); - return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD; - } + const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees + glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix); + return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD; } bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 50e0474831..17bce655fd 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -34,7 +34,6 @@ Rig::CharacterControllerState convertCharacterControllerState(CharacterControlle } static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { - glm::mat4 worldToSensorMat = glm::inverse(myAvatar->getSensorToWorldMatrix()); // check for pinned hips.