diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index fddfa276f..01d270f82 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -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 ) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index e856b8ab3..3b3f1aec9 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -7,7 +7,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwbase/world.hpp" #include "../mwworld/class.hpp" #include "../mwworld/ptr.hpp" @@ -63,13 +62,12 @@ namespace MWRender mVanityToggleQueuedValue(false), mViewModeToggleQueued(false), mCameraDistance(0.f), - mThirdPersonMode(ThirdPersonViewMode::Standard), - mOverShoulderOffset(osg::Vec2f(30.0f, -10.0f)), - mDefaultShoulderIsRight(true), - mThirdPersionOffsetType(ThirdPersonOffsetType::RightShoulder), mFocalPointCurrentOffset(osg::Vec2d()), + mFocalPointTargetOffset(osg::Vec2d()), mFocalPointTransitionSpeed(1.f), - mSmoothedSpeed(0.f) + mSmoothedSpeed(0.f), + mDynamicCameraDistanceEnabled(false), + mShowCrosshairInThirdPersonMode(false) { mVanity.enabled = false; mVanity.allowed = true; @@ -124,7 +122,7 @@ 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) { offset.x() += mFocalPointCurrentOffset.x() * cos(getYaw()); offset.y() += mFocalPointCurrentOffset.x() * sin(getYaw()); @@ -209,7 +207,7 @@ 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) { @@ -223,68 +221,9 @@ namespace MWRender 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) { - if (mThirdPersonMode == ThirdPersonViewMode::Standard) - 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; + osg::Vec2d delta = mFocalPointTargetOffset - mFocalPointCurrentOffset; if (delta.length2() > 0) { float coef = duration * (1.0 + 5.0 / delta.length()) * mFocalPointTransitionSpeed; @@ -487,17 +426,15 @@ namespace MWRender float Camera::getCameraDistanceCorrection() const { - if (mThirdPersonMode == ThirdPersonViewMode::Standard) + if (!mDynamicCameraDistanceEnabled) return 0; - else - { - float pitchCorrection = std::max(-getPitch(), 0.f) * 50.f; - float smoothedSpeedSqr = mSmoothedSpeed * mSmoothedSpeed; - float speedCorrection = smoothedSpeedSqr / (smoothedSpeedSqr + 300.f*300.f) * 20.0f; + float pitchCorrection = std::max(-getPitch(), 0.f) * 50.f; - return pitchCorrection + speedCorrection; - } + float smoothedSpeedSqr = mSmoothedSpeed * mSmoothedSpeed; + float speedCorrection = smoothedSpeedSqr / (smoothedSpeedSqr + 300.f*300.f) * 20.0f; + + return pitchCorrection + speedCorrection; } void Camera::setCameraDistance() diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index eb7462945..5f3c5d6e4 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -23,12 +23,7 @@ namespace MWRender /// \brief Camera control class Camera { - public: - enum class ThirdPersonViewMode {Standard, OverShoulder}; - private: - enum class ThirdPersonOffsetType { RightShoulder, LeftShoulder, Combat, Swimming }; - struct CamData { float pitch, yaw, offset; }; @@ -60,17 +55,14 @@ namespace MWRender float mCameraDistance; - ThirdPersonViewMode mThirdPersonMode; - osg::Vec2f mOverShoulderOffset; - bool mDefaultShoulderIsRight; osg::Vec3d mFocalPointAdjustment; - - // Makes sense only if mThirdPersonMode is OverShoulder. - ThirdPersonOffsetType mThirdPersionOffsetType; osg::Vec2d mFocalPointCurrentOffset; + osg::Vec2d mFocalPointTargetOffset; float mFocalPointTransitionSpeed; float mSmoothedSpeed; + bool mDynamicCameraDistanceEnabled; + bool mShowCrosshairInThirdPersonMode; void updateFocalPointOffset(float duration); float getCameraDistanceCorrection() const; @@ -83,13 +75,10 @@ namespace MWRender MWWorld::Ptr getTrackingPtr() const; - void setThirdPersonViewMode(ThirdPersonViewMode mode) { mThirdPersonMode = mode; } - ThirdPersonViewMode getThirdPersonViewMode() const { return mThirdPersonMode; } - - void setOverShoulderOffset(float horizontal, float vertical); - void switchToLeftShoulder(); - void switchToRightShoulder(); - void switchToDefaultShoulder(); + void setFocalPointTransitionSpeed(float v) { mFocalPointTransitionSpeed = v; } + void setFocalPointTargetOffset(osg::Vec2d v) { mFocalPointTargetOffset = 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); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index c7dd5ca63..d91da5a08 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -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; diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 6700f5ce6..d6a0f89c3 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -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 mPlayerAnimation; osg::ref_ptr mPlayerNode; std::unique_ptr mCamera; + std::unique_ptr mViewOverShoulderController; osg::Vec3f mCurrentCameraPos; osg::ref_ptr mStateUpdater; diff --git a/apps/openmw/mwrender/viewovershoulder.cpp b/apps/openmw/mwrender/viewovershoulder.cpp new file mode 100644 index 000000000..f908b1430 --- /dev/null +++ b/apps/openmw/mwrender/viewovershoulder.cpp @@ -0,0 +1,99 @@ +#include "viewovershoulder.hpp" + +#include + +#include + +#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; + } + +} \ No newline at end of file diff --git a/apps/openmw/mwrender/viewovershoulder.hpp b/apps/openmw/mwrender/viewovershoulder.hpp new file mode 100644 index 000000000..80ac30865 --- /dev/null +++ b/apps/openmw/mwrender/viewovershoulder.hpp @@ -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 diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 80a9a01ae..209ab538e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1869,44 +1869,13 @@ 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(playerPos, focal, focalObstacleLimit); + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castSphere(focal - focalOffset, focal, focalObstacleLimit); if (result.mHit) { double adjustmentCoef = -(result.mHitPos + result.mHitNormal * focalObstacleLimit - focal).length() / offsetLen;