diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index 5caaba5c9b..760e21cce1 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -75,9 +75,10 @@ Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, Physic updateRotation(); updateScale(); updatePosition(); + setPosition(mWorldPosition, true); addCollisionMask(getCollisionMask()); - commitPositionChange(); + updateCollisionObjectPosition(); } Actor::~Actor() @@ -122,88 +123,77 @@ int Actor::getCollisionMask() const void Actor::updatePosition() { - std::unique_lock lock(mPositionMutex); - osg::Vec3f position = mPtr.getRefData().getPosition().asVec3(); + std::scoped_lock lock(mPositionMutex); + mWorldPosition = mPtr.getRefData().getPosition().asVec3(); +} - mPosition = position; - mPreviousPosition = position; +osg::Vec3f Actor::getWorldPosition() const +{ + std::scoped_lock lock(mPositionMutex); + return mWorldPosition; +} - mTransformUpdatePending = true; - updateCollisionObjectPosition(); +void Actor::setNextPosition(const osg::Vec3f& position) +{ + mNextPosition = position; +} + +osg::Vec3f Actor::getNextPosition() const +{ + return mNextPosition; } void Actor::updateCollisionObjectPosition() { + std::scoped_lock lock(mPositionMutex); + mShape->setLocalScaling(Misc::Convert::toBullet(mScale)); osg::Vec3f scaledTranslation = mRotation * osg::componentMultiply(mMeshTranslation, mScale); osg::Vec3f newPosition = scaledTranslation + mPosition; mLocalTransform.setOrigin(Misc::Convert::toBullet(newPosition)); mLocalTransform.setRotation(Misc::Convert::toBullet(mRotation)); - -} - -void Actor::commitPositionChange() -{ - std::unique_lock lock(mPositionMutex); - if (mScaleUpdatePending) - { - mShape->setLocalScaling(Misc::Convert::toBullet(mScale)); - mScaleUpdatePending = false; - } - if (mTransformUpdatePending) - { - mCollisionObject->setWorldTransform(mLocalTransform); - mTransformUpdatePending = false; - } + mCollisionObject->setWorldTransform(mLocalTransform); } osg::Vec3f Actor::getCollisionObjectPosition() const { - std::unique_lock lock(mPositionMutex); + std::scoped_lock lock(mPositionMutex); return Misc::Convert::toOsg(mLocalTransform.getOrigin()); } -void Actor::setPosition(const osg::Vec3f &position, bool updateCollisionObject) +void Actor::setPosition(const osg::Vec3f& position, bool reset) { - std::unique_lock lock(mPositionMutex); - if (mTransformUpdatePending) + if (reset) { - mCollisionObject->setWorldTransform(mLocalTransform); - mTransformUpdatePending = false; + mPreviousPosition = position; + mNextPosition = position; } else - { mPreviousPosition = mPosition; + mPosition = position; +} - mPosition = position; - if (updateCollisionObject) - { - updateCollisionObjectPosition(); - mCollisionObject->setWorldTransform(mLocalTransform); - } - } +void Actor::adjustPosition(const osg::Vec3f& offset) +{ + mPosition += offset; + mPreviousPosition += offset; } osg::Vec3f Actor::getPosition() const { - std::unique_lock lock(mPositionMutex); return mPosition; } osg::Vec3f Actor::getPreviousPosition() const { - std::unique_lock lock(mPositionMutex); return mPreviousPosition; } void Actor::updateRotation () { - std::unique_lock lock(mPositionMutex); + std::scoped_lock lock(mPositionMutex); if (mRotation == mPtr.getRefData().getBaseNode()->getAttitude()) return; mRotation = mPtr.getRefData().getBaseNode()->getAttitude(); - - mTransformUpdatePending = true; - updateCollisionObjectPosition(); } bool Actor::isRotationallyInvariant() const @@ -213,37 +203,33 @@ bool Actor::isRotationallyInvariant() const void Actor::updateScale() { - std::unique_lock lock(mPositionMutex); + std::scoped_lock lock(mPositionMutex); float scale = mPtr.getCellRef().getScale(); osg::Vec3f scaleVec(scale,scale,scale); mPtr.getClass().adjustScale(mPtr, scaleVec, false); mScale = scaleVec; - mScaleUpdatePending = true; scaleVec = osg::Vec3f(scale,scale,scale); mPtr.getClass().adjustScale(mPtr, scaleVec, true); mRenderingScale = scaleVec; - - mTransformUpdatePending = true; - updateCollisionObjectPosition(); } osg::Vec3f Actor::getHalfExtents() const { - std::unique_lock lock(mPositionMutex); + std::scoped_lock lock(mPositionMutex); return osg::componentMultiply(mHalfExtents, mScale); } osg::Vec3f Actor::getOriginalHalfExtents() const { - std::unique_lock lock(mPositionMutex); + std::scoped_lock lock(mPositionMutex); return mHalfExtents; } osg::Vec3f Actor::getRenderingHalfExtents() const { - std::unique_lock lock(mPositionMutex); + std::scoped_lock lock(mPositionMutex); return osg::componentMultiply(mHalfExtents, mRenderingScale); } @@ -274,7 +260,7 @@ void Actor::setWalkingOnWater(bool walkingOnWater) void Actor::setCanWaterWalk(bool waterWalk) { - std::unique_lock lock(mPositionMutex); + std::scoped_lock lock(mPositionMutex); if (waterWalk != mCanWaterWalk) { mCanWaterWalk = waterWalk; diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp index ef7b368b99..00ba162afd 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -57,13 +57,20 @@ namespace MWPhysics bool isRotationallyInvariant() const; /** - * Set mPosition and mPreviousPosition to the position in the Ptr's RefData. This should be used + * Set mWorldPosition to the position in the Ptr's RefData. This is used by the physics simulation to account for * when an object is "instantly" moved/teleported as opposed to being moved by the physics simulation. */ void updatePosition(); + osg::Vec3f getWorldPosition() const; + + /** + * Used by the physics simulation to store the simulation result. Used in conjunction with mWorldPosition + * to account for e.g. scripted movements + */ + void setNextPosition(const osg::Vec3f& position); + osg::Vec3f getNextPosition() const; void updateCollisionObjectPosition(); - void commitPositionChange(); /** * Returns the half extents of the collision body (scaled according to collision scale) @@ -83,9 +90,9 @@ namespace MWPhysics /** * Store the current position into mPreviousPosition, then move to this position. - * Optionally, inform the physics engine about the change of position. */ - void setPosition(const osg::Vec3f& position, bool updateCollisionObject=true); + void setPosition(const osg::Vec3f& position, bool reset=false); + void adjustPosition(const osg::Vec3f& offset); osg::Vec3f getPosition() const; @@ -159,11 +166,11 @@ namespace MWPhysics osg::Vec3f mScale; osg::Vec3f mRenderingScale; + osg::Vec3f mWorldPosition; + osg::Vec3f mNextPosition; osg::Vec3f mPosition; osg::Vec3f mPreviousPosition; btTransform mLocalTransform; - bool mScaleUpdatePending; - bool mTransformUpdatePending; mutable std::mutex mPositionMutex; osg::Vec3f mForce; diff --git a/apps/openmw/mwphysics/mtphysics.cpp b/apps/openmw/mwphysics/mtphysics.cpp index f105efce5e..1b99b4c3f6 100644 --- a/apps/openmw/mwphysics/mtphysics.cpp +++ b/apps/openmw/mwphysics/mtphysics.cpp @@ -102,9 +102,18 @@ namespace stats.addToFallHeight(-actorData.mFallHeight); } - osg::Vec3f interpolateMovements(const MWPhysics::ActorFrameData& actorData, float timeAccum, float physicsDt) + osg::Vec3f interpolateMovements(MWPhysics::ActorFrameData& actorData, float timeAccum, float physicsDt) { const float interpolationFactor = timeAccum / physicsDt; + + // account for force change of actor's position in the main thread + const auto correction = actorData.mActorRaw->getWorldPosition() - actorData.mOrigin; + if (correction.length() != 0) + { + actorData.mActorRaw->adjustPosition(correction); + actorData.mPosition = actorData.mActorRaw->getPosition(); + } + return actorData.mPosition * interpolationFactor + actorData.mActorRaw->getPreviousPosition() * (1.f - interpolationFactor); } @@ -182,7 +191,6 @@ namespace MWPhysics mPostSimBarrier = std::make_unique(mNumThreads, [&]() { - udpateActorsAabbs(); mNewFrame = false; if (mLOSCacheExpiry >= 0) { @@ -229,6 +237,9 @@ namespace MWPhysics updateMechanics(data); if (mAdvanceSimulation) updateStandingCollision(data, standingCollisions); + + if (mMovementResults.find(data.mPtr) != mMovementResults.end()) + data.mActorRaw->setNextPosition(mMovementResults[data.mPtr]); } } @@ -245,10 +256,6 @@ namespace MWPhysics if (mAdvanceSimulation) mWorldFrameData = std::make_unique(); - // update each actor position based on latest data - for (auto& data : mActorsFrameData) - data.updatePosition(); - // we are asked to skip the simulation (load a savegame for instance) // just return the actors' reference position without applying the movements if (skipSimulation) @@ -256,7 +263,10 @@ namespace MWPhysics standingCollisions.clear(); mMovementResults.clear(); for (const auto& m : mActorsFrameData) - mMovementResults[m.mPtr] = m.mPosition; + { + m.mActorRaw->setPosition(m.mActorRaw->getWorldPosition(), true); + mMovementResults[m.mPtr] = m.mActorRaw->getWorldPosition(); + } return mMovementResults; } @@ -271,6 +281,11 @@ namespace MWPhysics for (auto& data : mActorsFrameData) updateStandingCollision(data, standingCollisions); } + for (auto& data : mActorsFrameData) + { + if (mMovementResults.find(data.mPtr) != mMovementResults.end()) + data.mActorRaw->setNextPosition(mMovementResults[data.mPtr]); + } return mMovementResults; } @@ -427,7 +442,7 @@ namespace MWPhysics { if (const auto actor = std::dynamic_pointer_cast(p)) { - actor->commitPositionChange(); + actor->updateCollisionObjectPosition(); mCollisionWorld->updateSingleAabb(actor->getCollisionObject()); } else if (const auto object = std::dynamic_pointer_cast(p)) @@ -485,28 +500,17 @@ namespace MWPhysics { if(const auto actor = actorData.mActor.lock()) { - if (actorData.mPosition == actor->getPosition()) - actor->setPosition(actorData.mPosition, false); // update previous position to make sure interpolation is correct - else + bool positionChanged = actorData.mPosition != actorData.mActorRaw->getPosition(); + actorData.mActorRaw->setPosition(actorData.mPosition); + if (positionChanged) { - actorData.mPositionChanged = true; - actor->setPosition(actorData.mPosition); + actor->updateCollisionObjectPosition(); + mCollisionWorld->updateSingleAabb(actor->getCollisionObject()); } } } } - void PhysicsTaskScheduler::udpateActorsAabbs() - { - std::unique_lock lock(mCollisionWorldMutex); - for (const auto& actorData : mActorsFrameData) - if (actorData.mPositionChanged) - { - if(const auto actor = actorData.mActor.lock()) - mCollisionWorld->updateSingleAabb(actor->getCollisionObject()); - } - } - bool PhysicsTaskScheduler::hasLineOfSight(const Actor* actor1, const Actor* actor2) { btVector3 pos1 = Misc::Convert::toBullet(actor1->getCollisionObjectPosition() + osg::Vec3f(0,0,actor1->getHalfExtents().z() * 0.9)); // eye level @@ -538,6 +542,5 @@ namespace MWPhysics mMovementResults[actorData.mPtr] = interpolateMovements(actorData, mTimeAccum, mPhysicsDt); updateMechanics(actorData); } - udpateActorsAabbs(); } } diff --git a/apps/openmw/mwphysics/mtphysics.hpp b/apps/openmw/mwphysics/mtphysics.hpp index 100e71a907..84ea93c088 100644 --- a/apps/openmw/mwphysics/mtphysics.hpp +++ b/apps/openmw/mwphysics/mtphysics.hpp @@ -49,7 +49,6 @@ namespace MWPhysics void syncComputation(); void worker(); void updateActorsPositions(); - void udpateActorsAabbs(); bool hasLineOfSight(const Actor* actor1, const Actor* actor2); void refreshLOSCache(); void updateAabbs(); diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 9a777bd454..6b94ef43b6 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -883,7 +883,7 @@ namespace MWPhysics ActorFrameData::ActorFrameData(const std::shared_ptr& actor, const MWWorld::Ptr character, const MWWorld::Ptr standingOn, bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel) : mActor(actor), mActorRaw(actor.get()), mStandingOn(standingOn), - mPositionChanged(false), mDidJump(false), mNeedLand(false), mMoveToWaterSurface(moveToWaterSurface), + mDidJump(false), mNeedLand(false), mMoveToWaterSurface(moveToWaterSurface), mWaterlevel(waterlevel), mSlowFall(slowFall), mOldHeight(0), mFallHeight(0), mMovement(movement), mPosition(), mRefpos() { const MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -893,10 +893,9 @@ namespace MWPhysics mWantJump = mPtr.getClass().getMovementSettings(mPtr).mPosition[2] != 0; mIsDead = mPtr.getClass().getCreatureStats(mPtr).isDead(); mWasOnGround = actor->getOnGround(); - } - void ActorFrameData::updatePosition() - { + mActorRaw->updatePosition(); + mOrigin = mActorRaw->getNextPosition(); mPosition = mActorRaw->getPosition(); if (mMoveToWaterSurface) { diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 36ef762d3e..4844b5e8ea 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -78,14 +78,12 @@ namespace MWPhysics struct ActorFrameData { ActorFrameData(const std::shared_ptr& actor, const MWWorld::Ptr character, const MWWorld::Ptr standingOn, bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel); - void updatePosition(); std::weak_ptr mActor; Actor* mActorRaw; MWWorld::Ptr mPtr; MWWorld::Ptr mStandingOn; bool mFlying; bool mSwimming; - bool mPositionChanged; bool mWasOnGround; bool mWantJump; bool mDidJump; @@ -97,6 +95,7 @@ namespace MWPhysics float mOldHeight; float mFallHeight; osg::Vec3f mMovement; + osg::Vec3f mOrigin; osg::Vec3f mPosition; ESM::Position mRefpos; }; diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 41df1870c5..ce45729b3b 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -32,7 +32,11 @@ namespace MWScript std::vector actors; MWBase::Environment::get().getWorld()->getActorsStandingOn (ptr, actors); for (auto& actor : actors) - MWBase::Environment::get().getWorld()->queueMovement(actor, diff); + { + osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3()); + actorPos += diff; + MWBase::Environment::get().getWorld()->moveObject(actor, actorPos.x(), actorPos.y(), actorPos.z()); + } } template diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index be32765ad4..ad6c337909 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1504,11 +1504,11 @@ namespace MWWorld const auto results = mPhysics->applyQueuedMovement(duration, mDiscardMovements); mDiscardMovements = false; - for(const auto& result : results) + for(const auto& [actor, position]: results) { // Handle player last, in case a cell transition occurs - if(result.first != getPlayerPtr()) - moveObjectImp(result.first, result.second.x(), result.second.y(), result.second.z(), false); + if(actor != getPlayerPtr()) + moveObjectImp(actor, position.x(), position.y(), position.z(), false); } const auto player = results.find(getPlayerPtr());