1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-31 14:36:39 +00:00

Explicitely store all the potential states an Actor can have into the

ActActorFrameData structure. It makes it easier to reason about the
simulation (and hopefully simplify it).
Remove atomics from Actor class as a side effect.

Rename mFloatToSurface to mInert to make is explicit what it represent, not what it is used for
Store the Actor rotation (1 Vec2) instead of the whole ESM::Position (2 Vec3)
This commit is contained in:
fredzio 2021-07-21 19:04:36 +02:00
parent 9472728fa4
commit 1bfaf353be
6 changed files with 67 additions and 41 deletions

View file

@ -82,7 +82,7 @@ Actor::~Actor()
void Actor::enableCollisionMode(bool collision) void Actor::enableCollisionMode(bool collision)
{ {
mInternalCollisionMode.store(collision, std::memory_order_release); mInternalCollisionMode = collision;
} }
void Actor::enableCollisionBody(bool collision) void Actor::enableCollisionBody(bool collision)
@ -250,22 +250,22 @@ void Actor::setInertialForce(const osg::Vec3f &force)
void Actor::setOnGround(bool grounded) void Actor::setOnGround(bool grounded)
{ {
mOnGround.store(grounded, std::memory_order_release); mOnGround = grounded;
} }
void Actor::setOnSlope(bool slope) void Actor::setOnSlope(bool slope)
{ {
mOnSlope.store(slope, std::memory_order_release); mOnSlope = slope;
} }
bool Actor::isWalkingOnWater() const bool Actor::isWalkingOnWater() const
{ {
return mWalkingOnWater.load(std::memory_order_acquire); return mWalkingOnWater;
} }
void Actor::setWalkingOnWater(bool walkingOnWater) void Actor::setWalkingOnWater(bool walkingOnWater)
{ {
mWalkingOnWater.store(walkingOnWater, std::memory_order_release); mWalkingOnWater = walkingOnWater;
} }
void Actor::setCanWaterWalk(bool waterWalk) void Actor::setCanWaterWalk(bool waterWalk)

View file

@ -1,7 +1,6 @@
#ifndef OPENMW_MWPHYSICS_ACTOR_H #ifndef OPENMW_MWPHYSICS_ACTOR_H
#define OPENMW_MWPHYSICS_ACTOR_H #define OPENMW_MWPHYSICS_ACTOR_H
#include <atomic>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
@ -37,7 +36,7 @@ namespace MWPhysics
bool getCollisionMode() const bool getCollisionMode() const
{ {
return mInternalCollisionMode.load(std::memory_order_acquire); return mInternalCollisionMode;
} }
btConvexShape* getConvexShape() const { return mConvexShape; } btConvexShape* getConvexShape() const { return mConvexShape; }
@ -123,14 +122,14 @@ namespace MWPhysics
bool getOnGround() const bool getOnGround() const
{ {
return mInternalCollisionMode.load(std::memory_order_acquire) && mOnGround.load(std::memory_order_acquire); return mInternalCollisionMode && mOnGround;
} }
void setOnSlope(bool slope); void setOnSlope(bool slope);
bool getOnSlope() const bool getOnSlope() const
{ {
return mInternalCollisionMode.load(std::memory_order_acquire) && mOnSlope.load(std::memory_order_acquire); return mInternalCollisionMode && mOnSlope;
} }
btCollisionObject* getCollisionObject() const btCollisionObject* getCollisionObject() const
@ -182,7 +181,7 @@ namespace MWPhysics
osg::Vec3f getScaledMeshTranslation() const; osg::Vec3f getScaledMeshTranslation() const;
bool mCanWaterWalk; bool mCanWaterWalk;
std::atomic<bool> mWalkingOnWater; bool mWalkingOnWater;
bool mRotationallyInvariant; bool mRotationallyInvariant;
@ -211,9 +210,9 @@ namespace MWPhysics
osg::Vec3f mLastStuckPosition; osg::Vec3f mLastStuckPosition;
osg::Vec3f mForce; osg::Vec3f mForce;
std::atomic<bool> mOnGround; bool mOnGround;
std::atomic<bool> mOnSlope; bool mOnSlope;
std::atomic<bool> mInternalCollisionMode; bool mInternalCollisionMode;
bool mExternalCollisionMode; bool mExternalCollisionMode;
PhysicsTaskScheduler* mTaskScheduler; PhysicsTaskScheduler* mTaskScheduler;

View file

