1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-26 18:35:20 +00:00

Merge branch 'third_person_new' into 'master'

Further improvements of "view over shoulder"

See merge request OpenMW/openmw!237
This commit is contained in:
elsid 2020-07-13 09:42:19 +00:00
commit bcd526b706
9 changed files with 243 additions and 53 deletions

View File

@ -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
)

View File

@ -62,9 +62,14 @@ namespace MWRender
mVanityToggleQueuedValue(false),
mViewModeToggleQueued(false),
mCameraDistance(0.f),
mThirdPersonMode(ThirdPersonViewMode::Standard),
mOverShoulderOffset(osg::Vec2f(30.0f, -10.0f)),
mSmoothTransitionToCombatMode(0.f)
mFocalPointCurrentOffset(osg::Vec2d()),
mFocalPointTargetOffset(osg::Vec2d()),
mFocalPointTransitionSpeedCoef(1.f),
mPreviousTransitionInfluence(0.f),
mSmoothedSpeed(0.f),
mZoomOutWhenMoveCoef(Settings::Manager::getFloat("zoom out when move coef", "Camera")),
mDynamicCameraDistanceEnabled(false),
mShowCrosshairInThirdPersonMode(false)
{
mVanity.enabled = false;
mVanity.allowed = true;
@ -119,14 +124,11 @@ 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)
{
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;
}
@ -207,35 +209,58 @@ 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)
{
rotateCamera(0.f, osg::DegreesToRadians(3.f * duration), true);
}
updateSmoothTransitionToCombatMode(duration);
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)
void Camera::setFocalPointTargetOffset(osg::Vec2d v)
{
mOverShoulderOffset = osg::Vec2f(horizontal, vertical);
mFocalPointTargetOffset = v;
mPreviousTransitionSpeed = mFocalPointTransitionSpeed;
mPreviousTransitionInfluence = 1.0f;
}
void Camera::updateSmoothTransitionToCombatMode(float duration)
void Camera::updateFocalPointOffset(float duration)
{
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 (duration <= 0)
return;
mSmoothTransitionToCombatMode += speed * duration;
if (mSmoothTransitionToCombatMode > 1)
mSmoothTransitionToCombatMode = 1;
if (mSmoothTransitionToCombatMode < 0)
mSmoothTransitionToCombatMode = 0;
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()) *
mFocalPointTransitionSpeedCoef * (1.0f - mPreviousTransitionInfluence);
mFocalPointCurrentOffset += delta * std::min(coef, 1.0f);
}
else
{
mPreviousExtraOffset = osg::Vec2d();
mPreviousTransitionInfluence = 0.f;
}
mFocalPointTransitionSpeed = (mFocalPointCurrentOffset - oldOffset) / duration;
}
void Camera::toggleViewMode(bool force)
@ -431,7 +456,15 @@ namespace MWRender
float Camera::getCameraDistanceCorrection() const
{
return mThirdPersonMode != ThirdPersonViewMode::Standard ? std::max(-getPitch(), 0.f) * 50.f : 0;
if (!mDynamicCameraDistanceEnabled)
return 0;
float pitchCorrection = std::max(-getPitch(), 0.f) * 50.f;
float smoothedSpeedSqr = mSmoothedSpeed * mSmoothedSpeed;
float speedCorrection = smoothedSpeedSqr / (smoothedSpeedSqr + 300.f*300.f) * mZoomOutWhenMoveCoef;
return pitchCorrection + speedCorrection;
}
void Camera::setCameraDistance()

View File

@ -23,9 +23,6 @@ namespace MWRender
/// \brief Camera control
class Camera
{
public:
enum class ThirdPersonViewMode {Standard, OverShoulder};
private:
struct CamData {
float pitch, yaw, offset;
@ -58,14 +55,23 @@ namespace MWRender
float mCameraDistance;
ThirdPersonViewMode mThirdPersonMode;
osg::Vec2f mOverShoulderOffset;
osg::Vec3d mFocalPointAdjustment;
osg::Vec2d mFocalPointCurrentOffset;
osg::Vec2d mFocalPointTargetOffset;
float mFocalPointTransitionSpeedCoef;
// 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);
// 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;
float mZoomOutWhenMoveCoef;
bool mDynamicCameraDistanceEnabled;
bool mShowCrosshairInThirdPersonMode;
void updateFocalPointOffset(float duration);
float getCameraDistanceCorrection() const;
osg::ref_ptr<osg::NodeCallback> mUpdateCallback;
@ -76,8 +82,10 @@ namespace MWRender
MWWorld::Ptr getTrackingPtr() const;
void setThirdPersonViewMode(ThirdPersonViewMode mode) { mThirdPersonMode = mode; }
void setOverShoulderOffset(float horizontal, float vertical);
void setFocalPointTransitionSpeed(float v) { mFocalPointTransitionSpeedCoef = v; }
void setFocalPointTargetOffset(osg::Vec2d 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);

View File

@ -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;

View File

@ -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<NpcAnimation> mPlayerAnimation;
osg::ref_ptr<SceneUtil::PositionAttitudeTransform> mPlayerNode;
std::unique_ptr<Camera> mCamera;
std::unique_ptr<ViewOverShoulderController> mViewOverShoulderController;
osg::Vec3f mCurrentCameraPos;
osg::ref_ptr<StateUpdater> mStateUpdater;

View File

@ -0,0 +1,96 @@
#include "viewovershoulder.hpp"
#include <osg/Quat>
#include <components/settings/settings.hpp>
#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);
mCamera->setFocalPointTargetOffset({mOverShoulderHorizontalOffset, mOverShoulderVerticalOffset});
}
void ViewOverShoulderController::update()
{
if (mCamera->isVanityOrPreviewModeEnabled() || mCamera->isFirstPerson())
return;
Mode oldMode = mMode;
auto ptr = mCamera->getTrackingPtr();
if (ptr.getClass().isActor() && ptr.getClass().getCreatureStats(ptr).getDrawState() != MWMechanics::DrawState_Nothing)
mMode = Mode::Combat;
else if (MWBase::Environment::get().getWorld()->isSwimming(ptr))
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);
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;
}
}

View File

@ -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

View File

@ -150,3 +150,27 @@ 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.
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.

View File

@ -42,6 +42,12 @@ 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
# 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.