mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-01 03:21:41 +00:00
356 lines
13 KiB
C++
356 lines
13 KiB
C++
#ifndef OPENMW_MWPHYSICS_PHYSICSSYSTEM_H
|
|
#define OPENMW_MWPHYSICS_PHYSICSSYSTEM_H
|
|
|
|
#include <array>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <set>
|
|
#include <span>
|
|
#include <unordered_map>
|
|
#include <variant>
|
|
|
|
#include <osg/BoundingBox>
|
|
#include <osg/Quat>
|
|
#include <osg/Timer>
|
|
#include <osg/ref_ptr>
|
|
|
|
#include <components/detournavigator/collisionshapetype.hpp>
|
|
|
|
#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<const MWWorld::LiveCellRefBase*, std::shared_ptr<Actor>>;
|
|
|
|
struct ContactPoint
|
|
{
|
|
MWWorld::Ptr mObject;
|
|
osg::Vec3f mPoint;
|
|
osg::Vec3f mNormal;
|
|
};
|
|
|
|
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(Actor& actor, bool inert, bool waterCollision, float slowFall, float waterlevel);
|
|
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 ProjectileFrameData
|
|
{
|
|
explicit ProjectileFrameData(Projectile& projectile);
|
|
osg::Vec3f mPosition;
|
|
osg::Vec3f mMovement;
|
|
const btCollisionObject* mCaster;
|
|
const btCollisionObject* mCollisionObject;
|
|
Projectile* mProjectile;
|
|
};
|
|
|
|
struct WorldFrameData
|
|
{
|
|
WorldFrameData();
|
|
bool mIsInStorm;
|
|
osg::Vec3f mStormDirection;
|
|
};
|
|
|
|
template <class Ptr, class FrameData>
|
|
class SimulationImpl
|
|
{
|
|
public:
|
|
explicit SimulationImpl(const std::weak_ptr<Ptr>& ptr, FrameData&& data)
|
|
: mPtr(ptr)
|
|
, mData(data)
|
|
{
|
|
}
|
|
|
|
std::optional<std::pair<std::shared_ptr<Ptr>, std::reference_wrapper<FrameData>>> lock()
|
|
{
|
|
if (auto locked = mPtr.lock())
|
|
return { { std::move(locked), std::ref(mData) } };
|
|
return std::nullopt;
|
|
}
|
|
|
|
private:
|
|
std::weak_ptr<Ptr> mPtr;
|
|
FrameData mData;
|
|
};
|
|
|
|
using ActorSimulation = SimulationImpl<Actor, ActorFrameData>;
|
|
using ProjectileSimulation = SimulationImpl<Projectile, ProjectileFrameData>;
|
|
using Simulation = std::variant<ActorSimulation, ProjectileSimulation>;
|
|
|
|
class PhysicsSystem : public RayCastingInterface
|
|
{
|
|
public:
|
|
PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> 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 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<MWWorld::Ptr> getCollisions(const MWWorld::ConstPtr& ptr, int collisionGroup,
|
|
int collisionMask) const; ///< get handles this object collides with
|
|
std::vector<ContactPoint> 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<MWWorld::Ptr, osg::Vec3f> getHitContact(const MWWorld::ConstPtr& actor, const osg::Vec3f& origin,
|
|
const osg::Quat& orientation, float queryDistance, std::vector<MWWorld::Ptr>& 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<MWWorld::Ptr>& targets = std::vector<MWWorld::Ptr>(), int mask = CollisionType_Default,
|
|
int group = 0xff) const override;
|
|
using RayCastingInterface::castRay;
|
|
|
|
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<MWWorld::Ptr>& 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<MWWorld::Ptr>& 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 <class Function>
|
|
void forEachAnimatedObject(Function&& function) const
|
|
{
|
|
std::for_each(mAnimatedObjects.begin(), mAnimatedObjects.end(), function);
|
|
}
|
|
|
|
bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius,
|
|
std::span<const MWWorld::ConstPtr> ignore, std::vector<MWWorld::Ptr>* occupyingActors) const;
|
|
|
|
void reportStats(unsigned int frameNumber, osg::Stats& stats) const;
|
|
void reportCollision(const btVector3& position, const btVector3& normal);
|
|
|
|
private:
|
|
void updateWater();
|
|
|
|
void prepareSimulation(bool willSimulate, std::vector<Simulation>& simulations);
|
|
|
|
std::unique_ptr<btBroadphaseInterface> mBroadphase;
|
|
std::unique_ptr<btDefaultCollisionConfiguration> mCollisionConfiguration;
|
|
std::unique_ptr<btCollisionDispatcher> mDispatcher;
|
|
std::unique_ptr<btCollisionWorld> mCollisionWorld;
|
|
std::unique_ptr<PhysicsTaskScheduler> mTaskScheduler;
|
|
|
|
std::unique_ptr<Resource::BulletShapeManager> mShapeManager;
|
|
Resource::ResourceSystem* mResourceSystem;
|
|
|
|
using ObjectMap = std::unordered_map<const MWWorld::LiveCellRefBase*, std::shared_ptr<Object>>;
|
|
ObjectMap mObjects;
|
|
|
|
std::map<Object*, bool> mAnimatedObjects; // stores pointers to elements in mObjects
|
|
|
|
ActorMap mActors;
|
|
|
|
using ProjectileMap = std::map<int, std::shared_ptr<Projectile>>;
|
|
ProjectileMap mProjectiles;
|
|
|
|
using HeightFieldMap = std::map<std::pair<int, int>, std::unique_ptr<HeightField>>;
|
|
HeightFieldMap mHeightFields;
|
|
|
|
bool mDebugDrawEnabled;
|
|
|
|
float mTimeAccum;
|
|
|
|
unsigned int mProjectileId;
|
|
|
|
float mWaterHeight;
|
|
bool mWaterEnabled;
|
|
|
|
std::unique_ptr<btCollisionObject> mWaterCollisionObject;
|
|
std::unique_ptr<btCollisionShape> mWaterCollisionShape;
|
|
|
|
std::unique_ptr<MWRender::DebugDrawer> mDebugDrawer;
|
|
|
|
osg::ref_ptr<osg::Group> mParentNode;
|
|
|
|
float mPhysicsDt;
|
|
|
|
DetourNavigator::CollisionShapeType mActorCollisionShapeType;
|
|
|
|
std::size_t mSimulationsCounter = 0;
|
|
std::array<std::vector<Simulation>, 2> mSimulations;
|
|
std::vector<std::pair<MWWorld::Ptr, osg::Vec3f>> mActorsPositions;
|
|
|
|
PhysicsSystem(const PhysicsSystem&);
|
|
PhysicsSystem& operator=(const PhysicsSystem&);
|
|
};
|
|
}
|
|
|
|
#endif
|