@ -119,15 +119,14 @@ namespace MWPhysics
WorldFrameData& worldData) WorldFrameData& worldData)
{ {
auto* physicActor = actor.mActorRaw; auto* physicActor = actor.mActorRaw;
const ESM::Position& refpos = actor.mRefpos;
// Reset per-frame data // Reset per-frame data
physicActor->setWalkingOnWater(false); actor.mWalkingOnWater = false;
// Anything to collide with? // Anything to collide with?
if(!physicActor->getCollisionMode() || actor.mSkipCollisionDetection) if(actor.mSkipCollisionDetection)
{ {
actor.mPosition += (osg::Quat(refpos.rot[0], osg::Vec3f(-1, 0, 0)) * actor.mPosition += (osg::Quat(actor.mRotation.x(), osg::Vec3f(-1, 0, 0)) *
osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1)) osg::Quat(actor.mRotation.y(), osg::Vec3f(0, 0, -1))
) * actor.mMovement * time; ) * actor.mMovement * time;
return; return;
} }
@ -151,22 +150,22 @@ namespace MWPhysics
if (actor.mPosition.z() < swimlevel || actor.mFlying) if (actor.mPosition.z() < swimlevel || actor.mFlying)
{ {
velocity = (osg::Quat(refpos.rot[0], osg::Vec3f(-1, 0, 0)) * osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))) * actor.mMovement; velocity = (osg::Quat(actor.mRotation.x(), osg::Vec3f(-1, 0, 0)) * osg::Quat(actor.mRotation.y(), osg::Vec3f(0, 0, -1))) * actor.mMovement;
} }
else else
{ {
velocity = (osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))) * actor.mMovement; velocity = (osg::Quat(actor.mRotation.y(), osg::Vec3f(0, 0, -1))) * actor.mMovement;
if ((velocity.z() > 0.f && physicActor->getOnGround() && !physicActor->getOnSlope()) if ((velocity.z() > 0.f && actor.mIsOnGround && !actor.mIsOnSlope)
|| (velocity.z() > 0.f && velocity.z() + inertia.z() <= -velocity.z() && physicActor->getOnSlope())) || (velocity.z() > 0.f && velocity.z() + inertia.z() <= -velocity.z() && actor.mIsOnSlope))
inertia = velocity; inertia = velocity;
else if (!physicActor->getOnGround() || physicActor->getOnSlope()) else if (!actor.mIsOnGround || actor.mIsOnSlope)
velocity = velocity + inertia; velocity = velocity + inertia;
} }
// Dead and paralyzed actors underwater will float to the surface, // Dead and paralyzed actors underwater will float to the surface,
// if the CharacterController tells us to do so // if the CharacterController tells us to do so
if (actor.mMovement.z() > 0 && actor.mFloatToSurface && actor.mPosition.z() < swimlevel) if (actor.mMovement.z() > 0 && actor.mInert && actor.mPosition.z() < swimlevel)
velocity = osg::Vec3f(0,0,1) * 25; velocity = osg::Vec3f(0,0,1) * 25;
if (actor.mWantJump) if (actor.mWantJump)
@ -190,7 +189,7 @@ namespace MWPhysics
* The initial velocity was set earlier (see above). * The initial velocity was set earlier (see above).
*/ */
float remainingTime = time; float remainingTime = time;
bool seenGround = physicActor->getOnGround() && !physicActor->getOnSlope() && !actor.mFlying; bool seenGround = actor.mIsOnGround && !actor.mIsOnSlope && !actor.mFlying;
int numTimesSlid = 0; int numTimesSlid = 0;
osg::Vec3f lastSlideNormal(0,0,1); osg::Vec3f lastSlideNormal(0,0,1);
@ -349,7 +348,7 @@ namespace MWPhysics
if (forceGroundTest || (inertia.z() <= 0.f && newPosition.z() >= swimlevel)) if (forceGroundTest || (inertia.z() <= 0.f && newPosition.z() >= swimlevel))
{ {
osg::Vec3f from = newPosition; osg::Vec3f from = newPosition;
auto dropDistance = 2*sGroundOffset + (physicActor->getOnGround() ? sStepSizeDown : 0); auto dropDistance = 2*sGroundOffset + (actor.mIsOnGround ? sStepSizeDown : 0);
osg::Vec3f to = newPosition - osg::Vec3f(0,0,dropDistance); osg::Vec3f to = newPosition - osg::Vec3f(0,0,dropDistance);
tracer.doTrace(colobj, from, to, collisionWorld); tracer.doTrace(colobj, from, to, collisionWorld);
if(tracer.mFraction < 1.0f) if(tracer.mFraction < 1.0f)
@ -365,7 +364,7 @@ namespace MWPhysics
actor.mStandingOn = ptrHolder->getPtr(); actor.mStandingOn = ptrHolder->getPtr();
if (standingOn->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Water) if (standingOn->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Water)
physicActor->setWalkingOnWater(true); actor.mWalkingOnWater = true;
if (!actor.mFlying && !isOnSlope) if (!actor.mFlying && !isOnSlope)
{ {
if (tracer.mFraction*dropDistance > sGroundOffset) if (tracer.mFraction*dropDistance > sGroundOffset)
@ -408,8 +407,8 @@ namespace MWPhysics
} }
physicActor->setInertialForce(inertia); physicActor->setInertialForce(inertia);
} }
physicActor->setOnGround(isOnGround); actor.mIsOnGround = isOnGround;
physicActor->setOnSlope(isOnSlope); actor.mIsOnSlope = isOnSlope;
actor.mPosition = newPosition; actor.mPosition = newPosition;
// remove what was added earlier in compensating for doTrace not taking interior transformation into account // remove what was added earlier in compensating for doTrace not taking interior transformation into account
@ -426,7 +425,7 @@ namespace MWPhysics
void MovementSolver::unstuck(ActorFrameData& actor, const btCollisionWorld* collisionWorld) void MovementSolver::unstuck(ActorFrameData& actor, const btCollisionWorld* collisionWorld)
{ {
auto* physicActor = actor.mActorRaw; auto* physicActor = actor.mActorRaw;
if(!physicActor->getCollisionMode() || actor.mSkipCollisionDetection) // noclipping/tcl if(actor.mSkipCollisionDetection) // noclipping/tcl
return; return;
auto* collisionObject = physicActor->getCollisionObject(); auto* collisionObject = physicActor->getCollisionObject();
@ -448,9 +447,9 @@ namespace MWPhysics
const auto verticalHalfExtent = osg::Vec3f(0.0, 0.0, physicActor->getHalfExtents().z()); const auto verticalHalfExtent = osg::Vec3f(0.0, 0.0, physicActor->getHalfExtents().z());
// use a 3d approximation of the movement vector to better judge player intent // use a 3d approximation of the movement vector to better judge player intent
auto velocity = (osg::Quat(actor.mRefpos.rot[0], osg::Vec3f(-1, 0, 0)) * osg::Quat(actor.mRefpos.rot[2], osg::Vec3f(0, 0, -1))) * actor.mMovement; auto velocity = (osg::Quat(actor.mRotation.x(), osg::Vec3f(-1, 0, 0)) * osg::Quat(actor.mRotation.y(), osg::Vec3f(0, 0, -1))) * actor.mMovement;
// try to pop outside of the world before doing anything else if we're inside of it // try to pop outside of the world before doing anything else if we're inside of it
if (!physicActor->getOnGround() || physicActor->getOnSlope()) if (!actor.mIsOnGround || actor.mIsOnSlope)
velocity += physicActor->getInertialForce(); velocity += physicActor->getInertialForce();
// because of the internal collision box offset hack, and the fact that we're moving the collision box manually, // because of the internal collision box offset hack, and the fact that we're moving the collision box manually,

View file

@ -54,7 +54,7 @@ namespace
{ {
const float heightDiff = actorData.mPosition.z() - actorData.mOldHeight; const float heightDiff = actorData.mPosition.z() - actorData.mOldHeight;
const bool isStillOnGround = (simulationPerformed && actorData.mWasOnGround && actorData.mActorRaw->getOnGround()); const bool isStillOnGround = (simulationPerformed && actorData.mWasOnGround && actorData.mIsOnGround);
if (isStillOnGround || actorData.mFlying || actorData.mSwimming || actorData.mSlowFall < 1) if (isStillOnGround || actorData.mFlying || actorData.mSwimming || actorData.mSlowFall < 1)
actorData.mNeedLand = true; actorData.mNeedLand = true;
@ -256,7 +256,12 @@ namespace MWPhysics
// these variables are accessed directly from the main thread, update them here to prevent accessing "too new" values // 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->setOnGround(data.mIsOnGround);
data.mActorRaw->setOnSlope(data.mIsOnSlope);
data.mActorRaw->setWalkingOnWater(data.mWalkingOnWater);
}
data.mActorRaw->setSimulationPosition(interpolateMovements(data, mTimeAccum, mPhysicsDt)); data.mActorRaw->setSimulationPosition(interpolateMovements(data, mTimeAccum, mPhysicsDt));
} }
} }
@ -555,7 +560,12 @@ namespace MWPhysics
actorData.mActorRaw->setSimulationPosition(interpolateMovements(actorData, mTimeAccum, mPhysicsDt)); actorData.mActorRaw->setSimulationPosition(interpolateMovements(actorData, mTimeAccum, mPhysicsDt));
updateMechanics(actorData); updateMechanics(actorData);
if (mAdvanceSimulation) if (mAdvanceSimulation)
{
actorData.mActorRaw->setStandingOnPtr(actorData.mStandingOn); actorData.mActorRaw->setStandingOnPtr(actorData.mStandingOn);
actorData.mActorRaw->setOnGround(actorData.mIsOnGround);
actorData.mActorRaw->setOnSlope(actorData.mIsOnSlope);
actorData.mActorRaw->setWalkingOnWater(actorData.mWalkingOnWater);
}
} }
refreshLOSCache(); refreshLOSCache();
} }

View file

@ -950,9 +950,24 @@ namespace MWPhysics
ActorFrameData::ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr standingOn, ActorFrameData::ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr standingOn,
bool waterCollision, float slowFall, float waterlevel) bool waterCollision, float slowFall, float waterlevel)
: mActor(actor), mActorRaw(actor.get()), mStandingOn(standingOn), : mActor(actor)
mDidJump(false), mNeedLand(false), mWaterCollision(waterCollision), mSkipCollisionDetection(actor->skipCollisions()), , mActorRaw(actor.get())
mWaterlevel(waterlevel), mSlowFall(slowFall), mOldHeight(0), mFallHeight(0), mMovement(actor->velocity()), mPosition(), mRefpos() , mStandingOn(standingOn)
, mWasOnGround(actor->getOnGround())
, mIsOnGround(actor->getOnGround())
, mIsOnSlope(actor->getOnSlope())
, mDidJump(false)
, mNeedLand(false)
, mWaterCollision(waterCollision)
, mWalkingOnWater(false)
, mSkipCollisionDetection(actor->skipCollisions() || !actor->getCollisionMode())
, mWaterlevel(waterlevel)
, mSlowFall(slowFall)
, mOldHeight(0)
, mFallHeight(0)
, mMovement(actor->velocity())
, mPosition()
, mRotation()
{ {
const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWBase::World *world = MWBase::Environment::get().getWorld();
const auto ptr = actor->getPtr(); const auto ptr = actor->getPtr();
@ -961,8 +976,7 @@ namespace MWPhysics
mWantJump = ptr.getClass().getMovementSettings(ptr).mPosition[2] != 0; mWantJump = ptr.getClass().getMovementSettings(ptr).mPosition[2] != 0;
auto& stats = ptr.getClass().getCreatureStats(ptr); auto& stats = ptr.getClass().getCreatureStats(ptr);
const bool godmode = ptr == world->getPlayerConstPtr() && world->getGodModeState(); const bool godmode = ptr == world->getPlayerConstPtr() && world->getGodModeState();
mFloatToSurface = stats.isDead() || (!godmode && stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getModifier() > 0); mInert = stats.isDead() || (!godmode && stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getModifier() > 0);
mWasOnGround = actor->getOnGround();
} }
void ActorFrameData::updatePosition(btCollisionWorld* world) void ActorFrameData::updatePosition(btCollisionWorld* world)
@ -975,7 +989,8 @@ namespace MWPhysics
MWBase::Environment::get().getWorld()->moveObject(mActorRaw->getPtr(), mPosition, false); MWBase::Environment::get().getWorld()->moveObject(mActorRaw->getPtr(), mPosition, false);
} }
mOldHeight = mPosition.z(); mOldHeight = mPosition.z();
mRefpos = mActorRaw->getPtr().getRefData().getPosition(); const auto rotation = mActorRaw->getPtr().getRefData().getPosition().asRotationVec3();
mRotation = osg::Vec2f(rotation.x(), rotation.z());
} }
WorldFrameData::WorldFrameData() WorldFrameData::WorldFrameData()

View file

@ -86,11 +86,14 @@ namespace MWPhysics
bool mFlying; bool mFlying;
bool mSwimming; bool mSwimming;
bool mWasOnGround; bool mWasOnGround;
bool mIsOnGround;
bool mIsOnSlope;
bool mWantJump; bool mWantJump;
bool mDidJump; bool mDidJump;
bool mFloatToSurface; bool mInert;
bool mNeedLand; bool mNeedLand;
bool mWaterCollision; bool mWaterCollision;
bool mWalkingOnWater;
bool mSkipCollisionDetection; bool mSkipCollisionDetection;
float mWaterlevel; float mWaterlevel;
float mSlowFall; float mSlowFall;
@ -98,7 +101,7 @@ namespace MWPhysics
float mFallHeight; float mFallHeight;
osg::Vec3f mMovement; osg::Vec3f mMovement;
osg::Vec3f mPosition; osg::Vec3f mPosition;
ESM::Position mRefpos; osg::Vec2f mRotation;
}; };
struct WorldFrameData struct WorldFrameData