1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-12-13 17:43:08 +00:00

Embed physics simulation results inside of actor class.

This gives finer control over reseting positions (switch off tcl is no
longer glitchy) and solve most of the erroneous usage of stale World::Ptr
indicated by:
"Error in frame: moveTo: object is not in this cell"
This commit is contained in:
fredzio 2020-12-18 09:22:02 +01:00
parent d01d5745c6
commit 4e7c9b6696
7 changed files with 74 additions and 76 deletions

View file

@ -76,6 +76,7 @@ Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, Physic
updateScale(); updateScale();
resetPosition(); resetPosition();
addCollisionMask(getCollisionMask()); addCollisionMask(getCollisionMask());
updateCollisionObjectPosition();
} }
Actor::~Actor() Actor::~Actor()
@ -139,7 +140,9 @@ osg::Vec3f Actor::getWorldPosition() const
void Actor::setSimulationPosition(const osg::Vec3f& position) void Actor::setSimulationPosition(const osg::Vec3f& position)
{ {
mSimulationPosition = position; if (!mResetSimulation)
mSimulationPosition = position;
mResetSimulation = false;
} }
osg::Vec3f Actor::getSimulationPosition() const osg::Vec3f Actor::getSimulationPosition() const
@ -193,9 +196,9 @@ void Actor::resetPosition()
mPreviousPosition = mWorldPosition; mPreviousPosition = mWorldPosition;
mPosition = mWorldPosition; mPosition = mWorldPosition;
mSimulationPosition = mWorldPosition; mSimulationPosition = mWorldPosition;
updateCollisionObjectPositionUnsafe();
mStandingOnPtr = nullptr; mStandingOnPtr = nullptr;
mWorldPositionChanged = false; mWorldPositionChanged = false;
mResetSimulation = true;
} }
osg::Vec3f Actor::getPosition() const osg::Vec3f Actor::getPosition() const

View file

@ -180,6 +180,7 @@ namespace MWPhysics
osg::Vec3f mPreviousPosition; osg::Vec3f mPreviousPosition;
osg::Vec3f mPositionOffset; osg::Vec3f mPositionOffset;
bool mWorldPositionChanged; bool mWorldPositionChanged;
bool mResetSimulation;
btTransform mLocalTransform; btTransform mLocalTransform;
mutable std::mutex mPositionMutex; mutable std::mutex mPositionMutex;

View file

