From 5009b66ef5bccc447603cc0d58592106cf0cd1a0 Mon Sep 17 00:00:00 2001 From: Frederic Chardon Date: Thu, 7 Oct 2021 21:47:05 +0200 Subject: [PATCH 1/4] Use std::variant in the physics simulation for the different types of objects. For now only support only for actors. --- apps/openmw/mwphysics/actor.cpp | 12 ++ apps/openmw/mwphysics/actor.hpp | 3 + apps/openmw/mwphysics/movementsolver.cpp | 2 +- apps/openmw/mwphysics/movementsolver.hpp | 2 +- apps/openmw/mwphysics/mtphysics.cpp | 175 ++++++++++++++++------- apps/openmw/mwphysics/mtphysics.hpp | 8 +- apps/openmw/mwphysics/physicssystem.cpp | 34 ++--- apps/openmw/mwphysics/physicssystem.hpp | 6 +- 8 files changed, 155 insertions(+), 87 deletions(-) diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index e140141e32..c7e2308e2f 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -12,6 +12,7 @@ #include "collisiontype.hpp" #include "mtphysics.hpp" +#include "trace.h" #include @@ -303,4 +304,15 @@ osg::Vec3f Actor::velocity() return std::exchange(mVelocity, osg::Vec3f()); } +bool Actor::canMoveToWaterSurface(float waterlevel, const btCollisionWorld* world) const +{ + 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); +} + } diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp index 0846401c1d..d2ebd78379 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -12,6 +12,7 @@ class btCollisionShape; class btCollisionObject; +class btCollisionWorld; class btConvexShape; namespace Resource @@ -165,6 +166,8 @@ namespace MWPhysics void setVelocity(osg::Vec3f velocity); osg::Vec3f velocity(); + bool canMoveToWaterSurface(float waterlevel, const btCollisionWorld* world) const; + private: MWWorld::Ptr mStandingOnPtr; /// Removes then re-adds the collision object to the dynamics world diff --git a/apps/openmw/mwphysics/movementsolver.cpp b/apps/openmw/mwphysics/movementsolver.cpp index 44a5391f0d..29810e9085 100644 --- a/apps/openmw/mwphysics/movementsolver.cpp +++ b/apps/openmw/mwphysics/movementsolver.cpp @@ -116,7 +116,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; diff --git a/apps/openmw/mwphysics/movementsolver.hpp b/apps/openmw/mwphysics/movementsolver.hpp index 30733eeec8..837004f232 100644 --- a/apps/openmw/mwphysics/movementsolver.hpp +++ b/apps/openmw/mwphysics/movementsolver.hpp @@ -43,7 +43,7 @@ namespace MWPhysics { 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 unstuck(ActorFrameData& actor, const btCollisionWorld* collisionWorld); }; } diff --git a/apps/openmw/mwphysics/mtphysics.cpp b/apps/openmw/mwphysics/mtphysics.cpp index a0dde67c2e..ad11878369 100644 --- a/apps/openmw/mwphysics/mtphysics.cpp +++ b/apps/openmw/mwphysics/mtphysics.cpp @@ -111,6 +111,106 @@ namespace return actorData.mPosition * interpolationFactor + actor.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(); + } + }; + + struct PreStep + { + btCollisionWorld* mCollisionWorld; + void operator()(MWPhysics::ActorSimulation& sim) const + { + MWPhysics::MovementSolver::unstuck(sim.second, mCollisionWorld); + } + }; + + 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()); + } + } + }; + + 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); + } + }; + + 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, frameData, mTimeAccum, mPhysicsDt)); + actor->setLastStuckPosition(frameData.mLastStuckPosition); + actor->setStuckFrames(frameData.mStuckFrames); + if (mAdvanceSimulation) + { + MWWorld::Ptr standingOn; + auto* ptrHolder = static_cast(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); + } + } + }; + } + namespace Config { /// @return either the number of thread as configured by the user, or 1 if Bullet doesn't support multithreading and user requested more than 1 background threads @@ -235,13 +335,12 @@ namespace MWPhysics return std::make_tuple(numSteps, actualDelta); } - void PhysicsTaskScheduler::applyQueuedMovements(float & timeAccum, std::vector>&& actors, std::vector&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) + void PhysicsTaskScheduler::applyQueuedMovements(float & timeAccum, std::vector&& 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 +358,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 +400,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(); @@ -467,47 +565,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(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 +594,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 +640,7 @@ namespace MWPhysics void PhysicsTaskScheduler::releaseSharedStates() { std::scoped_lock lock(mSimulationMutex, mUpdateAabbMutex); - mActors.clear(); + mSimulations.clear(); mUpdateAabb.clear(); } @@ -586,10 +649,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 +682,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); } } diff --git a/apps/openmw/mwphysics/mtphysics.hpp b/apps/openmw/mwphysics/mtphysics.hpp index 08997947e4..44330b2cc6 100644 --- a/apps/openmw/mwphysics/mtphysics.hpp +++ b/apps/openmw/mwphysics/mtphysics.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -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>&& actors, std::vector&& actorsData, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); + void applyQueuedMovements(float & timeAccum, std::vector&& 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& actor1, const std::shared_ptr& 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 mWorldFrameData; - std::vector> mActors; - std::vector mActorsFrameData; + std::vector mSimulations; std::unordered_set mCollisionObjects; float mDefaultPhysicsDt; float mPhysicsDt; diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 2a5bcb58bd..f6fc3902e7 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -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()) @@ -386,7 +373,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 @@ -727,11 +715,10 @@ namespace MWPhysics actor->setVelocity(osg::Vec3f()); } - std::pair>, std::vector> PhysicsSystem::prepareFrameData(bool willSimulate) + std::vector PhysicsSystem::prepareSimulation(bool willSimulate) { - std::pair>, std::vector> framedata; - framedata.first.reserve(mActors.size()); - framedata.second.reserve(mActors.size()); + std::vector simulations; + simulations.reserve(mActors.size()); const MWBase::World *world = MWBase::Environment::get().getWorld(); for (const auto& [ref, physicActor] : mActors) { @@ -760,14 +747,13 @@ 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; + return simulations; } void PhysicsSystem::stepSimulation(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats) @@ -793,9 +779,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); } } @@ -982,7 +968,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); diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 6ec4ebfda9..be9fa10aa6 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -107,6 +108,9 @@ namespace MWPhysics osg::Vec3f mStormDirection; }; + using ActorSimulation = std::pair, ActorFrameData>; + using Simulation = std::variant; + class PhysicsSystem : public RayCastingInterface { public: @@ -253,7 +257,7 @@ namespace MWPhysics void updateWater(); - std::pair>, std::vector> prepareFrameData(bool willSimulate); + std::vector prepareSimulation(bool willSimulate); std::unique_ptr mBroadphase; std::unique_ptr mCollisionConfiguration; From ad7a810a62f669f272ffa55b0609ac611bffcedc Mon Sep 17 00:00:00 2001 From: fredzio Date: Sat, 9 Oct 2021 18:13:54 +0200 Subject: [PATCH 2/4] Unify interface for Actor and Projectile --- apps/openmw/mwphysics/actor.cpp | 27 +------------------ apps/openmw/mwphysics/actor.hpp | 12 --------- apps/openmw/mwphysics/mtphysics.cpp | 2 +- apps/openmw/mwphysics/projectile.cpp | 30 +++++---------------- apps/openmw/mwphysics/projectile.hpp | 7 +---- apps/openmw/mwphysics/ptrholder.hpp | 40 ++++++++++++++++++++++++++++ 6 files changed, 50 insertions(+), 68 deletions(-) diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index c7e2308e2f..4857339228 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -23,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) @@ -134,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); @@ -192,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); @@ -294,16 +279,6 @@ bool Actor::skipCollisions() return std::exchange(mSkipCollisions, false); } -void Actor::setVelocity(osg::Vec3f velocity) -{ - mVelocity = velocity; -} - -osg::Vec3f Actor::velocity() -{ - return std::exchange(mVelocity, osg::Vec3f()); -} - bool Actor::canMoveToWaterSurface(float waterlevel, const btCollisionWorld* world) const { const float halfZ = getHalfExtents().z(); diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp index d2ebd78379..eb65928dec 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -60,7 +60,6 @@ namespace MWPhysics * to account for e.g. scripted movements */ void setSimulationPosition(const osg::Vec3f& position); - osg::Vec3f getSimulationPosition() const; void updateCollisionObjectPosition(); @@ -95,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, @@ -163,9 +158,6 @@ namespace MWPhysics bool skipCollisions(); - void setVelocity(osg::Vec3f velocity); - osg::Vec3f velocity(); - bool canMoveToWaterSurface(float waterlevel, const btCollisionWorld* world) const; private: @@ -193,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; diff --git a/apps/openmw/mwphysics/mtphysics.cpp b/apps/openmw/mwphysics/mtphysics.cpp index ad11878369..d3f8bad238 100644 --- a/apps/openmw/mwphysics/mtphysics.cpp +++ b/apps/openmw/mwphysics/mtphysics.cpp @@ -546,7 +546,7 @@ namespace MWPhysics } else if (const auto projectile = std::dynamic_pointer_cast(ptr)) { - projectile->commitPositionChange(); + projectile->updateCollisionObjectPosition(); mCollisionWorld->updateSingleAabb(projectile->getCollisionObject()); } } diff --git a/apps/openmw/mwphysics/projectile.cpp b/apps/openmw/mwphysics/projectile.cpp index 4efb245149..9f8962d5e6 100644 --- a/apps/openmw/mwphysics/projectile.cpp +++ b/apps/openmw/mwphysics/projectile.cpp @@ -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) diff --git a/apps/openmw/mwphysics/projectile.hpp b/apps/openmw/mwphysics/projectile.hpp index 5e4e487c03..10ed2c9582 100644 --- a/apps/openmw/mwphysics/projectile.hpp +++ b/apps/openmw/mwphysics/projectile.hpp @@ -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 mShape; btConvexShape* mConvexShape; - bool mTransformUpdatePending; bool mHitWater; std::atomic mActive; MWWorld::Ptr mCaster; const btCollisionObject* mCasterColObj; const btCollisionObject* mHitTarget; - osg::Vec3f mPosition; btVector3 mHitPosition; btVector3 mHitNormal; diff --git a/apps/openmw/mwphysics/ptrholder.hpp b/apps/openmw/mwphysics/ptrholder.hpp index e84f3d1cfe..fcd6ce203a 100644 --- a/apps/openmw/mwphysics/ptrholder.hpp +++ b/apps/openmw/mwphysics/ptrholder.hpp @@ -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 mCollisionObject; + osg::Vec3f mVelocity; + osg::Vec3f mSimulationPosition; + osg::Vec3f mPosition; + osg::Vec3f mPreviousPosition; }; } From a245981e4ec1975731838b2b0663ccead30b79c4 Mon Sep 17 00:00:00 2001 From: fredzio Date: Sat, 9 Oct 2021 18:15:45 +0200 Subject: [PATCH 3/4] Make interpolateMovement works with PtrHolder instead of Actor --- apps/openmw/mwphysics/mtphysics.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwphysics/mtphysics.cpp b/apps/openmw/mwphysics/mtphysics.cpp index d3f8bad238..525647230f 100644 --- a/apps/openmw/mwphysics/mtphysics.cpp +++ b/apps/openmw/mwphysics/mtphysics.cpp @@ -105,10 +105,10 @@ 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 @@ -190,7 +190,7 @@ namespace else if (heightDiff < 0) stats.addToFallHeight(-heightDiff); - actor->setSimulationPosition(::interpolateMovements(*actor, frameData, mTimeAccum, mPhysicsDt)); + actor->setSimulationPosition(::interpolateMovements(*actor, mTimeAccum, mPhysicsDt)); actor->setLastStuckPosition(frameData.mLastStuckPosition); actor->setStuckFrames(frameData.mStuckFrames); if (mAdvanceSimulation) From 3750eb9cd85ca3cbaaa150bda8159d653833bf1d Mon Sep 17 00:00:00 2001 From: fredzio Date: Sat, 9 Oct 2021 18:16:29 +0200 Subject: [PATCH 4/4] Move Projectile simulation to the background thread. --- apps/openmw/mwphysics/movementsolver.cpp | 26 ++++++++++++++ apps/openmw/mwphysics/movementsolver.hpp | 2 ++ apps/openmw/mwphysics/mtphysics.cpp | 22 ++++++++++++ apps/openmw/mwphysics/physicssystem.cpp | 44 +++++++++-------------- apps/openmw/mwphysics/physicssystem.hpp | 14 ++++++-- apps/openmw/mwworld/projectilemanager.cpp | 12 +++---- 6 files changed, 82 insertions(+), 38 deletions(-) diff --git a/apps/openmw/mwphysics/movementsolver.cpp b/apps/openmw/mwphysics/movementsolver.cpp index 29810e9085..0585fe91f3 100644 --- a/apps/openmw/mwphysics/movementsolver.cpp +++ b/apps/openmw/mwphysics/movementsolver.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -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" @@ -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(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) diff --git a/apps/openmw/mwphysics/movementsolver.hpp b/apps/openmw/mwphysics/movementsolver.hpp index 837004f232..1bbe76cbec 100644 --- a/apps/openmw/mwphysics/movementsolver.hpp +++ b/apps/openmw/mwphysics/movementsolver.hpp @@ -37,6 +37,7 @@ namespace MWPhysics class Actor; struct ActorFrameData; + struct ProjectileFrameData; struct WorldFrameData; class MovementSolver @@ -44,6 +45,7 @@ namespace MWPhysics 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, const WorldFrameData& worldData); + static void move(ProjectileFrameData& projectile, float time, const btCollisionWorld* collisionWorld); static void unstuck(ActorFrameData& actor, const btCollisionWorld* collisionWorld); }; } diff --git a/apps/openmw/mwphysics/mtphysics.cpp b/apps/openmw/mwphysics/mtphysics.cpp index 525647230f..a989493f4d 100644 --- a/apps/openmw/mwphysics/mtphysics.cpp +++ b/apps/openmw/mwphysics/mtphysics.cpp @@ -133,6 +133,9 @@ namespace frameData.mStuckFrames = actor->getStuckFrames(); frameData.mLastStuckPosition = actor->getLastStuckPosition(); } + void operator()(MWPhysics::ProjectileSimulation& sim) const + { + } }; struct PreStep @@ -142,6 +145,9 @@ namespace { MWPhysics::MovementSolver::unstuck(sim.second, mCollisionWorld); } + void operator()(MWPhysics::ProjectileSimulation& sim) const + { + } }; struct UpdatePosition @@ -157,6 +163,13 @@ namespace 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 @@ -168,6 +181,10 @@ namespace { MWPhysics::MovementSolver::move(sim.second, mPhysicsDt, mCollisionWorld, mWorldFrameData); } + void operator()(MWPhysics::ProjectileSimulation& sim) const + { + MWPhysics::MovementSolver::move(sim.second, mPhysicsDt, mCollisionWorld); + } }; struct Sync @@ -208,6 +225,11 @@ namespace actor->setInertialForce(frameData.mInertia); } } + void operator()(MWPhysics::ProjectileSimulation& sim) const + { + auto& [proj, frameData] = sim; + proj->setSimulationPosition(::interpolateMovements(*proj, mTimeAccum, mPhysicsDt)); + } }; } diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index f6fc3902e7..786378eb8c 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -580,33 +580,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()) @@ -718,7 +691,7 @@ namespace MWPhysics std::vector PhysicsSystem::prepareSimulation(bool willSimulate) { std::vector simulations; - simulations.reserve(mActors.size()); + simulations.reserve(mActors.size() + mProjectiles.size()); const MWBase::World *world = MWBase::Environment::get().getWorld(); for (const auto& [ref, physicActor] : mActors) { @@ -753,6 +726,12 @@ namespace MWPhysics if (willSimulate) handleJump(ptr); } + + for (const auto& [id, projectile] : mProjectiles) + { + simulations.emplace_back(ProjectileSimulation{projectile, ProjectileFrameData{*projectile}}); + } + return simulations; } @@ -981,6 +960,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()) diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index be9fa10aa6..a025f1cc8b 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -101,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(); @@ -109,7 +119,8 @@ namespace MWPhysics }; using ActorSimulation = std::pair, ActorFrameData>; - using Simulation = std::variant; + using ProjectileSimulation = std::pair, ProjectileFrameData>; + using Simulation = std::variant; class PhysicsSystem : public RayCastingInterface { @@ -128,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); diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 9ee137fab6..ea1d6bde1a 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -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);