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

Refactoring. Move all code related to 'view over shoulder' to a separate file.

This commit is contained in:
Petr Mikheev 2020-07-11 14:21:18 +02:00
parent 5bdf61a886
commit 51173ebcf5
8 changed files with 159 additions and 142 deletions

View File

@ -20,7 +20,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender add_openmw_dir (mwrender
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation 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 renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging
) )

View File

@ -7,7 +7,6 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
@ -63,13 +62,12 @@ namespace MWRender
mVanityToggleQueuedValue(false), mVanityToggleQueuedValue(false),
mViewModeToggleQueued(false), mViewModeToggleQueued(false),
mCameraDistance(0.f), mCameraDistance(0.f),
mThirdPersonMode(ThirdPersonViewMode::Standard),
mOverShoulderOffset(osg::Vec2f(30.0f, -10.0f)),
mDefaultShoulderIsRight(true),
mThirdPersionOffsetType(ThirdPersonOffsetType::RightShoulder),
mFocalPointCurrentOffset(osg::Vec2d()), mFocalPointCurrentOffset(osg::Vec2d()),
mFocalPointTargetOffset(osg::Vec2d()),
mFocalPointTransitionSpeed(1.f), mFocalPointTransitionSpeed(1.f),
mSmoothedSpeed(0.f) mSmoothedSpeed(0.f),
mDynamicCameraDistanceEnabled(false),
mShowCrosshairInThirdPersonMode(false)
{ {
mVanity.enabled = false; mVanity.enabled = false;
mVanity.allowed = true; mVanity.allowed = true;
@ -124,7 +122,7 @@ namespace MWRender
osg::Vec3d Camera::getFocalPointOffset() const osg::Vec3d Camera::getFocalPointOffset() const
{ {
osg::Vec3d offset(0, 0, 10.f); 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.x() += mFocalPointCurrentOffset.x() * cos(getYaw());
offset.y() += mFocalPointCurrentOffset.x() * sin(getYaw()); offset.y() += mFocalPointCurrentOffset.x() * sin(getYaw());
@ -209,7 +207,7 @@ namespace MWRender
// only show the crosshair in game mode // only show the crosshair in game mode
MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager(); MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager();
wm->showCrosshair(!wm->isGuiMode() && !mVanity.enabled && !mPreviewMode wm->showCrosshair(!wm->isGuiMode() && !mVanity.enabled && !mPreviewMode
&& (mFirstPersonView || mThirdPersonMode != ThirdPersonViewMode::Standard)); && (mFirstPersonView || mShowCrosshairInThirdPersonMode));
if(mVanity.enabled) if(mVanity.enabled)
{ {
@ -223,68 +221,9 @@ namespace MWRender
mSmoothedSpeed += osg::clampBetween(speed - mSmoothedSpeed, -maxDelta, maxDelta); 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) void Camera::updateFocalPointOffset(float duration)
{ {
if (mThirdPersonMode == ThirdPersonViewMode::Standard) osg::Vec2d delta = mFocalPointTargetOffset - mFocalPointCurrentOffset;
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) if (delta.length2() > 0)
{ {
float coef = duration * (1.0 + 5.0 / delta.length()) * mFocalPointTransitionSpeed; float coef = duration * (1.0 + 5.0 / delta.length()) * mFocalPointTransitionSpeed;
@ -487,17 +426,15 @@ namespace MWRender
float Camera::getCameraDistanceCorrection() const float Camera::getCameraDistanceCorrection() const
{ {
if (mThirdPersonMode == ThirdPersonViewMode::Standard) if (!mDynamicCameraDistanceEnabled)
return 0; return 0;
else
{
float pitchCorrection = std::max(-getPitch(), 0.f) * 50.f;
float smoothedSpeedSqr = mSmoothedSpeed * mSmoothedSpeed; float pitchCorrection = std::max(-getPitch(), 0.f) * 50.f;
float speedCorrection = smoothedSpeedSqr / (smoothedSpeedSqr + 300.f*300.f) * 20.0f;
return pitchCorrection + speedCorrection; float smoothedSpeedSqr = mSmoothedSpeed * mSmoothedSpeed;
} float speedCorrection = smoothedSpeedSqr / (smoothedSpeedSqr + 300.f*300.f) * 20.0f;
return pitchCorrection + speedCorrection;
} }
void Camera::setCameraDistance() void Camera::setCameraDistance()

View File

@ -23,12 +23,7 @@ namespace MWRender
/// \brief Camera control /// \brief Camera control
class Camera class Camera
{ {
public:
enum class ThirdPersonViewMode {Standard, OverShoulder};
private: private:
enum class ThirdPersonOffsetType { RightShoulder, LeftShoulder, Combat, Swimming };
struct CamData { struct CamData {
float pitch, yaw, offset; float pitch, yaw, offset;
}; };
@ -60,17 +55,14 @@ namespace MWRender
float mCameraDistance; float mCameraDistance;
ThirdPersonViewMode mThirdPersonMode;
osg::Vec2f mOverShoulderOffset;
bool mDefaultShoulderIsRight;
osg::Vec3d mFocalPointAdjustment; osg::Vec3d mFocalPointAdjustment;
// Makes sense only if mThirdPersonMode is OverShoulder.
ThirdPersonOffsetType mThirdPersionOffsetType;
osg::Vec2d mFocalPointCurrentOffset; osg::Vec2d mFocalPointCurrentOffset;
osg::Vec2d mFocalPointTargetOffset;
float mFocalPointTransitionSpeed; float mFocalPointTransitionSpeed;
float mSmoothedSpeed; float mSmoothedSpeed;
bool mDynamicCameraDistanceEnabled;
bool mShowCrosshairInThirdPersonMode;
void updateFocalPointOffset(float duration); void updateFocalPointOffset(float duration);
float getCameraDistanceCorrection() const; float getCameraDistanceCorrection() const;
@ -83,13 +75,10 @@ namespace MWRender
MWWorld::Ptr getTrackingPtr() const; MWWorld::Ptr getTrackingPtr() const;
void setThirdPersonViewMode(ThirdPersonViewMode mode) { mThirdPersonMode = mode; } void setFocalPointTransitionSpeed(float v) { mFocalPointTransitionSpeed = v; }
ThirdPersonViewMode getThirdPersonViewMode() const { return mThirdPersonMode; } void setFocalPointTargetOffset(osg::Vec2d v) { mFocalPointTargetOffset = v; }
void enableDynamicCameraDistance(bool v) { mDynamicCameraDistanceEnabled = v; }
void setOverShoulderOffset(float horizontal, float vertical); void enableCrosshairInThirdPersonMode(bool v) { mShowCrosshairInThirdPersonMode = v; }
void switchToLeftShoulder();
void switchToRightShoulder();
void switchToDefaultShoulder();
/// Update the view matrix of \a cam /// Update the view matrix of \a cam
void updateCamera(osg::Camera* cam); void updateCamera(osg::Camera* cam);

View File

@ -65,6 +65,7 @@
#include "vismask.hpp" #include "vismask.hpp"
#include "pathgrid.hpp" #include "pathgrid.hpp"
#include "camera.hpp" #include "camera.hpp"
#include "viewovershoulder.hpp"
#include "water.hpp" #include "water.hpp"
#include "terrainstorage.hpp" #include "terrainstorage.hpp"
#include "util.hpp" #include "util.hpp"
@ -306,6 +307,8 @@ namespace MWRender
mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath)); mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath));
mCamera.reset(new Camera(mViewer->getCamera())); 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); mViewer->setLightingMode(osgViewer::View::NO_LIGHT);
@ -366,7 +369,6 @@ namespace MWRender
float firstPersonFov = Settings::Manager::getFloat("first person field of view", "Camera"); float firstPersonFov = Settings::Manager::getFloat("first person field of view", "Camera");
mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f); mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f);
mStateUpdater->setFogEnd(mViewDistance); mStateUpdater->setFogEnd(mViewDistance);
updateThirdPersonViewMode();
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip)); mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip));
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance)); mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance));
@ -382,19 +384,6 @@ namespace MWRender
mWorkQueue = nullptr; 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() osgUtil::IncrementalCompileOperation* RenderingManager::getIncrementalCompileOperation()
{ {
return mViewer->getIncrementalCompileOperation(); return mViewer->getIncrementalCompileOperation();
@ -630,6 +619,8 @@ namespace MWRender
updateNavMesh(); updateNavMesh();
updateRecastMesh(); updateRecastMesh();
if (mViewOverShoulderController)
mViewOverShoulderController->update();
mCamera->update(dt, paused); mCamera->update(dt, paused);
osg::Vec3d focal, cameraPos; osg::Vec3d focal, cameraPos;

View File

@ -79,6 +79,7 @@ namespace MWRender
class NpcAnimation; class NpcAnimation;
class Pathgrid; class Pathgrid;
class Camera; class Camera;
class ViewOverShoulderController;
class Water; class Water;
class TerrainStorage; class TerrainStorage;
class LandManager; class LandManager;
@ -294,6 +295,7 @@ namespace MWRender
osg::ref_ptr<NpcAnimation> mPlayerAnimation; osg::ref_ptr<NpcAnimation> mPlayerAnimation;
osg::ref_ptr<SceneUtil::PositionAttitudeTransform> mPlayerNode; osg::ref_ptr<SceneUtil::PositionAttitudeTransform> mPlayerNode;
std::unique_ptr<Camera> mCamera; std::unique_ptr<Camera> mCamera;
std::unique_ptr<ViewOverShoulderController> mViewOverShoulderController;
osg::Vec3f mCurrentCameraPos; osg::Vec3f mCurrentCameraPos;
osg::ref_ptr<StateUpdater> mStateUpdater; osg::ref_ptr<StateUpdater> mStateUpdater;

View File

@ -0,0 +1,99 @@
#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);
}
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;
}
}

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

@ -1869,44 +1869,13 @@ namespace MWWorld
float cameraObstacleLimit = mRendering->getNearClipDistance() * 2.5f; float cameraObstacleLimit = mRendering->getNearClipDistance() * 2.5f;
float focalObstacleLimit = std::max(cameraObstacleLimit, 10.0f); float focalObstacleLimit = std::max(cameraObstacleLimit, 10.0f);
// Adjust focal point.
osg::Vec3d focal = camera->getFocalPoint(); osg::Vec3d focal = camera->getFocalPoint();
osg::Vec3d focalOffset = camera->getFocalPointOffset(); 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(); float offsetLen = focalOffset.length();
if (offsetLen > 0) 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) if (result.mHit)
{ {
double adjustmentCoef = -(result.mHitPos + result.mHitNormal * focalObstacleLimit - focal).length() / offsetLen; double adjustmentCoef = -(result.mHitPos + result.mHitNormal * focalObstacleLimit - focal).length() / offsetLen;