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:
commit
bcd526b706
@ -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
|
||||
)
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
96
apps/openmw/mwrender/viewovershoulder.cpp
Normal file
96
apps/openmw/mwrender/viewovershoulder.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
30
apps/openmw/mwrender/viewovershoulder.hpp
Normal file
30
apps/openmw/mwrender/viewovershoulder.hpp
Normal 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
|
@ -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.
|
||||
|
||||
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user