diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0dea3e2708..af330b4ad0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5527,7 +5527,7 @@ void Application::loadSettings() { // dictated that we should be in first person Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, isFirstPerson); Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !isFirstPerson); - _myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_THIRD_PERSON); + _myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_LOOK_AT); cameraMenuChanged(); auto inputs = pluginManager->getInputPlugins(); @@ -5984,7 +5984,7 @@ void Application::cameraModeChanged() { case CAMERA_MODE_FIRST_PERSON: Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true); break; - case CAMERA_MODE_THIRD_PERSON: + case CAMERA_MODE_LOOK_AT: Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true); break; case CAMERA_MODE_MIRROR: @@ -6006,7 +6006,7 @@ void Application::changeViewAsNeeded(float boomLength) { Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, false); Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true); cameraMenuChanged(); - } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON && !boomLengthGreaterThanMinimum) { + } else if (_myCamera.getMode() == CAMERA_MODE_LOOK_AT && !boomLengthGreaterThanMinimum) { Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true); Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, false); cameraMenuChanged(); @@ -6028,8 +6028,8 @@ void Application::cameraMenuChanged() { getMyAvatar()->setBoomLength(MyAvatar::ZOOM_MIN); } } else if (menu->isOptionChecked(MenuOption::ThirdPerson)) { - if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) { - _myCamera.setMode(CAMERA_MODE_THIRD_PERSON); + if (_myCamera.getMode() != CAMERA_MODE_LOOK_AT) { + _myCamera.setMode(CAMERA_MODE_LOOK_AT); if (getMyAvatar()->getBoomLength() == MyAvatar::ZOOM_MIN) { getMyAvatar()->setBoomLength(MyAvatar::ZOOM_DEFAULT); } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index fdea692f14..06fbc368db 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -99,6 +99,10 @@ static const QString USER_RECENTER_MODEL_FORCE_STAND = QStringLiteral("ForceStan static const QString USER_RECENTER_MODEL_AUTO = QStringLiteral("Auto"); static const QString USER_RECENTER_MODEL_DISABLE_HMD_LEAN = QStringLiteral("DisableHMDLean"); +const QString HEAD_BLENDING_NAME = "lookAroundAlpha"; +const QString HEAD_ALPHA_NAME = "additiveBlendAlpha"; +const float HEAD_ALPHA_BLENDING = 1.0f; + MyAvatar::SitStandModelType stringToUserRecenterModel(const QString& str) { if (str == USER_RECENTER_MODEL_FORCE_SIT) { return MyAvatar::ForceSit; @@ -936,6 +940,12 @@ void MyAvatar::simulate(float deltaTime, bool inView) { head->setPosition(headPosition); head->setScale(getModelScale()); head->simulate(deltaTime); + if (_scriptControlsHeadLookAt || qApp->getCamera().getMode() == CAMERA_MODE_LOOK_AT) { + updateHeadLookAt(deltaTime); + } else if (_headLookAtActive){ + resetHeadLookAt(); + _headLookAtActive = false; + } } // Record avatars movements. @@ -3402,6 +3412,9 @@ void MyAvatar::updateOrientation(float deltaTime) { glm::quat initialOrientation = getOrientationOutbound(); if (qApp->getCamera().getMode() != CAMERA_MODE_LOOK_AT) { setWorldOrientation(getWorldOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f)))); + _lookAtCameraTarget = getHead()->getPosition() + getWorldOrientation() * Vectors::FRONT; + _lookAtOffsetYaw = getWorldOrientation(); + _lookAtOffsetPitch = Quaternions::IDENTITY; } else { // Set look at vector float pitchIncrement = getDriveKey(PITCH) * _pitchSpeed * deltaTime @@ -3439,12 +3452,12 @@ void MyAvatar::updateOrientation(float deltaTime) { head->setBasePitch(0.0f); head->setBaseRoll(0.0f); // Attenuate head pitch - glm::vec3 cameraVector = (faceForward ? _lookAtOffsetPitch * getWorldOrientation() : getLookAtOffset()) * Vectors::UNIT_Z; - glm::vec3 cameraYawVector = _lookAtOffsetYaw * Vectors::UNIT_Z; - float upDownDirection = glm::dot(cameraVector, Vectors::UNIT_Y); + glm::vec3 cameraVector = (faceForward ? _lookAtOffsetPitch * getWorldOrientation() : getLookAtOffset()) * Vectors::FRONT; + glm::vec3 cameraYawVector = _lookAtOffsetYaw * Vectors::FRONT; + float upDownDirection = glm::dot(cameraVector, Vectors::UP); float upDownValue = abs(upDownDirection); - const float LOOK_UP_MIN_DOT = 0.25f; - const float LOOK_DOWN_MIN_DOT = 0.75f; + const float LOOK_UP_MIN_DOT = 0.35f; + const float LOOK_DOWN_MIN_DOT = 0.25f; if (upDownDirection < 0.0f) { float lookUpAttenuation = upDownValue > LOOK_UP_MIN_DOT ? (upDownValue - LOOK_UP_MIN_DOT) / (1.0f - LOOK_UP_MIN_DOT) : 0.0f; cameraVector = glm::mix(cameraVector, cameraYawVector, 1.0f - lookUpAttenuation); @@ -3454,8 +3467,8 @@ void MyAvatar::updateOrientation(float deltaTime) { } // Calculate the camera target point. glm::vec3 cameraPos = qApp->getCamera().getPosition(); - float distanceTargetFromCamera = 2.0f * glm::length(cameraPos - getWorldPosition()); - glm::vec3 targetPoint = qApp->getCamera().getPosition() + distanceTargetFromCamera * cameraVector; + const float targetDistanceFromHead = 2.0f; + glm::vec3 targetPoint = getHead()->getPosition() + targetDistanceFromHead * glm::normalize(cameraVector); const float LOOKAT_MIX_ALPHA = 0.05f; const float FPS = 60.0f; @@ -3464,9 +3477,7 @@ void MyAvatar::updateOrientation(float deltaTime) { float mixAlpha = LOOKAT_MIX_ALPHA * deltaTime * FPS; mixAlpha = targetSpeed != 0.0f ? mixAlpha * abs(targetSpeed) / _yawSpeed : mixAlpha; _lookAtCameraTarget = glm::mix(_lookAtCameraTarget, targetPoint, mixAlpha); - - // Set the head look at target - QMetaObject::invokeMethod(this, "headLookAt", Q_ARG(const glm::vec3&, _lookAtCameraTarget)); + _headLookAtActive = true; } else { head->setBaseYaw(0.0f); head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime @@ -3538,11 +3549,7 @@ glm::vec3 MyAvatar::scaleMotorSpeed(const glm::vec3 forward, const glm::vec3 rig } } else { // Desktop mode. - if (qApp->getCamera().getMode() == CAMERA_MODE_LOOK_AT) { - direction = (zSpeed * forward) + (xSpeed * right); - } else { - direction = (zSpeed * forward) + (xSpeed * right); - } + direction = (zSpeed * forward) + (xSpeed * right); auto length = glm::length(direction); if (length > EPSILON) { @@ -6348,29 +6355,62 @@ void MyAvatar::endSit(const glm::vec3& position, const glm::quat& rotation) { } } +void MyAvatar::resetHeadLookAt() { + if (_rigEnabled) { + _skeletonModel->getRig().setDirectionalBlending(HEAD_BLENDING_NAME, glm::vec3(), + HEAD_ALPHA_NAME, HEAD_ALPHA_BLENDING); + } +} + +void MyAvatar::updateHeadLookAt(float deltaTime) { + if (_rigEnabled) { + glm::vec3 lookAtTarget = _scriptControlsHeadLookAt ? _lookAtScriptTarget : _lookAtCameraTarget; + glm::vec3 avatarXVector = glm::normalize(getWorldOrientation() * Vectors::UNIT_X); + glm::vec3 avatarYVector = glm::normalize(getWorldOrientation() * Vectors::UNIT_Y); + glm::vec3 headToTargetVector = lookAtTarget - getHead()->getPosition(); + if (glm::length(headToTargetVector) > EPSILON) { + headToTargetVector = glm::normalize(headToTargetVector); + } + else { + // The target point is the avatar head + return; + } + + float xDot = glm::dot(avatarXVector, headToTargetVector); + float yDot = glm::dot(avatarYVector, headToTargetVector); + + bool isLookingUp = yDot >= 0.0f; + float xAngle = acosf(xDot); + float yAngle = acosf(yDot); + + // xBlend and yBlend are the values from -1.0 to 1.0 that set the directional blending. + // We compute them using the angles (0 to PI/2) => (1.0 to 0.0) and (PI/2 to PI) => (0.0 to -1.0) + float xBlend = -(xAngle - 0.5f * PI) / (0.5f * PI); + float yBlend = -(yAngle - 0.5f * PI) / (0.5f * PI); + + glm::vec3 lookAtBlend = glm::vec3(xBlend, yBlend, 0.0f); + _skeletonModel->getRig().setDirectionalBlending(HEAD_BLENDING_NAME, lookAtBlend, + HEAD_ALPHA_NAME, HEAD_ALPHA_BLENDING); + + if (_scriptControlsHeadLookAt) { + _scriptHeadControlSinceUpdate += deltaTime; + if (_scriptHeadControlSinceUpdate > MAX_LOOK_AT_TIME_SCRIPT_CONTROL) { + _scriptHeadControlSinceUpdate = 0.0f; + _scriptControlsHeadLookAt = false; + _lookAtCameraTarget = _lookAtScriptTarget; + } + } + } +} + void MyAvatar::headLookAt(const glm::vec3& lookAtTarget) { if (QThread::currentThread() != thread()) { BLOCKING_INVOKE_METHOD(this, "headLookAt", Q_ARG(const glm::vec3&, lookAtTarget)); return; } - if (_rigEnabled) { - glm::vec3 avatarXVector = getWorldOrientation() * Vectors::UNIT_X; - glm::vec3 avatarYVector = getWorldOrientation() * Vectors::UNIT_Y; - glm::vec3 headToTargetVector = lookAtTarget - getHead()->getPosition(); - if (glm::length(headToTargetVector) > EPSILON) { - headToTargetVector = glm::normalize(headToTargetVector); - } else { - // The target point is the avatar head - return; - } - float xOffset = -glm::dot(avatarXVector, headToTargetVector); - float yOffset = -glm::dot(avatarYVector, headToTargetVector); - const QString HEAD_BLENDING_NAME = "lookAroundAlpha"; - const QString HEAD_ALPHA_NAME = "additiveBlendAlpha"; - const float HEAD_ALPHA_BLENDING = 1.0f; - glm::vec3 lookAtBlend = glm::vec3(xOffset, yOffset, 0.0f); - _skeletonModel->getRig().setDirectionalBlending(HEAD_BLENDING_NAME, lookAtBlend, - HEAD_ALPHA_NAME, HEAD_ALPHA_BLENDING); - } -} \ No newline at end of file + _headLookAtActive = true; + _scriptControlsHeadLookAt = true; + _scriptHeadControlSinceUpdate = 0.0f; + _lookAtScriptTarget = lookAtTarget; +} diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index e570942480..d4bd7bbd3b 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -2627,9 +2627,15 @@ private: glm::vec3 _trackedHeadPosition; + const float MAX_LOOK_AT_TIME_SCRIPT_CONTROL = 2.0f; glm::quat _lookAtOffsetPitch; glm::quat _lookAtOffsetYaw; glm::vec3 _lookAtCameraTarget; + glm::vec3 _lookAtScriptTarget; + bool _headLookAtActive { false }; + bool _scriptControlsHeadLookAt { false }; + float _scriptHeadControlSinceUpdate { 0.0f }; + Setting::Handle _realWorldFieldOfView; Setting::Handle _useAdvancedMovementControls; @@ -2655,6 +2661,8 @@ private: void initHeadBones(); void initAnimGraph(); void initFlowFromFST(); + void updateHeadLookAt(float deltaTime); + void resetHeadLookAt(); // Avatar Preferences QUrl _fullAvatarURLFromPreferences; diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h index 2de5332538..bae4b9aeae 100644 --- a/libraries/shared/src/shared/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -183,7 +183,7 @@ private: void recompose(); void decompose(); - CameraMode _mode{ CAMERA_MODE_THIRD_PERSON }; + CameraMode _mode{ CAMERA_MODE_LOOK_AT }; glm::mat4 _transform; glm::mat4 _projection;