From 63d4564455af81c54d8a019e891f3de39d4f61da Mon Sep 17 00:00:00 2001 From: fredzio Date: Thu, 16 Mar 2023 21:33:36 +0100 Subject: [PATCH] In 0.46, SetPos was setting position of actors before physics simulation, and from this position movement was simulated. This changed with async physics merging, and at the same time problems started, mostly with abot's scenic travel. Skipping the simulation, switching off collisions, and other approaches were not correct as they either broke some mods, or some core mechanics of the engine such as teleportation or waterwalking. As it turns out, the way to go is to simply do _nothing_ (modulo some gymnastics to account for the 1 frame difference in case of async). Scripted movement and the unstucking logic tends to collide. Early out of unstuck in case the actor doesn't attempt to move. This means there is no AI package for NPC, which are the case for some boats and striders, or the player is content with their position. --- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwphysics/actor.cpp | 32 +++++++++---------- apps/openmw/mwphysics/actor.hpp | 3 +- apps/openmw/mwphysics/movementsolver.cpp | 3 ++ apps/openmw/mwphysics/mtphysics.cpp | 9 +++--- .../mwscript/transformationextensions.cpp | 8 ++--- apps/openmw/mwworld/worldimp.cpp | 4 +-- apps/openmw/mwworld/worldimp.hpp | 2 +- 8 files changed, 31 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index d408166841..5ebf8527c0 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -297,7 +297,7 @@ namespace MWBase = 0; ///< @return an updated Ptr - virtual MWWorld::Ptr moveObjectBy(const MWWorld::Ptr& ptr, const osg::Vec3f& vec) = 0; + virtual MWWorld::Ptr moveObjectBy(const MWWorld::Ptr& ptr, const osg::Vec3f& vec, bool moveToActive) = 0; ///< @return an updated Ptr virtual void scaleObject(const MWWorld::Ptr& ptr, float scale, bool force = false) = 0; diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index 5349392cb7..dec055d68f 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -185,8 +185,6 @@ namespace MWPhysics trans.setOrigin(Misc::Convert::toBullet(newPosition)); trans.setRotation(Misc::Convert::toBullet(mRotation)); mCollisionObject->setWorldTransform(trans); - - mWorldPositionChanged = false; } osg::Vec3f Actor::getCollisionObjectPosition() const @@ -198,14 +196,13 @@ namespace MWPhysics bool Actor::setPosition(const osg::Vec3f& position) { std::scoped_lock lock(mPositionMutex); + const bool worldPositionChanged = mPositionOffset.length2() != 0; applyOffsetChange(); - bool hasChanged = (mPosition.operator!=(position) && !mSkipSimulation) || mWorldPositionChanged; - if (!mSkipSimulation) - { - mPreviousPosition = mPosition; - mPosition = position; - } - return hasChanged; + if (worldPositionChanged || mSkipSimulation) + return true; + mPreviousPosition = mPosition; + mPosition = position; + return mPreviousPosition != mPosition; } void Actor::adjustPosition(const osg::Vec3f& offset) @@ -214,15 +211,16 @@ namespace MWPhysics mPositionOffset += offset; } - void Actor::applyOffsetChange() + osg::Vec3f Actor::applyOffsetChange() { - if (mPositionOffset.length() == 0) - return; - mPosition += mPositionOffset; - mPreviousPosition += mPositionOffset; - mSimulationPosition += mPositionOffset; - mPositionOffset = osg::Vec3f(); - mWorldPositionChanged = true; + if (mPositionOffset.length2() != 0) + { + mPosition += mPositionOffset; + mPreviousPosition += mPositionOffset; + mSimulationPosition += mPositionOffset; + mPositionOffset = osg::Vec3f(); + } + return mPosition; } void Actor::setRotation(osg::Quat quat) diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp index 543259c9e5..e53477506c 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -96,7 +96,7 @@ namespace MWPhysics void adjustPosition(const osg::Vec3f& offset); // apply position offset. Can't be called during simulation - void applyOffsetChange(); + osg::Vec3f applyOffsetChange(); /** * Returns the half extents of the collision body (scaled according to rendering scale) @@ -176,7 +176,6 @@ namespace MWPhysics osg::Vec3f mScale; osg::Vec3f mPositionOffset; - bool mWorldPositionChanged = false; bool mSkipSimulation = true; mutable std::mutex mPositionMutex; diff --git a/apps/openmw/mwphysics/movementsolver.cpp b/apps/openmw/mwphysics/movementsolver.cpp index a127124265..846be640e2 100644 --- a/apps/openmw/mwphysics/movementsolver.cpp +++ b/apps/openmw/mwphysics/movementsolver.cpp @@ -475,6 +475,9 @@ namespace MWPhysics if (actor.mSkipCollisionDetection) // noclipping/tcl return; + if (actor.mMovement.length2() == 0) // no AI nor player attempted to move, current position is assumed correct + return; + auto tempPosition = actor.mPosition; if (actor.mStuckFrames >= 10) diff --git a/apps/openmw/mwphysics/mtphysics.cpp b/apps/openmw/mwphysics/mtphysics.cpp index 0e61e60ed5..653380decf 100644 --- a/apps/openmw/mwphysics/mtphysics.cpp +++ b/apps/openmw/mwphysics/mtphysics.cpp @@ -176,16 +176,15 @@ namespace return; auto& [actor, frameDataRef] = *locked; auto& frameData = frameDataRef.get(); - actor->applyOffsetChange(); - frameData.mPosition = actor->getPosition(); + frameData.mPosition = actor->applyOffsetChange(); if (frameData.mWaterCollision && frameData.mPosition.z() < frameData.mWaterlevel && actor->canMoveToWaterSurface(frameData.mWaterlevel, mCollisionWorld)) { const auto offset = osg::Vec3f(0, 0, frameData.mWaterlevel - frameData.mPosition.z()); - MWBase::Environment::get().getWorld()->moveObjectBy(actor->getPtr(), offset); - actor->applyOffsetChange(); - frameData.mPosition = actor->getPosition(); + MWBase::Environment::get().getWorld()->moveObjectBy(actor->getPtr(), offset, false); + frameData.mPosition = actor->applyOffsetChange(); } + actor->updateCollisionObjectPosition(); frameData.mOldHeight = frameData.mPosition.z(); const auto rotation = actor->getPtr().getRefData().getPosition().asRotationVec3(); frameData.mRotation = osg::Vec2f(rotation.x(), rotation.z()); diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index c624d24753..34423cdf00 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -35,7 +35,7 @@ namespace MWScript std::vector actors; MWBase::Environment::get().getWorld()->getActorsStandingOn(ptr, actors); for (auto& actor : actors) - MWBase::Environment::get().getWorld()->moveObjectBy(actor, diff); + MWBase::Environment::get().getWorld()->moveObjectBy(actor, diff, false); } template @@ -331,7 +331,7 @@ namespace MWScript } dynamic_cast(runtime.getContext()) - .updatePtr(ptr, MWBase::Environment::get().getWorld()->moveObject(ptr, newPos, true, true)); + .updatePtr(ptr, MWBase::Environment::get().getWorld()->moveObjectBy(ptr, newPos - curPos, true)); } }; @@ -753,7 +753,7 @@ namespace MWScript // This approach can be used to create elevators. moveStandingActors(ptr, diff); dynamic_cast(runtime.getContext()) - .updatePtr(ptr, MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff)); + .updatePtr(ptr, MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff, false)); } }; @@ -788,7 +788,7 @@ namespace MWScript // This approach can be used to create elevators. moveStandingActors(ptr, diff); dynamic_cast(runtime.getContext()) - .updatePtr(ptr, MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff)); + .updatePtr(ptr, MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff, false)); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 555df2bbf1..26194deb2c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1249,14 +1249,14 @@ namespace MWWorld return moveObject(ptr, cell, position, movePhysics); } - MWWorld::Ptr World::moveObjectBy(const Ptr& ptr, const osg::Vec3f& vec) + MWWorld::Ptr World::moveObjectBy(const Ptr& ptr, const osg::Vec3f& vec, bool moveToActive) { auto* actor = mPhysics->getActor(ptr); osg::Vec3f newpos = ptr.getRefData().getPosition().asVec3() + vec; if (actor) actor->adjustPosition(vec); if (ptr.getClass().isActor()) - return moveObject(ptr, newpos, false, ptr != getPlayerPtr()); + return moveObject(ptr, newpos, false, moveToActive && ptr != getPlayerPtr()); return moveObject(ptr, newpos); } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 476a92eea7..f246728fe1 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -378,7 +378,7 @@ namespace MWWorld bool keepActive = false) override; ///< @return an updated Ptr - MWWorld::Ptr moveObjectBy(const Ptr& ptr, const osg::Vec3f& vec) override; + MWWorld::Ptr moveObjectBy(const Ptr& ptr, const osg::Vec3f& vec, bool moveToActive) override; ///< @return an updated Ptr void scaleObject(const Ptr& ptr, float scale, bool force = false) override;