@ -204,31 +204,31 @@ namespace MWPhysics
thread.join(); thread.join();
} }
const PtrPositionList& PhysicsTaskScheduler::moveActors(int numSteps, float timeAccum, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) const std::vector<MWWorld::Ptr>& PhysicsTaskScheduler::moveActors(int numSteps, float timeAccum, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
{ {
// This function run in the main thread. // This function run in the main thread.
// While the mSimulationMutex is held, background physics threads can't run. // While the mSimulationMutex is held, background physics threads can't run.
std::unique_lock lock(mSimulationMutex); std::unique_lock lock(mSimulationMutex);
for (auto& data : actorsData) mMovedActors.clear();
data.updatePosition();
// start by finishing previous background computation // start by finishing previous background computation
if (mNumThreads != 0) if (mNumThreads != 0)
{ {
for (auto& data : mActorsFrameData) for (auto& data : mActorsFrameData)
{ {
// Ignore actors that were deleted while the background thread was running // Only return actors that are still part of the scene
if (!data.mActor.lock()) if (std::any_of(actorsData.begin(), actorsData.end(), [&data](const auto& newFrameData) { return data.mActorRaw->getPtr() == newFrameData.mActorRaw->getPtr(); }))
continue; {
updateMechanics(data);
updateMechanics(data); // these variables are accessed directly from the main thread, update them here to prevent accessing "too new" values
if (mAdvanceSimulation) if (mAdvanceSimulation)
data.mActorRaw->setStandingOnPtr(data.mStandingOn); data.mActorRaw->setStandingOnPtr(data.mStandingOn);
data.mActorRaw->setSimulationPosition(interpolateMovements(data, mTimeAccum, mPhysicsDt));
if (mMovementResults.find(data.mPtr) != mMovementResults.end()) mMovedActors.emplace_back(data.mActorRaw->getPtr());
data.mActorRaw->setSimulationPosition(mMovementResults[data.mPtr]); }
} }
if (mFrameNumber == frameNumber - 1) if (mFrameNumber == frameNumber - 1)
@ -243,6 +243,8 @@ namespace MWPhysics
} }
// init // init
for (auto& data : actorsData)
data.updatePosition();
mRemainingSteps = numSteps; mRemainingSteps = numSteps;
mTimeAccum = timeAccum; mTimeAccum = timeAccum;
mActorsFrameData = std::move(actorsData); mActorsFrameData = std::move(actorsData);
@ -257,52 +259,28 @@ namespace MWPhysics
if (mNumThreads == 0) if (mNumThreads == 0)
{ {
mMovementResults.clear();
syncComputation(); syncComputation();
return mMovedActors;
for (auto& data : mActorsFrameData)
{
if (mAdvanceSimulation)
data.mActorRaw->setStandingOnPtr(data.mStandingOn);
if (mMovementResults.find(data.mPtr) != mMovementResults.end())
data.mActorRaw->setSimulationPosition(mMovementResults[data.mPtr]);
}
return mMovementResults;
} }
// Remove actors that were deleted while the background thread was running
for (auto& data : mActorsFrameData)
{
if (!data.mActor.lock())
mMovementResults.erase(data.mPtr);
}
std::swap(mMovementResults, mPreviousMovementResults);
// mMovementResults is shared between all workers instance
// pre-allocate all nodes so that we don't need synchronization
mMovementResults.clear();
for (const auto& m : mActorsFrameData)
mMovementResults[m.mPtr] = m.mPosition;
lock.unlock(); lock.unlock();
mHasJob.notify_all(); mHasJob.notify_all();
return mPreviousMovementResults; return mMovedActors;
} }
const PtrPositionList& PhysicsTaskScheduler::resetSimulation(const ActorMap& actors) const std::vector<MWWorld::Ptr>& PhysicsTaskScheduler::resetSimulation(const ActorMap& actors)
{ {
std::unique_lock lock(mSimulationMutex); std::unique_lock lock(mSimulationMutex);
mMovementResults.clear(); mMovedActors.clear();
mPreviousMovementResults.clear();
mActorsFrameData.clear(); mActorsFrameData.clear();
for (const auto& [_, actor] : actors) for (const auto& [_, actor] : actors)
{ {
actor->resetPosition(); actor->resetPosition();
actor->setStandingOnPtr(nullptr); actor->setSimulationPosition(actor->getWorldPosition()); // resetPosition skip next simulation, now we need to "consume" it
mMovementResults[actor->getPtr()] = actor->getWorldPosition(); actor->updateCollisionObjectPosition();
mMovedActors.emplace_back(actor->getPtr());
} }
return mMovementResults; return mMovedActors;
} }
void PhysicsTaskScheduler::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const void PhysicsTaskScheduler::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const
@ -370,18 +348,18 @@ namespace MWPhysics
mCollisionWorld->removeCollisionObject(collisionObject); mCollisionWorld->removeCollisionObject(collisionObject);
} }
void PhysicsTaskScheduler::updateSingleAabb(std::weak_ptr<PtrHolder> ptr) void PhysicsTaskScheduler::updateSingleAabb(std::weak_ptr<PtrHolder> ptr, bool immediate)
{ {
if (mDeferAabbUpdate) if (!mDeferAabbUpdate || immediate)
{
std::unique_lock lock(mUpdateAabbMutex);
mUpdateAabb.insert(std::move(ptr));
}
else
{ {
std::unique_lock lock(mCollisionWorldMutex); std::unique_lock lock(mCollisionWorldMutex);
updatePtrAabb(ptr); updatePtrAabb(ptr);
} }
else
{
std::unique_lock lock(mUpdateAabbMutex);
mUpdateAabb.insert(std::move(ptr));
}
} }
bool PhysicsTaskScheduler::getLineOfSight(const std::weak_ptr<Actor>& actor1, const std::weak_ptr<Actor>& actor2) bool PhysicsTaskScheduler::getLineOfSight(const std::weak_ptr<Actor>& actor1, const std::weak_ptr<Actor>& actor2)
@ -484,7 +462,6 @@ namespace MWPhysics
{ {
auto& actorData = mActorsFrameData[job]; auto& actorData = mActorsFrameData[job];
handleFall(actorData, mAdvanceSimulation); handleFall(actorData, mAdvanceSimulation);
mMovementResults[actorData.mPtr] = interpolateMovements(actorData, mTimeAccum, mPhysicsDt);
} }
} }
@ -539,8 +516,11 @@ namespace MWPhysics
for (auto& actorData : mActorsFrameData) for (auto& actorData : mActorsFrameData)
{ {
handleFall(actorData, mAdvanceSimulation); handleFall(actorData, mAdvanceSimulation);
mMovementResults[actorData.mPtr] = interpolateMovements(actorData, mTimeAccum, mPhysicsDt); actorData.mActorRaw->setSimulationPosition(interpolateMovements(actorData, mTimeAccum, mPhysicsDt));
updateMechanics(actorData); updateMechanics(actorData);
mMovedActors.emplace_back(actorData.mActorRaw->getPtr());
if (mAdvanceSimulation)
actorData.mActorRaw->setStandingOnPtr(actorData.mStandingOn);
} }
} }
} }

