diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index e0f031b8c..3da94f97d 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 1e1d12f10..b3e7e1f67 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 209ab538e..80a9a01ae 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 20349e97f..21fec8fd6 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 1e62ff5bf..b960946d8 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.