From 5675d6ce81116261df3d320a163ba8d9500e41f2 Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Fri, 19 Jun 2020 22:03:11 +0200 Subject: [PATCH 1/6] Generalize calculation of focal point offset for 3rd person camera. When player swim, the view slowly switches from "over shoulder" to "above head". New functions to switch shoulder. --- apps/openmw/mwrender/camera.cpp | 93 +++++++++++++++++++++++++-------- apps/openmw/mwrender/camera.hpp | 17 ++++-- 2 files changed, 85 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 09ad404287..e0f031b8c3 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -7,6 +7,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/class.hpp" #include "../mwworld/ptr.hpp" @@ -64,7 +65,10 @@ namespace MWRender mCameraDistance(0.f), mThirdPersonMode(ThirdPersonViewMode::Standard), mOverShoulderOffset(osg::Vec2f(30.0f, -10.0f)), - mSmoothTransitionToCombatMode(0.f) + mDefaultShoulderIsRight(true), + mThirdPersionOffsetType(ThirdPersonOffsetType::RightShoulder), + mFocalPointCurrentOffset(osg::Vec2d()), + mFocalPointTransitionSpeed(1.f) { mVanity.enabled = false; mVanity.allowed = true; @@ -121,12 +125,9 @@ namespace MWRender osg::Vec3d offset(0, 0, 10.f); if (mThirdPersonMode == ThirdPersonViewMode::OverShoulder && !mPreviewMode && !mVanity.enabled) { - float horizontalOffset = mOverShoulderOffset.x() * (1.f - mSmoothTransitionToCombatMode); - float verticalOffset = mSmoothTransitionToCombatMode * 15.f + (1.f - mSmoothTransitionToCombatMode) * mOverShoulderOffset.y(); - - offset.x() += horizontalOffset * cos(getYaw()); - offset.y() += horizontalOffset * sin(getYaw()); - offset.z() += verticalOffset; + offset.x() += mFocalPointCurrentOffset.x() * cos(getYaw()); + offset.y() += mFocalPointCurrentOffset.x() * sin(getYaw()); + offset.z() += mFocalPointCurrentOffset.y(); } return offset; } @@ -214,28 +215,78 @@ namespace MWRender rotateCamera(0.f, osg::DegreesToRadians(3.f * duration), true); } - updateSmoothTransitionToCombatMode(duration); + updateFocalPointOffset(duration); } void Camera::setOverShoulderOffset(float horizontal, float vertical) { - mOverShoulderOffset = osg::Vec2f(horizontal, vertical); + mOverShoulderOffset = osg::Vec2f(std::abs(horizontal), vertical); + mDefaultShoulderIsRight = horizontal >= 0; } - void Camera::updateSmoothTransitionToCombatMode(float duration) + void Camera::switchToLeftShoulder() { - bool combatMode = true; - if (mTrackingPtr.getClass().isActor()) - combatMode = mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).getDrawState() != MWMechanics::DrawState_Nothing; - float speed = ((combatMode ? 1.f : 0.f) - mSmoothTransitionToCombatMode) * 5; - if (speed != 0) - speed += speed > 0 ? 1 : -1; + if (mThirdPersionOffsetType == ThirdPersonOffsetType::RightShoulder) + mThirdPersionOffsetType = ThirdPersonOffsetType::LeftShoulder; + } - mSmoothTransitionToCombatMode += speed * duration; - if (mSmoothTransitionToCombatMode > 1) - mSmoothTransitionToCombatMode = 1; - if (mSmoothTransitionToCombatMode < 0) - mSmoothTransitionToCombatMode = 0; + void Camera::switchToRightShoulder() + { + if (mThirdPersionOffsetType == ThirdPersonOffsetType::LeftShoulder) + mThirdPersionOffsetType = ThirdPersonOffsetType::RightShoulder; + } + + void Camera::switchToDefaultShoulder() + { + if (mThirdPersionOffsetType == ThirdPersonOffsetType::LeftShoulder || mThirdPersionOffsetType == ThirdPersonOffsetType::RightShoulder) + mThirdPersionOffsetType = mDefaultShoulderIsRight ? ThirdPersonOffsetType::RightShoulder : ThirdPersonOffsetType::LeftShoulder; + } + + void Camera::updateFocalPointOffset(float duration) + { + if (mThirdPersonMode == ThirdPersonViewMode::Standard) + return; // In Standard mode there is no focal point offset. + + ThirdPersonOffsetType newOffsetType = mThirdPersionOffsetType; + if (mTrackingPtr.getClass().isActor() && mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).getDrawState() != MWMechanics::DrawState_Nothing) + newOffsetType = ThirdPersonOffsetType::Combat; + else if (MWBase::Environment::get().getWorld()->isSwimming(mTrackingPtr)) + newOffsetType = ThirdPersonOffsetType::Swimming; + else if (mThirdPersionOffsetType == ThirdPersonOffsetType::Combat || mThirdPersionOffsetType == ThirdPersonOffsetType::Swimming) + newOffsetType = mDefaultShoulderIsRight ? ThirdPersonOffsetType::RightShoulder : ThirdPersonOffsetType::LeftShoulder; + if (newOffsetType != mThirdPersionOffsetType) + { + if (newOffsetType == ThirdPersonOffsetType::Combat || mThirdPersionOffsetType == ThirdPersonOffsetType::Combat) + mFocalPointTransitionSpeed = 5; + else + mFocalPointTransitionSpeed = 1; + mThirdPersionOffsetType = newOffsetType; + } + + osg::Vec2d focalPointTargetOffset; + switch (mThirdPersionOffsetType) + { + case ThirdPersonOffsetType::RightShoulder: + focalPointTargetOffset = mOverShoulderOffset; + break; + case ThirdPersonOffsetType::LeftShoulder: + focalPointTargetOffset = mOverShoulderOffset + focalPointTargetOffset.x() *= -1; + break; + case ThirdPersonOffsetType::Combat: + case ThirdPersonOffsetType::Swimming: + default: + focalPointTargetOffset = osg::Vec2d(0, 15); + } + + osg::Vec2d delta = focalPointTargetOffset - mFocalPointCurrentOffset; + if (delta.length2() > 0) + { + float coef = duration * (1.0 + 5.0 / delta.length()) * mFocalPointTransitionSpeed; + mFocalPointCurrentOffset += delta * std::min(coef, 1.0f); + } + else + mFocalPointTransitionSpeed = 1.f; } void Camera::toggleViewMode(bool force) diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index 6398f48252..1e1d12f107 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -27,6 +27,8 @@ namespace MWRender enum class ThirdPersonViewMode {Standard, OverShoulder}; private: + enum class ThirdPersonOffsetType { RightShoulder, LeftShoulder, Combat, Swimming }; + struct CamData { float pitch, yaw, offset; }; @@ -60,12 +62,15 @@ namespace MWRender ThirdPersonViewMode mThirdPersonMode; osg::Vec2f mOverShoulderOffset; + bool mDefaultShoulderIsRight; osg::Vec3d mFocalPointAdjustment; - // Makes sense only if mThirdPersonMode is OverShoulder. Can be in range [0, 1]. - // Used for smooth transition from non-combat camera position (0) to combat camera position (1). - float mSmoothTransitionToCombatMode; - void updateSmoothTransitionToCombatMode(float duration); + // Makes sense only if mThirdPersonMode is OverShoulder. + ThirdPersonOffsetType mThirdPersionOffsetType; + osg::Vec2d mFocalPointCurrentOffset; + float mFocalPointTransitionSpeed; + + void updateFocalPointOffset(float duration); float getCameraDistanceCorrection() const; osg::ref_ptr mUpdateCallback; @@ -79,6 +84,10 @@ namespace MWRender void setThirdPersonViewMode(ThirdPersonViewMode mode) { mThirdPersonMode = mode; } void setOverShoulderOffset(float horizontal, float vertical); + void switchToLeftShoulder(); + void switchToRightShoulder(); + void switchToDefaultShoulder(); + /// Update the view matrix of \a cam void updateCamera(osg::Camera* cam); From ef03f2c033ebd8a6c2de41d7ed692b0715050123 Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Sat, 20 Jun 2020 00:46:26 +0200 Subject: [PATCH 2/6] Auto switch shoulder --- apps/openmw/mwrender/camera.cpp | 2 +- apps/openmw/mwrender/camera.hpp | 3 +- apps/openmw/mwworld/worldimp.cpp | 35 +++++++++++++++++-- .../reference/modding/settings/camera.rst | 12 +++++++ files/settings-default.cfg | 3 ++ 5 files changed, 51 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index e0f031b8c3..3da94f97da 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -270,7 +270,7 @@ namespace MWRender focalPointTargetOffset = mOverShoulderOffset; break; case ThirdPersonOffsetType::LeftShoulder: - focalPointTargetOffset = mOverShoulderOffset + focalPointTargetOffset = mOverShoulderOffset; focalPointTargetOffset.x() *= -1; break; case ThirdPersonOffsetType::Combat: diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index 1e1d12f107..b3e7e1f67a 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -82,8 +82,9 @@ namespace MWRender MWWorld::Ptr getTrackingPtr() const; void setThirdPersonViewMode(ThirdPersonViewMode mode) { mThirdPersonMode = mode; } - void setOverShoulderOffset(float horizontal, float vertical); + ThirdPersonViewMode getThirdPersonViewMode() const { return mThirdPersonMode; } + void setOverShoulderOffset(float horizontal, float vertical); void switchToLeftShoulder(); void switchToRightShoulder(); void switchToDefaultShoulder(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 209ab538ed..80a9a01ae6 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1869,13 +1869,44 @@ namespace MWWorld float cameraObstacleLimit = mRendering->getNearClipDistance() * 2.5f; float focalObstacleLimit = std::max(cameraObstacleLimit, 10.0f); - // Adjust focal point. osg::Vec3d focal = camera->getFocalPoint(); osg::Vec3d focalOffset = camera->getFocalPointOffset(); + osg::Vec3d playerPos = focal - focalOffset; + + static const bool autoSwitchShoulder = Settings::Manager::getBool("auto switch shoulder", "Camera"); + if (camera->getThirdPersonViewMode() == MWRender::Camera::ThirdPersonViewMode::OverShoulder + && autoSwitchShoulder && !camera->isVanityOrPreviewModeEnabled()) + { + const float limitToSwitch = 120; // switch to other shoulder if wall is closer than this limit + const float limitToSwitchBack = 300; // switch back to default shoulder if there is no walls at this distance + auto orient = osg::Quat(camera->getYaw(), osg::Vec3d(0,0,1)); + int mask = MWPhysics::CollisionType_World | MWPhysics::CollisionType_Door | MWPhysics::CollisionType_HeightMap; + MWPhysics::PhysicsSystem::RayResult rayRight = mPhysics->castRay( + playerPos + orient * osg::Vec3d(28, 0, 0), + playerPos + orient * osg::Vec3d(limitToSwitchBack, limitToSwitchBack, 0), + {}, {}, mask); + MWPhysics::PhysicsSystem::RayResult rayLeft = mPhysics->castRay( + playerPos + orient * osg::Vec3d(-28, 0, 0), + playerPos + orient * osg::Vec3d(-limitToSwitchBack, limitToSwitchBack, 0), + {}, {}, mask); + MWPhysics::PhysicsSystem::RayResult rayForward = mPhysics->castRay( + playerPos, playerPos + orient * osg::Vec3d(0, limitToSwitchBack, 0), + {}, {}, mask); + bool rightTooClose = rayRight.mHit && (rayRight.mHitPos - playerPos).length2() < limitToSwitch * limitToSwitch; + bool leftTooClose = rayLeft.mHit && (rayLeft.mHitPos - playerPos).length2() < limitToSwitch * limitToSwitch; + if (!rayRight.mHit && leftTooClose) + camera->switchToRightShoulder(); + else if (!rayLeft.mHit && rightTooClose) + camera->switchToLeftShoulder(); + else if (!rayRight.mHit && !rayLeft.mHit && !rayForward.mHit) + camera->switchToDefaultShoulder(); + } + + // Adjust focal point. float offsetLen = focalOffset.length(); if (offsetLen > 0) { - MWPhysics::PhysicsSystem::RayResult result = mPhysics->castSphere(focal - focalOffset, focal, focalObstacleLimit); + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castSphere(playerPos, focal, focalObstacleLimit); if (result.mHit) { double adjustmentCoef = -(result.mHitPos + result.mHitNormal * focalObstacleLimit - focal).length() / offsetLen; diff --git a/docs/source/reference/modding/settings/camera.rst b/docs/source/reference/modding/settings/camera.rst index 20349e97fd..21fec8fd60 100644 --- a/docs/source/reference/modding/settings/camera.rst +++ b/docs/source/reference/modding/settings/camera.rst @@ -150,3 +150,15 @@ Recommened values: 30 -10 for the right shoulder, -30 -10 for the left shoulder. This setting can only be configured by editing the settings configuration file. +auto switch shoulder +-------------------- + +:Type: boolean +:Range: True/False +:Default: True + +This setting makes difference only in third person mode if 'view over shoulder' is enabled. +When player is close to an obstacle, automatically switches camera to the shoulder that is farther away from the obstacle. + +This setting can only be configured by editing the settings configuration file. + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 1e62ff5bff..b960946d82 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -42,6 +42,9 @@ view over shoulder = false # Makes sense only if 'view over shoulder' is true. First number is horizontal offset (negative value means offset to the left), second number is vertical offset. view over shoulder offset = 30 -10 +# Switch shoulder automatically when player is close to an obstacle. +auto switch shoulder = true + [Cells] # Preload cells in a background thread. All settings starting with 'preload' have no effect unless this is enabled. From 5bdf61a886d6da3913593ffe607d51c50e9783fb Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Mon, 22 Jun 2020 19:26:37 +0200 Subject: [PATCH 3/6] Slightly pulls camera away when the character moves --- apps/openmw/mwrender/camera.cpp | 19 +++++++++++++++++-- apps/openmw/mwrender/camera.hpp | 2 ++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 3da94f97da..e856b8ab34 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -68,7 +68,8 @@ namespace MWRender mDefaultShoulderIsRight(true), mThirdPersionOffsetType(ThirdPersonOffsetType::RightShoulder), mFocalPointCurrentOffset(osg::Vec2d()), - mFocalPointTransitionSpeed(1.f) + mFocalPointTransitionSpeed(1.f), + mSmoothedSpeed(0.f) { mVanity.enabled = false; mVanity.allowed = true; @@ -216,6 +217,10 @@ namespace MWRender } updateFocalPointOffset(duration); + + float speed = mTrackingPtr.getClass().getSpeed(mTrackingPtr); + float maxDelta = 300.f * duration; + mSmoothedSpeed += osg::clampBetween(speed - mSmoothedSpeed, -maxDelta, maxDelta); } void Camera::setOverShoulderOffset(float horizontal, float vertical) @@ -482,7 +487,17 @@ namespace MWRender float Camera::getCameraDistanceCorrection() const { - return mThirdPersonMode != ThirdPersonViewMode::Standard ? std::max(-getPitch(), 0.f) * 50.f : 0; + if (mThirdPersonMode == ThirdPersonViewMode::Standard) + return 0; + else + { + float pitchCorrection = std::max(-getPitch(), 0.f) * 50.f; + + float smoothedSpeedSqr = mSmoothedSpeed * mSmoothedSpeed; + float speedCorrection = smoothedSpeedSqr / (smoothedSpeedSqr + 300.f*300.f) * 20.0f; + + return pitchCorrection + speedCorrection; + } } void Camera::setCameraDistance() diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index b3e7e1f67a..eb7462945f 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -70,6 +70,8 @@ namespace MWRender osg::Vec2d mFocalPointCurrentOffset; float mFocalPointTransitionSpeed; + float mSmoothedSpeed; + void updateFocalPointOffset(float duration); float getCameraDistanceCorrection() const; From 51173ebcf5c7f9dfce9f57ceea6cfb30bf9ea6c9 Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Sat, 11 Jul 2020 14:21:18 +0200 Subject: [PATCH 4/6] Refactoring. Move all code related to 'view over shoulder' to a separate file. --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwrender/camera.cpp | 89 +++----------------- apps/openmw/mwrender/camera.hpp | 25 ++---- apps/openmw/mwrender/renderingmanager.cpp | 19 ++--- apps/openmw/mwrender/renderingmanager.hpp | 2 + apps/openmw/mwrender/viewovershoulder.cpp | 99 +++++++++++++++++++++++ apps/openmw/mwrender/viewovershoulder.hpp | 30 +++++++ apps/openmw/mwworld/worldimp.cpp | 35 +------- 8 files changed, 159 insertions(+), 142 deletions(-) create mode 100644 apps/openmw/mwrender/viewovershoulder.cpp create mode 100644 apps/openmw/mwrender/viewovershoulder.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index fddfa276f9..01d270f829 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -20,7 +20,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender actors objects renderingmanager animation rotatecontroller sky npcanimation vismask creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation - bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation + bulletdebugdraw globalmap characterpreview camera viewovershoulder localmap water terrainstorage ripplesimulation renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging ) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index e856b8ab34..3b3f1aec93 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -7,7 +7,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwbase/world.hpp" #include "../mwworld/class.hpp" #include "../mwworld/ptr.hpp" @@ -63,13 +62,12 @@ namespace MWRender mVanityToggleQueuedValue(false), mViewModeToggleQueued(false), mCameraDistance(0.f), - mThirdPersonMode(ThirdPersonViewMode::Standard), - mOverShoulderOffset(osg::Vec2f(30.0f, -10.0f)), - mDefaultShoulderIsRight(true), - mThirdPersionOffsetType(ThirdPersonOffsetType::RightShoulder), mFocalPointCurrentOffset(osg::Vec2d()), + mFocalPointTargetOffset(osg::Vec2d()), mFocalPointTransitionSpeed(1.f), - mSmoothedSpeed(0.f) + mSmoothedSpeed(0.f), + mDynamicCameraDistanceEnabled(false), + mShowCrosshairInThirdPersonMode(false) { mVanity.enabled = false; mVanity.allowed = true; @@ -124,7 +122,7 @@ namespace MWRender osg::Vec3d Camera::getFocalPointOffset() const { osg::Vec3d offset(0, 0, 10.f); - if (mThirdPersonMode == ThirdPersonViewMode::OverShoulder && !mPreviewMode && !mVanity.enabled) + if (!mPreviewMode && !mVanity.enabled) { offset.x() += mFocalPointCurrentOffset.x() * cos(getYaw()); offset.y() += mFocalPointCurrentOffset.x() * sin(getYaw()); @@ -209,7 +207,7 @@ namespace MWRender // only show the crosshair in game mode MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager(); wm->showCrosshair(!wm->isGuiMode() && !mVanity.enabled && !mPreviewMode - && (mFirstPersonView || mThirdPersonMode != ThirdPersonViewMode::Standard)); + && (mFirstPersonView || mShowCrosshairInThirdPersonMode)); if(mVanity.enabled) { @@ -223,68 +221,9 @@ namespace MWRender mSmoothedSpeed += osg::clampBetween(speed - mSmoothedSpeed, -maxDelta, maxDelta); } - void Camera::setOverShoulderOffset(float horizontal, float vertical) - { - mOverShoulderOffset = osg::Vec2f(std::abs(horizontal), vertical); - mDefaultShoulderIsRight = horizontal >= 0; - } - - void Camera::switchToLeftShoulder() - { - if (mThirdPersionOffsetType == ThirdPersonOffsetType::RightShoulder) - mThirdPersionOffsetType = ThirdPersonOffsetType::LeftShoulder; - } - - void Camera::switchToRightShoulder() - { - if (mThirdPersionOffsetType == ThirdPersonOffsetType::LeftShoulder) - mThirdPersionOffsetType = ThirdPersonOffsetType::RightShoulder; - } - - void Camera::switchToDefaultShoulder() - { - if (mThirdPersionOffsetType == ThirdPersonOffsetType::LeftShoulder || mThirdPersionOffsetType == ThirdPersonOffsetType::RightShoulder) - mThirdPersionOffsetType = mDefaultShoulderIsRight ? ThirdPersonOffsetType::RightShoulder : ThirdPersonOffsetType::LeftShoulder; - } - void Camera::updateFocalPointOffset(float duration) { - if (mThirdPersonMode == ThirdPersonViewMode::Standard) - return; // In Standard mode there is no focal point offset. - - ThirdPersonOffsetType newOffsetType = mThirdPersionOffsetType; - if (mTrackingPtr.getClass().isActor() && mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).getDrawState() != MWMechanics::DrawState_Nothing) - newOffsetType = ThirdPersonOffsetType::Combat; - else if (MWBase::Environment::get().getWorld()->isSwimming(mTrackingPtr)) - newOffsetType = ThirdPersonOffsetType::Swimming; - else if (mThirdPersionOffsetType == ThirdPersonOffsetType::Combat || mThirdPersionOffsetType == ThirdPersonOffsetType::Swimming) - newOffsetType = mDefaultShoulderIsRight ? ThirdPersonOffsetType::RightShoulder : ThirdPersonOffsetType::LeftShoulder; - if (newOffsetType != mThirdPersionOffsetType) - { - if (newOffsetType == ThirdPersonOffsetType::Combat || mThirdPersionOffsetType == ThirdPersonOffsetType::Combat) - mFocalPointTransitionSpeed = 5; - else - mFocalPointTransitionSpeed = 1; - mThirdPersionOffsetType = newOffsetType; - } - - osg::Vec2d focalPointTargetOffset; - switch (mThirdPersionOffsetType) - { - case ThirdPersonOffsetType::RightShoulder: - focalPointTargetOffset = mOverShoulderOffset; - break; - case ThirdPersonOffsetType::LeftShoulder: - focalPointTargetOffset = mOverShoulderOffset; - focalPointTargetOffset.x() *= -1; - break; - case ThirdPersonOffsetType::Combat: - case ThirdPersonOffsetType::Swimming: - default: - focalPointTargetOffset = osg::Vec2d(0, 15); - } - - osg::Vec2d delta = focalPointTargetOffset - mFocalPointCurrentOffset; + osg::Vec2d delta = mFocalPointTargetOffset - mFocalPointCurrentOffset; if (delta.length2() > 0) { float coef = duration * (1.0 + 5.0 / delta.length()) * mFocalPointTransitionSpeed; @@ -487,17 +426,15 @@ namespace MWRender float Camera::getCameraDistanceCorrection() const { - if (mThirdPersonMode == ThirdPersonViewMode::Standard) + if (!mDynamicCameraDistanceEnabled) return 0; - else - { - float pitchCorrection = std::max(-getPitch(), 0.f) * 50.f; - float smoothedSpeedSqr = mSmoothedSpeed * mSmoothedSpeed; - float speedCorrection = smoothedSpeedSqr / (smoothedSpeedSqr + 300.f*300.f) * 20.0f; + float pitchCorrection = std::max(-getPitch(), 0.f) * 50.f; - return pitchCorrection + speedCorrection; - } + float smoothedSpeedSqr = mSmoothedSpeed * mSmoothedSpeed; + float speedCorrection = smoothedSpeedSqr / (smoothedSpeedSqr + 300.f*300.f) * 20.0f; + + return pitchCorrection + speedCorrection; } void Camera::setCameraDistance() diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index eb7462945f..5f3c5d6e49 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -23,12 +23,7 @@ namespace MWRender /// \brief Camera control class Camera { - public: - enum class ThirdPersonViewMode {Standard, OverShoulder}; - private: - enum class ThirdPersonOffsetType { RightShoulder, LeftShoulder, Combat, Swimming }; - struct CamData { float pitch, yaw, offset; }; @@ -60,17 +55,14 @@ namespace MWRender float mCameraDistance; - ThirdPersonViewMode mThirdPersonMode; - osg::Vec2f mOverShoulderOffset; - bool mDefaultShoulderIsRight; osg::Vec3d mFocalPointAdjustment; - - // Makes sense only if mThirdPersonMode is OverShoulder. - ThirdPersonOffsetType mThirdPersionOffsetType; osg::Vec2d mFocalPointCurrentOffset; + osg::Vec2d mFocalPointTargetOffset; float mFocalPointTransitionSpeed; float mSmoothedSpeed; + bool mDynamicCameraDistanceEnabled; + bool mShowCrosshairInThirdPersonMode; void updateFocalPointOffset(float duration); float getCameraDistanceCorrection() const; @@ -83,13 +75,10 @@ namespace MWRender MWWorld::Ptr getTrackingPtr() const; - void setThirdPersonViewMode(ThirdPersonViewMode mode) { mThirdPersonMode = mode; } - ThirdPersonViewMode getThirdPersonViewMode() const { return mThirdPersonMode; } - - void setOverShoulderOffset(float horizontal, float vertical); - void switchToLeftShoulder(); - void switchToRightShoulder(); - void switchToDefaultShoulder(); + void setFocalPointTransitionSpeed(float v) { mFocalPointTransitionSpeed = v; } + void setFocalPointTargetOffset(osg::Vec2d v) { mFocalPointTargetOffset = v; } + void enableDynamicCameraDistance(bool v) { mDynamicCameraDistanceEnabled = v; } + void enableCrosshairInThirdPersonMode(bool v) { mShowCrosshairInThirdPersonMode = v; } /// Update the view matrix of \a cam void updateCamera(osg::Camera* cam); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index c7dd5ca637..d91da5a082 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -65,6 +65,7 @@ #include "vismask.hpp" #include "pathgrid.hpp" #include "camera.hpp" +#include "viewovershoulder.hpp" #include "water.hpp" #include "terrainstorage.hpp" #include "util.hpp" @@ -306,6 +307,8 @@ namespace MWRender mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath)); mCamera.reset(new Camera(mViewer->getCamera())); + if (Settings::Manager::getBool("view over shoulder", "Camera")) + mViewOverShoulderController.reset(new ViewOverShoulderController(mCamera.get())); mViewer->setLightingMode(osgViewer::View::NO_LIGHT); @@ -366,7 +369,6 @@ namespace MWRender float firstPersonFov = Settings::Manager::getFloat("first person field of view", "Camera"); mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f); mStateUpdater->setFogEnd(mViewDistance); - updateThirdPersonViewMode(); mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip)); mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance)); @@ -382,19 +384,6 @@ namespace MWRender mWorkQueue = nullptr; } - void RenderingManager::updateThirdPersonViewMode() - { - if (Settings::Manager::getBool("view over shoulder", "Camera")) - mCamera->setThirdPersonViewMode(Camera::ThirdPersonViewMode::OverShoulder); - else - mCamera->setThirdPersonViewMode(Camera::ThirdPersonViewMode::Standard); - - std::stringstream offset(Settings::Manager::getString("view over shoulder offset", "Camera")); - float horizontal = 30.f, vertical = -10.f; - offset >> horizontal >> vertical; - mCamera->setOverShoulderOffset(horizontal, vertical); - } - osgUtil::IncrementalCompileOperation* RenderingManager::getIncrementalCompileOperation() { return mViewer->getIncrementalCompileOperation(); @@ -630,6 +619,8 @@ namespace MWRender updateNavMesh(); updateRecastMesh(); + if (mViewOverShoulderController) + mViewOverShoulderController->update(); mCamera->update(dt, paused); osg::Vec3d focal, cameraPos; diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 6700f5ce6c..d6a0f89c31 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -79,6 +79,7 @@ namespace MWRender class NpcAnimation; class Pathgrid; class Camera; + class ViewOverShoulderController; class Water; class TerrainStorage; class LandManager; @@ -294,6 +295,7 @@ namespace MWRender osg::ref_ptr mPlayerAnimation; osg::ref_ptr mPlayerNode; std::unique_ptr mCamera; + std::unique_ptr mViewOverShoulderController; osg::Vec3f mCurrentCameraPos; osg::ref_ptr mStateUpdater; diff --git a/apps/openmw/mwrender/viewovershoulder.cpp b/apps/openmw/mwrender/viewovershoulder.cpp new file mode 100644 index 0000000000..f908b14302 --- /dev/null +++ b/apps/openmw/mwrender/viewovershoulder.cpp @@ -0,0 +1,99 @@ +#include "viewovershoulder.hpp" + +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/ptr.hpp" +#include "../mwworld/refdata.hpp" + +#include "../mwmechanics/drawstate.hpp" + +namespace MWRender +{ + + ViewOverShoulderController::ViewOverShoulderController(Camera* camera) : + mCamera(camera), mMode(Mode::RightShoulder), + mAutoSwitchShoulder(Settings::Manager::getBool("auto switch shoulder", "Camera")), + mOverShoulderHorizontalOffset(30.f), mOverShoulderVerticalOffset(-10.f) + { + std::stringstream offset(Settings::Manager::getString("view over shoulder offset", "Camera")); + offset >> mOverShoulderHorizontalOffset >> mOverShoulderVerticalOffset; + mDefaultShoulderIsRight = mOverShoulderHorizontalOffset >= 0; + mOverShoulderHorizontalOffset = std::abs(mOverShoulderHorizontalOffset); + + mCamera->enableDynamicCameraDistance(true); + mCamera->enableCrosshairInThirdPersonMode(true); + } + + void ViewOverShoulderController::update() + { + if (mCamera->isVanityOrPreviewModeEnabled() || mCamera->isFirstPerson()) + return; + + Mode newMode = mMode; + auto ptr = mCamera->getTrackingPtr(); + if (ptr.getClass().isActor() && ptr.getClass().getCreatureStats(ptr).getDrawState() != MWMechanics::DrawState_Nothing) + newMode = Mode::Combat; + else if (MWBase::Environment::get().getWorld()->isSwimming(ptr)) + newMode = Mode::Swimming; + else if (mMode == Mode::Combat || mMode == Mode::Swimming) + newMode = mDefaultShoulderIsRight ? Mode::RightShoulder : Mode::LeftShoulder; + if (newMode != mMode) + { + if (newMode == Mode::Combat || mMode == Mode::Combat) + mCamera->setFocalPointTransitionSpeed(5.f); + else + mCamera->setFocalPointTransitionSpeed(1.f); + mMode = newMode; + } + + if (mAutoSwitchShoulder && (mMode == Mode::LeftShoulder || mMode == Mode::RightShoulder)) + trySwitchShoulder(); + + osg::Vec2d focalPointTargetOffset; + switch (mMode) + { + case Mode::RightShoulder: + mCamera->setFocalPointTargetOffset({mOverShoulderHorizontalOffset, mOverShoulderVerticalOffset}); + break; + case Mode::LeftShoulder: + mCamera->setFocalPointTargetOffset({-mOverShoulderHorizontalOffset, mOverShoulderVerticalOffset}); + break; + case Mode::Combat: + case Mode::Swimming: + default: + mCamera->setFocalPointTargetOffset({0, 15}); + } + } + + void ViewOverShoulderController::trySwitchShoulder() + { + const float limitToSwitch = 120; // switch to other shoulder if wall is closer than this limit + const float limitToSwitchBack = 300; // switch back to default shoulder if there is no walls at this distance + + auto orient = osg::Quat(mCamera->getYaw(), osg::Vec3d(0,0,1)); + osg::Vec3d playerPos = mCamera->getFocalPoint() - mCamera->getFocalPointOffset(); + + MWBase::World* world = MWBase::Environment::get().getWorld(); + osg::Vec3d sideOffset = orient * osg::Vec3d(world->getHalfExtents(mCamera->getTrackingPtr()).x() - 1, 0, 0); + float rayRight = world->getDistToNearestRayHit( + playerPos + sideOffset, orient * osg::Vec3d(1, 1, 0), limitToSwitchBack + 1); + float rayLeft = world->getDistToNearestRayHit( + playerPos - sideOffset, orient * osg::Vec3d(-1, 1, 0), limitToSwitchBack + 1); + float rayForward = world->getDistToNearestRayHit( + playerPos, orient * osg::Vec3d(0, 1, 0), limitToSwitchBack + 1); + + if (rayLeft < limitToSwitch && rayRight > limitToSwitchBack) + mMode = Mode::RightShoulder; + else if (rayRight < limitToSwitch && rayLeft > limitToSwitchBack) + mMode = Mode::LeftShoulder; + else if (rayLeft > limitToSwitchBack && rayRight > limitToSwitchBack && rayForward > limitToSwitchBack) + mMode = mDefaultShoulderIsRight ? Mode::RightShoulder : Mode::LeftShoulder; + } + +} \ No newline at end of file diff --git a/apps/openmw/mwrender/viewovershoulder.hpp b/apps/openmw/mwrender/viewovershoulder.hpp new file mode 100644 index 0000000000..80ac308656 --- /dev/null +++ b/apps/openmw/mwrender/viewovershoulder.hpp @@ -0,0 +1,30 @@ +#ifndef VIEWOVERSHOULDER_H +#define VIEWOVERSHOULDER_H + +#include "camera.hpp" + +namespace MWRender +{ + + class ViewOverShoulderController + { + public: + ViewOverShoulderController(Camera* camera); + + void update(); + + private: + void trySwitchShoulder(); + enum class Mode { RightShoulder, LeftShoulder, Combat, Swimming }; + + Camera* mCamera; + Mode mMode; + bool mAutoSwitchShoulder; + float mOverShoulderHorizontalOffset; + float mOverShoulderVerticalOffset; + bool mDefaultShoulderIsRight; + }; + +} + +#endif // VIEWOVERSHOULDER_H diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 80a9a01ae6..209ab538ed 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1869,44 +1869,13 @@ namespace MWWorld float cameraObstacleLimit = mRendering->getNearClipDistance() * 2.5f; float focalObstacleLimit = std::max(cameraObstacleLimit, 10.0f); + // Adjust focal point. osg::Vec3d focal = camera->getFocalPoint(); osg::Vec3d focalOffset = camera->getFocalPointOffset(); - osg::Vec3d playerPos = focal - focalOffset; - - static const bool autoSwitchShoulder = Settings::Manager::getBool("auto switch shoulder", "Camera"); - if (camera->getThirdPersonViewMode() == MWRender::Camera::ThirdPersonViewMode::OverShoulder - && autoSwitchShoulder && !camera->isVanityOrPreviewModeEnabled()) - { - const float limitToSwitch = 120; // switch to other shoulder if wall is closer than this limit - const float limitToSwitchBack = 300; // switch back to default shoulder if there is no walls at this distance - auto orient = osg::Quat(camera->getYaw(), osg::Vec3d(0,0,1)); - int mask = MWPhysics::CollisionType_World | MWPhysics::CollisionType_Door | MWPhysics::CollisionType_HeightMap; - MWPhysics::PhysicsSystem::RayResult rayRight = mPhysics->castRay( - playerPos + orient * osg::Vec3d(28, 0, 0), - playerPos + orient * osg::Vec3d(limitToSwitchBack, limitToSwitchBack, 0), - {}, {}, mask); - MWPhysics::PhysicsSystem::RayResult rayLeft = mPhysics->castRay( - playerPos + orient * osg::Vec3d(-28, 0, 0), - playerPos + orient * osg::Vec3d(-limitToSwitchBack, limitToSwitchBack, 0), - {}, {}, mask); - MWPhysics::PhysicsSystem::RayResult rayForward = mPhysics->castRay( - playerPos, playerPos + orient * osg::Vec3d(0, limitToSwitchBack, 0), - {}, {}, mask); - bool rightTooClose = rayRight.mHit && (rayRight.mHitPos - playerPos).length2() < limitToSwitch * limitToSwitch; - bool leftTooClose = rayLeft.mHit && (rayLeft.mHitPos - playerPos).length2() < limitToSwitch * limitToSwitch; - if (!rayRight.mHit && leftTooClose) - camera->switchToRightShoulder(); - else if (!rayLeft.mHit && rightTooClose) - camera->switchToLeftShoulder(); - else if (!rayRight.mHit && !rayLeft.mHit && !rayForward.mHit) - camera->switchToDefaultShoulder(); - } - - // Adjust focal point. float offsetLen = focalOffset.length(); if (offsetLen > 0) { - MWPhysics::PhysicsSystem::RayResult result = mPhysics->castSphere(playerPos, focal, focalObstacleLimit); + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castSphere(focal - focalOffset, focal, focalObstacleLimit); if (result.mHit) { double adjustmentCoef = -(result.mHitPos + result.mHitNormal * focalObstacleLimit - focal).length() / offsetLen; From 173c1fdabb507f0dd204a0219b06d96af33a47fe Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Sun, 12 Jul 2020 01:55:20 +0200 Subject: [PATCH 5/6] Make transition in 'auto switch shoulder' smoother. --- apps/openmw/mwrender/camera.cpp | 35 +++++++++++++++++++++-- apps/openmw/mwrender/camera.hpp | 12 ++++++-- apps/openmw/mwrender/viewovershoulder.cpp | 27 ++++++++--------- 3 files changed, 53 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 3b3f1aec93..1e6ea2843e 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -64,7 +64,8 @@ namespace MWRender mCameraDistance(0.f), mFocalPointCurrentOffset(osg::Vec2d()), mFocalPointTargetOffset(osg::Vec2d()), - mFocalPointTransitionSpeed(1.f), + mFocalPointTransitionSpeedCoef(1.f), + mPreviousTransitionInfluence(0.f), mSmoothedSpeed(0.f), mDynamicCameraDistanceEnabled(false), mShowCrosshairInThirdPersonMode(false) @@ -221,16 +222,44 @@ namespace MWRender mSmoothedSpeed += osg::clampBetween(speed - mSmoothedSpeed, -maxDelta, maxDelta); } + void Camera::setFocalPointTargetOffset(osg::Vec2d v) + { + mFocalPointTargetOffset = v; + mPreviousTransitionSpeed = mFocalPointTransitionSpeed; + mPreviousTransitionInfluence = 1.0f; + } + void Camera::updateFocalPointOffset(float duration) { + if (duration <= 0) + return; + + osg::Vec2d oldOffset = mFocalPointCurrentOffset; + + if (mPreviousTransitionInfluence > 0) + { + mFocalPointCurrentOffset -= mPreviousExtraOffset; + mPreviousExtraOffset = mPreviousExtraOffset / mPreviousTransitionInfluence + mPreviousTransitionSpeed * duration; + mPreviousTransitionInfluence = + std::max(0.f, mPreviousTransitionInfluence - duration * mFocalPointTransitionSpeedCoef); + mPreviousExtraOffset *= mPreviousTransitionInfluence; + mFocalPointCurrentOffset += mPreviousExtraOffset; + } + osg::Vec2d delta = mFocalPointTargetOffset - mFocalPointCurrentOffset; if (delta.length2() > 0) { - float coef = duration * (1.0 + 5.0 / delta.length()) * mFocalPointTransitionSpeed; + float coef = duration * (1.0 + 5.0 / delta.length()) * + mFocalPointTransitionSpeedCoef * (1.0f - mPreviousTransitionInfluence); mFocalPointCurrentOffset += delta * std::min(coef, 1.0f); } else - mFocalPointTransitionSpeed = 1.f; + { + mPreviousExtraOffset = osg::Vec2d(); + mPreviousTransitionInfluence = 0.f; + } + + mFocalPointTransitionSpeed = (mFocalPointCurrentOffset - oldOffset) / duration; } void Camera::toggleViewMode(bool force) diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index 5f3c5d6e49..bd70d6d9a1 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -58,7 +58,13 @@ namespace MWRender osg::Vec3d mFocalPointAdjustment; osg::Vec2d mFocalPointCurrentOffset; osg::Vec2d mFocalPointTargetOffset; - float mFocalPointTransitionSpeed; + float mFocalPointTransitionSpeedCoef; + + // This fields are used to make focal point transition smooth if previous transition was not finished. + float mPreviousTransitionInfluence; + osg::Vec2d mFocalPointTransitionSpeed; + osg::Vec2d mPreviousTransitionSpeed; + osg::Vec2d mPreviousExtraOffset; float mSmoothedSpeed; bool mDynamicCameraDistanceEnabled; @@ -75,8 +81,8 @@ namespace MWRender MWWorld::Ptr getTrackingPtr() const; - void setFocalPointTransitionSpeed(float v) { mFocalPointTransitionSpeed = v; } - void setFocalPointTargetOffset(osg::Vec2d v) { mFocalPointTargetOffset = v; } + void setFocalPointTransitionSpeed(float v) { mFocalPointTransitionSpeedCoef = v; } + void setFocalPointTargetOffset(osg::Vec2d v); void enableDynamicCameraDistance(bool v) { mDynamicCameraDistanceEnabled = v; } void enableCrosshairInThirdPersonMode(bool v) { mShowCrosshairInThirdPersonMode = v; } diff --git a/apps/openmw/mwrender/viewovershoulder.cpp b/apps/openmw/mwrender/viewovershoulder.cpp index f908b14302..5d7ec7117a 100644 --- a/apps/openmw/mwrender/viewovershoulder.cpp +++ b/apps/openmw/mwrender/viewovershoulder.cpp @@ -28,6 +28,7 @@ namespace MWRender mCamera->enableDynamicCameraDistance(true); mCamera->enableCrosshairInThirdPersonMode(true); + mCamera->setFocalPointTargetOffset({mOverShoulderHorizontalOffset, mOverShoulderVerticalOffset}); } void ViewOverShoulderController::update() @@ -35,27 +36,23 @@ namespace MWRender if (mCamera->isVanityOrPreviewModeEnabled() || mCamera->isFirstPerson()) return; - Mode newMode = mMode; + Mode oldMode = mMode; auto ptr = mCamera->getTrackingPtr(); if (ptr.getClass().isActor() && ptr.getClass().getCreatureStats(ptr).getDrawState() != MWMechanics::DrawState_Nothing) - newMode = Mode::Combat; + mMode = Mode::Combat; else if (MWBase::Environment::get().getWorld()->isSwimming(ptr)) - newMode = Mode::Swimming; - else if (mMode == Mode::Combat || mMode == Mode::Swimming) - newMode = mDefaultShoulderIsRight ? Mode::RightShoulder : Mode::LeftShoulder; - if (newMode != mMode) - { - if (newMode == Mode::Combat || mMode == Mode::Combat) - mCamera->setFocalPointTransitionSpeed(5.f); - else - mCamera->setFocalPointTransitionSpeed(1.f); - mMode = newMode; - } - + mMode = Mode::Swimming; + else if (oldMode == Mode::Combat || oldMode == Mode::Swimming) + mMode = mDefaultShoulderIsRight ? Mode::RightShoulder : Mode::LeftShoulder; if (mAutoSwitchShoulder && (mMode == Mode::LeftShoulder || mMode == Mode::RightShoulder)) trySwitchShoulder(); + if (oldMode == mMode) return; + + if (oldMode == Mode::Combat || mMode == Mode::Combat) + mCamera->setFocalPointTransitionSpeed(5.f); + else + mCamera->setFocalPointTransitionSpeed(1.f); - osg::Vec2d focalPointTargetOffset; switch (mMode) { case Mode::RightShoulder: From 383fa3d3ab040e41880bc55d6ac74be82f4d4be0 Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Sun, 12 Jul 2020 21:03:18 +0200 Subject: [PATCH 6/6] Make 'zoom out when move' configurable. --- apps/openmw/mwrender/camera.cpp | 3 ++- apps/openmw/mwrender/camera.hpp | 1 + docs/source/reference/modding/settings/camera.rst | 12 ++++++++++++ files/settings-default.cfg | 3 +++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 1e6ea2843e..7531e712b5 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -67,6 +67,7 @@ namespace MWRender mFocalPointTransitionSpeedCoef(1.f), mPreviousTransitionInfluence(0.f), mSmoothedSpeed(0.f), + mZoomOutWhenMoveCoef(Settings::Manager::getFloat("zoom out when move coef", "Camera")), mDynamicCameraDistanceEnabled(false), mShowCrosshairInThirdPersonMode(false) { @@ -461,7 +462,7 @@ namespace MWRender float pitchCorrection = std::max(-getPitch(), 0.f) * 50.f; float smoothedSpeedSqr = mSmoothedSpeed * mSmoothedSpeed; - float speedCorrection = smoothedSpeedSqr / (smoothedSpeedSqr + 300.f*300.f) * 20.0f; + float speedCorrection = smoothedSpeedSqr / (smoothedSpeedSqr + 300.f*300.f) * mZoomOutWhenMoveCoef; return pitchCorrection + speedCorrection; } diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index bd70d6d9a1..59fa53047a 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -67,6 +67,7 @@ namespace MWRender osg::Vec2d mPreviousExtraOffset; float mSmoothedSpeed; + float mZoomOutWhenMoveCoef; bool mDynamicCameraDistanceEnabled; bool mShowCrosshairInThirdPersonMode; diff --git a/docs/source/reference/modding/settings/camera.rst b/docs/source/reference/modding/settings/camera.rst index 21fec8fd60..be636cef4f 100644 --- a/docs/source/reference/modding/settings/camera.rst +++ b/docs/source/reference/modding/settings/camera.rst @@ -162,3 +162,15 @@ When player is close to an obstacle, automatically switches camera to the should This setting can only be configured by editing the settings configuration file. +zoom out when move coef +----------------------- + +:Type: floating point +:Range: Any +:Default: 20 + +This setting makes difference only in third person mode if 'view over shoulder' is enabled. +Slightly pulls camera away (or closer in case of negative value) when the character moves. To disable set it to zero. + +This setting can only be configured by editing the settings configuration file. + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index b960946d82..35be128a89 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -45,6 +45,9 @@ view over shoulder offset = 30 -10 # Switch shoulder automatically when player is close to an obstacle. auto switch shoulder = true +# Slightly pulls camera away when the character moves. Works only in 'view over shoulder' mode. Set to 0 to disable. +zoom out when move coef = 20 + [Cells] # Preload cells in a background thread. All settings starting with 'preload' have no effect unless this is enabled.