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 a9ebd110d1..7055ec0246 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -6812,6 +6812,50 @@ 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) * min(1.0f, TAU * timeScale); + glm::vec3 estimatedCameraPosition = defaultEyesPosition + getWorldOrientation() * _cameraEyesOffset; + return estimatedCameraPosition; +} + +bool MyAvatar::isJumping() { + return (_characterController.getState() == CharacterController::State::InAir || + _characterController.getState() == CharacterController::State::Takeoff) && !isFlying(); +} + 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);