From be27b51279f6b38243e7fbbe624f6572b2e0aff4 Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Sun, 4 Oct 2020 20:12:45 +0200 Subject: [PATCH] Add head bobbing in first person mode --- CHANGELOG.md | 1 + apps/launcher/advancedpage.cpp | 2 + apps/openmw/mwrender/camera.cpp | 31 +++++++++++- apps/openmw/mwrender/camera.hpp | 8 +++- .../reference/modding/settings/camera.rst | 47 +++++++++++++++++++ files/settings-default.cfg | 12 +++++ files/ui/advancedpage.ui | 10 ++++ 7 files changed, 108 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc0b2848c..080627b82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ Feature #390: 3rd person look "over the shoulder" Feature #2386: Distant Statics in the form of Object Paging Feature #4894: Consider actors as obstacles for pathfinding + Feature #5043: Head Bobbing Feature #5297: Add a search function to the "Datafiles" tab of the OpenMW launcher Feature #5362: Show the soul gems' trapped soul in count dialog Feature #5445: Handle NiLines diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index 2e929faf5..422f06ed4 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -133,6 +133,7 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(autoSwitchShoulderCheckBox, "auto switch shoulder", "Camera"); loadSettingBool(previewIfStandStillCheckBox, "preview if stand still", "Camera"); loadSettingBool(deferredPreviewRotationCheckBox, "deferred preview rotation", "Camera"); + loadSettingBool(headBobbingCheckBox, "head bobbing", "Camera"); defaultShoulderComboBox->setCurrentIndex( mEngineSettings.getVector2("view over shoulder offset", "Camera").x() >= 0 ? 0 : 1); } @@ -247,6 +248,7 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(autoSwitchShoulderCheckBox, "auto switch shoulder", "Camera"); saveSettingBool(previewIfStandStillCheckBox, "preview if stand still", "Camera"); saveSettingBool(deferredPreviewRotationCheckBox, "deferred preview rotation", "Camera"); + saveSettingBool(headBobbingCheckBox, "head bobbing", "Camera"); osg::Vec2f shoulderOffset = mEngineSettings.getVector2("view over shoulder offset", "Camera"); if (defaultShoulderComboBox->currentIndex() != (shoulderOffset.x() >= 0 ? 0 : 1)) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 0d6ed262b..a42e06a41 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -80,6 +80,7 @@ namespace MWRender mZoomOutWhenMoveCoef(Settings::Manager::getFloat("zoom out when move coef", "Camera")), mDynamicCameraDistanceEnabled(false), mShowCrosshairInThirdPersonMode(false), + mHeadBobbingEnabled(Settings::Manager::getBool("head bobbing", "Camera")), mDeferredRotation(osg::Vec3f()), mDeferredRotationDisabled(false) { @@ -104,7 +105,9 @@ namespace MWRender osg::Matrix worldMat = osg::computeLocalToWorld(nodepaths[0]); osg::Vec3d position = worldMat.getTrans(); - if (!isFirstPerson()) + if (isFirstPerson()) + position.z() += mHeadBobbingOffset; + else { position.z() += mHeight * mHeightScale; @@ -143,13 +146,31 @@ namespace MWRender osg::Vec3d focal, position; getPosition(focal, position); - osg::Quat orient = osg::Quat(getPitch(), osg::Vec3d(1,0,0)) * osg::Quat(getYaw(), osg::Vec3d(0,0,1)); + osg::Quat orient = osg::Quat(mRoll, osg::Vec3d(0, 1, 0)) * osg::Quat(mPitch, osg::Vec3d(1, 0, 0)) * osg::Quat(mYaw, osg::Vec3d(0, 0, 1)); osg::Vec3d forward = orient * osg::Vec3d(0,1,0); osg::Vec3d up = orient * osg::Vec3d(0,0,1); cam->setViewMatrixAsLookAt(position, position + forward, up); } + void Camera::updateHeadBobbing(float duration) { + static const float doubleStepLength = Settings::Manager::getFloat("head bobbing step", "Camera") * 2; + static const float stepHeight = Settings::Manager::getFloat("head bobbing height", "Camera"); + static const float maxRoll = osg::DegreesToRadians(Settings::Manager::getFloat("head bobbing roll", "Camera")); + + if (MWBase::Environment::get().getWorld()->isOnGround(mTrackingPtr)) + mHeadBobbingWeight = std::min(mHeadBobbingWeight + duration * 5, 1.f); + else + mHeadBobbingWeight = std::max(mHeadBobbingWeight - duration * 5, 0.f); + + float doubleStepState = mTotalMovement / doubleStepLength - std::floor(mTotalMovement / doubleStepLength); // from 0 to 1 during 2 steps + float stepState = std::abs(doubleStepState * 4 - 2) - 1; // from -1 to 1 on even steps and from 1 to -1 on odd steps + float effect = (1 - std::cos(stepState * osg::DegreesToRadians(30.f))) * 7.5f; // range from 0 to 1 + float coef = std::min(mSmoothedSpeed / 300.f, 1.f) * mHeadBobbingWeight; + mHeadBobbingOffset = (0.5f - effect) * coef * stepHeight; // range from -stepHeight/2 to stepHeight/2 + mRoll = osg::sign(stepState) * effect * coef * maxRoll; // range from -maxRoll to maxRoll + } + void Camera::reset() { togglePreviewMode(false); @@ -198,10 +219,16 @@ namespace MWRender if(mMode == Mode::Vanity) rotateCamera(0.f, osg::DegreesToRadians(3.f * duration), true); + if (isFirstPerson() && mHeadBobbingEnabled) + updateHeadBobbing(duration); + else + mRoll = mHeadBobbingOffset = 0; + updateFocalPointOffset(duration); updatePosition(); float speed = mTrackingPtr.getClass().getCurrentSpeed(mTrackingPtr); + mTotalMovement += speed * duration; speed /= (1.f + speed / 500.f); float maxDelta = 300.f * duration; mSmoothedSpeed += osg::clampBetween(speed - mSmoothedSpeed, -maxDelta, maxDelta); diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index b3f6026eb..6fe392683 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -46,7 +46,7 @@ namespace MWRender bool mIsNearest; float mHeight, mBaseCameraDistance; - float mPitch, mYaw; + float mPitch, mYaw, mRoll; bool mVanityToggleQueued; bool mVanityToggleQueuedValue; @@ -72,6 +72,12 @@ namespace MWRender bool mDynamicCameraDistanceEnabled; bool mShowCrosshairInThirdPersonMode; + bool mHeadBobbingEnabled; + float mHeadBobbingOffset; + float mHeadBobbingWeight = 0; // Value from 0 to 1 for smooth enabling/disabling. + float mTotalMovement = 0; // Needed for head bobbing. + void updateHeadBobbing(float duration); + void updateFocalPointOffset(float duration); void updatePosition(); float getCameraDistanceCorrection() const; diff --git a/docs/source/reference/modding/settings/camera.rst b/docs/source/reference/modding/settings/camera.rst index 1025a2fbd..5701947dc 100644 --- a/docs/source/reference/modding/settings/camera.rst +++ b/docs/source/reference/modding/settings/camera.rst @@ -199,3 +199,50 @@ If disabled then the camera rotates rather than the character. This setting can be controlled in Advanced tab of the launcher. +head bobbing +------------ + +:Type: boolean +:Range: True/False +:Default: False + +Enables head bobbing when move in first person mode. + +This setting can be controlled in Advanced tab of the launcher. + +head bobbing step +----------------- + +:Type: floating point +:Range: >0 +:Default: 90.0 + +Makes diffence only in first person mode if 'head bobbing' is enabled. +Length of each step. + +This setting can only be configured by editing the settings configuration file. + +head bobbing height +------------------- + +:Type: floating point +:Range: Any +:Default: 3.0 + +Makes diffence only in first person mode if 'head bobbing' is enabled. +Amplitude of the head bobbing. + +This setting can only be configured by editing the settings configuration file. + +head bobbing roll +----------------- + +:Type: floating point +:Range: 0-90 +:Default: 0.2 + +Makes diffence only in first person mode if 'head bobbing' is enabled. +Maximum roll angle in degrees. + +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 fab5fe569..3f87907ae 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -54,6 +54,18 @@ preview if stand still = false # Rotate the character to the view direction after exiting preview mode. deferred preview rotation = true +# Enables head bobbing in first person mode +head bobbing = false + +# Length of each step +head bobbing step = 90.0 + +# Amplitude of the bobbing effect +head bobbing height = 3.0 + +# Maximum camera roll angle (degrees) +head bobbing roll = 0.2 + [Cells] # Preload cells in a background thread. All settings starting with 'preload' have no effect unless this is enabled. diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 3a91db791..0410d67fd 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -553,6 +553,16 @@ True: In non-combat mode camera is positioned behind the character's shoulder. C + + + + <html><head/><body><p>Enables head bobbing when move in first person mode.</p></body></html> + + + Head bobbing in 1st person mode + + +