From 93b723a0662b55004d3f2c7738a78a833f719d16 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Wed, 1 Nov 2023 19:47:08 +0100 Subject: [PATCH 01/15] Apply legs yaw to accumulated movement. --- apps/openmw/mwrender/animation.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 0741e24a69..bac9dbb56c 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1235,9 +1235,11 @@ namespace MWRender mRootController->setEnabled(enable); if (enable) { - mRootController->setRotate(osg::Quat(mLegsYawRadians, osg::Vec3f(0, 0, 1)) - * osg::Quat(mBodyPitchRadians, osg::Vec3f(1, 0, 0))); + osg::Quat legYaw = osg::Quat(mLegsYawRadians, osg::Vec3f(0, 0, 1)); + mRootController->setRotate(legYaw * osg::Quat(mBodyPitchRadians, osg::Vec3f(1, 0, 0))); yawOffset = mLegsYawRadians; + // When yawing the root, also update the accumulated movement. + movement = legYaw * movement; } } if (mSpineController) From f41de6b02da923f24656b6510d4041c39f144688 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Wed, 1 Nov 2023 19:48:16 +0100 Subject: [PATCH 02/15] Use accumulated movement whenever possible. Apply diagonal movement by rotating accumulated movement and sliding based on that, rather than ignoring accumulated movement. --- apps/openmw/mwmechanics/character.cpp | 98 +++++++++++++++---------- components/settings/categories/game.hpp | 1 + files/settings-default.cfg | 5 ++ 3 files changed, 67 insertions(+), 37 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 068d91ab42..1fca6c6b71 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2387,48 +2387,72 @@ namespace MWMechanics } osg::Vec3f moved = mAnimation->runAnimation(mSkipAnim && !isScriptedAnimPlaying() ? 0.f : duration); - if (duration > 0.0f) - moved /= duration; - else - moved = osg::Vec3f(0.f, 0.f, 0.f); - moved.x() *= scale; - moved.y() *= scale; - - // Ensure we're moving in generally the right direction... - if (speed > 0.f && moved != osg::Vec3f()) + if (mPtr.getClass().isActor() && isMovementAnimationControlled() && !isScriptedAnimPlaying()) { - float l = moved.length(); - if (std::abs(movement.x() - moved.x()) > std::abs(moved.x()) / 2 - || std::abs(movement.y() - moved.y()) > std::abs(moved.y()) / 2 - || std::abs(movement.z() - moved.z()) > std::abs(moved.z()) / 2) - { - moved = movement; - // For some creatures getSpeed doesn't work, so we adjust speed to the animation. - // TODO: Fix Creature::getSpeed. - float newLength = moved.length(); - if (newLength > 0 && !cls.isNpc()) - moved *= (l / newLength); - } - } + if (duration > 0.0f) + moved /= duration; + else + moved = osg::Vec3f(0.f, 0.f, 0.f); - if (mFloatToSurface && cls.isActor()) - { - if (cls.getCreatureStats(mPtr).isDead() - || (!godmode - && cls.getCreatureStats(mPtr) - .getMagicEffects() - .getOrDefault(ESM::MagicEffect::Paralyze) - .getModifier() - > 0)) - { - moved.z() = 1.0; - } - } + moved.x() *= scale; + moved.y() *= scale; - // Update movement - if (isMovementAnimationControlled() && mPtr.getClass().isActor() && !isScriptedAnimPlaying()) + if (speed > 0.f && moved != osg::Vec3f()) + { + // Ensure we're moving in generally the right direction + // This is necessary when the "turn to movement direction" feature is off, as animations + // will not be rotated to match diagonal movement. In this case we have to slide the + // character diagonally. + + // First decide the general direction expected from the current animation + float animMovementAngle = 0; + if (!Settings::game().mTurnToMovementDirection || isFirstPersonPlayer) + { + if (cls.getMovementSettings(mPtr).mIsStrafing) + animMovementAngle = movement.x() > 0 ? -osg::PI_2f : osg::PI_2f; + else + animMovementAngle = movement.y() >= 0 ? 0 : -osg::PIf; + } + else + { + animMovementAngle = mAnimation->getLegsYawRadians(); + if (movement.y() < 0) + animMovementAngle -= osg::PIf; + } + + const float epsilon = 0.001f; + float targetMovementAngle = std::atan2(-movement.x(), movement.y()); + float diff = targetMovementAngle - animMovementAngle; + if (std::fabsf(diff) > epsilon) + { + moved = osg::Quat(diff, osg::Vec3f(0, 0, 1)) * moved; + } + + if (isPlayer && Settings::game().mPlayerMovementIgnoresAnimation) + { + moved = movement; + } + } + + if (mFloatToSurface) + { + if (cls.getCreatureStats(mPtr).isDead() + || (!godmode + && cls.getCreatureStats(mPtr) + .getMagicEffects() + .getOrDefault(ESM::MagicEffect::Paralyze) + .getModifier() + > 0)) + { + moved.z() = 1.0; + } + } + + // Update movement + && !isScriptedAnimPlaying() world->queueMovement(mPtr, moved); + } mSkipAnim = false; diff --git a/components/settings/categories/game.hpp b/components/settings/categories/game.hpp index ded367a54c..375d8ef819 100644 --- a/components/settings/categories/game.hpp +++ b/components/settings/categories/game.hpp @@ -74,6 +74,7 @@ namespace Settings "unarmed creature attacks damage armor" }; SettingValue mActorCollisionShapeType{ mIndex, "Game", "actor collision shape type" }; + SettingValue mPlayerMovementIgnoresAnimation{ mIndex, "Game", "player movement ignores animation" }; }; } diff --git a/files/settings-default.cfg b/files/settings-default.cfg index bbc6b4d1c8..da1c97519a 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -365,6 +365,11 @@ unarmed creature attacks damage armor = false # 2 = Cylinder actor collision shape type = 0 +# When false the player character will base movement on animations. This will sway the camera +# while moving in third person like in vanilla, and reproduce movement bugs caused by glitchy +# vanilla animations. +player movement ignores animation = false + [General] # Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16). From c59fb6c91c9fb5bee9b42fd87d29de95965af605 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Wed, 1 Nov 2023 20:29:47 +0100 Subject: [PATCH 03/15] Changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e8516ae25..97ea9246d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Bug #4754: Stack of ammunition cannot be equipped partially Bug #4816: GetWeaponDrawn returns 1 before weapon is attached Bug #5057: Weapon swing sound plays at same pitch whether it hits or misses + Bug #5062: Root bone rotations for NPC animation don't work the same as for creature animation Bug #5129: Stuttering animation on Centurion Archer Bug #5280: Unskinned shapes in skinned equipment are rendered in the wrong place Bug #5371: Keyframe animation tracks are used for any file that begins with an X From a0f8bbc621d8181ec4acca3d8e28c558cffcba41 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Wed, 1 Nov 2023 20:34:39 +0100 Subject: [PATCH 04/15] Bad merge --- apps/openmw/mwmechanics/character.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 1fca6c6b71..05c0ad2264 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2450,7 +2450,6 @@ namespace MWMechanics } // Update movement - && !isScriptedAnimPlaying() world->queueMovement(mPtr, moved); } From 452f7a470e095f2c3ba5bdd3d09dadd2aecff628 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Wed, 1 Nov 2023 23:19:13 +0100 Subject: [PATCH 05/15] fabsf -> abs --- apps/openmw/mwmechanics/character.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 05c0ad2264..4eb223e250 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2424,7 +2424,7 @@ namespace MWMechanics const float epsilon = 0.001f; float targetMovementAngle = std::atan2(-movement.x(), movement.y()); float diff = targetMovementAngle - animMovementAngle; - if (std::fabsf(diff) > epsilon) + if (std::abs(diff) > epsilon) { moved = osg::Quat(diff, osg::Vec3f(0, 0, 1)) * moved; } From 9c93d907dcf09bd84a56b9af56c94810643b81ca Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Thu, 2 Nov 2023 19:30:51 +0100 Subject: [PATCH 06/15] Settings page entry for the "player movement ignores animation" setting. --- apps/launcher/settingspage.cpp | 2 ++ files/ui/settingspage.ui | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/apps/launcher/settingspage.cpp b/apps/launcher/settingspage.cpp index 5869cc3a73..b8539671b5 100644 --- a/apps/launcher/settingspage.cpp +++ b/apps/launcher/settingspage.cpp @@ -191,6 +191,7 @@ bool Launcher::SettingsPage::loadSettings() } loadSettingBool(Settings::game().mTurnToMovementDirection, *turnToMovementDirectionCheckBox); loadSettingBool(Settings::game().mSmoothMovement, *smoothMovementCheckBox); + loadSettingBool(Settings::game().mPlayerMovementIgnoresAnimation, *playerMovementIgnoresAnimationCheckBox); distantLandCheckBox->setCheckState( Settings::terrain().mDistantTerrain && Settings::terrain().mObjectPaging ? Qt::Checked : Qt::Unchecked); @@ -338,6 +339,7 @@ void Launcher::SettingsPage::saveSettings() saveSettingBool(*shieldSheathingCheckBox, Settings::game().mShieldSheathing); saveSettingBool(*turnToMovementDirectionCheckBox, Settings::game().mTurnToMovementDirection); saveSettingBool(*smoothMovementCheckBox, Settings::game().mSmoothMovement); + saveSettingBool(*playerMovementIgnoresAnimationCheckBox, Settings::game().mPlayerMovementIgnoresAnimation); const bool wantDistantLand = distantLandCheckBox->checkState() == Qt::Checked; if (wantDistantLand != (Settings::terrain().mDistantTerrain && Settings::terrain().mObjectPaging)) diff --git a/files/ui/settingspage.ui b/files/ui/settingspage.ui index c61b4f4229..45fbc11a12 100644 --- a/files/ui/settingspage.ui +++ b/files/ui/settingspage.ui @@ -14,7 +14,7 @@ - 0 + 1 @@ -293,8 +293,8 @@ 0 0 - 680 - 882 + 671 + 774 @@ -377,6 +377,16 @@ + + + + <html><head/><body><p>In third person, the camera will bob along with the movement animations of the player. Enabling this option disables this by having the player character move independently of its animation. This was the default behavior of OpenMW 0.48 and earlier.</p></body></html> + + + Player movement ignores animation + + + From fdba857464d1bc0232da1b5587b1a32bc4c8076e Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Fri, 3 Nov 2023 01:21:45 +0100 Subject: [PATCH 07/15] Document the "player movement ignores animation" setting. --- docs/source/reference/modding/settings/game.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst index c88ae4e28f..215e060b1c 100644 --- a/docs/source/reference/modding/settings/game.rst +++ b/docs/source/reference/modding/settings/game.rst @@ -517,3 +517,13 @@ will not be useful with another. * 0: Axis-aligned bounding box * 1: Rotating box * 2: Cylinder + +player movement ignores animation +-------------------------- + +:Type: boolean +:Range: True/False +:Default: False + +In third person, the camera will sway along with the movement animations of the player. +Enabling this option disables this swaying by having the player character move independently of its animation. From 4886d31d89025f297285e9ce761f3a32ed610087 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sat, 4 Nov 2023 13:37:23 +0100 Subject: [PATCH 08/15] Language, bob -> sway --- files/ui/settingspage.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/ui/settingspage.ui b/files/ui/settingspage.ui index 45fbc11a12..1f5f206f67 100644 --- a/files/ui/settingspage.ui +++ b/files/ui/settingspage.ui @@ -380,7 +380,7 @@ - <html><head/><body><p>In third person, the camera will bob along with the movement animations of the player. Enabling this option disables this by having the player character move independently of its animation. This was the default behavior of OpenMW 0.48 and earlier.</p></body></html> + <html><head/><body><p>In third person, the camera will sway along with the movement animations of the player. Enabling this option disables this swaying by having the player character move independently of its animation. This was the default behavior of OpenMW 0.48 and earlier.</p></body></html> Player movement ignores animation From 68fe1361f1a4926e178f7f49917e06381fd153f6 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sat, 4 Nov 2023 14:00:13 +0100 Subject: [PATCH 09/15] Attempt at clarifying the code --- apps/openmw/mwmechanics/character.cpp | 34 +++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 4eb223e250..08cb8f6ccd 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2386,26 +2386,26 @@ namespace MWMechanics } } - osg::Vec3f moved = mAnimation->runAnimation(mSkipAnim && !isScriptedAnimPlaying() ? 0.f : duration); + osg::Vec3f movementFromAnimation = mAnimation->runAnimation(mSkipAnim && !isScriptedAnimPlaying() ? 0.f : duration); if (mPtr.getClass().isActor() && isMovementAnimationControlled() && !isScriptedAnimPlaying()) { if (duration > 0.0f) - moved /= duration; + movementFromAnimation /= duration; else - moved = osg::Vec3f(0.f, 0.f, 0.f); + movementFromAnimation = osg::Vec3f(0.f, 0.f, 0.f); - moved.x() *= scale; - moved.y() *= scale; + movementFromAnimation.x() *= scale; + movementFromAnimation.y() *= scale; - if (speed > 0.f && moved != osg::Vec3f()) + if (speed > 0.f && movementFromAnimation != osg::Vec3f()) { - // Ensure we're moving in generally the right direction - // This is necessary when the "turn to movement direction" feature is off, as animations - // will not be rotated to match diagonal movement. In this case we have to slide the - // character diagonally. - - // First decide the general direction expected from the current animation + // Ensure we're moving in the right general direction. In vanilla, all horizontal movement is taken from animations, + // even when moving diagonally (which doesn't have a corresponding animation). So to acheive diagonal movement, + // we have to rotate the movement taken from the animation to the intended direction. + // + // Note that while a complete movement animation cycle will have a well defined direction, no individual frame will, and + // therefore we have to determine the direction separately from the value of the movementFromAnimation variable. float animMovementAngle = 0; if (!Settings::game().mTurnToMovementDirection || isFirstPersonPlayer) { @@ -2426,12 +2426,12 @@ namespace MWMechanics float diff = targetMovementAngle - animMovementAngle; if (std::abs(diff) > epsilon) { - moved = osg::Quat(diff, osg::Vec3f(0, 0, 1)) * moved; + movementFromAnimation = osg::Quat(diff, osg::Vec3f(0, 0, 1)) * movementFromAnimation; } - if (isPlayer && Settings::game().mPlayerMovementIgnoresAnimation) + if (!(isPlayer && Settings::game().mPlayerMovementIgnoresAnimation)) { - moved = movement; + movement = movementFromAnimation; } } @@ -2445,12 +2445,12 @@ namespace MWMechanics .getModifier() > 0)) { - moved.z() = 1.0; + movement.z() = 1.0; } } // Update movement - world->queueMovement(mPtr, moved); + world->queueMovement(mPtr, movement); } mSkipAnim = false; From 1edc82062de593aefc8ea702e05a84e01075a946 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sat, 4 Nov 2023 14:13:02 +0100 Subject: [PATCH 10/15] Account for strafing when draw state is not nothing, and "turn to movement direction" is true --- apps/openmw/mwmechanics/character.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 08cb8f6ccd..73bec3f2ae 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2407,13 +2407,8 @@ namespace MWMechanics // Note that while a complete movement animation cycle will have a well defined direction, no individual frame will, and // therefore we have to determine the direction separately from the value of the movementFromAnimation variable. float animMovementAngle = 0; - if (!Settings::game().mTurnToMovementDirection || isFirstPersonPlayer) - { - if (cls.getMovementSettings(mPtr).mIsStrafing) - animMovementAngle = movement.x() > 0 ? -osg::PI_2f : osg::PI_2f; - else - animMovementAngle = movement.y() >= 0 ? 0 : -osg::PIf; - } + if (cls.getMovementSettings(mPtr).mIsStrafing) + animMovementAngle = movement.x() > 0 ? -osg::PI_2f : osg::PI_2f; else { animMovementAngle = mAnimation->getLegsYawRadians(); From 475bb1af659dad998fbe81d32665259f84fd0a09 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sat, 4 Nov 2023 14:34:41 +0100 Subject: [PATCH 11/15] Move calculating the animation direction into its own function to help simplify update(). Eliminate a pointless epsilon. --- apps/openmw/mwmechanics/character.cpp | 60 +++++++++++++++++---------- apps/openmw/mwmechanics/character.hpp | 2 + 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 73bec3f2ae..6c898ee23e 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2398,36 +2398,19 @@ namespace MWMechanics movementFromAnimation.x() *= scale; movementFromAnimation.y() *= scale; - if (speed > 0.f && movementFromAnimation != osg::Vec3f()) + if (speed > 0.f && movementFromAnimation != osg::Vec3f() + && !(isPlayer && Settings::game().mPlayerMovementIgnoresAnimation)) { // Ensure we're moving in the right general direction. In vanilla, all horizontal movement is taken from animations, // even when moving diagonally (which doesn't have a corresponding animation). So to acheive diagonal movement, // we have to rotate the movement taken from the animation to the intended direction. // // Note that while a complete movement animation cycle will have a well defined direction, no individual frame will, and - // therefore we have to determine the direction separately from the value of the movementFromAnimation variable. - float animMovementAngle = 0; - if (cls.getMovementSettings(mPtr).mIsStrafing) - animMovementAngle = movement.x() > 0 ? -osg::PI_2f : osg::PI_2f; - else - { - animMovementAngle = mAnimation->getLegsYawRadians(); - if (movement.y() < 0) - animMovementAngle -= osg::PIf; - } - - const float epsilon = 0.001f; + // therefore we have to determine the direction based on the currently playing cycle instead. + float animMovementAngle = getAnimationMovementDirection(); float targetMovementAngle = std::atan2(-movement.x(), movement.y()); float diff = targetMovementAngle - animMovementAngle; - if (std::abs(diff) > epsilon) - { - movementFromAnimation = osg::Quat(diff, osg::Vec3f(0, 0, 1)) * movementFromAnimation; - } - - if (!(isPlayer && Settings::game().mPlayerMovementIgnoresAnimation)) - { - movement = movementFromAnimation; - } + movement = osg::Quat(diff, osg::Vec3f(0, 0, 1)) * movementFromAnimation; } if (mFloatToSurface) @@ -2927,6 +2910,39 @@ namespace MWMechanics MWBase::Environment::get().getSoundManager()->playSound3D(mPtr, *soundId, volume, pitch); } + float CharacterController::getAnimationMovementDirection() + { + switch (mMovementState) + { + case CharState_RunLeft: + case CharState_SneakLeft: + case CharState_SwimWalkLeft: + case CharState_SwimRunLeft: + case CharState_WalkLeft: + return osg::PI_2f; + case CharState_RunRight: + case CharState_SneakRight: + case CharState_SwimWalkRight: + case CharState_SwimRunRight: + case CharState_WalkRight: + return -osg::PI_2f; + case CharState_RunForward: + case CharState_SneakForward: + case CharState_SwimRunForward: + case CharState_SwimWalkForward: + case CharState_WalkForward: + return mAnimation->getLegsYawRadians(); + case CharState_RunBack: + case CharState_SneakBack: + case CharState_SwimWalkBack: + case CharState_SwimRunBack: + case CharState_WalkBack: + return mAnimation->getLegsYawRadians() - osg::PIf; + + } + return 0.0f; + } + void CharacterController::updateHeadTracking(float duration) { const osg::Node* head = mAnimation->getNode("Bip01 Head"); diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index c3d45fe0fb..d92c0ffe5a 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -319,6 +319,8 @@ namespace MWMechanics void playSwishSound() const; + float getAnimationMovementDirection(); + MWWorld::MovementDirectionFlags getSupportedMovementDirections() const; }; } From c7c3a52e6a7e6afb20efa57bb81623d24bd62425 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sat, 4 Nov 2023 14:41:08 +0100 Subject: [PATCH 12/15] Clang --- apps/openmw/mwmechanics/character.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 6c898ee23e..c245a17155 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2386,7 +2386,8 @@ namespace MWMechanics } } - osg::Vec3f movementFromAnimation = mAnimation->runAnimation(mSkipAnim && !isScriptedAnimPlaying() ? 0.f : duration); + osg::Vec3f movementFromAnimation + = mAnimation->runAnimation(mSkipAnim && !isScriptedAnimPlaying() ? 0.f : duration); if (mPtr.getClass().isActor() && isMovementAnimationControlled() && !isScriptedAnimPlaying()) { @@ -2401,12 +2402,14 @@ namespace MWMechanics if (speed > 0.f && movementFromAnimation != osg::Vec3f() && !(isPlayer && Settings::game().mPlayerMovementIgnoresAnimation)) { - // Ensure we're moving in the right general direction. In vanilla, all horizontal movement is taken from animations, - // even when moving diagonally (which doesn't have a corresponding animation). So to acheive diagonal movement, - // we have to rotate the movement taken from the animation to the intended direction. - // - // Note that while a complete movement animation cycle will have a well defined direction, no individual frame will, and - // therefore we have to determine the direction based on the currently playing cycle instead. + // Ensure we're moving in the right general direction. In vanilla, all horizontal movement is taken from + // animations, even when moving diagonally (which doesn't have a corresponding animation). So to acheive + // diagonal movement, we have to rotate the movement taken from the animation to the intended + // direction. + // + // Note that while a complete movement animation cycle will have a well defined direction, no individual + // frame will, and therefore we have to determine the direction based on the currently playing cycle + // instead. float animMovementAngle = getAnimationMovementDirection(); float targetMovementAngle = std::atan2(-movement.x(), movement.y()); float diff = targetMovementAngle - animMovementAngle; @@ -2938,7 +2941,6 @@ namespace MWMechanics case CharState_SwimRunBack: case CharState_WalkBack: return mAnimation->getLegsYawRadians() - osg::PIf; - } return 0.0f; } From 9ebec27dafa2811d47373f7d17638c86cdd3b0fd Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sat, 4 Nov 2023 16:18:36 +0100 Subject: [PATCH 13/15] use const. --- apps/openmw/mwmechanics/character.cpp | 2 +- apps/openmw/mwmechanics/character.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c245a17155..b9df3d1082 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2913,7 +2913,7 @@ namespace MWMechanics MWBase::Environment::get().getSoundManager()->playSound3D(mPtr, *soundId, volume, pitch); } - float CharacterController::getAnimationMovementDirection() + float CharacterController::getAnimationMovementDirection() const { switch (mMovementState) { diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index d92c0ffe5a..63491ec776 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -319,7 +319,7 @@ namespace MWMechanics void playSwishSound() const; - float getAnimationMovementDirection(); + float getAnimationMovementDirection() const; MWWorld::MovementDirectionFlags getSupportedMovementDirections() const; }; From e86a4ebafebb93dc7883b974a5e915916ed92a8b Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sat, 4 Nov 2023 21:01:06 +0100 Subject: [PATCH 14/15] Fixes based on comments by capo --- apps/openmw/mwmechanics/character.cpp | 3 ++- docs/source/reference/modding/settings/game.rst | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index b9df3d1082..da2bf4b488 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2941,8 +2941,9 @@ namespace MWMechanics case CharState_SwimRunBack: case CharState_WalkBack: return mAnimation->getLegsYawRadians() - osg::PIf; + default: + return 0.0f; } - return 0.0f; } void CharacterController::updateHeadTracking(float duration) diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst index 215e060b1c..31cc2703f2 100644 --- a/docs/source/reference/modding/settings/game.rst +++ b/docs/source/reference/modding/settings/game.rst @@ -519,7 +519,7 @@ will not be useful with another. * 2: Cylinder player movement ignores animation --------------------------- +--------------------------------- :Type: boolean :Range: True/False @@ -527,3 +527,5 @@ player movement ignores animation In third person, the camera will sway along with the movement animations of the player. Enabling this option disables this swaying by having the player character move independently of its animation. + +This setting can be controlled in the Settings tab of the launcher, under Visuals. From acf6178ea5089af4a7d89627a2f57374939e05c8 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sun, 5 Nov 2023 16:46:11 +0100 Subject: [PATCH 15/15] `movement = movementFromAnimation;` also when speed is 0. --- apps/openmw/mwmechanics/character.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index da2bf4b488..dd7b97b6a5 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2399,8 +2399,7 @@ namespace MWMechanics movementFromAnimation.x() *= scale; movementFromAnimation.y() *= scale; - if (speed > 0.f && movementFromAnimation != osg::Vec3f() - && !(isPlayer && Settings::game().mPlayerMovementIgnoresAnimation)) + if (speed > 0.f && movementFromAnimation != osg::Vec3f()) { // Ensure we're moving in the right general direction. In vanilla, all horizontal movement is taken from // animations, even when moving diagonally (which doesn't have a corresponding animation). So to acheive @@ -2413,9 +2412,12 @@ namespace MWMechanics float animMovementAngle = getAnimationMovementDirection(); float targetMovementAngle = std::atan2(-movement.x(), movement.y()); float diff = targetMovementAngle - animMovementAngle; - movement = osg::Quat(diff, osg::Vec3f(0, 0, 1)) * movementFromAnimation; + movementFromAnimation = osg::Quat(diff, osg::Vec3f(0, 0, 1)) * movementFromAnimation; } + if (!(isPlayer && Settings::game().mPlayerMovementIgnoresAnimation)) + movement = movementFromAnimation; + if (mFloatToSurface) { if (cls.getCreatureStats(mPtr).isDead()