#ifndef OPENMW_MWPHYSICS_PHYSICSSYSTEM_H #define OPENMW_MWPHYSICS_PHYSICSSYSTEM_H #include #include #include #include #include #include #include #include #include #include #include "../mwworld/ptr.hpp" #include "collisiontype.hpp" #include "raycasting.hpp" namespace osg { class Group; class Object; class Stats; } namespace MWRender { class DebugDrawer; } namespace Resource { class BulletShapeManager; class ResourceSystem; } class btCollisionWorld; class btBroadphaseInterface; class btDefaultCollisionConfiguration; class btCollisionDispatcher; class btCollisionObject; class btCollisionShape; class btVector3; namespace MWPhysics { class HeightField; class Object; class Actor; class PhysicsTaskScheduler; class Projectile; using ActorMap = std::unordered_map>; struct ContactPoint { MWWorld::Ptr mObject; osg::Vec3f mPoint; osg::Vec3f mNormal; }; struct LOSRequest { LOSRequest(const std::weak_ptr& a1, const std::weak_ptr& a2); std::array, 2> mActors; std::array mRawActors; bool mResult; bool mStale; int mAge; }; bool operator==(const LOSRequest& lhs, const LOSRequest& rhs) noexcept; struct ActorFrameData { ActorFrameData(Actor& actor, bool inert, bool waterCollision, float slowFall, float waterlevel); void updatePosition(Actor& actor, btCollisionWorld* world); osg::Vec3f mPosition; osg::Vec3f mInertia; const btCollisionObject* mStandingOn; bool mIsOnGround; bool mIsOnSlope; bool mWalkingOnWater; const bool mInert; btCollisionObject* mCollisionObject; const float mSwimLevel; const float mSlowFall; osg::Vec2f mRotation; osg::Vec3f mMovement; osg::Vec3f mLastStuckPosition; const float mWaterlevel; const float mHalfExtentsZ; float mOldHeight; unsigned int mStuckFrames; const bool mFlying; const bool mWasOnGround; const bool mIsAquatic; const bool mWaterCollision; const bool mSkipCollisionDetection; }; struct WorldFrameData { WorldFrameData(); bool mIsInStorm; osg::Vec3f mStormDirection; }; class PhysicsSystem : public RayCastingInterface { public: PhysicsSystem (Resource::ResourceSystem* resourceSystem, osg::ref_ptr parentNode); virtual ~PhysicsSystem (); Resource::BulletShapeManager* getShapeManager(); void enableWater(float height); void setWaterHeight(float height); void disableWater(); void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, osg::Quat rotation, int collisionType = CollisionType_World); void addActor (const MWWorld::Ptr& ptr, const std::string& mesh); 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); Actor* getActor(const MWWorld::Ptr& ptr); const Actor* getActor(const MWWorld::ConstPtr& ptr) const; const Object* getObject(const MWWorld::ConstPtr& ptr) const; Projectile* getProjectile(int projectileId) const; // Object or Actor void remove (const MWWorld::Ptr& ptr); void updateScale (const MWWorld::Ptr& ptr); void updateRotation (const MWWorld::Ptr& ptr, osg::Quat rotate); void updatePosition (const MWWorld::Ptr& ptr); void addHeightField(const float* heights, int x, int y, int size, int verts, float minH, float maxH, const osg::Object* holdObject); void removeHeightField (int x, int y); const HeightField* getHeightField(int x, int y) const; bool toggleCollisionMode(); /// Determine new position based on all queued movements, then clear the list. void stepSimulation(float dt, bool skipSimulation, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); /// Apply new positions to actors void moveActors(); void debugDraw(); std::vector getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const; ///< get handles this object collides with std::vector getCollisionsPoints(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const; osg::Vec3f traceDown(const MWWorld::Ptr &ptr, const osg::Vec3f& position, float maxHeight); std::pair getHitContact(const MWWorld::ConstPtr& actor, const osg::Vec3f &origin, const osg::Quat &orientation, float queryDistance, std::vector& targets); /// Get distance from \a point to the collision shape of \a target. Uses a raycast to find where the /// target vector hits the collision shape and then calculates distance from the intersection point. /// This can be used to find out how much nearer we need to move to the target for a "getHitContact" to be successful. /// \note Only Actor targets are supported at the moment. float getHitDistance(const osg::Vec3f& point, const MWWorld::ConstPtr& target) const override; /// @param me Optional, a Ptr to ignore in the list of results. targets are actors to filter for, ignoring all other actors. RayCastingResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, const MWWorld::ConstPtr& ignore = MWWorld::ConstPtr(), const std::vector& targets = std::vector(), int mask = CollisionType_Default, int group=0xff) const override; RayCastingResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius, int mask = CollisionType_Default, int group=0xff) const override; /// Return true if actor1 can see actor2. bool getLineOfSight(const MWWorld::ConstPtr& actor1, const MWWorld::ConstPtr& actor2) const override; bool isOnGround (const MWWorld::Ptr& actor); bool canMoveToWaterSurface (const MWWorld::ConstPtr &actor, const float waterlevel); /// Get physical half extents (scaled) of the given actor. osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor) const; /// Get physical half extents (not scaled) of the given actor. osg::Vec3f getOriginalHalfExtents(const MWWorld::ConstPtr& actor) const; /// @see MWPhysics::Actor::getRenderingHalfExtents osg::Vec3f getRenderingHalfExtents(const MWWorld::ConstPtr& actor) const; /// Get the position of the collision shape for the actor. Use together with getHalfExtents() to get the collision bounds in world space. /// @note The collision shape's origin is in its center, so the position returned can be described as center of the actor collision box in world space. osg::Vec3f getCollisionObjectPosition(const MWWorld::ConstPtr& actor) const; /// Get bounding box in world space of the given object. osg::BoundingBox getBoundingBox(const MWWorld::ConstPtr &object) const; /// Queues velocity movement for a Ptr. If a Ptr is already queued, its velocity will /// be overwritten. Valid until the next call to stepSimulation void queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &velocity); /// Clear the queued movements list without applying. void clearQueuedMovement(); /// Return true if \a actor has been standing on \a object in this frame /// This will trigger whenever the object is directly below the actor. /// It doesn't matter if the actor is stationary or moving. bool isActorStandingOn(const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object) const; /// Get the handle of all actors standing on \a object in this frame. void getActorsStandingOn(const MWWorld::ConstPtr& object, std::vector& out) const; /// Return true if \a actor has collided with \a object in this frame. /// This will detect running into objects, but will not detect climbing stairs, stepping up a small object, etc. bool isActorCollidingWith(const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object) const; /// Get the handle of all actors colliding with \a object in this frame. void getActorsCollidingWith(const MWWorld::ConstPtr& object, std::vector& out) const; bool toggleDebugRendering(); /// Mark the given object as a 'non-solid' object. A non-solid object means that /// \a isOnSolidGround will return false for actors standing on that object. void markAsNonSolid (const MWWorld::ConstPtr& ptr); bool isOnSolidGround (const MWWorld::Ptr& actor) const; void updateAnimatedCollisionShape(const MWWorld::Ptr& object); template void forEachAnimatedObject(Function&& function) const { std::for_each(mAnimatedObjects.begin(), mAnimatedObjects.end(), function); } bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore, std::vector* occupyingActors) const; void reportStats(unsigned int frameNumber, osg::Stats& stats) const; void reportCollision(const btVector3& position, const btVector3& normal); private: void updateWater(); std::pair>, std::vector> prepareFrameData(bool willSimulate); std::unique_ptr mBroadphase; std::unique_ptr mCollisionConfiguration; std::unique_ptr mDispatcher; std::unique_ptr mCollisionWorld; std::unique_ptr mTaskScheduler; std::unique_ptr mShapeManager; Resource::ResourceSystem* mResourceSystem; using ObjectMap = std::unordered_map>; ObjectMap mObjects; std::set mAnimatedObjects; // stores pointers to elements in mObjects ActorMap mActors; using ProjectileMap = std::map>; ProjectileMap mProjectiles; using HeightFieldMap = std::map, std::unique_ptr>; HeightFieldMap mHeightFields; bool mDebugDrawEnabled; float mTimeAccum; unsigned int mProjectileId; float mWaterHeight; bool mWaterEnabled; std::unique_ptr mWaterCollisionObject; std::unique_ptr mWaterCollisionShape; std::unique_ptr mDebugDrawer; osg::ref_ptr mParentNode; float mPhysicsDt; PhysicsSystem (const PhysicsSystem&); PhysicsSystem& operator= (const PhysicsSystem&); }; } #endif