Files
OpenEngine/open_engine/include/open_engine/physics.hpp

192 lines
6.4 KiB
C++

#ifndef PHYSICS_HPP
#define PHYSICS_HPP
#include "logging.hpp"
#include <Jolt/Jolt.h>
#include <Jolt/Physics/Body/BodyInterface.h>
#include <Jolt/RegisterTypes.h>
#include <Jolt/Core/Factory.h>
#include <Jolt/Core/TempAllocator.h>
#include <Jolt/Core/JobSystemThreadPool.h>
#include <Jolt/Physics/PhysicsSettings.h>
#include <Jolt/Physics/PhysicsSystem.h>
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Jolt/Physics/Body/BodyActivationListener.h>
#include <cstdarg>
#include <glm/glm.hpp>
using namespace JPH;
using namespace JPH::literals;
// Callback for traces, connect this to your own trace function if you have one
static void TraceImpl(const char *in_fmt, ...)
{
// Format the message
va_list list;
va_start(list, in_fmt);
char buffer[1024];
vsnprintf(buffer, sizeof(buffer), in_fmt, list);
va_end(list);
OE_CORE_TRACE("Jolt: {}", buffer);
}
#ifdef JPH_ENABLE_ASSERTS
// Callback for asserts, connect this to your own assert handler if you have one
static bool AssertFailedImpl(const char *in_expression, const char *in_message, const char *in_file, uint in_line)
{
OE_CORE_ERROR("Jolt: Assert in file: {}:{} :({}) {}", in_file, in_line, in_expression, (in_message != nullptr? in_message : ""));
// Breakpoint
return true;
};
#endif // JPH_ENABLE_ASSERTS
// Layer that objects can be in, determines which other objects it can collide with
// Typically you at least want to have 1 layer for moving bodies and 1 layer for static bodies, but you can have more
// layers if you want. E.g. you could have a layer for high detail collision (which is not used by the physics simulation
// but only if you do collision testing).
namespace Layers {
static constexpr ObjectLayer NON_MOVING = 0;
static constexpr ObjectLayer MOVING = 1;
static constexpr ObjectLayer NUM_LAYERS = 2;
};
class ObjectLayerPairFilterImpl : public ObjectLayerPairFilter
{
public:
virtual bool ShouldCollide(ObjectLayer object1, ObjectLayer object2) const override;
};
// Each broadphase layer results in a separate bounding volume tree in the broad phase. You at least want to have
// a layer for non-moving and moving objects to avoid having to update a tree full of static objects every frame.
// You can have a 1-on-1 mapping between object layers and broadphase layers (like in this case) but if you have
// many object layers you'll be creating many broad phase trees, which is not efficient. If you want to fine tune
// your broadphase layers define JPH_TRACK_BROADPHASE_STATS and look at the stats reported on the TTY.
namespace BroadPhaseLayers
{
static constexpr BroadPhaseLayer NON_MOVING(0);
static constexpr BroadPhaseLayer MOVING(1);
static constexpr uint NUM_LAYERS(2);
};
inline JPH::Vec3 ToJolt(const glm::vec3& v) { return JPH::Vec3(v.x, v.y, v.z); };
class BPLayerInterfaceImpl final : public BroadPhaseLayerInterface
{
public:
BPLayerInterfaceImpl()
{
object_to_broad_phase[Layers::NON_MOVING] = BroadPhaseLayers::NON_MOVING;
object_to_broad_phase[Layers::MOVING] = BroadPhaseLayers::MOVING;
}
virtual uint GetNumBroadPhaseLayers() const override { return BroadPhaseLayers::NUM_LAYERS; };
virtual BroadPhaseLayer GetBroadPhaseLayer(ObjectLayer layer) const override
{
JPH_ASSERT(layer < Layers::NUM_LAYERS);
return object_to_broad_phase[layer];
}
private:
BroadPhaseLayer object_to_broad_phase[Layers::NUM_LAYERS];
};
class ObjectVsBroadPhaseLayerFilterImpl : public ObjectVsBroadPhaseLayerFilter
{
public:
virtual bool ShouldCollide(ObjectLayer layer1, BroadPhaseLayer layer2) const override
{
switch (layer1)
{
case Layers::NON_MOVING:
return layer2 == BroadPhaseLayers::MOVING;
case Layers::MOVING:
return true;
default:
JPH_ASSERT(false);
return false;
}
}
};
// An example contact listener
class MyContactListener : public ContactListener
{
public:
// See: ContactListener
virtual ValidateResult OnContactValidate(const Body &inBody1, const Body &inBody2, RVec3Arg inBaseOffset, const CollideShapeResult &inCollisionResult) override
{
OE_CORE_TRACE("Jolt: Contact validate callback");
// Allows you to ignore a contact before it is created (using layers to not make objects collide is cheaper!)
return ValidateResult::AcceptAllContactsForThisBodyPair;
}
virtual void OnContactAdded(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) override
{
OE_CORE_TRACE("Jolt: A contact was added");
}
virtual void OnContactPersisted(const Body &inBody1, const Body &inBody2, const ContactManifold &inManifold, ContactSettings &ioSettings) override
{
OE_CORE_TRACE("Jolt: A contact was persisted");
}
virtual void OnContactRemoved(const SubShapeIDPair &inSubShapePair) override
{
OE_CORE_TRACE("Jolt: A contact was removed");
}
};
// An example activation listener
class MyBodyActivationListener : public BodyActivationListener
{
public:
virtual void OnBodyActivated(const BodyID &inBodyID, uint64 inBodyUserData) override
{
OE_CORE_TRACE("Jolt: A body got activated");
}
virtual void OnBodyDeactivated(const BodyID &inBodyID, uint64 inBodyUserData) override
{
OE_CORE_TRACE("Jolt: A body went to sleep");
}
};
namespace OpenEngine {
// Consider making this static like the renderer
class PhysicsEngine
{
public:
PhysicsEngine();
~PhysicsEngine();
// TODO: Temp, make abstract function to create a body
BodyInterface& GetBodyInterface() { return physics_system.GetBodyInterface(); };
void Update( float delta_time, int collision_steps);
private:
PhysicsSystem physics_system;
Ref<TempAllocatorImpl> tmp_allocator;
Ref<JobSystemThreadPool> job_system;
MyBodyActivationListener body_activation_listener;
MyContactListener contact_listener;
BPLayerInterfaceImpl broad_phase_layer_interface;
ObjectVsBroadPhaseLayerFilterImpl object_vs_broadphase_layer_filter;
ObjectLayerPairFilterImpl object_vs_object_layer_filter;
};
}
#endif // PHYSICS_HPP