diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b318f07d8f..22bf3e2555 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -709,6 +709,7 @@ static const QString STATE_CAMERA_FIRST_PERSON = "CameraFirstPerson"; static const QString STATE_CAMERA_THIRD_PERSON = "CameraThirdPerson"; static const QString STATE_CAMERA_ENTITY = "CameraEntity"; static const QString STATE_CAMERA_INDEPENDENT = "CameraIndependent"; +static const QString STATE_CAMERA_LOOK_AT = "CameraLookAt"; static const QString STATE_SNAP_TURN = "SnapTurn"; static const QString STATE_ADVANCED_MOVEMENT_CONTROLS = "AdvancedMovement"; static const QString STATE_GROUNDED = "Grounded"; @@ -925,7 +926,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR, - STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, + STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, STATE_CAMERA_LOOK_AT, STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED, STATE_PLATFORM_WINDOWS, STATE_PLATFORM_MAC, STATE_PLATFORM_ANDROID, STATE_LEFT_HAND_DOMINANT, STATE_RIGHT_HAND_DOMINANT, STATE_STRAFE_ENABLED } }); DependencyManager::set(); @@ -1872,6 +1873,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _applicationStateDevice->setInputVariant(STATE_CAMERA_THIRD_PERSON, []() -> float { return qApp->getCamera().getMode() == CAMERA_MODE_THIRD_PERSON ? 1 : 0; }); + // Look at camera and third person camera use the same input mapping + _applicationStateDevice->setInputVariant(STATE_CAMERA_THIRD_PERSON, []() -> float { + return qApp->getCamera().getMode() == CAMERA_MODE_LOOK_AT ? 1 : 0; + }); _applicationStateDevice->setInputVariant(STATE_CAMERA_ENTITY, []() -> float { return qApp->getCamera().getMode() == CAMERA_MODE_ENTITY ? 1 : 0; }); @@ -3607,8 +3612,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { _myCamera.setPosition(myAvatar->getDefaultEyePosition()); _myCamera.setOrientation(myAvatar->getMyHead()->getHeadOrientation()); } - } - else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { + } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON || _myCamera.getMode() == CAMERA_MODE_LOOK_AT) { if (isHMDMode()) { if (!_thirdPersonHMDCameraBoomValid) { @@ -3625,22 +3629,24 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { _myCamera.setOrientation(glm::normalize(glmExtractRotation(worldCameraMat))); _myCamera.setPosition(extractTranslation(worldCameraMat)); - } - else { + } else { _thirdPersonHMDCameraBoomValid = false; - - _myCamera.setOrientation(myAvatar->getHead()->getOrientation()); - if (isOptionChecked(MenuOption::CenterPlayerInView)) { + if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { + _myCamera.setOrientation(myAvatar->getHead()->getOrientation()); + if (isOptionChecked(MenuOption::CenterPlayerInView)) { + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + _myCamera.getOrientation() * boomOffset); + } else { + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + myAvatar->getWorldOrientation() * boomOffset); + } + } else { _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + _myCamera.getOrientation() * boomOffset); - } - else { - _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + myAvatar->getWorldOrientation() * boomOffset); + + myAvatar->getLookAtOffset() * boomOffset); + _myCamera.lookAt(myAvatar->getDefaultEyePosition()); } } - } - else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { + } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { _thirdPersonHMDCameraBoomValid= false; if (isHMDMode()) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4d1c20010c..5cd3ab20ca 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -451,7 +451,7 @@ QByteArray MyAvatar::toByteArrayStateful(AvatarDataDetail dataDetail, bool dropF _globalBoundingBoxDimensions.y = _characterController.getCapsuleHalfHeight(); _globalBoundingBoxDimensions.z = _characterController.getCapsuleRadius(); _globalBoundingBoxOffset = _characterController.getCapsuleLocalOffset(); - if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) { + if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT || mode == CAMERA_MODE_LOOK_AT) { // fake the avatar position that is sent up to the AvatarMixer glm::vec3 oldPosition = getWorldPosition(); setWorldPosition(getSkeletonPosition()); @@ -2556,7 +2556,7 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN glm::vec3 MyAvatar::getSkeletonPosition() const { CameraMode mode = qApp->getCamera().getMode(); - if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) { + if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT || mode == CAMERA_MODE_LOOK_AT) { // The avatar is rotated PI about the yAxis, so we have to correct for it // to get the skeleton offset contribution in the world-frame. const glm::quat FLIP = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); @@ -3322,11 +3322,15 @@ void MyAvatar::setRotationThreshold(float angleRadians) { } void MyAvatar::updateOrientation(float deltaTime) { - // Smoothly rotate body with arrow keys float targetSpeed = getDriveKey(YAW) * _yawSpeed; + bool faceForward = false; + if (qApp->getCamera().getMode() == CAMERA_MODE_LOOK_AT) { + targetSpeed = (getDriveKey(YAW) + getDriveKey(STEP_YAW) + getDriveKey(DELTA_YAW)) * _yawSpeed; + faceForward = getDriveKey(TRANSLATE_Z) != 0.0f; + } if (targetSpeed != 0.0f) { - const float ROTATION_RAMP_TIMESCALE = 0.1f; + const float ROTATION_RAMP_TIMESCALE = 0.5f; float blend = deltaTime / ROTATION_RAMP_TIMESCALE; if (blend > 1.0f) { blend = 1.0f; @@ -3349,7 +3353,9 @@ void MyAvatar::updateOrientation(float deltaTime) { float totalBodyYaw = _bodyYawDelta * deltaTime; // Rotate directly proportional to delta yaw and delta pitch from right-click mouse movement. - totalBodyYaw += getDriveKey(DELTA_YAW) * _yawSpeed / YAW_SPEED_DEFAULT; + if (qApp->getCamera().getMode() != CAMERA_MODE_LOOK_AT) { + totalBodyYaw += getDriveKey(DELTA_YAW) * _yawSpeed / YAW_SPEED_DEFAULT; + } // Comfort Mode: If you press any of the left/right rotation drive keys or input, you'll // get an instantaneous 15 degree turn. If you keep holding the key down you'll get another @@ -3394,7 +3400,19 @@ void MyAvatar::updateOrientation(float deltaTime) { // update body orientation by movement inputs glm::quat initialOrientation = getOrientationOutbound(); - setWorldOrientation(getWorldOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f)))); + if (qApp->getCamera().getMode() != CAMERA_MODE_LOOK_AT) { + setWorldOrientation(getWorldOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f)))); + } else { + // Set look at vector + float pitchIncrement = getDriveKey(PITCH) * _pitchSpeed * deltaTime + + getDriveKey(DELTA_PITCH) * _pitchSpeed / PITCH_SPEED_DEFAULT; + _lookAtOffsetYaw = _lookAtOffsetYaw * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f))); + _lookAtOffsetPitch = _lookAtOffsetPitch * glm::quat(glm::radians(glm::vec3(pitchIncrement, 0.0f, 0.0f))); + if (faceForward) { + setWorldOrientation(glm::slerp(getWorldOrientation(), _lookAtOffsetYaw, 0.25f)); + } + + } if (snapTurn) { // Whether or not there is an existing smoothing going on, just reset the smoothing timer and set the starting position as the avatar's current position, then smooth to the new position. @@ -3417,7 +3435,7 @@ void MyAvatar::updateOrientation(float deltaTime) { head->setBaseRoll(ROLL(euler)); } else { head->setBaseYaw(0.0f); - head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime + head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime + getDriveKey(DELTA_PITCH) * _pitchSpeed / PITCH_SPEED_DEFAULT); head->setBaseRoll(0.0f); } @@ -3486,7 +3504,15 @@ glm::vec3 MyAvatar::scaleMotorSpeed(const glm::vec3 forward, const glm::vec3 rig } } else { // Desktop mode. - direction = (zSpeed * forward) + (xSpeed * right); + if (qApp->getCamera().getMode() == CAMERA_MODE_LOOK_AT) { + //glm::vec3 frontVector = getWorldOrientation() * IDENTITY_FORWARD; + //glm::vec3 frontVectorXY = glm::normalize(glm::vec3(frontVector.x, frontVector.y, 0.0f)); + //direction = (zSpeed * forward) + (1.0f - glm::abs(glm::dot(frontVectorXY, forward))) * right; + direction = (zSpeed * forward); + } else { + direction = (zSpeed * forward) + (xSpeed * right); + } + auto length = glm::length(direction); if (length > EPSILON) { direction /= length; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 0108fb5eda..966babfd2f 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1747,6 +1747,8 @@ public: glm::vec3 getNextPosition() { return _goToPending ? _goToPosition : getWorldPosition(); } void prepareAvatarEntityDataForReload(); + glm::quat getLookAtOffset() { return _lookAtOffsetYaw * _lookAtOffsetPitch; } + /**jsdoc * Creates a new grab that grabs an entity. * @function MyAvatar.grab @@ -2618,6 +2620,9 @@ private: glm::vec3 _trackedHeadPosition; + glm::quat _lookAtOffsetPitch; + glm::quat _lookAtOffsetYaw; + Setting::Handle _realWorldFieldOfView; Setting::Handle _useAdvancedMovementControls; Setting::Handle _showPlayArea; diff --git a/libraries/shared/src/shared/Camera.cpp b/libraries/shared/src/shared/Camera.cpp index 787b7bfb1a..4716713a2b 100644 --- a/libraries/shared/src/shared/Camera.cpp +++ b/libraries/shared/src/shared/Camera.cpp @@ -67,6 +67,8 @@ CameraMode stringToMode(const QString& mode) { return CAMERA_MODE_INDEPENDENT; } else if (mode == "entity") { return CAMERA_MODE_ENTITY; + } else if (mode == "look at") { + return CAMERA_MODE_LOOK_AT; } return CAMERA_MODE_NULL; } @@ -82,6 +84,8 @@ QString modeToString(CameraMode mode) { return "independent"; } else if (mode == CAMERA_MODE_ENTITY) { return "entity"; + } else if (mode == CAMERA_MODE_LOOK_AT) { + return "look at"; } return "unknown"; } diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h index 0df83cfb9a..2de5332538 100644 --- a/libraries/shared/src/shared/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -23,6 +23,7 @@ enum CameraMode CAMERA_MODE_MIRROR, CAMERA_MODE_INDEPENDENT, CAMERA_MODE_ENTITY, + CAMERA_MODE_LOOK_AT, NUM_CAMERA_MODES };