#ifndef OPENMW_MWPHYSICS_MTPHYSICS_H #define OPENMW_MWPHYSICS_MTPHYSICS_H #include <atomic> #include <condition_variable> #include <thread> #include <shared_mutex> #include <boost/optional/optional.hpp> #include <BulletCollision/CollisionDispatch/btCollisionWorld.h> #include "physicssystem.hpp" #include "ptrholder.hpp" namespace Misc { class Barrier; } namespace MWPhysics { class PhysicsTaskScheduler { public: PhysicsTaskScheduler(float physicsDt, std::shared_ptr<btCollisionWorld> collisionWorld); ~PhysicsTaskScheduler(); /// @brief move actors taking into account desired movements and collisions /// @param numSteps how much simulation step to run /// @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 const PtrPositionList& moveActors(int numSteps, float timeAccum, std::vector<ActorFrameData>&& actorsData, CollisionMap& standingCollisions, bool skip); // Thread safe wrappers void rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const; void convexSweepTest(const btConvexShape* castShape, const btTransform& from, const btTransform& to, btCollisionWorld::ConvexResultCallback& resultCallback) const; void contactTest(btCollisionObject* colObj, btCollisionWorld::ContactResultCallback& resultCallback); boost::optional<btVector3> getHitPoint(const btTransform& from, btCollisionObject* target); void aabbTest(const btVector3& aabbMin, const btVector3& aabbMax, btBroadphaseAabbCallback& callback); void getAabb(const btCollisionObject* obj, btVector3& min, btVector3& max); void setCollisionFilterMask(btCollisionObject* collisionObject, int collisionFilterMask); void addCollisionObject(btCollisionObject* collisionObject, int collisionFilterGroup, int collisionFilterMask); void removeCollisionObject(btCollisionObject* collisionObject); void updateSingleAabb(std::weak_ptr<PtrHolder> ptr); bool getLineOfSight(const std::weak_ptr<Actor>& actor1, const std::weak_ptr<Actor>& actor2); private: void syncComputation(); void worker(); void updateActorsPositions(); void udpateActorsAabbs(); bool hasLineOfSight(const Actor* actor1, const Actor* actor2); void refreshLOSCache(); void updateAabbs(); void updatePtrAabb(const std::weak_ptr<PtrHolder>& ptr); std::unique_ptr<WorldFrameData> mWorldFrameData; std::vector<ActorFrameData> mActorsFrameData; PtrPositionList mMovementResults; PtrPositionList mPreviousMovementResults; const float mPhysicsDt; float mTimeAccum; std::shared_ptr<btCollisionWorld> mCollisionWorld; std::vector<LOSRequest> mLOSCache; std::set<std::weak_ptr<PtrHolder>, std::owner_less<std::weak_ptr<PtrHolder>>> mUpdateAabb; // TODO: use std::experimental::flex_barrier or std::barrier once it becomes a thing std::unique_ptr<Misc::Barrier> mPreStepBarrier; std::unique_ptr<Misc::Barrier> mPostStepBarrier; std::unique_ptr<Misc::Barrier> mPostSimBarrier; int mNumThreads; int mNumJobs; int mRemainingSteps; int mLOSCacheExpiry; bool mDeferAabbUpdate; bool mNewFrame; bool mAdvanceSimulation; bool mThreadSafeBullet; bool mQuit; std::atomic<int> mNextJob; std::atomic<int> mNextLOS; std::vector<std::thread> mThreads; mutable std::shared_timed_mutex mSimulationMutex; mutable std::shared_timed_mutex mCollisionWorldMutex; mutable std::shared_timed_mutex mLOSCacheMutex; mutable std::mutex mUpdateAabbMutex; std::condition_variable_any mHasJob; }; } #endif