From 76c68481215b14889257aef5ea4b79483f1e4e15 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 5c260b096d..d89e43fe29 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -305,7 +305,7 @@ namespace MWBase virtual MWWorld::Ptr moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, const osg::Vec3f& position, bool movePhysics=true, bool keepActive=false) = 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 a23a2e56f4..291f13b214 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -176,8 +176,6 @@ void Actor::updateCollisionObjectPosition() trans.setOrigin(Misc::Convert::toBullet(newPosition)); trans.setRotation(Misc::Convert::toBullet(mRotation)); mCollisionObject->setWorldTransform(trans); - - mWorldPositionChanged = false; } osg::Vec3f Actor::getCollisionObjectPosition() const @@ -189,14 +187,13 @@ osg::Vec3f Actor::getCollisionObjectPosition() const 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) @@ -205,15 +202,16 @@ void Actor::adjustPosition(const osg::Vec3f& offset) 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 146a065063..024d8c862c 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -95,7 +95,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) @@ -189,7 +189,6 @@ namespace MWPhysics osg::Vec3f mScale; osg::Vec3f mPositionOffset; - bool mWorldPositionChanged; bool mSkipSimulation; mutable std::mutex mPositionMutex; diff --git a/apps/openmw/mwphysics/movementsolver.cpp b/apps/openmw/mwphysics/movementsolver.cpp index da07d5975b..abe23bd3b6 100644 --- a/apps/openmw/mwphysics/movementsolver.cpp +++ b/apps/openmw/mwphysics/movementsolver.cpp @@ -452,6 +452,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 dabd1fdcad..5f42166df2 100644 --- a/apps/openmw/mwphysics/mtphysics.cpp +++ b/apps/openmw/mwphysics/mtphysics.cpp @@ -167,15 +167,14 @@ 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 8e42419ff7..09409311ca 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -33,7 +33,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 @@ -328,7 +328,7 @@ namespace MWScript } dynamic_cast(runtime.getContext()).updatePtr(ptr, - MWBase::Environment::get().getWorld()->moveObject(ptr, newPos, true, true)); + MWBase::Environment::get().getWorld()->moveObjectBy(ptr, newPos - curPos, true)); } }; @@ -757,7 +757,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)); + MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff, false)); } }; @@ -793,7 +793,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)); + MWBase::Environment::get().getWorld()->moveObjectBy(ptr, diff, false)); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 001a295796..5b07ef3ff8 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1276,14 +1276,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 3e5b3d7731..f85cfc5629 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -394,7 +394,7 @@ namespace MWWorld MWWorld::Ptr moveObject (const Ptr& ptr, CellStore* newCell, const osg::Vec3f& position, bool movePhysics=true, 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;