diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 92bc54d43f..ba7cf472ed 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3618,7 +3618,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { _myCamera.setPosition(extractTranslation(camMat)); _myCamera.setOrientation(glmExtractRotation(camMat)); } else { - _myCamera.setPosition(myAvatar->getLookAtPivotPoint()); + _myCamera.setPosition(myAvatar->getCameraEyesPosition(deltaTime)); _myCamera.setOrientation(myAvatar->getLookAtRotation()); } } else if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 74305975ae..70c8d7f422 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -6812,6 +6812,53 @@ glm::vec3 MyAvatar::getLookAtPivotPoint() { return yAxisEyePosition; } +glm::vec3 MyAvatar::getCameraEyesPosition(float deltaTime) { + glm::vec3 defaultEyesPosition = getLookAtPivotPoint(); + if (isFlying()) { + return defaultEyesPosition; + } + glm::vec3 avatarFrontVector = getWorldOrientation() * Vectors::FRONT; + glm::vec3 avatarUpVector = getWorldOrientation() * Vectors::UP; + // Compute the offset between the default and real eye positions. + glm::vec3 defaultEyesToEyesVector = getHead()->getEyePosition() - defaultEyesPosition; + float FRONT_OFFSET_IDLE_MULTIPLIER = 2.0f; + float FRONT_OFFSET_JUMP_MULTIPLIER = 1.5f; + float frontOffset = FRONT_OFFSET_IDLE_MULTIPLIER * glm::length(defaultEyesPosition - getDefaultEyePosition()); + + // Looking down will aproximate move the camera forward to meet the real eye position + float mixAlpha = glm::dot(_lookAtPitch * Vectors::FRONT, -avatarUpVector); + + // When jumping the camera should follow the real eye on the Y coordenate + float upOffset = 0.0f; + if (isJumping() || _characterController.getState() == CharacterController::State::Takeoff) { + upOffset = glm::dot(defaultEyesToEyesVector, avatarUpVector); + + frontOffset = glm::dot(defaultEyesToEyesVector, avatarFrontVector) * FRONT_OFFSET_JUMP_MULTIPLIER; + mixAlpha = 1.0f; + } else { + // Limit the range effect from 45 to 90 degrees + const float HEAD_OFFSET_DOT_THRESHOLD = 0.7f; // 45 degrees aprox + mixAlpha = mixAlpha < HEAD_OFFSET_DOT_THRESHOLD ? 0.0f : (mixAlpha - HEAD_OFFSET_DOT_THRESHOLD) / + (1.0f - HEAD_OFFSET_DOT_THRESHOLD); + } + const float FPS = 60.0f; + float timeScale = deltaTime * FPS; + frontOffset = frontOffset < 0.0f ? 0.0f : mixAlpha * frontOffset; + glm::vec3 cameraOffset = upOffset * Vectors::UP + frontOffset * Vectors::FRONT; + const float TAU = 0.1f; + _cameraEyesOffset = _cameraEyesOffset + (cameraOffset - _cameraEyesOffset) * TAU * timeScale; + glm::vec3 estimatedCameraPosition = defaultEyesPosition + getWorldOrientation() * _cameraEyesOffset; + return estimatedCameraPosition; +} + +bool MyAvatar::isJumping() { + if (_skeletonModel->isLoaded()) { + return (_skeletonModel->getRig().getState() == Rig::RigRole::InAir || + _skeletonModel->getRig().getState() == Rig::RigRole::Takeoff) && !isFlying(); + } + return false; +} + bool MyAvatar::setPointAt(const glm::vec3& pointAtTarget) { if (QThread::currentThread() != thread()) { bool result = false; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 0f139ddbff..00ad0af398 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1919,6 +1919,8 @@ public: bool getIsJointOverridden(int jointIndex) const; glm::vec3 getLookAtPivotPoint(); + glm::vec3 getCameraEyesPosition(float deltaTime); + bool isJumping(); public slots: @@ -2973,6 +2975,8 @@ private: // used to prevent character from jumping after endSit is called. bool _endSitKeyPressComplete { false }; + + glm::vec3 _cameraEyesOffset; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 8f5eddac00..f47dbc37e1 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -115,6 +115,16 @@ public: Seated }; + enum class RigRole { + Idle = 0, + Turn, + Move, + Hover, + Takeoff, + InAir, + Seated + }; + Rig(); virtual ~Rig(); @@ -257,6 +267,7 @@ public: bool getFlowActive() const; bool getNetworkGraphActive() const; void setDirectionalBlending(const QString& targetName, const glm::vec3& blendingTarget, const QString& alphaName, float alpha); + const RigRole getState() const { return _state; } signals: void onLoadComplete(); @@ -339,15 +350,6 @@ protected: AnimVariantMap _animVars; AnimVariantMap _networkVars; - enum class RigRole { - Idle = 0, - Turn, - Move, - Hover, - Takeoff, - InAir, - Seated - }; RigRole _state { RigRole::Idle }; RigRole _desiredState { RigRole::Idle }; float _desiredStateAge { 0.0f };