mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-19 22:53:50 +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/environment.hpp"
|
||||
|
||||
#include "../mwmechanics/actorutil.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwmechanics/movement.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/refdata.hpp"
|
||||
|
||||
#include "actor.hpp"
|
||||
#include "collisiontype.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "physicssystem.hpp"
|
||||
#include "stepper.hpp"
|
||||
#include "trace.h"
|
||||
|
||||
|
@ -78,24 +74,26 @@ namespace MWPhysics
|
|||
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,
|
||||
bool isFlying, float waterlevel, float slowFall, const btCollisionWorld* collisionWorld,
|
||||
std::map<MWWorld::Ptr, MWWorld::Ptr>& standingCollisionTracker)
|
||||
void MovementSolver::move(ActorFrameData& actor, float time, const btCollisionWorld* collisionWorld,
|
||||
WorldFrameData& worldData)
|
||||
{
|
||||
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
|
||||
// (Not sure if gravity should still apply?)
|
||||
if (!ptr.getClass().isMobile(ptr))
|
||||
return position;
|
||||
return;
|
||||
|
||||
// Reset per-frame data
|
||||
physicActor->setWalkingOnWater(false);
|
||||
// Anything to collide with?
|
||||
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))
|
||||
) * movement * time;
|
||||
) * actor.mMovement * time;
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
// other actors are using to collide against this actor.
|
||||
// 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();
|
||||
float swimlevel = waterlevel + halfExtents.z() - (physicActor->getRenderingHalfExtents().z() * 2 * fSwimHeightScale);
|
||||
float swimlevel = actor.mWaterlevel + halfExtents.z() - (physicActor->getRenderingHalfExtents().z() * 2 * fSwimHeightScale);
|
||||
|
||||
ActorTracer tracer;
|
||||
|
||||
osg::Vec3f inertia = physicActor->getInertialForce();
|
||||
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
|
||||
{
|
||||
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())
|
||||
|| (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
|
||||
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;
|
||||
|
||||
if (ptr.getClass().getMovementSettings(ptr).mPosition[2])
|
||||
{
|
||||
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;
|
||||
}
|
||||
if (actor.mWantJump)
|
||||
actor.mDidJump = true;
|
||||
|
||||
// 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())));
|
||||
static const float fStromWalkMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fStromWalkMult")->mValue.getFloat();
|
||||
velocity *= 1.f-(fStromWalkMult * (angleDegrees/180.f));
|
||||
|
@ -170,7 +146,7 @@ namespace MWPhysics
|
|||
|
||||
Stepper stepper(collisionWorld, colobj);
|
||||
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.
|
||||
* 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;
|
||||
|
||||
// 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);
|
||||
velocity = slide(velocity, down);
|
||||
|
@ -235,7 +211,7 @@ namespace MWPhysics
|
|||
if (result)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
else
|
||||
|
@ -245,7 +221,7 @@ namespace MWPhysics
|
|||
|
||||
// Do not allow sliding upward if there is gravity.
|
||||
// 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);
|
||||
|
||||
if ((newVelocity-velocity).length2() < 0.01)
|
||||
|
@ -269,11 +245,11 @@ namespace MWPhysics
|
|||
const btCollisionObject* standingOn = tracer.mHitObject;
|
||||
PtrHolder* ptrHolder = static_cast<PtrHolder*>(standingOn->getUserPointer());
|
||||
if (ptrHolder)
|
||||
standingCollisionTracker[ptr] = ptrHolder->getPtr();
|
||||
actor.mStandingOn = ptrHolder->getPtr();
|
||||
|
||||
if (standingOn->getBroadphaseHandle()->m_collisionFilterGroup == CollisionType_Water)
|
||||
physicActor->setWalkingOnWater(true);
|
||||
if (!isFlying)
|
||||
if (!actor.mFlying)
|
||||
newPosition.z() = tracer.mEndPos.z() + sGroundOffset;
|
||||
|
||||
isOnGround = true;
|
||||
|
@ -292,7 +268,7 @@ namespace MWPhysics
|
|||
btVector3 aabbMin, aabbMax;
|
||||
tracer.mHitObject->getCollisionShape()->getAabb(tracer.mHitObject->getWorldTransform(), aabbMin, aabbMax);
|
||||
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 *= 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));
|
||||
else
|
||||
{
|
||||
inertia.z() -= time * Constants::GravityConst * Constants::UnitsPerMeter;
|
||||
if (inertia.z() < 0)
|
||||
inertia.z() *= slowFall;
|
||||
if (slowFall < 1.f) {
|
||||
inertia.x() *= slowFall;
|
||||
inertia.y() *= slowFall;
|
||||
inertia.z() *= actor.mSlowFall;
|
||||
if (actor.mSlowFall < 1.f) {
|
||||
inertia.x() *= actor.mSlowFall;
|
||||
inertia.y() *= actor.mSlowFall;
|
||||
}
|
||||
physicActor->setInertialForce(inertia);
|
||||
}
|
||||
|
@ -319,6 +295,6 @@ namespace MWPhysics
|
|||
physicActor->setOnSlope(isOnSlope);
|
||||
|
||||
newPosition.z() -= halfExtents.z(); // remove what was added at the beginning
|
||||
return newPosition;
|
||||
actor.mPosition = newPosition;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,13 +5,18 @@
|
|||
|
||||
#include <osg/Vec3f>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
class btCollisionWorld;
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class Ptr;
|
||||
}
|
||||
|
||||
namespace MWPhysics
|
||||
{
|
||||
class Actor;
|
||||
struct ActorFrameData;
|
||||
struct WorldFrameData;
|
||||
|
||||
class MovementSolver
|
||||
{
|
||||
|
@ -31,9 +36,7 @@ namespace MWPhysics
|
|||
|
||||
public:
|
||||
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,
|
||||
bool isFlying, float waterlevel, float slowFall, const btCollisionWorld* collisionWorld,
|
||||
std::map<MWWorld::Ptr, MWWorld::Ptr>& standingCollisionTracker);
|
||||
static void move(ActorFrameData& actor, float time, const btCollisionWorld* collisionWorld, WorldFrameData& worldData);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -31,9 +31,11 @@
|
|||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwmechanics/actorutil.hpp"
|
||||
#include "../mwmechanics/movement.hpp"
|
||||
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
|
||||
#include "../mwrender/bulletdebugdraw.hpp"
|
||||
|
||||
|
@ -687,14 +689,106 @@ namespace MWPhysics
|
|||
numSteps = std::min(numSteps, maxAllowedSteps);
|
||||
|
||||
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
|
||||
mStandingCollisions.clear();
|
||||
for (auto& actorData : mActorsFrameData)
|
||||
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();
|
||||
for (const auto& m : mMovementQueue)
|
||||
{
|
||||
|
@ -702,7 +796,10 @@ namespace MWPhysics
|
|||
const auto& movement = m.second;
|
||||
const auto foundActor = mActors.find(character);
|
||||
if (foundActor == mActors.end()) // actor was already removed from the scene
|
||||
{
|
||||
mStandingCollisions.erase(character);
|
||||
continue;
|
||||
}
|
||||
auto physicActor = foundActor->second;
|
||||
|
||||
float waterlevel = -std::numeric_limits<float>::max();
|
||||
|
@ -713,56 +810,27 @@ namespace MWPhysics
|
|||
const MWMechanics::MagicEffects& effects = character.getClass().getCreatureStats(character).getMagicEffects();
|
||||
|
||||
bool waterCollision = false;
|
||||
bool moveToWaterSurface = false;
|
||||
if (cell->getCell()->hasWater() && effects.get(ESM::MagicEffect::WaterWalking).getMagnitude())
|
||||
{
|
||||
if (!world->isUnderwater(character.getCell(), osg::Vec3f(character.getRefData().getPosition().asVec3())))
|
||||
waterCollision = true;
|
||||
else if (physicActor->getCollisionMode() && canMoveToWaterSurface(character, waterlevel))
|
||||
{
|
||||
const osg::Vec3f actorPosition = physicActor->getPosition();
|
||||
physicActor->setPosition(osg::Vec3f(actorPosition.x(), actorPosition.y(), waterlevel));
|
||||
moveToWaterSurface = true;
|
||||
waterCollision = true;
|
||||
}
|
||||
}
|
||||
|
||||
physicActor->setCanWaterWalk(waterCollision);
|
||||
|
||||
// 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);
|
||||
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);
|
||||
actorsFrameData.emplace_back(std::move(physicActor), character, mStandingCollisions[character], moveToWaterSurface, movement, slowFall, waterlevel);
|
||||
}
|
||||
mMovementQueue.clear();
|
||||
return mMovementResults;
|
||||
return actorsFrameData;
|
||||
}
|
||||
|
||||
void PhysicsSystem::stepSimulation()
|
||||
|
@ -896,4 +964,61 @@ namespace MWPhysics
|
|||
stats.setAttribute(frameNumber, "Physics Objects", mObjects.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
|
||||
#define OPENMW_MWPHYSICS_PHYSICSSYSTEM_H
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
@ -53,6 +54,51 @@ namespace MWPhysics
|
|||
class HeightField;
|
||||
class Object;
|
||||
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
|
||||
{
|
||||
|
@ -191,6 +237,8 @@ namespace MWPhysics
|
|||
|
||||
void updateWater();
|
||||
|
||||
std::vector<ActorFrameData> prepareFrameData();
|
||||
|
||||
osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;
|
||||
|
||||
std::unique_ptr<btBroadphaseInterface> mBroadphase;
|
||||
|
@ -224,6 +272,8 @@ namespace MWPhysics
|
|||
using PtrVelocityList = std::vector<std::pair<MWWorld::Ptr, osg::Vec3f>>;
|
||||
PtrVelocityList mMovementQueue;
|
||||
PtrPositionList mMovementResults;
|
||||
std::unique_ptr<WorldFrameData> mWorldFrameData;
|
||||
std::vector<ActorFrameData> mActorsFrameData;
|
||||
|
||||
float mTimeAccum;
|
||||
|
||||
|
|
Loading…
Reference in a new issue