View file

@ -32,9 +32,9 @@ namespace MWPhysics
/// @param timeAccum accumulated time from previous run to interpolate movements /// @param timeAccum accumulated time from previous run to interpolate movements
/// @param actorsData per actor data needed to compute new positions /// @param actorsData per actor data needed to compute new positions
/// @return new position of each actor /// @return new position of each actor
const PtrPositionList& moveActors(int numSteps, float timeAccum, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); const std::vector<MWWorld::Ptr>& moveActors(int numSteps, float timeAccum, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);
const PtrPositionList& resetSimulation(const ActorMap& actors); const std::vector<MWWorld::Ptr>& resetSimulation(const ActorMap& actors);
// Thread safe wrappers // Thread safe wrappers
void rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const; void rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const;
@ -46,7 +46,7 @@ namespace MWPhysics
void setCollisionFilterMask(btCollisionObject* collisionObject, int collisionFilterMask); void setCollisionFilterMask(btCollisionObject* collisionObject, int collisionFilterMask);
void addCollisionObject(btCollisionObject* collisionObject, int collisionFilterGroup, int collisionFilterMask); void addCollisionObject(btCollisionObject* collisionObject, int collisionFilterGroup, int collisionFilterMask);
void removeCollisionObject(btCollisionObject* collisionObject); void removeCollisionObject(btCollisionObject* collisionObject);
void updateSingleAabb(std::weak_ptr<PtrHolder> ptr); void updateSingleAabb(std::weak_ptr<PtrHolder> ptr, bool immediate=false);
bool getLineOfSight(const std::weak_ptr<Actor>& actor1, const std::weak_ptr<Actor>& actor2); bool getLineOfSight(const std::weak_ptr<Actor>& actor1, const std::weak_ptr<Actor>& actor2);
private: private:
@ -60,8 +60,7 @@ namespace MWPhysics
std::unique_ptr<WorldFrameData> mWorldFrameData; std::unique_ptr<WorldFrameData> mWorldFrameData;
std::vector<ActorFrameData> mActorsFrameData; std::vector<ActorFrameData> mActorsFrameData;
PtrPositionList mMovementResults; std::vector<MWWorld::Ptr> mMovedActors;
PtrPositionList mPreviousMovementResults;
const float mPhysicsDt; const float mPhysicsDt;
float mTimeAccum; float mTimeAccum;
std::shared_ptr<btCollisionWorld> mCollisionWorld; std::shared_ptr<btCollisionWorld> mCollisionWorld;

View file

@ -437,7 +437,7 @@ namespace MWPhysics
ActorMap::iterator found = mActors.find(ptr); ActorMap::iterator found = mActors.find(ptr);
if (found == mActors.end()) if (found == mActors.end())
return ptr.getRefData().getPosition().asVec3(); return ptr.getRefData().getPosition().asVec3();
found->second->resetPosition(); resetPosition(ptr);
return MovementSolver::traceDown(ptr, position, found->second.get(), mCollisionWorld.get(), maxHeight); return MovementSolver::traceDown(ptr, position, found->second.get(), mCollisionWorld.get(), maxHeight);
} }
@ -647,6 +647,17 @@ namespace MWPhysics
} }
} }
void PhysicsSystem::resetPosition(const MWWorld::ConstPtr &ptr)
{
ActorMap::iterator foundActor = mActors.find(ptr);
if (foundActor != mActors.end())
{
foundActor->second->resetPosition();
mTaskScheduler->updateSingleAabb(foundActor->second, true);
return;
}
}
void PhysicsSystem::addActor (const MWWorld::Ptr& ptr, const std::string& mesh) void PhysicsSystem::addActor (const MWWorld::Ptr& ptr, const std::string& mesh)
{ {
osg::ref_ptr<const Resource::BulletShape> shape = mShapeManager->getShape(mesh); osg::ref_ptr<const Resource::BulletShape> shape = mShapeManager->getShape(mesh);
@ -683,6 +694,8 @@ namespace MWPhysics
if (found != mActors.end()) if (found != mActors.end())
{ {
bool cmode = found->second->getCollisionMode(); bool cmode = found->second->getCollisionMode();
if (cmode)
resetPosition(found->first);
cmode = !cmode; cmode = !cmode;
found->second->enableCollisionMode(cmode); found->second->enableCollisionMode(cmode);
// NB: Collision body isn't disabled for vanilla TCL compatibility // NB: Collision body isn't disabled for vanilla TCL compatibility
@ -711,7 +724,7 @@ namespace MWPhysics
mMovementQueue.clear(); mMovementQueue.clear();
} }
const PtrPositionList& PhysicsSystem::applyQueuedMovement(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) const std::vector<MWWorld::Ptr>& PhysicsSystem::applyQueuedMovement(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
{ {
mTimeAccum += dt; mTimeAccum += dt;
@ -930,7 +943,6 @@ namespace MWPhysics
void ActorFrameData::updatePosition() void ActorFrameData::updatePosition()
{ {
mActorRaw->updatePosition(); mActorRaw->updatePosition();
mOrigin = mActorRaw->getSimulationPosition();
mPosition = mActorRaw->getPosition(); mPosition = mActorRaw->getPosition();
if (mMoveToWaterSurface) if (mMoveToWaterSurface)
{ {

View file

@ -50,8 +50,6 @@ class btVector3;
namespace MWPhysics namespace MWPhysics
{ {
using PtrPositionList = std::map<MWWorld::Ptr, osg::Vec3f>;
class HeightField; class HeightField;
class Object; class Object;
class Actor; class Actor;
@ -99,7 +97,6 @@ namespace MWPhysics
float mOldHeight; float mOldHeight;
float mFallHeight; float mFallHeight;
osg::Vec3f mMovement; osg::Vec3f mMovement;
osg::Vec3f mOrigin;
osg::Vec3f mPosition; osg::Vec3f mPosition;
ESM::Position mRefpos; ESM::Position mRefpos;
}; };
@ -147,6 +144,7 @@ namespace MWPhysics
void updateScale (const MWWorld::Ptr& ptr); void updateScale (const MWWorld::Ptr& ptr);
void updateRotation (const MWWorld::Ptr& ptr); void updateRotation (const MWWorld::Ptr& ptr);
void updatePosition (const MWWorld::Ptr& ptr); void updatePosition (const MWWorld::Ptr& ptr);
void resetPosition(const MWWorld::ConstPtr &ptr);
void addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject); void addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject);
@ -210,7 +208,7 @@ namespace MWPhysics
void queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity); void queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity);
/// Apply all queued movements, then clear the list. /// Apply all queued movements, then clear the list.
const PtrPositionList& applyQueuedMovement(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); const std::vector<MWWorld::Ptr>& applyQueuedMovement(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);
/// Clear the queued movements list without applying. /// Clear the queued movements list without applying.
void clearQueuedMovement(); void clearQueuedMovement();

View file

@ -1356,12 +1356,7 @@ namespace MWWorld
} }
moveObject(ptr, ptr.getCell(), pos.x(), pos.y(), pos.z()); moveObject(ptr, ptr.getCell(), pos.x(), pos.y(), pos.z());
if (ptr.getClass().isActor()) mPhysics->resetPosition(ptr);
{
MWPhysics::Actor* actor = mPhysics->getActor(ptr);
if (actor)
actor->resetPosition();
}
} }
void World::fixPosition() void World::fixPosition()
@ -1512,16 +1507,26 @@ namespace MWWorld
mProjectileManager->processHits(); mProjectileManager->processHits();
mDiscardMovements = false; mDiscardMovements = false;
for(const auto& [actor, position]: results) for(const auto& actor : results)
{ {
// Handle player last, in case a cell transition occurs // Handle player last, in case a cell transition occurs
if(actor != getPlayerPtr()) if(actor != getPlayerPtr())
{
auto* physactor = mPhysics->getActor(actor);
assert(physactor);
const auto position = physactor->getSimulationPosition();
moveObjectImp(actor, position.x(), position.y(), position.z(), false); moveObjectImp(actor, position.x(), position.y(), position.z(), false);
}
} }
const auto player = results.find(getPlayerPtr()); const auto player = std::find(results.begin(), results.end(), getPlayerPtr());
if (player != results.end()) if (player != results.end())
moveObjectImp(player->first, player->second.x(), player->second.y(), player->second.z(), false); {
auto* physactor = mPhysics->getActor(*player);
assert(physactor);
const auto position = physactor->getSimulationPosition();
moveObjectImp(*player, position.x(), position.y(), position.z(), false);
}
} }
void World::updateNavigator() void World::updateNavigator()