mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-20 15:40:32 +00:00
Merge branch 'backproj' into 'master'
Move the projectile simulation to the background thread See merge request OpenMW/openmw!1276
This commit is contained in:
commit
e3cfe5d35a
@ -12,6 +12,7 @@
|
||||
|
||||
#include "collisiontype.hpp"
|
||||
#include "mtphysics.hpp"
|
||||
#include "trace.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
@ -22,7 +23,7 @@ namespace MWPhysics
|
||||
Actor::Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, PhysicsTaskScheduler* scheduler, bool canWaterWalk)
|
||||
: mStandingOnPtr(nullptr), mCanWaterWalk(canWaterWalk), mWalkingOnWater(false)
|
||||
, mMeshTranslation(shape->mCollisionBox.center), mOriginalHalfExtents(shape->mCollisionBox.extents)
|
||||
, mVelocity(0,0,0), mStuckFrames(0), mLastStuckPosition{0, 0, 0}
|
||||
, mStuckFrames(0), mLastStuckPosition{0, 0, 0}
|
||||
, mForce(0.f, 0.f, 0.f), mOnGround(true), mOnSlope(false)
|
||||
, mInternalCollisionMode(true)
|
||||
, mExternalCollisionMode(true)
|
||||
@ -133,11 +134,6 @@ void Actor::setSimulationPosition(const osg::Vec3f& position)
|
||||
mSimulationPosition = position;
|
||||
}
|
||||
|
||||
osg::Vec3f Actor::getSimulationPosition() const
|
||||
{
|
||||
return mSimulationPosition;
|
||||
}
|
||||
|
||||
osg::Vec3f Actor::getScaledMeshTranslation() const
|
||||
{
|
||||
return mRotation * osg::componentMultiply(mMeshTranslation, mScale);
|
||||
@ -191,16 +187,6 @@ void Actor::applyOffsetChange()
|
||||
mWorldPositionChanged = true;
|
||||
}
|
||||
|
||||
osg::Vec3f Actor::getPosition() const
|
||||
{
|
||||
return mPosition;
|
||||
}
|
||||
|
||||
osg::Vec3f Actor::getPreviousPosition() const
|
||||
{
|
||||
return mPreviousPosition;
|
||||
}
|
||||
|
||||
void Actor::setRotation(osg::Quat quat)
|
||||
{
|
||||
std::scoped_lock lock(mPositionMutex);
|
||||
@ -293,14 +279,15 @@ bool Actor::skipCollisions()
|
||||
return std::exchange(mSkipCollisions, false);
|
||||
}
|
||||
|
||||
void Actor::setVelocity(osg::Vec3f velocity)
|
||||
bool Actor::canMoveToWaterSurface(float waterlevel, const btCollisionWorld* world) const
|
||||
{
|
||||
mVelocity = velocity;
|
||||
}
|
||||
|
||||
osg::Vec3f Actor::velocity()
|
||||
{
|
||||
return std::exchange(mVelocity, osg::Vec3f());
|
||||
const float halfZ = getHalfExtents().z();
|
||||
const osg::Vec3f actorPosition = getPosition();
|
||||
const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ);
|
||||
const osg::Vec3f destinationPosition(actorPosition.x(), actorPosition.y(), waterlevel + halfZ);
|
||||
MWPhysics::ActorTracer tracer;
|
||||
tracer.doTrace(getCollisionObject(), startingPosition, destinationPosition, world);
|
||||
return (tracer.mFraction >= 1.0f);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
class btCollisionShape;
|
||||
class btCollisionObject;
|
||||
class btCollisionWorld;
|
||||
class btConvexShape;
|
||||
|
||||
namespace Resource
|
||||
@ -59,7 +60,6 @@ namespace MWPhysics
|
||||
* to account for e.g. scripted movements
|
||||
*/
|
||||
void setSimulationPosition(const osg::Vec3f& position);
|
||||
osg::Vec3f getSimulationPosition() const;
|
||||
|
||||
void updateCollisionObjectPosition();
|
||||
|
||||
@ -94,10 +94,6 @@ namespace MWPhysics
|
||||
// apply position offset. Can't be called during simulation
|
||||
void applyOffsetChange();
|
||||
|
||||
osg::Vec3f getPosition() const;
|
||||
|
||||
osg::Vec3f getPreviousPosition() const;
|
||||
|
||||
/**
|
||||
* Returns the half extents of the collision body (scaled according to rendering scale)
|
||||
* @note The reason we need this extra method is because of an inconsistency in MW - NPC race scales aren't applied to the collision shape,
|
||||
@ -162,8 +158,7 @@ namespace MWPhysics
|
||||
|
||||
bool skipCollisions();
|
||||
|
||||
void setVelocity(osg::Vec3f velocity);
|
||||
osg::Vec3f velocity();
|
||||
bool canMoveToWaterSurface(float waterlevel, const btCollisionWorld* world) const;
|
||||
|
||||
private:
|
||||
MWWorld::Ptr mStandingOnPtr;
|
||||
@ -190,11 +185,7 @@ namespace MWPhysics
|
||||
osg::Quat mRotation;
|
||||
|
||||
osg::Vec3f mScale;
|
||||
osg::Vec3f mSimulationPosition;
|
||||
osg::Vec3f mPosition;
|
||||
osg::Vec3f mPreviousPosition;
|
||||
osg::Vec3f mPositionOffset;
|
||||
osg::Vec3f mVelocity;
|
||||
bool mWorldPositionChanged;
|
||||
bool mSkipCollisions;
|
||||
bool mSkipSimulation;
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <BulletCollision/CollisionDispatch/btCollisionObject.h>
|
||||
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
|
||||
#include <BulletCollision/CollisionShapes/btCollisionShape.h>
|
||||
#include <BulletCollision/CollisionShapes/btConvexShape.h>
|
||||
|
||||
#include <components/esm/loadgmst.hpp>
|
||||
#include <components/misc/convert.hpp>
|
||||
@ -19,6 +20,8 @@
|
||||
#include "constants.hpp"
|
||||
#include "contacttestwrapper.h"
|
||||
#include "physicssystem.hpp"
|
||||
#include "projectile.hpp"
|
||||
#include "projectileconvexcallback.hpp"
|
||||
#include "stepper.hpp"
|
||||
#include "trace.h"
|
||||
|
||||
@ -116,7 +119,7 @@ namespace MWPhysics
|
||||
}
|
||||
|
||||
void MovementSolver::move(ActorFrameData& actor, float time, const btCollisionWorld* collisionWorld,
|
||||
WorldFrameData& worldData)
|
||||
const WorldFrameData& worldData)
|
||||
{
|
||||
// Reset per-frame data
|
||||
actor.mWalkingOnWater = false;
|
||||
@ -398,6 +401,29 @@ namespace MWPhysics
|
||||
actor.mPosition.z() -= actor.mHalfExtentsZ; // vanilla-accurate
|
||||
}
|
||||
|
||||
void MovementSolver::move(ProjectileFrameData& projectile, float time, const btCollisionWorld* collisionWorld)
|
||||
{
|
||||
btVector3 btFrom = Misc::Convert::toBullet(projectile.mPosition);
|
||||
btVector3 btTo = Misc::Convert::toBullet(projectile.mPosition + projectile.mMovement * time);
|
||||
|
||||
if (btFrom == btTo)
|
||||
return;
|
||||
|
||||
ProjectileConvexCallback resultCallback(projectile.mCaster, projectile.mCollisionObject, btFrom, btTo, projectile.mProjectile);
|
||||
resultCallback.m_collisionFilterMask = 0xff;
|
||||
resultCallback.m_collisionFilterGroup = CollisionType_Projectile;
|
||||
|
||||
const btQuaternion btrot = btQuaternion::getIdentity();
|
||||
btTransform from_ (btrot, btFrom);
|
||||
btTransform to_ (btrot, btTo);
|
||||
|
||||
const btCollisionShape* shape = projectile.mCollisionObject->getCollisionShape();
|
||||
assert(shape->isConvex());
|
||||
collisionWorld->convexSweepTest(static_cast<const btConvexShape*>(shape), from_, to_, resultCallback);
|
||||
|
||||
projectile.mPosition = Misc::Convert::toOsg(projectile.mProjectile->isActive() ? btTo : resultCallback.m_hitPointWorld);
|
||||
}
|
||||
|
||||
btVector3 addMarginToDelta(btVector3 delta)
|
||||
{
|
||||
if(delta.length2() == 0.0)
|
||||
|
@ -37,13 +37,15 @@ namespace MWPhysics
|
||||
|
||||
class Actor;
|
||||
struct ActorFrameData;
|
||||
struct ProjectileFrameData;
|
||||
struct WorldFrameData;
|
||||
|
||||
class MovementSolver
|
||||
{
|
||||
public:
|
||||
static osg::Vec3f traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, Actor* actor, btCollisionWorld* collisionWorld, float maxHeight);
|
||||
static void move(ActorFrameData& actor, float time, const btCollisionWorld* collisionWorld, WorldFrameData& worldData);
|
||||
static void move(ActorFrameData& actor, float time, const btCollisionWorld* collisionWorld, const WorldFrameData& worldData);
|
||||
static void move(ProjectileFrameData& projectile, float time, const btCollisionWorld* collisionWorld);
|
||||
static void unstuck(ActorFrameData& actor, const btCollisionWorld* collisionWorld);
|
||||
};
|
||||
}
|
||||
|
@ -105,10 +105,132 @@ namespace
|
||||
return actorData.mPosition.z() < actorData.mSwimLevel;
|
||||
}
|
||||
|
||||
osg::Vec3f interpolateMovements(MWPhysics::Actor& actor, MWPhysics::ActorFrameData& actorData, float timeAccum, float physicsDt)
|
||||
osg::Vec3f interpolateMovements(const MWPhysics::PtrHolder& ptr, float timeAccum, float physicsDt)
|
||||
{
|
||||
const float interpolationFactor = std::clamp(timeAccum / physicsDt, 0.0f, 1.0f);
|
||||
return actorData.mPosition * interpolationFactor + actor.getPreviousPosition() * (1.f - interpolationFactor);
|
||||
return ptr.getPosition() * interpolationFactor + ptr.getPreviousPosition() * (1.f - interpolationFactor);
|
||||
}
|
||||
|
||||
namespace Visitors
|
||||
{
|
||||
struct InitPosition
|
||||
{
|
||||
const btCollisionWorld* mCollisionWorld;
|
||||
void operator()(MWPhysics::ActorSimulation& sim) const
|
||||
{
|
||||
auto& [actor, frameData] = sim;
|
||||
actor->applyOffsetChange();
|
||||
frameData.mPosition = actor->getPosition();
|
||||
if (frameData.mWaterCollision && frameData.mPosition.z() < frameData.mWaterlevel && actor->canMoveToWaterSurface(frameData.mWaterlevel, mCollisionWorld))
|
||||
{
|
||||
frameData.mPosition.z() = frameData.mWaterlevel;
|
||||
MWBase::Environment::get().getWorld()->moveObject(actor->getPtr(), frameData.mPosition, false);
|
||||
}
|
||||
frameData.mOldHeight = frameData.mPosition.z();
|
||||
const auto rotation = actor->getPtr().getRefData().getPosition().asRotationVec3();
|
||||
frameData.mRotation = osg::Vec2f(rotation.x(), rotation.z());
|
||||
frameData.mInertia = actor->getInertialForce();
|
||||
frameData.mStuckFrames = actor->getStuckFrames();
|
||||
frameData.mLastStuckPosition = actor->getLastStuckPosition();
|
||||
}
|
||||
void operator()(MWPhysics::ProjectileSimulation& sim) const
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct PreStep
|
||||
{
|
||||
btCollisionWorld* mCollisionWorld;
|
||||
void operator()(MWPhysics::ActorSimulation& sim) const
|
||||
{
|
||||
MWPhysics::MovementSolver::unstuck(sim.second, mCollisionWorld);
|
||||
}
|
||||
void operator()(MWPhysics::ProjectileSimulation& sim) const
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct UpdatePosition
|
||||
{
|
||||
btCollisionWorld* mCollisionWorld;
|
||||
void operator()(MWPhysics::ActorSimulation& sim) const
|
||||
{
|
||||
auto& [actor, frameData] = sim;
|
||||
if (actor->setPosition(frameData.mPosition))
|
||||
{
|
||||
frameData.mPosition = actor->getPosition(); // account for potential position change made by script
|
||||
actor->updateCollisionObjectPosition();
|
||||
mCollisionWorld->updateSingleAabb(actor->getCollisionObject());
|
||||
}
|
||||
}
|
||||
void operator()(MWPhysics::ProjectileSimulation& sim) const
|
||||
{
|
||||
auto& [proj, frameData] = sim;
|
||||
proj->setPosition(frameData.mPosition);
|
||||
proj->updateCollisionObjectPosition();
|
||||
mCollisionWorld->updateSingleAabb(proj->getCollisionObject());
|
||||
}
|
||||
};
|
||||
|
||||
struct Move
|
||||
{
|
||||
const float mPhysicsDt;
|
||||
const btCollisionWorld* mCollisionWorld;
|
||||
const MWPhysics::WorldFrameData& mWorldFrameData;
|
||||
void operator()(MWPhysics::ActorSimulation& sim) const
|
||||
{
|
||||
MWPhysics::MovementSolver::move(sim.second, mPhysicsDt, mCollisionWorld, mWorldFrameData);
|
||||
}
|
||||
void operator()(MWPhysics::ProjectileSimulation& sim) const
|
||||
{
|
||||
MWPhysics::MovementSolver::move(sim.second, mPhysicsDt, mCollisionWorld);
|
||||
}
|
||||
};
|
||||
|
||||
struct Sync
|
||||
{
|
||||
const bool mAdvanceSimulation;
|
||||
const float mTimeAccum;
|
||||
const float mPhysicsDt;
|
||||
const MWPhysics::PhysicsTaskScheduler* scheduler;
|
||||
void operator()(MWPhysics::ActorSimulation& sim) const
|
||||
{
|
||||
auto& [actor, frameData] = sim;
|
||||
auto ptr = actor->getPtr();
|
||||
|
||||
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
|
||||
const float heightDiff = frameData.mPosition.z() - frameData.mOldHeight;
|
||||
const bool isStillOnGround = (mAdvanceSimulation && frameData.mWasOnGround && frameData.mIsOnGround);
|
||||
|
||||
if (isStillOnGround || frameData.mFlying || isUnderWater(frameData) || frameData.mSlowFall < 1)
|
||||
stats.land(ptr == MWMechanics::getPlayer() && (frameData.mFlying || isUnderWater(frameData)));
|
||||
else if (heightDiff < 0)
|
||||
stats.addToFallHeight(-heightDiff);
|
||||
|
||||
actor->setSimulationPosition(::interpolateMovements(*actor, mTimeAccum, mPhysicsDt));
|
||||
actor->setLastStuckPosition(frameData.mLastStuckPosition);
|
||||
actor->setStuckFrames(frameData.mStuckFrames);
|
||||
if (mAdvanceSimulation)
|
||||
{
|
||||
MWWorld::Ptr standingOn;
|
||||
auto* ptrHolder = static_cast<MWPhysics::PtrHolder*>(scheduler->getUserPointer(frameData.mStandingOn));
|
||||
if (ptrHolder)
|
||||
standingOn = ptrHolder->getPtr();
|
||||
actor->setStandingOnPtr(standingOn);
|
||||
// the "on ground" state of an actor might have been updated by a traceDown, don't overwrite the change
|
||||
if (actor->getOnGround() == frameData.mWasOnGround)
|
||||
actor->setOnGround(frameData.mIsOnGround);
|
||||
actor->setOnSlope(frameData.mIsOnSlope);
|
||||
actor->setWalkingOnWater(frameData.mWalkingOnWater);
|
||||
actor->setInertialForce(frameData.mInertia);
|
||||
}
|
||||
}
|
||||
void operator()(MWPhysics::ProjectileSimulation& sim) const
|
||||
{
|
||||
auto& [proj, frameData] = sim;
|
||||
proj->setSimulationPosition(::interpolateMovements(*proj, mTimeAccum, mPhysicsDt));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace Config
|
||||
@ -235,13 +357,12 @@ namespace MWPhysics
|
||||
return std::make_tuple(numSteps, actualDelta);
|
||||
}
|
||||
|
||||
void PhysicsTaskScheduler::applyQueuedMovements(float & timeAccum, std::vector<std::shared_ptr<Actor>>&& actors, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
|
||||
void PhysicsTaskScheduler::applyQueuedMovements(float & timeAccum, std::vector<Simulation>&& simulations, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
|
||||
{
|
||||
// This function run in the main thread.
|
||||
// While the mSimulationMutex is held, background physics threads can't run.
|
||||
|
||||
MaybeExclusiveLock lock(mSimulationMutex, mNumThreads);
|
||||
assert(actors.size() == actorsData.size());
|
||||
|
||||
double timeStart = mTimer->tick();
|
||||
|
||||
@ -259,19 +380,19 @@ namespace MWPhysics
|
||||
timeAccum -= numSteps*newDelta;
|
||||
|
||||
// init
|
||||
for (size_t i = 0; i < actors.size(); ++i)
|
||||
const Visitors::InitPosition vis{mCollisionWorld};
|
||||
for (auto& sim : simulations)
|
||||
{
|
||||
actorsData[i].updatePosition(*actors[i], mCollisionWorld);
|
||||
std::visit(vis, sim);
|
||||
}
|
||||
mPrevStepCount = numSteps;
|
||||
mRemainingSteps = numSteps;
|
||||
mTimeAccum = timeAccum;
|
||||
mPhysicsDt = newDelta;
|
||||
mActors = std::move(actors);
|
||||
mActorsFrameData = std::move(actorsData);
|
||||
mSimulations = std::move(simulations);
|
||||
mAdvanceSimulation = (mRemainingSteps != 0);
|
||||
mNewFrame = true;
|
||||
mNumJobs = mActorsFrameData.size();
|
||||
mNumJobs = mSimulations.size();
|
||||
mNextLOS.store(0, std::memory_order_relaxed);
|
||||
mNextJob.store(0, std::memory_order_release);
|
||||
|
||||
@ -301,8 +422,7 @@ namespace MWPhysics
|
||||
MaybeExclusiveLock lock(mSimulationMutex, mNumThreads);
|
||||
mBudget.reset(mDefaultPhysicsDt);
|
||||
mAsyncBudget.reset(0.0f);
|
||||
mActors.clear();
|
||||
mActorsFrameData.clear();
|
||||
mSimulations.clear();
|
||||
for (const auto& [_, actor] : actors)
|
||||
{
|
||||
actor->updatePosition();
|
||||
@ -448,7 +568,7 @@ namespace MWPhysics
|
||||
}
|
||||
else if (const auto projectile = std::dynamic_pointer_cast<Projectile>(ptr))
|
||||
{
|
||||
projectile->commitPositionChange();
|
||||
projectile->updateCollisionObjectPosition();
|
||||
mCollisionWorld->updateSingleAabb(projectile->getCollisionObject());
|
||||
}
|
||||
}
|
||||
@ -467,47 +587,11 @@ namespace MWPhysics
|
||||
|
||||
void PhysicsTaskScheduler::updateActorsPositions()
|
||||
{
|
||||
for (size_t i = 0; i < mActors.size(); ++i)
|
||||
const Visitors::UpdatePosition vis{mCollisionWorld};
|
||||
for (auto& sim : mSimulations)
|
||||
{
|
||||
if (mActors[i]->setPosition(mActorsFrameData[i].mPosition))
|
||||
{
|
||||
MaybeExclusiveLock lock(mCollisionWorldMutex, mNumThreads);
|
||||
mActorsFrameData[i].mPosition = mActors[i]->getPosition(); // account for potential position change made by script
|
||||
mActors[i]->updateCollisionObjectPosition();
|
||||
mCollisionWorld->updateSingleAabb(mActors[i]->getCollisionObject());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsTaskScheduler::updateActor(Actor& actor, ActorFrameData& actorData, bool simulationPerformed, float timeAccum, float dt) const
|
||||
{
|
||||
auto ptr = actor.getPtr();
|
||||
|
||||
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
|
||||
const float heightDiff = actorData.mPosition.z() - actorData.mOldHeight;
|
||||
const bool isStillOnGround = (simulationPerformed && actorData.mWasOnGround && actorData.mIsOnGround);
|
||||
|
||||
if (isStillOnGround || actorData.mFlying || isUnderWater(actorData) || actorData.mSlowFall < 1)
|
||||
stats.land(ptr == MWMechanics::getPlayer() && (actorData.mFlying || isUnderWater(actorData)));
|
||||
else if (heightDiff < 0)
|
||||
stats.addToFallHeight(-heightDiff);
|
||||
|
||||
actor.setSimulationPosition(interpolateMovements(actor, actorData, timeAccum, dt));
|
||||
actor.setLastStuckPosition(actorData.mLastStuckPosition);
|
||||
actor.setStuckFrames(actorData.mStuckFrames);
|
||||
if (simulationPerformed)
|
||||
{
|
||||
MWWorld::Ptr standingOn;
|
||||
auto* ptrHolder = static_cast<MWPhysics::PtrHolder*>(getUserPointer(actorData.mStandingOn));
|
||||
if (ptrHolder)
|
||||
standingOn = ptrHolder->getPtr();
|
||||
actor.setStandingOnPtr(standingOn);
|
||||
// the "on ground" state of an actor might have been updated by a traceDown, don't overwrite the change
|
||||
if (actor.getOnGround() == actorData.mWasOnGround)
|
||||
actor.setOnGround(actorData.mIsOnGround);
|
||||
actor.setOnSlope(actorData.mIsOnSlope);
|
||||
actor.setWalkingOnWater(actorData.mWalkingOnWater);
|
||||
actor.setInertialForce(actorData.mInertia);
|
||||
MaybeExclusiveLock lock(mCollisionWorldMutex, mNumThreads);
|
||||
std::visit(vis, sim);
|
||||
}
|
||||
}
|
||||
|
||||
@ -532,10 +616,11 @@ namespace MWPhysics
|
||||
{
|
||||
mPreStepBarrier->wait([this] { afterPreStep(); });
|
||||
int job = 0;
|
||||
const Visitors::Move vis{mPhysicsDt, mCollisionWorld, *mWorldFrameData};
|
||||
while ((job = mNextJob.fetch_add(1, std::memory_order_relaxed)) < mNumJobs)
|
||||
{
|
||||
MaybeLock lockColWorld(mCollisionWorldMutex, mNumThreads);
|
||||
MovementSolver::move(mActorsFrameData[job], mPhysicsDt, mCollisionWorld, *mWorldFrameData);
|
||||
std::visit(vis, mSimulations[job]);
|
||||
}
|
||||
|
||||
mPostStepBarrier->wait([this] { afterPostStep(); });
|
||||
@ -577,7 +662,7 @@ namespace MWPhysics
|
||||
void PhysicsTaskScheduler::releaseSharedStates()
|
||||
{
|
||||
std::scoped_lock lock(mSimulationMutex, mUpdateAabbMutex);
|
||||
mActors.clear();
|
||||
mSimulations.clear();
|
||||
mUpdateAabb.clear();
|
||||
}
|
||||
|
||||
@ -586,10 +671,11 @@ namespace MWPhysics
|
||||
updateAabbs();
|
||||
if (!mRemainingSteps)
|
||||
return;
|
||||
for (size_t i = 0; i < mActors.size(); ++i)
|
||||
const Visitors::PreStep vis{mCollisionWorld};
|
||||
for (auto& sim : mSimulations)
|
||||
{
|
||||
MaybeExclusiveLock lock(mCollisionWorldMutex, mNumThreads);
|
||||
MovementSolver::unstuck(mActorsFrameData[i], mCollisionWorld);
|
||||
std::visit(vis, sim);
|
||||
}
|
||||
}
|
||||
|
||||
@ -618,7 +704,8 @@ namespace MWPhysics
|
||||
|
||||
void PhysicsTaskScheduler::syncWithMainThread()
|
||||
{
|
||||
for (size_t i = 0; i < mActors.size(); ++i)
|
||||
updateActor(*mActors[i], mActorsFrameData[i], mAdvanceSimulation, mTimeAccum, mPhysicsDt);
|
||||
const Visitors::Sync vis{mAdvanceSimulation, mTimeAccum, mPhysicsDt, this};
|
||||
for (auto& sim : mSimulations)
|
||||
std::visit(vis, sim);
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <shared_mutex>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
#include <variant>
|
||||
|
||||
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
|
||||
|
||||
@ -39,7 +40,7 @@ namespace MWPhysics
|
||||
/// @param timeAccum accumulated time from previous run to interpolate movements
|
||||
/// @param actorsData per actor data needed to compute new positions
|
||||
/// @return new position of each actor
|
||||
void applyQueuedMovements(float & timeAccum, std::vector<std::shared_ptr<Actor>>&& actors, std::vector<ActorFrameData>&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);
|
||||
void applyQueuedMovements(float & timeAccum, std::vector<Simulation>&& simulations, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats);
|
||||
|
||||
void resetSimulation(const ActorMap& actors);
|
||||
|
||||
@ -57,14 +58,12 @@ namespace MWPhysics
|
||||
bool getLineOfSight(const std::shared_ptr<Actor>& actor1, const std::shared_ptr<Actor>& actor2);
|
||||
void debugDraw();
|
||||
void* getUserPointer(const btCollisionObject* object) const;
|
||||
|
||||
void releaseSharedStates(); // destroy all objects whose destructor can't be safely called from ~PhysicsTaskScheduler()
|
||||
|
||||
private:
|
||||
void doSimulation();
|
||||
void worker();
|
||||
void updateActorsPositions();
|
||||
void updateActor(Actor& actor, ActorFrameData& actorData, bool simulationPerformed, float timeAccum, float dt) const;
|
||||
bool hasLineOfSight(const Actor* actor1, const Actor* actor2);
|
||||
void refreshLOSCache();
|
||||
void updateAabbs();
|
||||
@ -77,8 +76,7 @@ namespace MWPhysics
|
||||
void syncWithMainThread();
|
||||
|
||||
std::unique_ptr<WorldFrameData> mWorldFrameData;
|
||||
std::vector<std::shared_ptr<Actor>> mActors;
|
||||
std::vector<ActorFrameData> mActorsFrameData;
|
||||
std::vector<Simulation> mSimulations;
|
||||
std::unordered_set<const btCollisionObject*> mCollisionObjects;
|
||||
float mDefaultPhysicsDt;
|
||||
float mPhysicsDt;
|
||||
|
@ -60,19 +60,6 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
bool canMoveToWaterSurface(const MWPhysics::Actor* physicActor, const float waterlevel, btCollisionWorld* world)
|
||||
{
|
||||
if (!physicActor)
|
||||
return false;
|
||||
const float halfZ = physicActor->getHalfExtents().z();
|
||||
const osg::Vec3f actorPosition = physicActor->getPosition();
|
||||
const osg::Vec3f startingPosition(actorPosition.x(), actorPosition.y(), actorPosition.z() + halfZ);
|
||||
const osg::Vec3f destinationPosition(actorPosition.x(), actorPosition.y(), waterlevel + halfZ);
|
||||
MWPhysics::ActorTracer tracer;
|
||||
tracer.doTrace(physicActor->getCollisionObject(), startingPosition, destinationPosition, world);
|
||||
return (tracer.mFraction >= 1.0f);
|
||||
}
|
||||
|
||||
void handleJump(const MWWorld::Ptr &ptr)
|
||||
{
|
||||
if (!ptr.getClass().isActor())
|
||||
@ -388,7 +375,8 @@ namespace MWPhysics
|
||||
|
||||
bool PhysicsSystem::canMoveToWaterSurface(const MWWorld::ConstPtr &actor, const float waterlevel)
|
||||
{
|
||||
return ::canMoveToWaterSurface(getActor(actor), waterlevel, mCollisionWorld.get());
|
||||
const auto* physactor = getActor(actor);
|
||||
return physactor && physactor->canMoveToWaterSurface(waterlevel, mCollisionWorld.get());
|
||||
}
|
||||
|
||||
osg::Vec3f PhysicsSystem::getHalfExtents(const MWWorld::ConstPtr &actor) const
|
||||
@ -594,33 +582,6 @@ namespace MWPhysics
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsSystem::updateProjectile(const int projectileId, const osg::Vec3f &position) const
|
||||
{
|
||||
const auto foundProjectile = mProjectiles.find(projectileId);
|
||||
assert(foundProjectile != mProjectiles.end());
|
||||
auto* projectile = foundProjectile->second.get();
|
||||
|
||||
btVector3 btFrom = Misc::Convert::toBullet(projectile->getPosition());
|
||||
btVector3 btTo = Misc::Convert::toBullet(position);
|
||||
|
||||
if (btFrom == btTo)
|
||||
return;
|
||||
|
||||
ProjectileConvexCallback resultCallback(projectile->getCasterCollisionObject(), projectile->getCollisionObject(), btFrom, btTo, projectile);
|
||||
resultCallback.m_collisionFilterMask = 0xff;
|
||||
resultCallback.m_collisionFilterGroup = CollisionType_Projectile;
|
||||
|
||||
const btQuaternion btrot = btQuaternion::getIdentity();
|
||||
btTransform from_ (btrot, btFrom);
|
||||
btTransform to_ (btrot, btTo);
|
||||
|
||||
mTaskScheduler->convexSweepTest(projectile->getConvexShape(), from_, to_, resultCallback);
|
||||
|
||||
const auto newpos = projectile->isActive() ? position : Misc::Convert::toOsg(projectile->getHitPosition());
|
||||
projectile->setPosition(newpos);
|
||||
mTaskScheduler->updateSingleAabb(foundProjectile->second);
|
||||
}
|
||||
|
||||
void PhysicsSystem::updateRotation(const MWWorld::Ptr &ptr, osg::Quat rotate)
|
||||
{
|
||||
if (auto foundObject = mObjects.find(ptr.mRef); foundObject != mObjects.end())
|
||||
@ -729,11 +690,10 @@ namespace MWPhysics
|
||||
actor->setVelocity(osg::Vec3f());
|
||||
}
|
||||
|
||||
std::pair<std::vector<std::shared_ptr<Actor>>, std::vector<ActorFrameData>> PhysicsSystem::prepareFrameData(bool willSimulate)
|
||||
std::vector<Simulation> PhysicsSystem::prepareSimulation(bool willSimulate)
|
||||
{
|
||||
std::pair<std::vector<std::shared_ptr<Actor>>, std::vector<ActorFrameData>> framedata;
|
||||
framedata.first.reserve(mActors.size());
|
||||
framedata.second.reserve(mActors.size());
|
||||
std::vector<Simulation> simulations;
|
||||
simulations.reserve(mActors.size() + mProjectiles.size());
|
||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
for (const auto& [ref, physicActor] : mActors)
|
||||
{
|
||||
@ -762,14 +722,19 @@ namespace MWPhysics
|
||||
const bool godmode = ptr == world->getPlayerConstPtr() && world->getGodModeState();
|
||||
const bool inert = stats.isDead() || (!godmode && stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getModifier() > 0);
|
||||
|
||||
framedata.first.emplace_back(physicActor);
|
||||
framedata.second.emplace_back(*physicActor, inert, waterCollision, slowFall, waterlevel);
|
||||
simulations.emplace_back(ActorSimulation{physicActor, ActorFrameData{*physicActor, inert, waterCollision, slowFall, waterlevel}});
|
||||
|
||||
// if the simulation will run, a jump request will be fulfilled. Update mechanics accordingly.
|
||||
if (willSimulate)
|
||||
handleJump(ptr);
|
||||
}
|
||||
return framedata;
|
||||
|
||||
for (const auto& [id, projectile] : mProjectiles)
|
||||
{
|
||||
simulations.emplace_back(ProjectileSimulation{projectile, ProjectileFrameData{*projectile}});
|
||||
}
|
||||
|
||||
return simulations;
|
||||
}
|
||||
|
||||
void PhysicsSystem::stepSimulation(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats)
|
||||
@ -795,9 +760,9 @@ namespace MWPhysics
|
||||
mTaskScheduler->resetSimulation(mActors);
|
||||
else
|
||||
{
|
||||
auto [actors, framedata] = prepareFrameData(mTimeAccum >= mPhysicsDt);
|
||||
auto simulations = prepareSimulation(mTimeAccum >= mPhysicsDt);
|
||||
// modifies mTimeAccum
|
||||
mTaskScheduler->applyQueuedMovements(mTimeAccum, std::move(actors), std::move(framedata), frameStart, frameNumber, stats);
|
||||
mTaskScheduler->applyQueuedMovements(mTimeAccum, std::move(simulations), frameStart, frameNumber, stats);
|
||||
}
|
||||
}
|
||||
|
||||
@ -984,7 +949,7 @@ namespace MWPhysics
|
||||
{
|
||||
actor.applyOffsetChange();
|
||||
mPosition = actor.getPosition();
|
||||
if (mWaterCollision && mPosition.z() < mWaterlevel && canMoveToWaterSurface(&actor, mWaterlevel, world))
|
||||
if (mWaterCollision && mPosition.z() < mWaterlevel && actor.canMoveToWaterSurface(mWaterlevel, world))
|
||||
{
|
||||
mPosition.z() = mWaterlevel;
|
||||
MWBase::Environment::get().getWorld()->moveObject(actor.getPtr(), mPosition, false);
|
||||
@ -997,6 +962,15 @@ namespace MWPhysics
|
||||
mLastStuckPosition = actor.getLastStuckPosition();
|
||||
}
|
||||
|
||||
ProjectileFrameData::ProjectileFrameData(Projectile& projectile)
|
||||
: mPosition(projectile.getPosition())
|
||||
, mMovement(projectile.velocity())
|
||||
, mCaster(projectile.getCasterCollisionObject())
|
||||
, mCollisionObject(projectile.getCollisionObject())
|
||||
, mProjectile(&projectile)
|
||||
{
|
||||
}
|
||||
|
||||
WorldFrameData::WorldFrameData()
|
||||
: mIsInStorm(MWBase::Environment::get().getWorld()->isInStorm())
|
||||
, mStormDirection(MWBase::Environment::get().getWorld()->getStormDirection())
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <variant>
|
||||
|
||||
#include <osg/Quat>
|
||||
#include <osg/BoundingBox>
|
||||
@ -100,6 +101,16 @@ namespace MWPhysics
|
||||
const bool mSkipCollisionDetection;
|
||||
};
|
||||
|
||||
struct ProjectileFrameData
|
||||
{
|
||||
explicit ProjectileFrameData(Projectile& projectile);
|
||||
osg::Vec3f mPosition;
|
||||
osg::Vec3f mMovement;
|
||||
const btCollisionObject* mCaster;
|
||||
const btCollisionObject* mCollisionObject;
|
||||
Projectile* mProjectile;
|
||||
};
|
||||
|
||||
struct WorldFrameData
|
||||
{
|
||||
WorldFrameData();
|
||||
@ -107,6 +118,10 @@ namespace MWPhysics
|
||||
osg::Vec3f mStormDirection;
|
||||
};
|
||||
|
||||
using ActorSimulation = std::pair<std::shared_ptr<Actor>, ActorFrameData>;
|
||||
using ProjectileSimulation = std::pair<std::shared_ptr<Projectile>, ProjectileFrameData>;
|
||||
using Simulation = std::variant<ActorSimulation, ProjectileSimulation>;
|
||||
|
||||
class PhysicsSystem : public RayCastingInterface
|
||||
{
|
||||
public:
|
||||
@ -124,7 +139,6 @@ namespace MWPhysics
|
||||
|
||||
int addProjectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, const std::string& mesh, bool computeRadius);
|
||||
void setCaster(int projectileId, const MWWorld::Ptr& caster);
|
||||
void updateProjectile(const int projectileId, const osg::Vec3f &position) const;
|
||||
void removeProjectile(const int projectileId);
|
||||
|
||||
void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated);
|
||||
@ -253,7 +267,7 @@ namespace MWPhysics
|
||||
|
||||
void updateWater();
|
||||
|
||||
std::pair<std::vector<std::shared_ptr<Actor>>, std::vector<ActorFrameData>> prepareFrameData(bool willSimulate);
|
||||
std::vector<Simulation> prepareSimulation(bool willSimulate);
|
||||
|
||||
std::unique_ptr<btBroadphaseInterface> mBroadphase;
|
||||
std::unique_ptr<btDefaultCollisionConfiguration> mCollisionConfiguration;
|
||||
|
@ -31,14 +31,15 @@ Projectile::Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, f
|
||||
mCollisionObject->setCollisionShape(mShape.get());
|
||||
mCollisionObject->setUserPointer(this);
|
||||
|
||||
setPosition(position);
|
||||
mPosition = position;
|
||||
mPreviousPosition = position;
|
||||
setCaster(caster);
|
||||
|
||||
const int collisionMask = CollisionType_World | CollisionType_HeightMap |
|
||||
CollisionType_Actor | CollisionType_Door | CollisionType_Water | CollisionType_Projectile;
|
||||
mTaskScheduler->addCollisionObject(mCollisionObject.get(), CollisionType_Projectile, collisionMask);
|
||||
|
||||
commitPositionChange();
|
||||
updateCollisionObjectPosition();
|
||||
}
|
||||
|
||||
Projectile::~Projectile()
|
||||
@ -48,29 +49,12 @@ Projectile::~Projectile()
|
||||
mTaskScheduler->removeCollisionObject(mCollisionObject.get());
|
||||
}
|
||||
|
||||
void Projectile::commitPositionChange()
|
||||
void Projectile::updateCollisionObjectPosition()
|
||||
{
|
||||
std::scoped_lock lock(mMutex);
|
||||
if (mTransformUpdatePending)
|
||||
{
|
||||
auto& trans = mCollisionObject->getWorldTransform();
|
||||
trans.setOrigin(Misc::Convert::toBullet(mPosition));
|
||||
mCollisionObject->setWorldTransform(trans);
|
||||
mTransformUpdatePending = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Projectile::setPosition(const osg::Vec3f &position)
|
||||
{
|
||||
std::scoped_lock lock(mMutex);
|
||||
mPosition = position;
|
||||
mTransformUpdatePending = true;
|
||||
}
|
||||
|
||||
osg::Vec3f Projectile::getPosition() const
|
||||
{
|
||||
std::scoped_lock lock(mMutex);
|
||||
return mPosition;
|
||||
auto& trans = mCollisionObject->getWorldTransform();
|
||||
trans.setOrigin(Misc::Convert::toBullet(mPosition));
|
||||
mCollisionObject->setWorldTransform(trans);
|
||||
}
|
||||
|
||||
void Projectile::hit(const btCollisionObject* target, btVector3 pos, btVector3 normal)
|
||||
|
@ -36,10 +36,7 @@ namespace MWPhysics
|
||||
|
||||
btConvexShape* getConvexShape() const { return mConvexShape; }
|
||||
|
||||
void commitPositionChange();
|
||||
|
||||
void setPosition(const osg::Vec3f& position);
|
||||
osg::Vec3f getPosition() const;
|
||||
void updateCollisionObjectPosition();
|
||||
|
||||
bool isActive() const
|
||||
{
|
||||
@ -80,13 +77,11 @@ namespace MWPhysics
|
||||
std::unique_ptr<btCollisionShape> mShape;
|
||||
btConvexShape* mConvexShape;
|
||||
|
||||
bool mTransformUpdatePending;
|
||||
bool mHitWater;
|
||||
std::atomic<bool> mActive;
|
||||
MWWorld::Ptr mCaster;
|
||||
const btCollisionObject* mCasterColObj;
|
||||
const btCollisionObject* mHitTarget;
|
||||
osg::Vec3f mPosition;
|
||||
btVector3 mHitPosition;
|
||||
btVector3 mHitNormal;
|
||||
|
||||
|
@ -30,9 +30,49 @@ namespace MWPhysics
|
||||
return mCollisionObject.get();
|
||||
}
|
||||
|
||||
void setVelocity(osg::Vec3f velocity)
|
||||
{
|
||||
mVelocity = velocity;
|
||||
}
|
||||
|
||||
osg::Vec3f velocity()
|
||||
{
|
||||
return std::exchange(mVelocity, osg::Vec3f());
|
||||
}
|
||||
|
||||
void setSimulationPosition(const osg::Vec3f& position)
|
||||
{
|
||||
mSimulationPosition = position;
|
||||
}
|
||||
|
||||
osg::Vec3f getSimulationPosition() const
|
||||
{
|
||||
return mSimulationPosition;
|
||||
}
|
||||
|
||||
void setPosition(const osg::Vec3f& position)
|
||||
{
|
||||
mPreviousPosition = mPosition;
|
||||
mPosition = position;
|
||||
}
|
||||
|
||||
osg::Vec3f getPosition() const
|
||||
{
|
||||
return mPosition;
|
||||
}
|
||||
|
||||
osg::Vec3f getPreviousPosition() const
|
||||
{
|
||||
return mPreviousPosition;
|
||||
}
|
||||
|
||||
protected:
|
||||
MWWorld::Ptr mPtr;
|
||||
std::unique_ptr<btCollisionObject> mCollisionObject;
|
||||
osg::Vec3f mVelocity;
|
||||
osg::Vec3f mSimulationPosition;
|
||||
osg::Vec3f mPosition;
|
||||
osg::Vec3f mPreviousPosition;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -432,7 +432,7 @@ namespace MWWorld
|
||||
float speed = fTargetSpellMaxSpeed * magicBoltState.mSpeed;
|
||||
osg::Vec3f direction = orient * osg::Vec3f(0,1,0);
|
||||
direction.normalize();
|
||||
osg::Vec3f newPos = projectile->getPosition() + direction * duration * speed;
|
||||
projectile->setVelocity(direction * speed);
|
||||
|
||||
update(magicBoltState, duration);
|
||||
|
||||
@ -441,8 +441,6 @@ namespace MWWorld
|
||||
if (!caster.isEmpty() && caster.getClass().isActor() && caster != MWMechanics::getPlayer())
|
||||
caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors);
|
||||
projectile->setValidTargets(targetActors);
|
||||
|
||||
mPhysics->updateProjectile(magicBoltState.mProjectileId, newPos);
|
||||
}
|
||||
}
|
||||
|
||||
@ -460,7 +458,7 @@ namespace MWWorld
|
||||
// simulating aerodynamics at all
|
||||
projectileState.mVelocity -= osg::Vec3f(0, 0, Constants::GravityConst * Constants::UnitsPerMeter * 0.1f) * duration;
|
||||
|
||||
osg::Vec3f newPos = projectile->getPosition() + projectileState.mVelocity * duration;
|
||||
projectile->setVelocity(projectileState.mVelocity);
|
||||
|
||||
// rotation does not work well for throwing projectiles - their roll angle will depend on shooting direction.
|
||||
if (!projectileState.mThrown)
|
||||
@ -479,8 +477,6 @@ namespace MWWorld
|
||||
if (!caster.isEmpty() && caster.getClass().isActor() && caster != MWMechanics::getPlayer())
|
||||
caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors);
|
||||
projectile->setValidTargets(targetActors);
|
||||
|
||||
mPhysics->updateProjectile(projectileState.mProjectileId, newPos);
|
||||
}
|
||||
}
|
||||
|
||||
@ -493,7 +489,7 @@ namespace MWWorld
|
||||
|
||||
auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId);
|
||||
|
||||
const auto pos = projectile->getPosition();
|
||||
const auto pos = projectile->getSimulationPosition();
|
||||
projectileState.mNode->setPosition(pos);
|
||||
|
||||
if (projectile->isActive())
|
||||
@ -529,7 +525,7 @@ namespace MWWorld
|
||||
|
||||
auto* projectile = mPhysics->getProjectile(magicBoltState.mProjectileId);
|
||||
|
||||
const auto pos = projectile->getPosition();
|
||||
const auto pos = projectile->getSimulationPosition();
|
||||
magicBoltState.mNode->setPosition(pos);
|
||||
for (const auto& sound : magicBoltState.mSounds)
|
||||
sound->setPosition(pos);
|
||||
|
Loading…
x
Reference in New Issue
Block a user