mirror of
https://github.com/OpenMW/openmw.git
synced 2025-12-09 12:04:31 +00:00
We need to update the collision world after each step.
Change order of traversal simulation step to make it rare enough to be parallelizable
Before:
for actor in actors:
repeat numstep:
solve(actor)
After:
repeat numstep:
for actor in actors:
solve(actor)
Introduce struct ActorFrameData to pack all data that is necessary for
the solver
This commit is contained in:
parent
d76cc5d0a9
commit
91b3926a49
4 changed files with 255 additions and 101 deletions
|
|
@ -10,18 +10,14 @@
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
|
||||||
#include "../mwmechanics/actorutil.hpp"
|
|
||||||
#include "../mwmechanics/creaturestats.hpp"
|
|
||||||
#include "../mwmechanics/movement.hpp"
|
|
||||||
|
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
#include "../mwworld/player.hpp"
|
|
||||||
#include "../mwworld/refdata.hpp"
|
#include "../mwworld/refdata.hpp"
|
||||||
|
|
||||||
#include "actor.hpp"
|
#include "actor.hpp"
|
||||||
#include "collisiontype.hpp"
|
#include "collisiontype.hpp"
|
||||||
#include "constants.hpp"
|
#include "constants.hpp"
|
||||||
|
#include "physicssystem.hpp"
|
||||||
#include "stepper.hpp"
|
#include "stepper.hpp"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
|
|
@ -78,24 +74,26 @@ namespace MWPhysics
|
||||||
return tracer.mEndPos-offset + osg::Vec3f(0.f, 0.f, sGroundOffset);
|
return tracer.mEndPos-offset + osg::Vec3f(0.f, 0.f, sGroundOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Vec3f MovementSolver::move(osg::Vec3f position, const MWWorld::Ptr &ptr, Actor* physicActor, const osg::Vec3f &movement, float time,
|
void MovementSolver::move(ActorFrameData& actor, float time, const btCollisionWorld* collisionWorld,
|
||||||
bool isFlying, float waterlevel, float slowFall, const btCollisionWorld* collisionWorld,
|
WorldFrameData& worldData)
|
||||||
std::map<MWWorld::Ptr, MWWorld::Ptr>& standingCollisionTracker)
|
|
||||||
{
|
{
|
||||||
const ESM::Position& refpos = ptr.getRefData().getPosition();
|
auto* physicActor = actor.mActorRaw;
|
||||||
|
auto ptr = actor.mPtr;
|
||||||
|
const ESM::Position& refpos = actor.mRefpos;
|
||||||
// Early-out for totally static creatures
|
// Early-out for totally static creatures
|
||||||
// (Not sure if gravity should still apply?)
|
// (Not sure if gravity should still apply?)
|
||||||
if (!ptr.getClass().isMobile(ptr))
|
if (!ptr.getClass().isMobile(ptr))
|
||||||
return position;
|
return;
|
||||||
|
|
||||||
// Reset per-frame data
|
// Reset per-frame data
|
||||||
physicActor->setWalkingOnWater(false);
|
physicActor->setWalkingOnWater(false);
|
||||||
// Anything to collide with?
|
// Anything to collide with?
|
||||||
if(!physicActor->getCollisionMode())
|
if(!physicActor->getCollisionMode())
|
||||||
{
|
{
|
||||||
return position + (osg::Quat(refpos.rot[0], osg::Vec3f(-1, 0, 0)) *
|
actor.mPosition += (osg::Quat(refpos.rot[0], osg::Vec3f(-1, 0, 0)) *
|
||||||
osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))
|
osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))
|
||||||
) * movement * time;
|
) * actor.mMovement * time;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const btCollisionObject *colobj = physicActor->getCollisionObject();
|
const btCollisionObject *colobj = physicActor->getCollisionObject();
|
||||||
|
|
@ -105,23 +103,23 @@ namespace MWPhysics
|
||||||
// That means the collision shape used for moving this actor is in a different spot than the collision shape
|
// That means the collision shape used for moving this actor is in a different spot than the collision shape
|
||||||
// other actors are using to collide against this actor.
|
// other actors are using to collide against this actor.
|
||||||
// While this is strictly speaking wrong, it's needed for MW compatibility.
|
// While this is strictly speaking wrong, it's needed for MW compatibility.
|
||||||
position.z() += halfExtents.z();
|
actor.mPosition.z() += halfExtents.z();
|
||||||
|
|
||||||
static const float fSwimHeightScale = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fSwimHeightScale")->mValue.getFloat();
|
static const float fSwimHeightScale = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fSwimHeightScale")->mValue.getFloat();
|
||||||
float swimlevel = waterlevel + halfExtents.z() - (physicActor->getRenderingHalfExtents().z() * 2 * fSwimHeightScale);
|
float swimlevel = actor.mWaterlevel + halfExtents.z() - (physicActor->getRenderingHalfExtents().z() * 2 * fSwimHeightScale);
|
||||||
|
|
||||||
ActorTracer tracer;
|
ActorTracer tracer;
|
||||||
|
|
||||||
osg::Vec3f inertia = physicActor->getInertialForce();
|
osg::Vec3f inertia = physicActor->getInertialForce();
|
||||||
osg::Vec3f velocity;
|
osg::Vec3f velocity;
|
||||||
|
|
||||||
if (position.z() < swimlevel || isFlying)
|
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))) * movement;
|
velocity = (osg::Quat(refpos.rot[0], osg::Vec3f(-1, 0, 0)) * osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))) * actor.mMovement;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
velocity = (osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))) * movement;
|
velocity = (osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))) * actor.mMovement;
|
||||||
|
|
||||||
if ((velocity.z() > 0.f && physicActor->getOnGround() && !physicActor->getOnSlope())
|
if ((velocity.z() > 0.f && physicActor->getOnGround() && !physicActor->getOnSlope())
|
||||||
|| (velocity.z() > 0.f && velocity.z() + inertia.z() <= -velocity.z() && physicActor->getOnSlope()))
|
|| (velocity.z() > 0.f && velocity.z() + inertia.z() <= -velocity.z() && physicActor->getOnSlope()))
|
||||||
|
|
@ -131,38 +129,16 @@ namespace MWPhysics
|
||||||
}
|
}
|
||||||
|
|
||||||
// dead actors underwater will float to the surface, if the CharacterController tells us to do so
|
// dead actors underwater will float to the surface, if the CharacterController tells us to do so
|
||||||
if (movement.z() > 0 && ptr.getClass().getCreatureStats(ptr).isDead() && position.z() < swimlevel)
|
if (actor.mMovement.z() > 0 && actor.mIsDead && actor.mPosition.z() < swimlevel)
|
||||||
velocity = osg::Vec3f(0,0,1) * 25;
|
velocity = osg::Vec3f(0,0,1) * 25;
|
||||||
|
|
||||||
if (ptr.getClass().getMovementSettings(ptr).mPosition[2])
|
if (actor.mWantJump)
|
||||||
{
|
actor.mDidJump = true;
|
||||||
const bool isPlayer = (ptr == MWMechanics::getPlayer());
|
|
||||||
// Advance acrobatics and set flag for GetPCJumping
|
|
||||||
if (isPlayer)
|
|
||||||
{
|
|
||||||
ptr.getClass().skillUsageSucceeded(ptr, ESM::Skill::Acrobatics, 0);
|
|
||||||
MWBase::Environment::get().getWorld()->getPlayer().setJumping(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrease fatigue
|
|
||||||
if (!isPlayer || !MWBase::Environment::get().getWorld()->getGodModeState())
|
|
||||||
{
|
|
||||||
const MWWorld::Store<ESM::GameSetting> &gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
|
||||||
const float fFatigueJumpBase = gmst.find("fFatigueJumpBase")->mValue.getFloat();
|
|
||||||
const float fFatigueJumpMult = gmst.find("fFatigueJumpMult")->mValue.getFloat();
|
|
||||||
const float normalizedEncumbrance = std::min(1.f, ptr.getClass().getNormalizedEncumbrance(ptr));
|
|
||||||
const float fatigueDecrease = fFatigueJumpBase + normalizedEncumbrance * fFatigueJumpMult;
|
|
||||||
MWMechanics::DynamicStat<float> fatigue = ptr.getClass().getCreatureStats(ptr).getFatigue();
|
|
||||||
fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease);
|
|
||||||
ptr.getClass().getCreatureStats(ptr).setFatigue(fatigue);
|
|
||||||
}
|
|
||||||
ptr.getClass().getMovementSettings(ptr).mPosition[2] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now that we have the effective movement vector, apply wind forces to it
|
// Now that we have the effective movement vector, apply wind forces to it
|
||||||
if (MWBase::Environment::get().getWorld()->isInStorm())
|
if (worldData.mIsInStorm)
|
||||||
{
|
{
|
||||||
osg::Vec3f stormDirection = MWBase::Environment::get().getWorld()->getStormDirection();
|
osg::Vec3f stormDirection = worldData.mStormDirection;
|
||||||
float angleDegrees = osg::RadiansToDegrees(std::acos(stormDirection * velocity / (stormDirection.length() * velocity.length())));
|
float angleDegrees = osg::RadiansToDegrees(std::acos(stormDirection * velocity / (stormDirection.length() * velocity.length())));
|
||||||
static const float fStromWalkMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fStromWalkMult")->mValue.getFloat();
|
static const float fStromWalkMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fStromWalkMult")->mValue.getFloat();
|
||||||
velocity *= 1.f-(fStromWalkMult * (angleDegrees/180.f));
|
velocity *= 1.f-(fStromWalkMult * (angleDegrees/180.f));
|
||||||
|
|
@ -170,7 +146,7 @@ namespace MWPhysics
|
||||||
|
|
||||||
Stepper stepper(collisionWorld, colobj);
|
Stepper stepper(collisionWorld, colobj);
|
||||||
osg::Vec3f origVelocity = velocity;
|
osg::Vec3f origVelocity = velocity;
|
||||||
osg::Vec3f newPosition = position;
|
osg::Vec3f newPosition = actor.mPosition;
|
||||||
/*
|
/*
|
||||||
* A loop to find newPosition using tracer, if successful different from the starting position.
|
* A loop to find newPosition using tracer, if successful different from the starting position.
|
||||||
* nextpos is the local variable used to find potential newPosition, using velocity and remainingTime
|
* nextpos is the local variable used to find potential newPosition, using velocity and remainingTime
|
||||||
|
|
@ -182,7 +158,7 @@ namespace MWPhysics
|
||||||
osg::Vec3f nextpos = newPosition + velocity * remainingTime;
|
osg::Vec3f nextpos = newPosition + velocity * remainingTime;
|
||||||
|
|
||||||
// If not able to fly, don't allow to swim up into the air
|
// If not able to fly, don't allow to swim up into the air
|
||||||
if(!isFlying && nextpos.z() > swimlevel && newPosition.z() < swimlevel)
|
if(!actor.mFlying && nextpos.z() > swimlevel && newPosition.z() < swimlevel)
|
||||||
{
|
{
|
||||||
const osg::Vec3f down(0,0,-1);
|
const osg::Vec3f down(0,0,-1);
|
||||||
velocity = slide(velocity, down);
|
velocity = slide(velocity, down);
|
||||||
|
|
@ -235,7 +211,7 @@ namespace MWPhysics
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
// don't let pure water creatures move out of water after stepMove
|
// don't let pure water creatures move out of water after stepMove
|
||||||
if (ptr.getClass().isPureWaterCreature(ptr) && newPosition.z() + halfExtents.z() > waterlevel)
|
if (ptr.getClass().isPureWaterCreature(ptr) && newPosition.z() + halfExtents.z() > actor.mWaterlevel)
|
||||||
newPosition = oldPosition;
|
newPosition = oldPosition;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -245,7 +221,7 @@ namespace MWPhysics
|
||||||
|
|
||||||
// Do not allow sliding upward if there is gravity.
|
// Do not allow sliding upward if there is gravity.
|
||||||
// Stepping will have taken care of that.
|
// Stepping will have taken care of that.
|
||||||
if(!(newPosition.z() < swimlevel || isFlying))
|
if(!(newPosition.z() < swimlevel || actor.mFlying))
|
||||||
newVelocity.z() = std::min(newVelocity.z(), 0.0f);
|
newVelocity.z() = std::min(newVelocity.z(), 0.0f);
|
||||||
|
|
||||||
if ((newVelocity-velocity).length2() < 0.01)
|
if ((newVelocity-velocity).length2() < 0.01)
|
||||||
|
|
@ -269,11 +245,11 @@ namespace MWPhysics
|
||||||
const btCollisionObject* standingOn = tracer.mHitObject;
|
const btCollisionObject* standingOn = tracer.mHitObject;
|
||||||
PtrHolder* ptrHolder = static_cast<PtrHolder*>(standingOn->getUserPointer());
|
PtrHolder* ptrHolder = static_cast<PtrHolder*>(standingOn->getUserPointer());
|
||||||
if (ptrHolder)
|
if (ptrHolder)
|
||||||
standingCollisionTracker[ptr] = ptrHolder->getPtr();
|
actor.mStandingOn = ptrHolder->getPtr();
|
||||||
|
|
||||||
if (standingOn->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Water)
|
if (standingOn->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Water)
|
||||||
physicActor->setWalkingOnWater(true);
|
physicActor->setWalkingOnWater(true);
|
||||||
if (!isFlying)
|
if (!actor.mFlying)
|
||||||
newPosition.z() = tracer.mEndPos.z() + sGroundOffset;
|
newPosition.z() = tracer.mEndPos.z() + sGroundOffset;
|
||||||
|
|
||||||
isOnGround = true;
|
isOnGround = true;
|
||||||
|
|
@ -292,7 +268,7 @@ namespace MWPhysics
|
||||||
btVector3 aabbMin, aabbMax;
|
btVector3 aabbMin, aabbMax;
|
||||||
tracer.mHitObject->getCollisionShape()->getAabb(tracer.mHitObject->getWorldTransform(), aabbMin, aabbMax);
|
tracer.mHitObject->getCollisionShape()->getAabb(tracer.mHitObject->getWorldTransform(), aabbMin, aabbMax);
|
||||||
btVector3 center = (aabbMin + aabbMax) / 2.f;
|
btVector3 center = (aabbMin + aabbMax) / 2.f;
|
||||||
inertia = osg::Vec3f(position.x() - center.x(), position.y() - center.y(), 0);
|
inertia = osg::Vec3f(actor.mPosition.x() - center.x(), actor.mPosition.y() - center.y(), 0);
|
||||||
inertia.normalize();
|
inertia.normalize();
|
||||||
inertia *= 100;
|
inertia *= 100;
|
||||||
}
|
}
|
||||||
|
|
@ -302,16 +278,16 @@ namespace MWPhysics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if((isOnGround && !isOnSlope) || newPosition.z() < swimlevel || isFlying)
|
if((isOnGround && !isOnSlope) || newPosition.z() < swimlevel || actor.mFlying)
|
||||||
physicActor->setInertialForce(osg::Vec3f(0.f, 0.f, 0.f));
|
physicActor->setInertialForce(osg::Vec3f(0.f, 0.f, 0.f));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
inertia.z() -= time * Constants::GravityConst * Constants::UnitsPerMeter;
|
inertia.z() -= time * Constants::GravityConst * Constants::UnitsPerMeter;
|
||||||
if (inertia.z() < 0)
|
if (inertia.z() < 0)
|
||||||
inertia.z() *= slowFall;
|
inertia.z() *= actor.mSlowFall;
|
||||||
if (slowFall < 1.f) {
|
if (actor.mSlowFall < 1.f) {
|
||||||
inertia.x() *= slowFall;
|
inertia.x() *= actor.mSlowFall;
|
||||||
inertia.y() *= slowFall;
|
inertia.y() *= actor.mSlowFall;
|
||||||
}
|
}
|
||||||
physicActor->setInertialForce(inertia);
|
physicActor->setInertialForce(inertia);
|
||||||
}
|
}
|
||||||
|
|
@ -319,6 +295,6 @@ namespace MWPhysics
|
||||||
physicActor->setOnSlope(isOnSlope);
|
physicActor->setOnSlope(isOnSlope);
|
||||||
|
|
||||||
newPosition.z() -= halfExtents.z(); // remove what was added at the beginning
|
newPosition.z() -= halfExtents.z(); // remove what was added at the beginning
|
||||||
return newPosition;
|
actor.mPosition = newPosition;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,18 @@
|
||||||
|
|
||||||
#include <osg/Vec3f>
|
#include <osg/Vec3f>
|
||||||
|
|
||||||
#include "../mwworld/ptr.hpp"
|
|
||||||
|
|
||||||
class btCollisionWorld;
|
class btCollisionWorld;
|
||||||
|
|
||||||
|
namespace MWWorld
|
||||||
|
{
|
||||||
|
class Ptr;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWPhysics
|
namespace MWPhysics
|
||||||
{
|
{
|
||||||
class Actor;
|
class Actor;
|
||||||
|
struct ActorFrameData;
|
||||||
|
struct WorldFrameData;
|
||||||
|
|
||||||
class MovementSolver
|
class MovementSolver
|
||||||
{
|
{
|
||||||
|
|
@ -31,9 +36,7 @@ namespace MWPhysics
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static osg::Vec3f traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, Actor* actor, btCollisionWorld* collisionWorld, float maxHeight);
|
static osg::Vec3f traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, Actor* actor, btCollisionWorld* collisionWorld, float maxHeight);
|
||||||
static osg::Vec3f move(osg::Vec3f position, const MWWorld::Ptr &ptr, Actor* physicActor, const osg::Vec3f &movement, float time,
|
static void move(ActorFrameData& actor, float time, const btCollisionWorld* collisionWorld, WorldFrameData& worldData);
|
||||||
bool isFlying, float waterlevel, float slowFall, const btCollisionWorld* collisionWorld,
|
|
||||||
std::map<MWWorld::Ptr, MWWorld::Ptr>& standingCollisionTracker);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,11 @@
|
||||||
|
|
||||||
#include "../mwmechanics/creaturestats.hpp"
|
#include "../mwmechanics/creaturestats.hpp"
|
||||||
#include "../mwmechanics/actorutil.hpp"
|
#include "../mwmechanics/actorutil.hpp"
|
||||||
|
#include "../mwmechanics/movement.hpp"
|
||||||
|
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
#include "../mwworld/cellstore.hpp"
|
#include "../mwworld/cellstore.hpp"
|
||||||
|
#include "../mwworld/player.hpp"
|
||||||
|
|
||||||
#include "../mwrender/bulletdebugdraw.hpp"
|
#include "../mwrender/bulletdebugdraw.hpp"
|
||||||
|
|
||||||
|
|
@ -687,14 +689,106 @@ namespace MWPhysics
|
||||||
numSteps = std::min(numSteps, maxAllowedSteps);
|
numSteps = std::min(numSteps, maxAllowedSteps);
|
||||||
|
|
||||||
mTimeAccum -= numSteps * mPhysicsDt;
|
mTimeAccum -= numSteps * mPhysicsDt;
|
||||||
|
mActorsFrameData = prepareFrameData();
|
||||||
|
bool advanceSimulation = (numSteps != 0);
|
||||||
|
if (advanceSimulation)
|
||||||
|
mWorldFrameData = std::make_unique<WorldFrameData>();
|
||||||
|
|
||||||
if (numSteps)
|
// update each actor position based on latest data
|
||||||
|
for (auto& data : mActorsFrameData)
|
||||||
|
data.updatePosition();
|
||||||
|
|
||||||
|
mMovementResults.clear();
|
||||||
|
while (numSteps--)
|
||||||
{
|
{
|
||||||
// Collision events should be available on every frame
|
for (auto& actorData : mActorsFrameData)
|
||||||
mStandingCollisions.clear();
|
MovementSolver::move(actorData, mPhysicsDt, mCollisionWorld.get(), *mWorldFrameData);
|
||||||
|
|
||||||
|
// update actors position
|
||||||
|
for (auto& actorData : mActorsFrameData)
|
||||||
|
{
|
||||||
|
if(const auto actor = actorData.mActor.lock())
|
||||||
|
{
|
||||||
|
if (actorData.mPosition != actorData.mActorRaw->getPosition())
|
||||||
|
actorData.mPositionChanged = true;
|
||||||
|
actorData.mActorRaw->setPosition(actorData.mPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const MWWorld::Ptr player = MWMechanics::getPlayer();
|
for (auto& actorData : mActorsFrameData)
|
||||||
|
{
|
||||||
|
// handle fall of actor
|
||||||
|
const float heightDiff = actorData.mPosition.z() - actorData.mOldHeight;
|
||||||
|
|
||||||
|
const bool isStillOnGround = (advanceSimulation && actorData.mWasOnGround && actorData.mActorRaw->getOnGround());
|
||||||
|
|
||||||
|
if (isStillOnGround || actorData.mFlying || actorData.mSwimming || actorData.mSlowFall < 1)
|
||||||
|
actorData.mNeedLand = true;
|
||||||
|
else if (heightDiff < 0)
|
||||||
|
actorData.mFallHeight += heightDiff;
|
||||||
|
|
||||||
|
// interpolate position
|
||||||
|
const float interpolationFactor = mTimeAccum / mPhysicsDt;
|
||||||
|
mMovementResults[actorData.mPtr] = actorData.mPosition * interpolationFactor + actorData.mActorRaw->getPreviousPosition() * (1.f - interpolationFactor);
|
||||||
|
|
||||||
|
// update mechanics if actor jumped
|
||||||
|
if (actorData.mDidJump)
|
||||||
|
{
|
||||||
|
const bool isPlayer = (actorData.mPtr == MWMechanics::getPlayer());
|
||||||
|
// Advance acrobatics and set flag for GetPCJumping
|
||||||
|
if (isPlayer)
|
||||||
|
{
|
||||||
|
actorData.mPtr.getClass().skillUsageSucceeded(actorData.mPtr, ESM::Skill::Acrobatics, 0);
|
||||||
|
MWBase::Environment::get().getWorld()->getPlayer().setJumping(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrease fatigue
|
||||||
|
if (!isPlayer || !MWBase::Environment::get().getWorld()->getGodModeState())
|
||||||
|
{
|
||||||
|
const MWWorld::Store<ESM::GameSetting> &gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||||
|
const float fFatigueJumpBase = gmst.find("fFatigueJumpBase")->mValue.getFloat();
|
||||||
|
const float fFatigueJumpMult = gmst.find("fFatigueJumpMult")->mValue.getFloat();
|
||||||
|
const float normalizedEncumbrance = std::min(1.f, actorData.mPtr.getClass().getNormalizedEncumbrance(actorData.mPtr));
|
||||||
|
const float fatigueDecrease = fFatigueJumpBase + normalizedEncumbrance * fFatigueJumpMult;
|
||||||
|
MWMechanics::DynamicStat<float> fatigue = actorData.mPtr.getClass().getCreatureStats(actorData.mPtr).getFatigue();
|
||||||
|
fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease);
|
||||||
|
actorData.mPtr.getClass().getCreatureStats(actorData.mPtr).setFatigue(fatigue);
|
||||||
|
}
|
||||||
|
actorData.mPtr.getClass().getMovementSettings(actorData.mPtr).mPosition[2] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
MWMechanics::CreatureStats& stats = actorData.mPtr.getClass().getCreatureStats(actorData.mPtr);
|
||||||
|
if (actorData.mNeedLand)
|
||||||
|
stats.land(actorData.mPtr == MWMechanics::getPlayer() && (actorData.mFlying || actorData.mSwimming));
|
||||||
|
else if (actorData.mFallHeight < 0)
|
||||||
|
stats.addToFallHeight(-actorData.mFallHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update actors aabb
|
||||||
|
for (const auto& actorData : mActorsFrameData)
|
||||||
|
if (actorData.mPositionChanged)
|
||||||
|
mCollisionWorld->updateSingleAabb(actorData.mActorRaw->getCollisionObject());
|
||||||
|
|
||||||
|
// update standing collisions map
|
||||||
|
if (advanceSimulation)
|
||||||
|
{
|
||||||
|
mStandingCollisions.clear();
|
||||||
|
for (auto& actorData : mActorsFrameData)
|
||||||
|
{
|
||||||
|
if (!actorData.mStandingOn.isEmpty())
|
||||||
|
mStandingCollisions[actorData.mPtr] = actorData.mStandingOn;
|
||||||
|
else
|
||||||
|
mStandingCollisions.erase(actorData.mPtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mMovementResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ActorFrameData> PhysicsSystem::prepareFrameData()
|
||||||
|
{
|
||||||
|
std::vector<ActorFrameData> actorsFrameData;
|
||||||
|
actorsFrameData.reserve(mMovementQueue.size());
|
||||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||||
for (const auto& m : mMovementQueue)
|
for (const auto& m : mMovementQueue)
|
||||||
{
|
{
|
||||||
|
|
@ -702,7 +796,10 @@ namespace MWPhysics
|
||||||
const auto& movement = m.second;
|
const auto& movement = m.second;
|
||||||
const auto foundActor = mActors.find(character);
|
const auto foundActor = mActors.find(character);
|
||||||
if (foundActor == mActors.end()) // actor was already removed from the scene
|
if (foundActor == mActors.end()) // actor was already removed from the scene
|
||||||
|
{
|
||||||
|
mStandingCollisions.erase(character);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
auto physicActor = foundActor->second;
|
auto physicActor = foundActor->second;
|
||||||
|
|
||||||
float waterlevel = -std::numeric_limits<float>::max();
|
float waterlevel = -std::numeric_limits<float>::max();
|
||||||
|
|
@ -713,56 +810,27 @@ namespace MWPhysics
|
||||||
const MWMechanics::MagicEffects& effects = character.getClass().getCreatureStats(character).getMagicEffects();
|
const MWMechanics::MagicEffects& effects = character.getClass().getCreatureStats(character).getMagicEffects();
|
||||||
|
|
||||||
bool waterCollision = false;
|
bool waterCollision = false;
|
||||||
|
bool moveToWaterSurface = false;
|
||||||
if (cell->getCell()->hasWater() && effects.get(ESM::MagicEffect::WaterWalking).getMagnitude())
|
if (cell->getCell()->hasWater() && effects.get(ESM::MagicEffect::WaterWalking).getMagnitude())
|
||||||
{
|
{
|
||||||
if (!world->isUnderwater(character.getCell(), osg::Vec3f(character.getRefData().getPosition().asVec3())))
|
if (!world->isUnderwater(character.getCell(), osg::Vec3f(character.getRefData().getPosition().asVec3())))
|
||||||
waterCollision = true;
|
waterCollision = true;
|
||||||
else if (physicActor->getCollisionMode() && canMoveToWaterSurface(character, waterlevel))
|
else if (physicActor->getCollisionMode() && canMoveToWaterSurface(character, waterlevel))
|
||||||
{
|
{
|
||||||
const osg::Vec3f actorPosition = physicActor->getPosition();
|
moveToWaterSurface = true;
|
||||||
physicActor->setPosition(osg::Vec3f(actorPosition.x(), actorPosition.y(), waterlevel));
|
|
||||||
waterCollision = true;
|
waterCollision = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
physicActor->setCanWaterWalk(waterCollision);
|
physicActor->setCanWaterWalk(waterCollision);
|
||||||
|
|
||||||
// Slow fall reduces fall speed by a factor of (effect magnitude / 200)
|
// Slow fall reduces fall speed by a factor of (effect magnitude / 200)
|
||||||
float slowFall = 1.f - std::max(0.f, std::min(1.f, effects.get(ESM::MagicEffect::SlowFall).getMagnitude() * 0.005f));
|
const float slowFall = 1.f - std::max(0.f, std::min(1.f, effects.get(ESM::MagicEffect::SlowFall).getMagnitude() * 0.005f));
|
||||||
|
|
||||||
bool flying = world->isFlying(character);
|
actorsFrameData.emplace_back(std::move(physicActor), character, mStandingCollisions[character], moveToWaterSurface, movement, slowFall, waterlevel);
|
||||||
bool swimming = world->isSwimming(character);
|
|
||||||
|
|
||||||
bool wasOnGround = physicActor->getOnGround();
|
|
||||||
osg::Vec3f position = physicActor->getPosition();
|
|
||||||
float oldHeight = position.z();
|
|
||||||
bool positionChanged = false;
|
|
||||||
for (int i=0; i<numSteps; ++i)
|
|
||||||
{
|
|
||||||
position = MovementSolver::move(position, physicActor->getPtr(), physicActor.get(), movement, mPhysicsDt,
|
|
||||||
flying, waterlevel, slowFall, mCollisionWorld.get(), mStandingCollisions);
|
|
||||||
if (position != physicActor->getPosition())
|
|
||||||
positionChanged = true;
|
|
||||||
physicActor->setPosition(position); // always set even if unchanged to make sure interpolation is correct
|
|
||||||
}
|
|
||||||
if (positionChanged)
|
|
||||||
mCollisionWorld->updateSingleAabb(physicActor->getCollisionObject());
|
|
||||||
|
|
||||||
float interpolationFactor = mTimeAccum / mPhysicsDt;
|
|
||||||
osg::Vec3f interpolated = position * interpolationFactor + physicActor->getPreviousPosition() * (1.f - interpolationFactor);
|
|
||||||
|
|
||||||
float heightDiff = position.z() - oldHeight;
|
|
||||||
|
|
||||||
MWMechanics::CreatureStats& stats = character.getClass().getCreatureStats(character);
|
|
||||||
bool isStillOnGround = (numSteps > 0 && wasOnGround && physicActor->getOnGround());
|
|
||||||
if (isStillOnGround || flying || swimming || slowFall < 1)
|
|
||||||
stats.land(character == player && (flying || swimming));
|
|
||||||
else if (heightDiff < 0)
|
|
||||||
stats.addToFallHeight(-heightDiff);
|
|
||||||
|
|
||||||
mMovementResults.emplace(character, interpolated);
|
|
||||||
}
|
}
|
||||||
mMovementQueue.clear();
|
mMovementQueue.clear();
|
||||||
return mMovementResults;
|
return actorsFrameData;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsSystem::stepSimulation()
|
void PhysicsSystem::stepSimulation()
|
||||||
|
|
@ -896,4 +964,61 @@ namespace MWPhysics
|
||||||
stats.setAttribute(frameNumber, "Physics Objects", mObjects.size());
|
stats.setAttribute(frameNumber, "Physics Objects", mObjects.size());
|
||||||
stats.setAttribute(frameNumber, "Physics HeightFields", mHeightFields.size());
|
stats.setAttribute(frameNumber, "Physics HeightFields", mHeightFields.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ActorFrameData::ActorFrameData(const std::shared_ptr<Actor>& 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),
|
||||||
|
mWaterlevel(waterlevel), mSlowFall(slowFall), mOldHeight(0), mFallHeight(0), mMovement(movement), mPosition(), mRefpos()
|
||||||
|
{
|
||||||
|
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||||
|
mPtr = actor->getPtr();
|
||||||
|
mFlying = world->isFlying(character);
|
||||||
|
mSwimming = world->isSwimming(character);
|
||||||
|
mWantJump = mPtr.getClass().getMovementSettings(mPtr).mPosition[2] != 0;
|
||||||
|
mIsDead = mPtr.getClass().getCreatureStats(mPtr).isDead();
|
||||||
|
mWasOnGround = actor->getOnGround();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActorFrameData::updatePosition()
|
||||||
|
{
|
||||||
|
mPosition = mActorRaw->getPosition();
|
||||||
|
if (mMoveToWaterSurface)
|
||||||
|
{
|
||||||
|
mPosition.z() = mWaterlevel;
|
||||||
|
mActorRaw->setPosition(mPosition);
|
||||||
|
}
|
||||||
|
mOldHeight = mPosition.z();
|
||||||
|
mRefpos = mPtr.getRefData().getPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldFrameData::WorldFrameData()
|
||||||
|
: mIsInStorm(MWBase::Environment::get().getWorld()->isInStorm())
|
||||||
|
, mStormDirection(MWBase::Environment::get().getWorld()->getStormDirection())
|
||||||
|
{}
|
||||||
|
|
||||||
|
LOSRequest::LOSRequest(const std::weak_ptr<Actor>& a1, const std::weak_ptr<Actor>& a2)
|
||||||
|
: mResult(false), mStale(false), mAge(0)
|
||||||
|
{
|
||||||
|
// we use raw actor pointer pair to uniquely identify request
|
||||||
|
// sort the pointer value in ascending order to not duplicate equivalent requests, eg. getLOS(A, B) and getLOS(B, A)
|
||||||
|
auto* raw1 = a1.lock().get();
|
||||||
|
auto* raw2 = a2.lock().get();
|
||||||
|
assert(raw1 != raw2);
|
||||||
|
if (raw1 < raw2)
|
||||||
|
{
|
||||||
|
mActors = {a1, a2};
|
||||||
|
mRawActors = {raw1, raw2};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mActors = {a2, a1};
|
||||||
|
mRawActors = {raw2, raw1};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const LOSRequest& lhs, const LOSRequest& rhs) noexcept
|
||||||
|
{
|
||||||
|
return lhs.mRawActors == rhs.mRawActors;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef OPENMW_MWPHYSICS_PHYSICSSYSTEM_H
|
#ifndef OPENMW_MWPHYSICS_PHYSICSSYSTEM_H
|
||||||
#define OPENMW_MWPHYSICS_PHYSICSSYSTEM_H
|
#define OPENMW_MWPHYSICS_PHYSICSSYSTEM_H
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
@ -53,6 +54,51 @@ namespace MWPhysics
|
||||||
class HeightField;
|
class HeightField;
|
||||||
class Object;
|
class Object;
|
||||||
class Actor;
|
class Actor;
|
||||||
|
class PhysicsTaskScheduler;
|
||||||
|
|
||||||
|
struct LOSRequest
|
||||||
|
{
|
||||||
|
LOSRequest(const std::weak_ptr<Actor>& a1, const std::weak_ptr<Actor>& a2);
|
||||||
|
std::array<std::weak_ptr<Actor>, 2> mActors;
|
||||||
|
std::array<const Actor*, 2> mRawActors;
|
||||||
|
bool mResult;
|
||||||
|
bool mStale;
|
||||||
|
int mAge;
|
||||||
|
};
|
||||||
|
bool operator==(const LOSRequest& lhs, const LOSRequest& rhs) noexcept;
|
||||||
|
|
||||||
|
struct ActorFrameData
|
||||||
|
{
|
||||||
|
ActorFrameData(const std::shared_ptr<Actor>& actor, const MWWorld::Ptr character, const MWWorld::Ptr standingOn, bool moveToWaterSurface, osg::Vec3f movement, float slowFall, float waterlevel);
|
||||||
|
void updatePosition();
|
||||||
|
std::weak_ptr<Actor> mActor;
|
||||||
|
Actor* mActorRaw;
|
||||||
|
MWWorld::Ptr mPtr;
|
||||||
|
MWWorld::Ptr mStandingOn;
|
||||||
|
bool mFlying;
|
||||||
|
bool mSwimming;
|
||||||
|
bool mPositionChanged;
|
||||||
|
bool mWasOnGround;
|
||||||
|
bool mWantJump;
|
||||||
|
bool mDidJump;
|
||||||
|
bool mIsDead;
|
||||||
|
bool mNeedLand;
|
||||||
|
bool mMoveToWaterSurface;
|
||||||
|
float mWaterlevel;
|
||||||
|
float mSlowFall;
|
||||||
|
float mOldHeight;
|
||||||
|
float mFallHeight;
|
||||||
|
osg::Vec3f mMovement;
|
||||||
|
osg::Vec3f mPosition;
|
||||||
|
ESM::Position mRefpos;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WorldFrameData
|
||||||
|
{
|
||||||
|
WorldFrameData();
|
||||||
|
bool mIsInStorm;
|
||||||
|
osg::Vec3f mStormDirection;
|
||||||
|
};
|
||||||
|
|
||||||
class PhysicsSystem : public RayCastingInterface
|
class PhysicsSystem : public RayCastingInterface
|
||||||
{
|
{
|
||||||
|
|
@ -191,6 +237,8 @@ namespace MWPhysics
|
||||||
|
|
||||||
void updateWater();
|
void updateWater();
|
||||||
|
|
||||||
|
std::vector<ActorFrameData> prepareFrameData();
|
||||||
|
|
||||||
osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;
|
osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;
|
||||||
|
|
||||||
std::unique_ptr<btBroadphaseInterface> mBroadphase;
|
std::unique_ptr<btBroadphaseInterface> mBroadphase;
|
||||||
|
|
@ -224,6 +272,8 @@ namespace MWPhysics
|
||||||
using PtrVelocityList = std::vector<std::pair<MWWorld::Ptr, osg::Vec3f>>;
|
using PtrVelocityList = std::vector<std::pair<MWWorld::Ptr, osg::Vec3f>>;
|
||||||
PtrVelocityList mMovementQueue;
|
PtrVelocityList mMovementQueue;
|
||||||
PtrPositionList mMovementResults;
|
PtrPositionList mMovementResults;
|
||||||
|
std::unique_ptr<WorldFrameData> mWorldFrameData;
|
||||||
|
std::vector<ActorFrameData> mActorsFrameData;
|
||||||
|
|
||||||
float mTimeAccum;
|
float mTimeAccum;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue