diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 877cb7570b..54bc581ef5 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -55,6 +55,8 @@ namespace MWRender mFirstPersonView(true), mMode(Mode::Normal), mVanityAllowed(true), + mStandingPreviewAllowed(Settings::Manager::getBool("preview if stand still", "Camera")), + mDeferredRotationAllowed(Settings::Manager::getBool("deferred preview rotation", "Camera")), mNearest(30.f), mFurthest(800.f), mIsNearest(false), @@ -200,6 +202,24 @@ namespace MWRender mSmoothedSpeed += osg::clampBetween(speed - mSmoothedSpeed, -maxDelta, maxDelta); mMaxNextCameraDistance = mCameraDistance + duration * (100.f + mBaseCameraDistance); + updateStandingPreviewMode(); + } + + void Camera::updateStandingPreviewMode() + { + if (!mStandingPreviewAllowed) + return; + float speed = mTrackingPtr.getClass().getSpeed(mTrackingPtr); + bool combat = mTrackingPtr.getClass().isActor() && + mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).getDrawState() != MWMechanics::DrawState_Nothing; + bool standingStill = speed == 0 && !combat && !mFirstPersonView; + if (!standingStill && mMode == Mode::StandingPreview) + { + mMode = Mode::Normal; + calculateDeferredRotation(); + } + else if (standingStill && mMode == Mode::Normal) + mMode = Mode::StandingPreview; } void Camera::setFocalPointTargetOffset(osg::Vec2d v) @@ -266,6 +286,7 @@ namespace MWRender mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).setSideMovementAngle(0); mFirstPersonView = !mFirstPersonView; + updateStandingPreviewMode(); processViewChange(); } @@ -296,6 +317,8 @@ namespace MWRender if ((mMode == Mode::Vanity) == enable) return true; mMode = enable ? Mode::Vanity : Mode::Normal; + if (!mDeferredRotationAllowed) + disableDeferredPreviewRotation(); if (!enable) calculateDeferredRotation(); @@ -312,8 +335,14 @@ namespace MWRender return; mMode = enable ? Mode::Preview : Mode::Normal; - if (!enable) + if (mMode == Mode::Normal) + updateStandingPreviewMode(); + if (mMode == Mode::Normal) + { + if (!mDeferredRotationAllowed) + disableDeferredPreviewRotation(); calculateDeferredRotation(); + } processViewChange(); } @@ -432,6 +461,13 @@ namespace MWRender rot *= delta; mDeferredRotation -= rot; + if (mDeferredRotationDisabled) + { + mDeferredRotationDisabled = delta > 0.0001; + rotateCameraToTrackingPtr(); + return; + } + auto& movement = mTrackingPtr.getClass().getMovementSettings(mTrackingPtr); movement.mRotation[0] += rot.x(); movement.mRotation[1] += rot.y(); @@ -453,16 +489,22 @@ namespace MWRender setYaw(-mTrackingPtr.getRefData().getPosition().rot[2] - mDeferredRotation.z()); } + void Camera::instantTransition() + { + mSkipFocalPointTransition = true; + mDeferredRotationDisabled = false; + mDeferredRotation = osg::Vec3f(); + rotateCameraToTrackingPtr(); + } + void Camera::calculateDeferredRotation() { MWWorld::Ptr ptr = mTrackingPtr; if (isVanityOrPreviewModeEnabled() || ptr.isEmpty()) return; - if (isFirstPerson() || mDeferredRotationDisabled) + if (mFirstPersonView) { - mDeferredRotationDisabled = false; - mDeferredRotation = osg::Vec3f(); - rotateCameraToTrackingPtr(); + instantTransition(); return; } diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index c5f7ec2b2c..f2e5c390db 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -24,7 +24,7 @@ namespace MWRender class Camera { public: - enum class Mode { Normal, Vanity, Preview }; + enum class Mode { Normal, Vanity, Preview, StandingPreview }; private: MWWorld::Ptr mTrackingPtr; @@ -38,6 +38,8 @@ namespace MWRender bool mFirstPersonView; Mode mMode; bool mVanityAllowed; + bool mStandingPreviewAllowed; + bool mDeferredRotationAllowed; float mNearest; float mFurthest; @@ -79,6 +81,7 @@ namespace MWRender osg::Vec3f mDeferredRotation; bool mDeferredRotationDisabled; void calculateDeferredRotation(); + void updateStandingPreviewMode(); public: Camera(osg::Camera* camera); @@ -90,7 +93,7 @@ namespace MWRender void setFocalPointTransitionSpeed(float v) { mFocalPointTransitionSpeedCoef = v; } void setFocalPointTargetOffset(osg::Vec2d v); - void skipFocalPointTransition() { mSkipFocalPointTransition = true; } + void instantTransition(); 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 ceb9407a16..4d708afe0a 100644 --- a/apps/openmw/mwrender/viewovershoulder.cpp +++ b/apps/openmw/mwrender/viewovershoulder.cpp @@ -77,6 +77,9 @@ namespace MWRender void ViewOverShoulderController::trySwitchShoulder() { + if (mCamera->getMode() != Camera::Mode::Normal) + return; + 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 diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ca451601ba..84f50cdeff 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -945,7 +945,7 @@ namespace MWWorld removeContainerScripts(getPlayerPtr()); mWorldScene->changeToInteriorCell(cellName, position, adjustPlayerPos, changeEvent); addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell()); - mRendering->getCamera()->skipFocalPointTransition(); + mRendering->getCamera()->instantTransition(); } void World::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos, bool changeEvent) @@ -961,7 +961,7 @@ namespace MWWorld removeContainerScripts(getPlayerPtr()); mWorldScene->changeToExteriorCell(position, adjustPlayerPos, changeEvent); addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell()); - mRendering->getCamera()->skipFocalPointTransition(); + mRendering->getCamera()->instantTransition(); } void World::changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent) diff --git a/docs/source/reference/modding/settings/camera.rst b/docs/source/reference/modding/settings/camera.rst index be636cef4f..18b6754a74 100644 --- a/docs/source/reference/modding/settings/camera.rst +++ b/docs/source/reference/modding/settings/camera.rst @@ -174,3 +174,27 @@ Slightly pulls camera away (or closer in case of negative value) when the charac This setting can only be configured by editing the settings configuration file. +preview if stand still +---------------------- + +:Type: boolean +:Range: True/False +:Default: False + +If enabled then the character rotation is not synchonized with the camera rotation while the character doesn't move and not in combat mode. + +This setting can only be configured by editing the settings configuration file. + +deferred preview rotation +------------------------- + +:Type: boolean +:Range: True/False +:Default: True + +Makes difference only in third person mode. +If enabled then the character smoothly rotates to the view direction after exiting preview or vanity mode. +If disabled then the camera rotates rather than the character. + +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 35be128a89..a9777fc427 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -48,6 +48,12 @@ 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 +# Automatically enable preview mode when player doesn't move. +preview if stand still = false + +# Rotate the character to the view direction after exiting preview mode. +deferred preview rotation = true + [Cells] # Preload cells in a background thread. All settings starting with 'preload' have no effect unless this is enabled.