// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
// SPDX-License-Identifier: CC0-1.0
// This file is in the public domain. It serves as an example to start building your own application using Jolt Physics. Feel free to copy paste without attribution!

// The Jolt headers don't include Jolt.h. Always include Jolt.h before including any other Jolt header.
// You can use Jolt.h in your precompiled header to speed up compilation.
#include <Ogre.h>
#include <OgreHardwareBufferManager.h>
#include <Jolt/Jolt.h>

// Jolt includes
#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/Character/Character.h>
#include <Jolt/Physics/Character/CharacterID.h>
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
#include <Jolt/Physics/Collision/Shape/MeshShape.h>
#include <Jolt/Physics/Collision/Shape/ConvexHullShape.h>
#include <Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h>
#include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
#include <Jolt/Physics/Collision/Shape/CylinderShape.h>
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
#include <Jolt/Physics/Collision/Shape/MutableCompoundShape.h>
#include <Jolt/Physics/Collision/Shape/HeightFieldShape.h>
#include <Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.h>
#include <Jolt/Physics/Collision/ContactListener.h>
#include <Jolt/Physics/Collision/RayCast.h>
#include <Jolt/Physics/Collision/CastResult.h>
#include <Jolt/Physics/Collision/CollisionCollectorImpl.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Jolt/Physics/Body/BodyActivationListener.h>
#include <Jolt/Renderer/DebugRendererSimple.h>

// STL includes
#include <iostream>
#include <cstdarg>
#include <thread>
#include "physics.h"

// Disable common warnings triggered by Jolt, you can use JPH_SUPPRESS_WARNING_PUSH / JPH_SUPPRESS_WARNING_POP to store and restore the warning state
JPH_SUPPRESS_WARNINGS

// All Jolt symbols are in the JPH namespace

// Callback for traces, connect this to your own trace function if you have one
static void TraceImpl(const char *inFMT, ...)
{
	// Format the message
	va_list list;
	va_start(list, inFMT);
	char buffer[1024];
	vsnprintf(buffer, sizeof(buffer), inFMT, list);
	va_end(list);

	// Print to the TTY
	std::cout << buffer << std::endl;
}

#ifdef JPH_ENABLE_ASSERTS

// Callback for asserts, connect this to your own assert handler if you have one
static bool AssertFailedImpl(const char *inExpression, const char *inMessage,
			     const char *inFile, uint inLine)
{
	// Print to the TTY
	std::cout << inFile << ":" << inLine << ": (" << inExpression << ") "
		  << (inMessage != nullptr ? inMessage : "") << std::endl;

	// Breakpoint
	return true;
};

#endif // JPH_ENABLE_ASSERTS

/// Class that determines if two object layers can collide
class ObjectLayerPairFilterImpl : public JPH::ObjectLayerPairFilter {
public:
	virtual bool ShouldCollide(JPH::ObjectLayer inObject1,
				   JPH::ObjectLayer inObject2) const override
	{
		switch (inObject1) {
		case Layers::NON_MOVING:
			return inObject2 == Layers::MOVING ||
			       inObject2 == Layers::HAIR;
		case Layers::MOVING:
			return inObject2 != Layers::HEAD;
		case Layers::SENSORS:
			return inObject2 == Layers::MOVING;
		case Layers::HAIR:
			return inObject2 == Layers::NON_MOVING ||
			       inObject2 == Layers::MOVING ||
			       inObject2 == Layers::HAIR ||
			       inObject2 == Layers::HEAD;
		case Layers::HEAD:
			return inObject2 == Layers::HAIR;
		default:
			JPH_ASSERT(false);
			return false;
		}
	}
};

// BroadPhaseLayerInterface implementation
// This defines a mapping between object and broadphase layers.
class BPLayerInterfaceImpl final : public JPH::BroadPhaseLayerInterface {
public:
	BPLayerInterfaceImpl()
	{
		// Create a mapping table from object to broad phase layer
		mObjectToBroadPhase[Layers::NON_MOVING] =
			BroadPhaseLayers::NON_MOVING;
		mObjectToBroadPhase[Layers::MOVING] = BroadPhaseLayers::MOVING;
		mObjectToBroadPhase[Layers::SENSORS] = BroadPhaseLayers::MOVING;
		mObjectToBroadPhase[Layers::HAIR] = BroadPhaseLayers::MOVING;
		mObjectToBroadPhase[Layers::HEAD] = BroadPhaseLayers::MOVING;
	}

	virtual uint GetNumBroadPhaseLayers() const override
	{
		return BroadPhaseLayers::NUM_LAYERS;
	}

	virtual JPH::BroadPhaseLayer
	GetBroadPhaseLayer(JPH::ObjectLayer inLayer) const override
	{
		JPH_ASSERT(inLayer < Layers::NUM_LAYERS);
		return mObjectToBroadPhase[inLayer];
	}

#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
	virtual const char *
	GetBroadPhaseLayerName(JPH::BroadPhaseLayer inLayer) const override
	{
		switch ((JPH::BroadPhaseLayer::Type)inLayer) {
		case (JPH::BroadPhaseLayer::Type)BroadPhaseLayers::NON_MOVING:
			return "NON_MOVING";
		case (JPH::BroadPhaseLayer::Type)BroadPhaseLayers::MOVING:
			return "MOVING";
		default:
			JPH_ASSERT(false);
			return "INVALID";
		}
	}
#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED

private:
	JPH::BroadPhaseLayer mObjectToBroadPhase[Layers::NUM_LAYERS];
};

/// Class that determines if an object layer can collide with a broadphase layer
class ObjectVsBroadPhaseLayerFilterImpl
	: public JPH::ObjectVsBroadPhaseLayerFilter {
public:
	virtual bool ShouldCollide(JPH::ObjectLayer inLayer1,
				   JPH::BroadPhaseLayer inLayer2) const override
	{
		switch (inLayer1) {
		case Layers::NON_MOVING:
			return inLayer2 == BroadPhaseLayers::MOVING;
		case Layers::MOVING:
			return true;
		case Layers::SENSORS:
			return inLayer2 == BroadPhaseLayers::MOVING;
		case Layers::HAIR:
			return inLayer2 == BroadPhaseLayers::NON_MOVING ||
			       inLayer2 == BroadPhaseLayers::MOVING;
		case Layers::HEAD:
			return inLayer2 == BroadPhaseLayers::MOVING;
		default:
			JPH_ASSERT(false);
			return false;
		}
	}
};

#if 0
// An example contact listener
class MyContactListener : public JPH::ContactListener {
public:
	// See: ContactListener
	virtual JPH::ValidateResult OnContactValidate(
		const JPH::Body &inBody1, const JPH::Body &inBody2,
		JPH::RVec3Arg inBaseOffset,
		const JPH::CollideShapeResult &inCollisionResult) override
	{
		std::cout << "Contact validate callback" << std::endl;

		// Allows you to ignore a contact before it is created (using layers to not make objects collide is cheaper!)
		return JPH::ValidateResult::AcceptAllContactsForThisBodyPair;
	}

	virtual void OnContactAdded(const JPH::Body &inBody1,
				    const JPH::Body &inBody2,
				    const JPH::ContactManifold &inManifold,
				    JPH::ContactSettings &ioSettings) override
	{
		std::cout << "A contact was added" << std::endl;
	}

	virtual void
	OnContactPersisted(const JPH::Body &inBody1, const JPH::Body &inBody2,
			   const JPH::ContactManifold &inManifold,
			   JPH::ContactSettings &ioSettings) override
	{
		std::cout << "A contact was persisted" << std::endl;
	}

	virtual void
	OnContactRemoved(const JPH::SubShapeIDPair &inSubShapePair) override
	{
		std::cout << "A contact was removed" << std::endl;
	}
};

// An example activation listener
class MyBodyActivationListener : public JPH::BodyActivationListener {
public:
	virtual void OnBodyActivated(const JPH::BodyID &inBodyID,
				     JPH::uint64 inBodyUserData) override
	{
		std::cout << "A body got activated" << std::endl;
	}

	virtual void OnBodyDeactivated(const JPH::BodyID &inBodyID,
				       JPH::uint64 inBodyUserData) override
	{
		std::cout << "A body went to sleep" << std::endl;
	}
};
#endif
class MyCollector : public JPH::CollideShapeBodyCollector {
public:
	MyCollector(JPH::PhysicsSystem *inSystem,
		    JPH::RVec3Arg inSurfacePosition,
		    JPH::Vec3Arg inSurfaceNormal, float inDeltaTime)
		: mSystem(inSystem)
		, mSurfacePosition(inSurfacePosition)
		, mSurfaceNormal(inSurfaceNormal)
		, mDeltaTime(inDeltaTime)
	{
	}

	virtual void AddHit(const JPH::BodyID &inBodyID) override
	{
		mInWater.insert(inBodyID);
	}

private:
	JPH::PhysicsSystem *mSystem;
	JPH::RVec3 mSurfacePosition;
	float mDeltaTime;

public:
	std::set<JPH::BodyID> mInWater;
	JPH::Vec3 mSurfaceNormal;
};

class DebugRenderer final : public JPH::DebugRendererSimple {
	Ogre::ManualObject *mObject;
	Ogre::SceneManager *mScnMgr;
	Ogre::SceneNode *mCameraNode;
	Ogre::MaterialPtr mat;
	struct line {
		Ogre::Vector3 from;
		Ogre::Vector3 to;
		Ogre::ColourValue c;
	};
	std::vector<struct line> mLines;
	struct tri {
		Ogre::Vector3 p[3];
		Ogre::ColourValue c;
	};
	std::vector<struct tri> mTriangles;

public:
	DebugRenderer(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode);
	~DebugRenderer() override;
	void DrawLine(JPH::RVec3Arg inFrom, JPH::RVec3Arg inTo,
		      JPH::ColorArg inColor) override
	{
		JPH::Vec4 color = inColor.ToVec4();
		mLines.push_back(
			{ { (float)inFrom[0], (float)inFrom[1],
			    (float)inFrom[2] },
			  { (float)inTo[0], (float)inTo[1], (float)inTo[2] },
			  Ogre::ColourValue(color[0], color[1], color[2],
					    color[3]) });
	}
	void DrawTriangle(JPH::RVec3Arg inV1, JPH::RVec3Arg inV2,
			  JPH::RVec3Arg inV3, JPH::ColorArg inColor,
			  ECastShadow inCastShadow = ECastShadow::Off) override
	{
		JPH::Vec4 color = inColor.ToVec4();
		Ogre::Vector3 p1 = JoltPhysics::convert(inV1);
		Ogre::Vector3 p2 = JoltPhysics::convert(inV2);
		Ogre::Vector3 p3 = JoltPhysics::convert(inV3);
		Ogre::ColourValue cv(color[0], color[1], color[2], color[3]);

		mLines.push_back({ p1, p2, cv });
		mLines.push_back({ p2, p3, cv });
		mLines.push_back({ p3, p1, cv });
	}
#if 0
	Batch CreateTriangleBatch(const Triangle *inTriangles,
				  int inTriangleCount) override
	{
		return Batch();
	}
	Batch CreateTriangleBatch(const Vertex *inVertices, int inVertexCount,
				  const JPH::uint32 *inIndices,
				  int inIndexCount) override
	{
		return Batch();
	}
#endif
	void DrawText3D(JPH::RVec3Arg inPosition,
			const std::string_view &inString,
			JPH::ColorArg inColor = JPH::Color::sWhite,
			float inHeight = 0.5f) override
	{
		std::cout << "text\n";
	}
#if 0
	void DrawGeometry(JPH::RMat44Arg inModelMatrix,
			  const JPH::AABox &inWorldSpaceBounds,
			  float inLODScaleSq, JPH::ColorArg inModelColor,
			  const GeometryRef &inGeometry,
			  ECullMode inCullMode = ECullMode::CullBackFace,
			  ECastShadow inCastShadow = ECastShadow::On,
			  EDrawMode inDrawMode = EDrawMode::Solid) override
	{
		std::cout << "geometry\n";
	}
#endif
	void updateCameraPos()
	{
		SetCameraPos(JoltPhysics::convert(
			mCameraNode->_getDerivedPosition()));
	}
	void finish()
	{
		Ogre::Vector3 d = mCameraNode->_getDerivedOrientation() *
				  Ogre::Vector3(0, 0, -1);
		int i;
		mObject->clear();
		mObject->begin(mat, Ogre::RenderOperation::OT_LINE_LIST);
		for (i = 0; i < mLines.size(); i++) {
#if 0
			float dproj1 = mLines[i].from.dotProduct(d);
			float dproj2 = mLines[i].to.dotProduct(d);
			if (dproj1 < 0 && dproj2 < 0)
				continue;
			if (dproj1 > 50 && dproj2 > 50)
				continue;
#endif
			mObject->position(mLines[i].from);
			mObject->colour(mLines[i].c);
			mObject->position(mLines[i].to);
			mObject->colour(mLines[i].c);
		}
		mObject->end();
		mLines.clear();
#if 0
		mObject->begin(mat, Ogre::RenderOperation::OT_TRIANGLE_LIST);
		for (i = 0; i < mTriangles.size(); i++) {
			mObject->position(mTriangles[i].p[0]);
			mObject->colour(mTriangles[i].c);
			mObject->position(mTriangles[i].p[1]);
			mObject->colour(mTriangles[i].c);
			mObject->position(mTriangles[i].p[2]);
			mObject->colour(mTriangles[i].c);
			mObject->triangle(0, 1, 2);
			mObject->triangle(2, 1, 0);
			std::cout << mTriangles[i].p[0] << " ";
			std::cout << mTriangles[i].p[1] << " ";
			std::cout << mTriangles[i].p[2] << " " << std::endl;
		}
		mObject->end();
#else
#if 0
		mObject->begin(mat, Ogre::RenderOperation::OT_LINE_LIST);
		for (i = 0; i < mTriangles.size(); i++) {
			float dproj1 = mTriangles[i].p[0].dotProduct(d);
			float dproj2 = mTriangles[i].p[1].dotProduct(d);
			float dproj3 = mTriangles[i].p[2].dotProduct(d);
			if (dproj1 < 0 && dproj2 < 0 && dproj3 < 0)
				continue;
			if (dproj1 > 50 && dproj2 > 50 && dproj3 > 50)
				continue;
			mObject->position(mTriangles[i].p[0]);
			mObject->colour(mTriangles[i].c);
			mObject->position(mTriangles[i].p[1]);
			mObject->colour(mTriangles[i].c);
			mObject->position(mTriangles[i].p[1]);
			mObject->colour(mTriangles[i].c);
			mObject->position(mTriangles[i].p[2]);
			mObject->colour(mTriangles[i].c);
			mObject->position(mTriangles[i].p[2]);
			mObject->colour(mTriangles[i].c);
			mObject->position(mTriangles[i].p[0]);
			mObject->colour(mTriangles[i].c);
		}
		mObject->end();
#endif
#endif
		mTriangles.clear();
	}
};
DebugRenderer::DebugRenderer(Ogre::SceneManager *scnMgr,
			     Ogre::SceneNode *cameraNode)
	: mObject(scnMgr->createManualObject("joltDebugRenderer"))
	, mScnMgr(scnMgr)
	, mCameraNode(cameraNode)
	, mat(Ogre::MaterialManager::getSingleton().create("joltDebugDraw",
							   "General"))
{
	Ogre::Technique *technique = mat->getTechnique(0);
	Ogre::Pass *pass = technique->getPass(0);
	Ogre::ColourValue color(1, 0, 0, 1);
	pass->setCullingMode(Ogre::CullingMode::CULL_NONE);
	pass->setVertexColourTracking(Ogre::TVC_AMBIENT);
	pass->setLightingEnabled(false);
	pass->setDepthWriteEnabled(false);
	pass->setDepthCheckEnabled(false);
	DebugRenderer::Initialize();
	scnMgr->getRootSceneNode()->attachObject(mObject);
	mLines.reserve(6000);
	mObject->estimateVertexCount(64000);
	mObject->estimateIndexCount(8000);
	mObject->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY);
}
DebugRenderer::~DebugRenderer()
{
	mObject->getParentSceneNode()->detachObject(mObject);
	mScnMgr->destroyManualObject(mObject);
}

namespace JoltPhysics
{
struct ShapeData {
	JPH::ShapeRefC shape;
};
Ogre::Vector3 convert(const JPH::Vec3Arg &vec)
{
	return { vec[0], vec[1], vec[2] };
}
Ogre::Vector3 convert(const JPH::RVec3Arg &vec)
{
	return { (float)vec[0], (float)vec[1], (float)vec[2] };
}
JPH::RVec3 convert(const Ogre::Vector3 &vec)
{
	return { vec.x, vec.y, vec.z };
}
Ogre::Quaternion convert(const JPH::QuatArg &rot)
{
	return { rot.GetW(), rot.GetX(), rot.GetY(), rot.GetZ() };
}
JPH::Quat convert(const Ogre::Quaternion &rot)
{
	return { rot.x, rot.y, rot.z, rot.w };
}
void CompoundShapeBuilder::addShape(JPH::ShapeRefC shape,
				    const Ogre::Vector3 &position,
				    const Ogre::Quaternion &rotation)
{
	shapeSettings.AddShape(JoltPhysics::convert<JPH::Vec3>(position),
			       JoltPhysics::convert(rotation), shape.GetPtr());
}
JPH::ShapeRefC CompoundShapeBuilder::build()
{
	JPH::ShapeSettings::ShapeResult result = shapeSettings.Create();
	return result.Get();
}
ContactListener::ContactListener()
	: JPH::ContactListener()
	, dispatch(nullptr)
{
}
JPH::ValidateResult ContactListener::OnContactValidate(
	const JPH::Body &inBody1, const JPH::Body &inBody2,
	JPH::RVec3Arg inBaseOffset,
	const JPH::CollideShapeResult &inCollisionResult)
{
	return JPH::ValidateResult::AcceptAllContactsForThisBodyPair;
}
void ContactListener::OnContactAdded(const JPH::Body &inBody1,
				     const JPH::Body &inBody2,
				     const JPH::ContactManifold &inManifold,
				     JPH::ContactSettings &ioSettings)
{
	reports.push_back({ true, inBody1.GetID(), inBody2.GetID(), inManifold,
			    ioSettings });
}
void ContactListener::OnContactPersisted(const JPH::Body &inBody1,
					 const JPH::Body &inBody2,
					 const JPH::ContactManifold &inManifold,
					 JPH::ContactSettings &ioSettings)
{
}
void ContactListener::OnContactRemoved(const JPH::SubShapeIDPair &inSubShapePair)
{
	reports.push_back({ false, inSubShapePair.GetBody1ID(),
			    inSubShapePair.GetBody2ID(), JPH::ContactManifold(),
			    JPH::ContactSettings() });
}
void ContactListener::update()
{
	for (auto contact : reports) {
		bool handled = false;
		if (listeners.find(contact.id1) != listeners.end()) {
			listeners[contact.id1](contact);
			handled = true;
		}
		if (listeners.find(contact.id2) != listeners.end()) {
			listeners[contact.id2](contact);
			handled = true;
		}
		if (!handled && dispatch) {
			dispatch(contact);
		}
	}
	reports.clear();
}
}

class Physics {
	// We need a temp allocator for temporary allocations during the physics update. We're
	// pre-allocating 10 MB to avoid having to do allocations during the physics update.
	// B.t.w. 10 MB is way too much for this example but it is a typical value you can use.
	// If you don't want to pre-allocate you can also use TempAllocatorMalloc to fall back to
	// malloc / free.
	JPH::TempAllocatorImpl temp_allocator;
	// We need a job system that will execute physics jobs on multiple threads. Typically
	// you would implement the JobSystem interface yourself and let Jolt Physics run on top
	// of your own job scheduler. JobSystemThreadPool is an example implementation.
	JPH::JobSystemThreadPool job_system;

	// Create mapping table from object layer to broadphase layer
	// Note: As this is an interface, PhysicsSystem will take a reference to this so this instance needs to stay alive!
	// Also have a look at BroadPhaseLayerInterfaceTable or BroadPhaseLayerInterfaceMask for a simpler interface.
	BPLayerInterfaceImpl broad_phase_layer_interface;

	// Create class that filters object vs broadphase layers
	// Note: As this is an interface, PhysicsSystem will take a reference to this so this instance needs to stay alive!
	// Also have a look at ObjectVsBroadPhaseLayerFilterTable or ObjectVsBroadPhaseLayerFilterMask for a simpler interface.
	ObjectVsBroadPhaseLayerFilterImpl object_vs_broadphase_layer_filter;

	// Create class that filters object vs object layers
	// Note: As this is an interface, PhysicsSystem will take a reference to this so this instance needs to stay alive!
	// Also have a look at ObjectLayerPairFilterTable or ObjectLayerPairFilterMask for a simpler interface.
	ObjectLayerPairFilterImpl object_vs_object_layer_filter;

	JPH::PhysicsSystem physics_system;
	DebugRenderer *mDebugRenderer;
	std::map<JPH::BodyID, Ogre::SceneNode *> id2node;
	std::map<Ogre::SceneNode *, JPH::BodyID> node2id;
	std::set<JPH::Character *> characters;
	std::set<JPH::BodyID> characterBodies;
	bool debugDraw;
	JPH::Vec3 gravity = JPH::Vec3(0.0f, -9.8f, 0.0f);
	std::unordered_map<uint32_t, JPH::Ref<JPH::GroupFilterTable> > groupFilters;

public:
	class ActivationListener : public JPH::BodyActivationListener {
	public:
		virtual void OnBodyActivated(const JPH::BodyID &inBodyID,
					     JPH::uint64 inBodyUserData) = 0;
		virtual void OnBodyDeactivated(const JPH::BodyID &inBodyID,
					       JPH::uint64 inBodyUserData) = 0;
	};

	JPH::PhysicsSystem *getPhysicsSystem()
	{
		return &physics_system;
	}

	JPH::GroupFilterTable *getOrCreateGroupFilter(uint32_t groupId,
						       uint32_t numSubGroups)
	{
		auto it = groupFilters.find(groupId);
		if (it != groupFilters.end())
			return it->second.GetPtr();
		JPH::Ref<JPH::GroupFilterTable> filter =
			new JPH::GroupFilterTable(numSubGroups);
		groupFilters[groupId] = filter;
		return filter.GetPtr();
	}

	JPH::GroupFilterTable *getGroupFilter(uint32_t groupId) const
	{
		auto it = groupFilters.find(groupId);
		if (it != groupFilters.end())
			return it->second.GetPtr();
		return nullptr;
	}

	Physics(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
		ActivationListener *activationListener = nullptr,
		JPH::ContactListener *contactListener = nullptr)
		: temp_allocator(10 * 1024 * 1024)
		, job_system(JPH::cMaxPhysicsJobs, JPH::cMaxPhysicsBarriers,
			     std::thread::hardware_concurrency() - 1)
		, mDebugRenderer(new DebugRenderer(scnMgr, cameraNode))
		, object_vs_broadphase_layer_filter{}
		, object_vs_object_layer_filter{}
		, debugDraw(false)
	{
		static int instanceCount = 0;
		OgreAssert(instanceCount == 0, "Bad initialisation");
		instanceCount++;

		// This is the max amount of rigid bodies that you can add to the physics system. If you try to add more you'll get an error.
		// Note: This value is low because this is a simple test. For a real project use something in the order of 65536.
		const uint cMaxBodies = 65536;

		// This determines how many mutexes to allocate to protect rigid bodies from concurrent access. Set it to 0 for the default settings.
		const uint cNumBodyMutexes = 0;

		// This is the max amount of body pairs that can be queued at any time (the broad phase will detect overlapping
		// body pairs based on their bounding boxes and will insert them into a queue for the narrowphase). If you make this buffer
		// too small the queue will fill up and the broad phase jobs will start to do narrow phase work. This is slightly less efficient.
		// Note: This value is low because this is a simple test. For a real project use something in the order of 65536.
		const uint cMaxBodyPairs = 65536;

		// This is the maximum size of the contact constraint buffer. If more contacts (collisions between bodies) are detected than this
		// number then these contacts will be ignored and bodies will start interpenetrating / fall through the world.
		// Note: This value is low because this is a simple test. For a real project use something in the order of 10240.
		const uint cMaxContactConstraints = 10240;

		// Now we can create the actual physics system.
		physics_system.Init(cMaxBodies, cNumBodyMutexes, cMaxBodyPairs,
				    cMaxContactConstraints,
				    broad_phase_layer_interface,
				    object_vs_broadphase_layer_filter,
				    object_vs_object_layer_filter);

		// A body activation listener gets notified when bodies activate and go to sleep
		// Note that this is called from a job so whatever you do here needs to be thread safe.
		// Registering one is entirely optional.
		if (activationListener)
			physics_system.SetBodyActivationListener(
				activationListener);

		// A contact listener gets notified when bodies (are about to) collide, and when they separate again.
		// Note that this is called from a job so whatever you do here needs to be thread safe.
		// Registering one is entirely optional.
		if (contactListener)
			physics_system.SetContactListener(contactListener);

		// The main way to interact with the bodies in the physics system is through the body interface. There is a locking and a non-locking
		// variant of this. We're going to use the locking version (even though we're not planning to access bodies from multiple threads)
		JPH::BodyInterface &body_interface =
			physics_system.GetBodyInterface();
#if 0

		// Next we can create a rigid body to serve as the floor, we make a large box
		// Create the settings for the collision volume (the shape).
		// Note that for simple shapes (like boxes) you can also directly construct a BoxShape.
		JPH::BoxShapeSettings floor_shape_settings(
			JPH::Vec3(100.0f, 1.0f, 100.0f));
		floor_shape_settings
			.SetEmbedded(); // A ref counted object on the stack (base class RefTarget) should be marked as such to prevent it from being freed when its reference count goes to 0.

		// Create the shape
		JPH::ShapeSettings::ShapeResult floor_shape_result =
			floor_shape_settings.Create();
		JPH::ShapeRefC floor_shape =
			floor_shape_result
				.Get(); // We don't expect an error here, but you can check floor_shape_result for HasError() / GetError()

		// Create the settings for the body itself. Note that here you can also set other properties like the restitution / friction.
		JPH::Body *floor;
		JPH::BodyID sphere_id;
		{
			using namespace JPH::literals;
			JPH::BodyCreationSettings floor_settings(
				floor_shape, JPH::RVec3(0.0_r, -1.0_r, 0.0_r),
				JPH::Quat::sIdentity(),
				JPH::EMotionType::Static, Layers::NON_MOVING);
			floor = body_interface.CreateBody(
				floor_settings); // Note that if we run out of bodies this can return nullptr
			// Create the actual rigid body
			// Add it to the world
			body_interface.AddBody(floor->GetID(),
					       JPH::EActivation::DontActivate);

			// Now create a dynamic body to bounce on the floor
			// Note that this uses the shorthand version of creating and adding a body to the world
			JPH::BodyCreationSettings sphere_settings(
				new JPH::SphereShape(0.5f),
				JPH::RVec3(0.0_r, 2.0_r, 0.0_r),
				JPH::Quat::sIdentity(),
				JPH::EMotionType::Dynamic, Layers::MOVING);
			sphere_id = body_interface.CreateAndAddBody(
				sphere_settings, JPH::EActivation::Activate);

			// Now you can interact with the dynamic body, in this case we're going to give it a velocity.
			// (note that if we had used CreateBody then we could have set the velocity straight on the body before adding it to the physics system)
			body_interface.SetLinearVelocity(
				sphere_id, JPH::Vec3(0.0f, -5.0f, 0.0f));
		}

		// We simulate the physics world in discrete time steps. 60 Hz is a good rate to update the physics system.
		const float cDeltaTime = 1.0f / 60.0f;

		// Optional step: Before starting the physics simulation you can optimize the broad phase. This improves collision detection performance (it's pointless here because we only have 2 bodies).
		// You should definitely not call this every frame or when e.g. streaming in a new level section as it is an expensive operation.
		// Instead insert all new objects in batches instead of 1 at a time to keep the broad phase efficient.
		physics_system.OptimizeBroadPhase();

		// Now we're ready to simulate the body, keep simulating until it goes to sleep
		uint step = 0;
		while (body_interface.IsActive(sphere_id)) {
			// Next step
			++step;

			// Output current position and velocity of the sphere
			JPH::RVec3 position =
				body_interface.GetCenterOfMassPosition(
					sphere_id);
			JPH::Vec3 velocity =
				body_interface.GetLinearVelocity(sphere_id);
			std::cout << "Step " << step << ": Position = ("
				  << position.GetX() << ", " << position.GetY()
				  << ", " << position.GetZ()
				  << "), Velocity = (" << velocity.GetX()
				  << ", " << velocity.GetY() << ", "
				  << velocity.GetZ() << ")" << std::endl;

			// If you take larger steps than 1 / 60th of a second you need to do multiple collision steps in order to keep the simulation stable. Do 1 collision step per 1 / 60th of a second (round up).
			const int cCollisionSteps = 1;

			// Step the world
			physics_system.Update(cDeltaTime, cCollisionSteps,
					      &temp_allocator, &job_system);
		}

		// Remove the sphere from the physics system. Note that the sphere itself keeps all of its state and can be re-added at any time.
		body_interface.RemoveBody(sphere_id);

		// Destroy the sphere. After this the sphere ID is no longer valid.
		body_interface.DestroyBody(sphere_id);

		// Remove and destroy the floor
		body_interface.RemoveBody(floor->GetID());
		body_interface.DestroyBody(floor->GetID());
#endif
		physics_system.SetGravity(gravity);
	}
	~Physics()
	{
		// Unregisters all types with the factory and cleans up the default material
		JPH::UnregisterTypes();

		// Destroy the factory
		delete JPH::Factory::sInstance;
		JPH::Factory::sInstance = nullptr;
	}
	float timeAccumulator = 0.0f;
	float fixedDeltaTime = 1.0f / 60.0f;
	void update(float dt)
	{
		JPH::BodyIDVector bodies;
		physics_system.GetBodies(bodies);
		JPH::BodyInterface &body_interface =
			physics_system.GetBodyInterface();
		for (JPH::BodyID bID : bodies) {
			if (id2node.find(bID) == id2node.end())
				continue;
			if (body_interface.GetMotionType(bID) ==
			    JPH::EMotionType::Kinematic) {
				Ogre::SceneNode *node = id2node[bID];
				body_interface.SetPositionAndRotationWhenChanged(
					bID,
					JoltPhysics::convert(
						node->_getDerivedPosition()),
					JoltPhysics::convert(
						node->_getDerivedOrientation()),
					JPH::EActivation::Activate);
			}
		}
		for (JPH::Character *ch : characters) {
			JPH::BodyID bID = ch->GetBodyID();
			if (id2node.find(bID) == id2node.end())
				continue;
			Ogre::SceneNode *node = id2node[bID];
			ch->SetRotation(JoltPhysics::convert(
				node->_getDerivedOrientation()));
		}
		int cCollisionSteps = 1;
		timeAccumulator += dt;
		if (debugDraw)
			cCollisionSteps = 4;
		while (timeAccumulator >= fixedDeltaTime) {
			physics_system.Update(fixedDeltaTime, cCollisionSteps,
					      &temp_allocator, &job_system);
			timeAccumulator -= fixedDeltaTime;
		}
		/* Always consume remaining time with a partial step so
		 * physics never sits idle for a frame.  This keeps character
		 * movement in sync with animation root motion which advances
		 * every frame regardless of physics step timing. */
		if (timeAccumulator > 0.0001f) {
			physics_system.Update(timeAccumulator, cCollisionSteps,
					      &temp_allocator, &job_system);
			timeAccumulator = 0;
		}
		for (JPH::BodyID bID : bodies) {
			JPH::RVec3 p;
			JPH::Quat q;
			if (id2node.find(bID) == id2node.end())
				continue;
			if (!body_interface.IsAdded(bID))
				continue;
			if (!body_interface.IsActive(bID))
				continue;
			if (body_interface.GetMotionType(bID) !=
			    JPH::EMotionType::Dynamic)
				continue;
			/* Skip character bodies - they are handled separately */
			if (characterBodies.find(bID) != characterBodies.end())
				continue;
			body_interface.GetPositionAndRotation(bID, p, q);
			Ogre::SceneNode *node = id2node[bID];
			node->_setDerivedPosition(JoltPhysics::convert(p));
			node->_setDerivedOrientation(JoltPhysics::convert(q));
		}
		/* Sync character positions back to scene nodes */
		for (JPH::Character *ch : characters) {
			JPH::BodyID bID = ch->GetBodyID();
			if (body_interface.IsAdded(bID)) {
				ch->PostSimulation(0.1f);
				Ogre::SceneNode *node = id2node[bID];
				if (node)
					node->_setDerivedPosition(
						JoltPhysics::convert(
							ch->GetPosition()));
			}
		}
		if (debugDraw) {
			mDebugRenderer->updateCameraPos();
			physics_system.DrawBodies(
				JPH::BodyManager::DrawSettings(),
				mDebugRenderer);
		}
		mDebugRenderer->finish();
		mDebugRenderer->NextFrame();
#if 0
		std::cout << "bodies: " << physics_system.GetNumBodies()
			  << " / "
			  << physics_system.GetNumActiveBodies(
				     JPH::EBodyType::RigidBody)
			  << std::endl;
#endif
	}
	void setDebugDraw(bool enable)
	{
		debugDraw = enable;
	}
	JPH::Vec3 getGravity() const
	{
		return gravity;
	}
	void setGravity(const JPH::Vec3 &g)
	{
		gravity = g;
		physics_system.SetGravity(gravity);
	}
	static JPH::ShapeRefC createBoxShape(float x, float y, float z)
	{
		return new JPH::BoxShape(JPH::Vec3(x, y, z));
	}
	static JPH::ShapeRefC createCylinderShape(float halfHeight,
						  float radius)
	{
		return new JPH::CylinderShape(halfHeight, radius);
	}
	JPH::BodyCreationSettings createBodyCreationSettings(
		JPH::Shape *shape, JPH::RVec3 &position, JPH::Quat &rotation,
		JPH::EMotionType motionType, JPH::ObjectLayer layer)
	{
		JPH::BodyCreationSettings body_settings(
			shape, position, rotation, motionType, layer);
		return body_settings;
	}
	JPH::BodyCreationSettings
	createBodyCreationSettings(JPH::ShapeSettings *shapeSettings,
				   JPH::RVec3 &position, JPH::Quat &rotation,
				   JPH::EMotionType motionType,
				   JPH::ObjectLayer layer)
	{
		JPH::BodyCreationSettings body_settings(
			shapeSettings, position, rotation, motionType, layer);
		return body_settings;
	}
	void addBody(const JPH::BodyID &body, JPH::EActivation activation)
	{
		JPH::BodyInterface &body_interface =
			physics_system.GetBodyInterface();
		body_interface.AddBody(body, activation);
	}
	bool isAdded(const JPH::BodyID &body)
	{
		JPH::BodyInterface &body_interface =
			physics_system.GetBodyInterface();
		return body_interface.IsAdded(body);
	}
	void addAngularImpulse(const JPH::BodyID &id,
			       const Ogre::Vector3 &impulse)
	{
		JPH::BodyInterface &body_interface =
			physics_system.GetBodyInterface();
		body_interface.AddAngularImpulse(
			id, JoltPhysics::convert<JPH::Vec3>(impulse));
	}
	JPH::BodyID createBody(const JPH::BodyCreationSettings &settings,
			       ActivationListener *listener = nullptr)
	{
		JPH::BodyInterface &body_interface =
			physics_system.GetBodyInterface();
		JPH::Body *body = body_interface.CreateBody(settings);
		if (!body)
			return JPH::BodyID();
		return body->GetID();
	}
	void removeBody(const JPH::BodyID &id)
	{
		JPH::BodyInterface &body_interface =
			physics_system.GetBodyInterface();
		body_interface.RemoveBody(id);
	}
	void destroyBody(const JPH::BodyID &id)
	{
		JPH::BodyInterface &body_interface =
			physics_system.GetBodyInterface();
		body_interface.DestroyBody(id);
		node2id.erase(id2node[id]);
		id2node.erase(id);
	}
	JPH::ShapeRefC createShape(const JPH::ShapeSettings &settings)
	{
		JPH::ShapeSettings::ShapeResult result = settings.Create();
		JPH::ShapeRefC shape = result.Get();
		return shape;
	}
	JPH::BodyID createBody(const JPH::Shape *shape, float mass,
			       const Ogre::Vector3 &position,
			       const Ogre::Quaternion &rotation,
			       JPH::EMotionType motion, JPH::ObjectLayer layer,
			       ActivationListener *listener = nullptr)
	{
		JPH::BodyCreationSettings bodySettings(
			shape, JoltPhysics::convert(position),
			JoltPhysics::convert(rotation), motion, layer);
		if (mass > 0.001f) {
			JPH::MassProperties msp;
			msp.ScaleToMass(mass);
			bodySettings.mMassPropertiesOverride = msp;
		}
		JPH::BodyID id = createBody(bodySettings, listener);
		if (shape->GetType() == JPH::EShapeType::HeightField) {
			JPH::BodyInterface &body_interface =
				physics_system.GetBodyInterface();
			body_interface.SetFriction(id, 1.0f);
		}
		return id;
	}
	JPH::BodyID createBody(const JPH::Shape *shape, float mass,
			       Ogre::SceneNode *node, JPH::EMotionType motion,
			       JPH::ObjectLayer layer,
			       ActivationListener *listener = nullptr)
	{
		const Ogre::Vector3 &position = node->_getDerivedPosition();
		const Ogre::Quaternion &rotation =
			node->_getDerivedOrientation();
		std::cout << "body position: " << position << std::endl;
		JPH::BodyID id = createBody(shape, mass, position, rotation,
					    motion, layer, listener);
		id2node[id] = node;
		node2id[node] = id;
		return id;
	}
	JPH::BodyID createSensor(const JPH::Shape *shape,
				 const Ogre::Vector3 &position,
				 const Ogre::Quaternion &rotation,
				 JPH::EMotionType motion,
				 JPH::ObjectLayer layer)
	{
		JPH::BodyCreationSettings bodySettings(
			shape, JoltPhysics::convert(position),
			JoltPhysics::convert(rotation), motion, layer);
		bodySettings.mIsSensor = true;
		return createBody(bodySettings, nullptr);
	}
	JPH::BodyID createSensor(const JPH::Shape *shape, Ogre::SceneNode *node,
				 JPH::EMotionType motion,
				 JPH::ObjectLayer layer)
	{
		const Ogre::Vector3 &position = node->_getDerivedPosition();
		const Ogre::Quaternion &rotation =
			node->_getDerivedOrientation();
		std::cout << "body position: " << position << std::endl;
		JPH::BodyCreationSettings bodySettings(
			shape, JoltPhysics::convert(position),
			JoltPhysics::convert(rotation), motion, layer);
		bodySettings.mIsSensor = true;
		JPH::BodyID id = createBody(bodySettings);
		if (shape->GetType() == JPH::EShapeType::HeightField) {
			JPH::BodyInterface &body_interface =
				physics_system.GetBodyInterface();
			body_interface.SetFriction(id, 0.7f);
		}
		id2node[id] = node;
		node2id[node] = id;
		return id;
	}
	void addShapeToCompound(JPH::Ref<JPH::Shape> compoundShape,
				JPH::ShapeRefC childShape,
				const Ogre::Vector3 &position,
				const Ogre::Quaternion &rotation)
	{
		JPH::MutableCompoundShape *master =
			static_cast<JPH::MutableCompoundShape *>(
				compoundShape.GetPtr());
		master->AddShape(JoltPhysics::convert<JPH::Vec3>(position),
				 JoltPhysics::convert(rotation),
				 childShape.GetPtr());
	}
	class CharacterListener : public JPH::ContactListener {};
	JPH::CharacterBase *createCharacter(Ogre::SceneNode *node,
					    float characterHeight,
					    float characterRadius)
	{
		JPH::CharacterSettings settings;
		settings.mLayer = Layers::MOVING;
		float characterRadiusStanding = 0.2f;
		settings.mSupportingVolume =
			JPH::Plane(JPH::Vec3::sAxisY(), -0.2f);
		settings.mShape =
			JPH::RotatedTranslatedShapeSettings(
				JPH::Vec3(0,
					  0.5f * characterHeight +
						  characterRadius,
					  0),
				JPH::Quat::sIdentity(),
				new JPH::CapsuleShape(0.5f * characterHeight,
						      characterRadius))
				.Create()
				.Get();
		settings.mSupportingVolume =
			JPH::Plane(JPH::Vec3::sAxisY(), -characterRadius);
		JPH::Character *ch = new JPH::Character(
			&settings,
			JoltPhysics::convert(node->_getDerivedPosition()),
			JoltPhysics::convert(node->_getDerivedOrientation()), 0,
			&physics_system);
		JPH::BodyID id = ch->GetBodyID();
		id2node[id] = node;
		node2id[node] = id;
		characterBodies.insert(id);
		characters.insert(ch);
		return ch;
	}
	JPH::CharacterBase *createCharacter(Ogre::SceneNode *node,
					    JPH::ShapeRefC shape)
	{
		JPH::CharacterSettings settings;
		settings.mLayer = Layers::MOVING;
		settings.mShape = shape;
		settings.mSupportingVolume =
			JPH::Plane(JPH::Vec3::sAxisY(), -0.2f);
		JPH::Character *ch = new JPH::Character(
			&settings,
			JoltPhysics::convert(node->_getDerivedPosition()),
			JoltPhysics::convert(node->_getDerivedOrientation()), 0,
			&physics_system);
		JPH::BodyID id = ch->GetBodyID();
		id2node[id] = node;
		node2id[node] = id;
		characterBodies.insert(id);
		characters.insert(ch);
		return ch;
	}
	JPH::ShapeRefC createBoxShape(Ogre::Vector3 extents)
	{
		JPH::Vec3 h(extents.x, extents.y, extents.z);
		return new JPH::BoxShape(h);
	}
	JPH::ShapeRefC createSphereShape(float radius)
	{
		return new JPH::SphereShape(radius);
	}
	JPH::ShapeRefC createCapsuleShape(float halfHeightOfCylinder,
					  float radius)
	{
		return new JPH::CapsuleShape(halfHeightOfCylinder, radius);
	}
	JPH::ShapeRefC createMeshShape(Ogre::MeshPtr mesh)
	{
		JPH::VertexList vertices;
		JPH::IndexedTriangleList triangles;
		std::vector<uint32_t> indices;
		int count = mesh->getNumSubMeshes();
		int i, j;
		int indexCount = 0;
		int vertexCount = 0;
		int sharedVertexOffset = 0;
		for (i = 0; i < count; i++) {
			Ogre::SubMesh *submesh = mesh->getSubMesh(i);
			indexCount += submesh->indexData->indexCount;
			if (submesh->useSharedVertices)
				vertexCount +=
					mesh->sharedVertexData->vertexCount;
			else
				vertexCount += submesh->vertexData->vertexCount;
		}
		indices.reserve(indexCount);
		vertices.reserve(vertexCount);
		size_t currentVertexOffset = 0;
		bool added_shared = false;
		for (i = 0; i < count; i++) {
			Ogre::SubMesh *submesh = mesh->getSubMesh(i);
			Ogre::VertexData *vertex_data =
				submesh->useSharedVertices ?
					mesh->sharedVertexData :
					submesh->vertexData;
			bool add_vertices =
				(submesh->useSharedVertices && !added_shared) ||
				!submesh->useSharedVertices;
			if (add_vertices) {
				if (submesh->useSharedVertices)
					sharedVertexOffset = vertices.size();
				const Ogre::VertexDeclaration *decl =
					vertex_data->vertexDeclaration;
				const Ogre::VertexBufferBinding *bind =
					vertex_data->vertexBufferBinding;
				const Ogre::VertexElement *position_element =
					decl->findElementBySemantic(
						Ogre::VES_POSITION);
				if (!position_element)
					continue;
				Ogre::HardwareVertexBufferSharedPtr vbuf =
					bind->getBuffer(
						position_element->getSource());
				unsigned char *vertex_buffer = static_cast<
					unsigned char *>(vbuf->lock(
					Ogre::HardwareBuffer::HBL_READ_ONLY));
				int vertexSize = vbuf->getVertexSize();
				for (j = 0; j < vertex_data->vertexCount; j++) {
					float *position_data;
					position_element
						->baseVertexPointerToElement(
							vertex_buffer,
							&position_data);
					vertices.push_back(
						{ position_data[0],
						  position_data[1],
						  position_data[2] });
					vertex_buffer += vertexSize;
				}
				if (submesh->useSharedVertices)
					added_shared = true;
				vbuf->unlock();
			}
			Ogre::HardwareIndexBufferSharedPtr ibuf =
				submesh->indexData->indexBuffer;
			size_t numIndices = submesh->indexData->indexCount;
			size_t vertexOffset = submesh->useSharedVertices ?
						      sharedVertexOffset :
						      currentVertexOffset;
			if (ibuf->getType() ==
			    Ogre::HardwareIndexBuffer::IT_32BIT) {
				unsigned int *pIndices = static_cast<
					unsigned int *>(ibuf->lock(
					Ogre::HardwareBuffer::HBL_READ_ONLY));
				for (j = 0; j < numIndices; j++) {
					indices.push_back(
						(uint32_t)pIndices[j] +
						vertexOffset);
				}
				ibuf->unlock();
			} else {
				unsigned short *pIndices = static_cast<
					unsigned short *>(ibuf->lock(
					Ogre::HardwareBuffer::HBL_READ_ONLY));
				for (j = 0; j < numIndices; j++) {
					indices.push_back(
						(uint32_t)pIndices[j] +
						vertexOffset);
				}
				ibuf->unlock();
			}
			currentVertexOffset = vertices.size();
		}
		triangles.resize(indices.size() / 3);
		for (j = 0; j < indices.size() / 3; j++)
			triangles[j] = { indices[j * 3 + 0], indices[j * 3 + 1],
					 indices[j * 3 + 2] };

		// Validate we have enough data
		if (vertices.size() < 3) {
			Ogre::LogManager::getSingleton().logMessage(
				"ERROR: Mesh has too few vertices: " +
				mesh->getName() + " (" +
				Ogre::StringConverter::toString(
					vertices.size()) +
				")");
			return JPH::ShapeRefC();
		}
		if (triangles.size() < 1) {
			Ogre::LogManager::getSingleton().logMessage(
				"ERROR: Mesh has no triangles: " +
				mesh->getName());
			return JPH::ShapeRefC();
		}

		JPH::MeshShapeSettings mesh_shape_settings(vertices, triangles);

		// Configure settings to be more permissive
		mesh_shape_settings.mPerTriangleUserData = false;

		// Log mesh stats for debugging
		Ogre::LogManager::getSingleton().logMessage(
			"Creating mesh shape: " + mesh->getName() +
			" - Vertices: " +
			Ogre::StringConverter::toString(vertices.size()) +
			" - Triangles: " +
			Ogre::StringConverter::toString(triangles.size()));

		// Sanitize the mesh data to help with validation
		mesh_shape_settings.Sanitize();

		JPH::ShapeSettings::ShapeResult result =
			mesh_shape_settings.Create();

		if (!result.Get()) {
			Ogre::LogManager::getSingleton().logMessage(
				"ERROR: Failed to create mesh shape: " +
				mesh->getName() +
				" - Error: " + result.GetError().c_str());
			return JPH::ShapeRefC();
		}

		Ogre::LogManager::getSingleton().logMessage(
			"Successfully created mesh shape for: " +
			mesh->getName());
		return result.Get();
	}
	JPH::ShapeRefC createMeshShape(Ogre::String meshName)
	{
		// Use singleton pattern like convex hull does
		auto p = Ogre::DefaultHardwareBufferManager::getSingletonPtr();
		if (!p) {
			new Ogre::DefaultHardwareBufferManager;
			p = Ogre::DefaultHardwareBufferManager::getSingletonPtr();
		}

		Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().load(
			meshName, "General");
		if (mesh.get()) {
			if (!mesh->isLoaded()) {
				mesh->setHardwareBufferManager(p);
				mesh->load();
			}
			return createMeshShape(mesh);
		}

		Ogre::LogManager::getSingleton().logMessage(
			"ERROR: Could not load mesh for collider: " + meshName);
		return JPH::ShapeRefC();
	}
	JPH::ShapeRefC createConvexHullShape(Ogre::MeshPtr mesh)
	{
		std::vector<JPH::Vec3> vertices;
		JPH::IndexedTriangleList triangles;
		std::vector<uint32_t> indices;
		int count = mesh->getNumSubMeshes();
		int i, j;
		int indexCount = 0;
		int vertexCount = 0;
		int sharedVertexOffset = 0;
		for (i = 0; i < count; i++) {
			Ogre::SubMesh *submesh = mesh->getSubMesh(i);
			indexCount += submesh->indexData->indexCount;
			if (submesh->useSharedVertices)
				vertexCount +=
					mesh->sharedVertexData->vertexCount;
			else
				vertexCount += submesh->vertexData->vertexCount;
		}
		indices.reserve(indexCount);
		vertices.reserve(vertexCount);
		size_t currentVertexOffset = 0;
		bool added_shared = false;
		for (i = 0; i < count; i++) {
			Ogre::SubMesh *submesh = mesh->getSubMesh(i);
			Ogre::VertexData *vertex_data =
				submesh->useSharedVertices ?
					mesh->sharedVertexData :
					submesh->vertexData;
			bool add_vertices =
				(submesh->useSharedVertices && !added_shared) ||
				!submesh->useSharedVertices;
			if (add_vertices) {
				if (submesh->useSharedVertices)
					sharedVertexOffset = vertices.size();
				const Ogre::VertexDeclaration *decl =
					vertex_data->vertexDeclaration;
				const Ogre::VertexBufferBinding *bind =
					vertex_data->vertexBufferBinding;
				const Ogre::VertexElement *position_element =
					decl->findElementBySemantic(
						Ogre::VES_POSITION);
				if (!position_element)
					continue;
				Ogre::HardwareVertexBufferSharedPtr vbuf =
					bind->getBuffer(
						position_element->getSource());
				unsigned char *vertex_buffer = static_cast<
					unsigned char *>(vbuf->lock(
					Ogre::HardwareBuffer::HBL_READ_ONLY));
				int vertexSize = vbuf->getVertexSize();
				for (j = 0; j < vertex_data->vertexCount; j++) {
					float *position_data;
					position_element
						->baseVertexPointerToElement(
							vertex_buffer,
							&position_data);
					vertices.push_back(
						{ position_data[0],
						  position_data[1],
						  position_data[2] });
					vertex_buffer += vertexSize;
				}
				if (submesh->useSharedVertices)
					added_shared = true;
				vbuf->unlock();
			}
			Ogre::HardwareIndexBufferSharedPtr ibuf =
				submesh->indexData->indexBuffer;
			size_t numIndices = submesh->indexData->indexCount;
			size_t vertexOffset = submesh->useSharedVertices ?
						      sharedVertexOffset :
						      currentVertexOffset;
			if (ibuf->getType() ==
			    Ogre::HardwareIndexBuffer::IT_32BIT) {
				unsigned int *pIndices = static_cast<
					unsigned int *>(ibuf->lock(
					Ogre::HardwareBuffer::HBL_READ_ONLY));
				for (j = 0; j < numIndices; j++) {
					indices.push_back(
						(uint32_t)pIndices[j] +
						vertexOffset);
				}
				ibuf->unlock();
			} else {
				unsigned short *pIndices = static_cast<
					unsigned short *>(ibuf->lock(
					Ogre::HardwareBuffer::HBL_READ_ONLY));
				for (j = 0; j < numIndices; j++) {
					indices.push_back(
						(uint32_t)pIndices[j] +
						vertexOffset);
				}
				ibuf->unlock();
			}
			currentVertexOffset = vertices.size();
		}
		triangles.resize(indices.size() / 3);
		for (j = 0; j < indices.size() / 3; j++)
			triangles[j] = { indices[j * 3 + 0], indices[j * 3 + 1],
					 indices[j * 3 + 2] };
		JPH::ConvexHullShapeSettings mesh_shape_settings(
			vertices.data(), vertices.size());
		JPH::ShapeSettings::ShapeResult result =
			mesh_shape_settings.Create();
		OgreAssert(result.Get(), "Can not create mesh shape");
		return result.Get();
	}
	JPH::ShapeRefC createConvexHullShape(Ogre::String meshName)
	{
		auto p = Ogre::DefaultHardwareBufferManager::getSingletonPtr();
		if (!p) {
			new Ogre::DefaultHardwareBufferManager;
			p = Ogre::DefaultHardwareBufferManager::getSingletonPtr();
		}
		Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().load(
			meshName, "General");
		if (mesh.get()) {
			if (!mesh->isLoaded()) {
				mesh->setHardwareBufferManager(p);
				mesh->load();
			}
			return createConvexHullShape(mesh);
		}
		OgreAssert(mesh.get(), "No file " + meshName);
		return JPH::ShapeRefC();
	}
	/// Create a height field shape of inSampleCount * inSampleCount vertices.
	/// The height field is a surface defined by: inOffset + inScale * (x, inSamples[y * inSampleCount + x], y).
	/// where x and y are integers in the range x and y e [0, inSampleCount - 1].
	/// inSampleCount: inSampleCount / mBlockSize must be minimally 2 and a power of 2 is the most efficient in terms of performance and storage.
	/// inSamples: inSampleCount^2 vertices.
	/// inMaterialIndices: (inSampleCount - 1)^2 indices that index into inMaterialList.
	JPH::ShapeRefC createHeightfieldShape(const float *samples,
					      Ogre::Vector3 offset,
					      Ogre::Vector3 scale,
					      int sampleCount)
	{
		int i;
		JPH::HeightFieldShapeSettings heightfieldSettings(
			samples, JoltPhysics::convert<JPH::Vec3>(offset),
			JoltPhysics::convert<JPH::Vec3>(scale),
			(uint32_t)sampleCount);
		for (i = 0; i < sampleCount; i++) {
			memcpy(heightfieldSettings.mHeightSamples.data() +
				       sampleCount * i,
			       samples + sampleCount * (sampleCount - i - 1),
			       sizeof(float) * sampleCount);
		}
		JPH::ShapeSettings::ShapeResult result =
			heightfieldSettings.Create();
		OgreAssert(result.Get(), "Can not create heightfield shape");
		return result.Get();
	}
	JPH::ShapeRefC createMutableCompoundShape(
		const std::vector<JPH::ShapeRefC> &shapes,
		const std::vector<Ogre::Vector3> &positions,
		const std::vector<Ogre::Quaternion> &rotations)
	{
		int i;
		OgreAssert(shapes.size() == positions.size() &&
				   shapes.size() == rotations.size(),
			   "bad parameters");
		JPH::MutableCompoundShapeSettings settings;
		for (i = 0; i < shapes.size(); i++)
			settings.AddShape(
				JoltPhysics::convert<JPH::Vec3>(positions[i]),
				JoltPhysics::convert(rotations[i]),
				shapes[i].GetPtr());
		JPH::ShapeSettings::ShapeResult result = settings.Create();
		OgreAssert(result.Get(), "Can not create compound shape");
		return result.Get();
	}
	JPH::ShapeRefC createStaticCompoundShape(
		const std::vector<JPH::ShapeRefC> &shapes,
		const std::vector<Ogre::Vector3> &positions,
		const std::vector<Ogre::Quaternion> &rotations)
	{
		int i;
		OgreAssert(shapes.size() == positions.size() &&
				   shapes.size() == rotations.size(),
			   "bad parameters");
		JPH::StaticCompoundShapeSettings settings;
		for (i = 0; i < shapes.size(); i++)
			settings.AddShape(
				JoltPhysics::convert<JPH::Vec3>(positions[i]),
				JoltPhysics::convert(rotations[i]),
				shapes[i].GetPtr());
		JPH::ShapeSettings::ShapeResult result = settings.Create();
		OgreAssert(result.Get(), "Can not create compound shape");
		return result.Get();
	}
	JPH::ShapeRefC
	createOffsetCenterOfMassShape(const Ogre::Vector3 &offset,
				      JPH::ShapeRefC shape)
	{
		JPH::OffsetCenterOfMassShapeSettings settings(
			JoltPhysics::convert<JPH::Vec3>(offset),
			shape.GetPtr());
		JPH::ShapeSettings::ShapeResult result = settings.Create();
		OgreAssert(result.Get(), "Can not create com offset shape");
		return result.Get();
	}
	JPH::ShapeRefC
	createRotatedTranslatedShape(const Ogre::Vector3 &offset,
				     const Ogre::Quaternion rotation,
				     JPH::ShapeRefC shape)
	{
		return JPH::RotatedTranslatedShapeSettings(
			       JoltPhysics::convert<JPH::Vec3>(offset),
			       JoltPhysics::convert(rotation), shape)
			.Create()
			.Get();
	}
	void applyBuoyancyImpulse(JPH::BodyID id,
				  const Ogre::Vector3 &surfacePosition,
				  const Ogre::Vector3 &surfaceNormal,
				  float buoyancy, float linearDrag,
				  float angularDrag,
				  const Ogre::Vector3 &fluidVelocity,
				  const Ogre::Vector3 &gravity, float dt)
	{
		JPH::BodyLockWrite lock(physics_system.GetBodyLockInterface(),
					id);
		JPH::Body &body = lock.GetBody();
		body.ApplyBuoyancyImpulse(
			JoltPhysics::convert(surfacePosition),
			JoltPhysics::convert<JPH::Vec3>(surfaceNormal),
			buoyancy, linearDrag, angularDrag,
			JoltPhysics::convert<JPH::Vec3>(fluidVelocity),
			JoltPhysics::convert<JPH::Vec3>(gravity), dt);
	}
	void applyBuoyancyImpulse(JPH::BodyID id,
				  const Ogre::Vector3 &surfacePosition,
				  const Ogre::Vector3 &surfaceNormal,
				  float buoyancy, float linearDrag,
				  float angularDrag,
				  const Ogre::Vector3 &fluidVelocity, float dt)
	{
		JPH::BodyLockWrite lock(physics_system.GetBodyLockInterface(),
					id);
		JPH::Body &body = lock.GetBody();
		body.ApplyBuoyancyImpulse(
			JoltPhysics::convert(surfacePosition),
			JoltPhysics::convert<JPH::Vec3>(surfaceNormal),
			buoyancy, linearDrag, angularDrag,
			JoltPhysics::convert<JPH::Vec3>(fluidVelocity),
			physics_system.GetGravity(), dt);
	}
	bool isActive(JPH::BodyID id)
	{
		return physics_system.GetBodyInterface().IsActive(id);
	}
	void activate(JPH::BodyID id)
	{
		return physics_system.GetBodyInterface().ActivateBody(id);
	}
	Ogre::Vector3 getPosition(JPH::BodyID id)
	{
		return JoltPhysics::convert(
			physics_system.GetBodyInterface().GetPosition(id));
	}
	Ogre::Quaternion getRotation(JPH::BodyID id)
	{
		return JoltPhysics::convert(
			physics_system.GetBodyInterface().GetRotation(id));
	}
	void getPositionAndRotation(JPH::BodyID id, Ogre::Vector3 &position,
				    Ogre::Quaternion &rotation)
	{
		JPH::RVec3 _position;
		JPH::Quat _rotation;
		physics_system.GetBodyInterface().GetPositionAndRotation(
			id, _position, _rotation);
		position = JoltPhysics::convert(_position);
		rotation = JoltPhysics::convert(_rotation);
	}
	void setPosition(JPH::BodyID id, const Ogre::Vector3 &position,
			 bool activate = true)
	{
		physics_system.GetBodyInterface().SetPosition(
			id, JoltPhysics::convert(position),
			activate ? JPH::EActivation::Activate :
				   JPH::EActivation::DontActivate);
	}
	void setRotation(JPH::BodyID id, const Ogre::Quaternion &rotation,
			 bool activate = true)
	{
		physics_system.GetBodyInterface().SetRotation(
			id, JoltPhysics::convert(rotation),
			activate ? JPH::EActivation::Activate :
				   JPH::EActivation::DontActivate);
	}
	void setPositionAndRotation(JPH::BodyID id,
				    const Ogre::Vector3 &position,
				    const Ogre::Quaternion &rotation,
				    bool activate = true)
	{
		physics_system.GetBodyInterface().SetPositionAndRotation(
			id, JoltPhysics::convert(position),
			JoltPhysics::convert(rotation),
			activate ? JPH::EActivation::Activate :
				   JPH::EActivation::DontActivate);
	}
	Ogre::Vector3 getLinearVelocity(JPH::BodyID id)
	{
		return JoltPhysics::convert(
			physics_system.GetBodyInterface().GetLinearVelocity(
				id));
	}
	Ogre::Vector3 getAngularVelocity(JPH::BodyID id)
	{
		return JoltPhysics::convert(
			physics_system.GetBodyInterface().GetAngularVelocity(
				id));
	}
	float getFriction(JPH::BodyID id)
	{
		return physics_system.GetBodyInterface().GetFriction(id);
	}
	void setFriction(JPH::BodyID id, float friction)
	{
		return physics_system.GetBodyInterface().SetFriction(id,
								     friction);
	}
	float getGravityFactor(JPH::BodyID id)
	{
		return physics_system.GetBodyInterface().GetGravityFactor(id);
	}
	void setGravityFactor(JPH::BodyID id, float factor)
	{
		return physics_system.GetBodyInterface().SetGravityFactor(
			id, factor);
	}
	void broadphaseQuery(float dt, const Ogre::Vector3 &position,
			     std::set<JPH::BodyID> &inWater)
	{
		JPH::RVec3 surface_point = JoltPhysics::convert(
			position + Ogre::Vector3(0, -0.1f, 0));

		MyCollector collector(&physics_system, surface_point,
				      JPH::Vec3::sAxisY(), dt);
		// Apply buoyancy to all bodies that intersect with the water
		// Detects bodies up to 0.1 units above the surface point
		// and up to 1000 units below (deep underwater)
		JPH::AABox water_box(-JPH::Vec3(1000, 1000, 1000),
				     JPH::Vec3(1000, 0.1f, 1000));
		water_box.Translate(JPH::Vec3(surface_point));
		physics_system.GetBroadPhaseQuery().CollideAABox(
			water_box, collector,
			JPH::SpecifiedBroadPhaseLayerFilter(
				BroadPhaseLayers::MOVING),
			JPH::SpecifiedObjectLayerFilter(Layers::MOVING));
		inWater.clear();
		for (JPH::BodyID inBodyID : collector.mInWater) {
			inWater.insert(inBodyID);
#if 0
				std::cout << "addHit: "
					  << JoltPhysics::convert(
						     body.GetPosition())
					  << std::endl;
#endif
		}
	}
	bool raycastQuery(Ogre::Vector3 startPoint, Ogre::Vector3 endPoint,
			  Ogre::Vector3 &position, JPH::BodyID &id)
	{
		int i;
		Ogre::Vector3 direction = endPoint - startPoint;
		JPH::RRayCast ray{ JoltPhysics::convert(startPoint),
				   JoltPhysics::convert<JPH::Vec3>(direction) };
		JPH::RayCastResult hit;
		bool hadHit = physics_system.GetNarrowPhaseQuery().CastRay(
			ray, hit, {},
			JPH::SpecifiedObjectLayerFilter(Layers::NON_MOVING));
		if (hadHit) {
			position = JoltPhysics::convert(
				ray.GetPointOnRay(hit.mFraction));
			id = hit.mBodyID;
		}
		return hadHit;
	}
	bool bodyIsCharacter(JPH::BodyID id) const
	{
		return characterBodies.find(id) != characterBodies.end();
	}
	void destroyCharacter(std::shared_ptr<JPH::Character> ch)
	{
		characterBodies.erase(characterBodies.find(ch->GetBodyID()));
		characters.erase(ch.get());
		Ogre::SceneNode *node = id2node[ch->GetBodyID()];
		id2node.erase(ch->GetBodyID());
		node2id.erase(node);
		ch = nullptr;
	}
	Ogre::SceneNode *getSceneNodeFromBodyID(JPH::BodyID id) const
	{
		auto it = id2node.find(id);
		if (it != id2node.end())
			return it->second;
		return nullptr;
	}
	void setRootMotionCharacter(JPH::BodyID id, bool enabled)
	{
		(void)id;
		(void)enabled;
		/* No longer needed - root motion drives physics velocity,
		 * and physics writes position back to scene node normally. */
	}
};

void physics()
{
	// Physics physics;
	// physics.update(1.0f / 60.0f);
}

static std::unique_ptr<Physics> phys = nullptr;
JoltPhysicsWrapper::JoltPhysicsWrapper(Ogre::SceneManager *scnMgr,
				       Ogre::SceneNode *cameraNode)
	: Ogre::Singleton<JoltPhysicsWrapper>()
{
	// Register allocation hook. In this example we'll just let Jolt use malloc / free but you can override these if you want (see Memory.h).
	// This needs to be done before any other Jolt function is called.
	JPH::RegisterDefaultAllocator();

	// Install trace and assert callbacks
	JPH::Trace = TraceImpl;
	JPH_IF_ENABLE_ASSERTS(JPH::AssertFailed = AssertFailedImpl;)

	// Create a factory, this class is responsible for creating instances of classes based on their name or hash and is mainly used for deserialization of saved data.
	// It is not directly used in this example but still required.
	JPH::Factory::sInstance = new JPH::Factory();
	// Register all physics types with the factory and install their collision handlers with the CollisionDispatch class.
	// If you have your own custom shape types you probably need to register their handlers with the CollisionDispatch before calling this function.
	// If you implement your own default material (PhysicsMaterial::sDefault) make sure to initialize it before this function or else this function will create one for you.
	JPH::RegisterTypes();

	phys = std::make_unique<Physics>(scnMgr, cameraNode, nullptr,
					 &contacts);
}

JoltPhysicsWrapper::~JoltPhysicsWrapper()
{
}

void JoltPhysicsWrapper::update(float dt)
{
	phys->update(dt);
	contacts.update();
}

void JoltPhysicsWrapper::addBody(const JPH::BodyID &body,
				 JPH::EActivation activation)
{
	phys->addBody(body, activation);
}

bool JoltPhysicsWrapper::isAdded(const JPH::BodyID &body)
{
	return phys->isAdded(body);
}

JPH::ShapeRefC JoltPhysicsWrapper::createBoxShape(const Ogre::Vector3 &extents)
{
	return phys->createBoxShape(extents);
}

JPH::ShapeRefC JoltPhysicsWrapper::createSphereShape(float radius)
{
	return phys->createSphereShape(radius);
}

JPH::ShapeRefC JoltPhysicsWrapper::createCapsuleShape(float halfHeight,
						      float radius)
{
	return phys->createCapsuleShape(halfHeight, radius);
}

JPH::ShapeRefC JoltPhysicsWrapper::createCylinderShape(float halfHeight,
						       float radius)
{
	return phys->createCylinderShape(halfHeight, radius);
}

JPH::ShapeRefC JoltPhysicsWrapper::createMeshShape(Ogre::MeshPtr mesh)
{
	return phys->createMeshShape(mesh);
}

JPH::ShapeRefC JoltPhysicsWrapper::createMeshShape(Ogre::String meshName)
{
	return phys->createMeshShape(meshName);
}

JPH::ShapeRefC JoltPhysicsWrapper::createConvexHullShape(Ogre::MeshPtr mesh)
{
	return phys->createConvexHullShape(mesh);
}

JPH::ShapeRefC JoltPhysicsWrapper::createConvexHullShape(Ogre::String meshName)
{
	return phys->createConvexHullShape(meshName);
}

JPH::ShapeRefC JoltPhysicsWrapper::createHeightfieldShape(const float *samples,
							  Ogre::Vector3 offset,
							  Ogre::Vector3 scale,
							  int sampleCount)
{
	return phys->createHeightfieldShape(samples, offset, scale,
					    sampleCount);
}

JPH::ShapeRefC JoltPhysicsWrapper::createMutableCompoundShape(
	const std::vector<JPH::ShapeRefC> &shapes,
	const std::vector<Ogre::Vector3> &positions,
	const std::vector<Ogre::Quaternion> &rotations)
{
	return phys->createMutableCompoundShape(shapes, positions, rotations);
}

JPH::ShapeRefC JoltPhysicsWrapper::createStaticCompoundShape(
	const std::vector<JPH::ShapeRefC> &shapes,
	const std::vector<Ogre::Vector3> &positions,
	const std::vector<Ogre::Quaternion> &rotations)
{
	return phys->createStaticCompoundShape(shapes, positions, rotations);
}

JPH::ShapeRefC
JoltPhysicsWrapper::createOffsetCenterOfMassShape(const Ogre::Vector3 &offset,
						  JPH::ShapeRefC shape)
{
	return phys->createOffsetCenterOfMassShape(offset, shape);
}

JPH::ShapeRefC JoltPhysicsWrapper::createRotatedTranslatedShape(
	const Ogre::Vector3 &offset, const Ogre::Quaternion rotation,
	JPH::ShapeRefC shape)
{
	return phys->createRotatedTranslatedShape(offset, rotation, shape);
}

JPH::BodyID
JoltPhysicsWrapper::createBody(const JPH::BodyCreationSettings &settings)
{
	return phys->createBody(settings);
}
JPH::BodyID JoltPhysicsWrapper::createBody(const JPH::Shape *shape, float mass,
					   const Ogre::Vector3 &position,
					   const Ogre::Quaternion &rotation,
					   JPH::EMotionType motion,
					   JPH::ObjectLayer layer)
{
	return phys->createBody(shape, mass, position, rotation, motion, layer);
}
JPH::BodyID JoltPhysicsWrapper::createBody(const JPH::Shape *shape, float mass,
					   Ogre::SceneNode *node,
					   JPH::EMotionType motion,
					   JPH::ObjectLayer layer)
{
	return phys->createBody(shape, mass, node, motion, layer);
}
JPH::BodyID JoltPhysicsWrapper::createSensor(const JPH::Shape *shape,
					     const Ogre::Vector3 &position,
					     const Ogre::Quaternion &rotation,
					     JPH::EMotionType motion,
					     JPH::ObjectLayer layer)
{
	return phys->createSensor(shape, position, rotation, motion, layer);
}
JPH::BodyID JoltPhysicsWrapper::createSensor(const JPH::Shape *shape,
					     Ogre::SceneNode *node,
					     JPH::EMotionType motion,
					     JPH::ObjectLayer layer)
{
	return phys->createSensor(shape, node, motion, layer);
}
JPH::CharacterBase *JoltPhysicsWrapper::createCharacter(Ogre::SceneNode *node,
							float characterHeight,
							float characterRadius)
{
	return phys->createCharacter(node, characterHeight, characterRadius);
}
JPH::CharacterBase *JoltPhysicsWrapper::createCharacter(Ogre::SceneNode *node,
							JPH::ShapeRefC shape)
{
	return phys->createCharacter(node, shape);
}
void JoltPhysicsWrapper::addShapeToCompound(JPH::Ref<JPH::Shape> compoundShape,
					    JPH::ShapeRefC childShape,
					    const Ogre::Vector3 &position,
					    const Ogre::Quaternion &rotation)
{
	phys->addShapeToCompound(compoundShape, childShape, position, rotation);
}
void JoltPhysicsWrapper::removeBody(const JPH::BodyID &id)
{
	phys->removeBody(id);
}
void JoltPhysicsWrapper::destroyBody(const JPH::BodyID &id)
{
	phys->destroyBody(id);
}
void JoltPhysicsWrapper::setDebugDraw(bool enable)
{
	phys->setDebugDraw(enable);
}
Ogre::Vector3 JoltPhysicsWrapper::getGravity() const
{
	return JoltPhysics::convert(phys->getGravity());
}
void JoltPhysicsWrapper::setGravity(const Ogre::Vector3 &gravity)
{
	phys->setGravity(JoltPhysics::convert<JPH::Vec3>(gravity));
}
void JoltPhysicsWrapper::broadphaseQuery(float dt,
					 const Ogre::Vector3 &position,
					 std::set<JPH::BodyID> &inWater)
{
	phys->broadphaseQuery(dt, position, inWater);
}
void JoltPhysicsWrapper::applyBuoyancyImpulse(
	JPH::BodyID id, const Ogre::Vector3 &surfacePosition,
	const Ogre::Vector3 &surfaceNormal, float buoyancy, float linearDrag,
	float angularDrag, const Ogre::Vector3 &fluidVelocity,
	const Ogre::Vector3 &gravity, float dt)
{
	phys->applyBuoyancyImpulse(id, surfacePosition, surfaceNormal, buoyancy,
				   linearDrag, angularDrag, fluidVelocity,
				   gravity, dt);
}
void JoltPhysicsWrapper::applyBuoyancyImpulse(
	JPH::BodyID id, const Ogre::Vector3 &surfacePosition,
	const Ogre::Vector3 &surfaceNormal, float buoyancy, float linearDrag,
	float angularDrag, const Ogre::Vector3 &fluidVelocity, float dt)
{
	phys->applyBuoyancyImpulse(id, surfacePosition, surfaceNormal, buoyancy,
				   linearDrag, angularDrag, fluidVelocity, dt);
}
bool JoltPhysicsWrapper::isActive(JPH::BodyID id)
{
	return phys->isActive(id);
}
void JoltPhysicsWrapper::activate(JPH::BodyID id)
{
	phys->activate(id);
}
Ogre::Vector3 JoltPhysicsWrapper::getPosition(JPH::BodyID id)
{
	return phys->getPosition(id);
}
void JoltPhysicsWrapper::setPosition(JPH::BodyID id,
				     const Ogre::Vector3 &position,
				     bool activate)
{
	return phys->setPosition(id, position, activate);
}
Ogre::Quaternion JoltPhysicsWrapper::getRotation(JPH::BodyID id)
{
	return phys->getRotation(id);
}
void JoltPhysicsWrapper::setRotation(JPH::BodyID id,
				     const Ogre::Quaternion &rotation,
				     bool activate)
{
	phys->setRotation(id, rotation, activate);
}
void JoltPhysicsWrapper::getPositionAndRotation(JPH::BodyID id,
						Ogre::Vector3 &position,
						Ogre::Quaternion &rotation)
{
	phys->getPositionAndRotation(id, position, rotation);
}
void JoltPhysicsWrapper::setPositionAndRotation(
	JPH::BodyID id, const Ogre::Vector3 &position,
	const Ogre::Quaternion &rotation, bool activate)
{
	phys->setPositionAndRotation(id, position, rotation, activate);
}
Ogre::Vector3 JoltPhysicsWrapper::getLinearVelocity(JPH::BodyID id)
{
	return phys->getLinearVelocity(id);
}
Ogre::Vector3 JoltPhysicsWrapper::getAngularVelocity(JPH::BodyID id)
{
	return phys->getAngularVelocity(id);
}
float JoltPhysicsWrapper::getFriction(JPH::BodyID id)
{
	return phys->getFriction(id);
}
void JoltPhysicsWrapper::setFriction(JPH::BodyID id, float friction)
{
	phys->setFriction(id, friction);
}
float JoltPhysicsWrapper::getGravityFactor(JPH::BodyID id)
{
	return phys->getGravityFactor(id);
}
void JoltPhysicsWrapper::setGravityFactor(JPH::BodyID id, float factor)
{
	phys->setGravityFactor(id, factor);
}
void JoltPhysicsWrapper::addAngularImpulse(const JPH::BodyID &id,
					   const Ogre::Vector3 &impulse)
{
	return phys->addAngularImpulse(id, impulse);
}
void JoltPhysicsWrapper::setDispatch(
	std::function<
		void(const JoltPhysics::ContactListener::ContactReport &report)>
		dispatcher)
{
	contacts.setDispatch(dispatcher);
}
void JoltPhysicsWrapper::addContactListener(
	const JPH::BodyID &id,
	std::function<
		void(const JoltPhysics::ContactListener::ContactReport &report)>
		listener)
{
	contacts.addListener(id, listener);
}
void JoltPhysicsWrapper::removeContactListener(const JPH::BodyID &id)
{
	contacts.removeListener(id);
}
bool JoltPhysicsWrapper::raycastQuery(Ogre::Vector3 startPoint,
				      Ogre::Vector3 endPoint,
				      Ogre::Vector3 &position, JPH::BodyID &id)
{
	return phys->raycastQuery(startPoint, endPoint, position, id);
}

bool JoltPhysicsWrapper::bodyIsCharacter(JPH::BodyID id) const
{
	return phys->bodyIsCharacter(id);
}

void JoltPhysicsWrapper::destroyCharacter(std::shared_ptr<JPH::Character> ch)
{
	phys->destroyCharacter(ch);
}

Ogre::SceneNode *
JoltPhysicsWrapper::getSceneNodeFromBodyID(JPH::BodyID id) const
{
	return phys->getSceneNodeFromBodyID(id);
}

void JoltPhysicsWrapper::setRootMotionCharacter(JPH::BodyID id, bool enabled)
{
	phys->setRootMotionCharacter(id, enabled);
}

JPH::PhysicsSystem *JoltPhysicsWrapper::getPhysicsSystem() const
{
	return phys->getPhysicsSystem();
}

JPH::GroupFilterTable *JoltPhysicsWrapper::getOrCreateGroupFilter(
	uint32_t groupId, uint32_t numSubGroups)
{
	return phys->getOrCreateGroupFilter(groupId, numSubGroups);
}

JPH::GroupFilterTable *JoltPhysicsWrapper::getGroupFilter(uint32_t groupId) const
{
	return phys->getGroupFilter(groupId);
}

template <>
JoltPhysicsWrapper *Ogre::Singleton<JoltPhysicsWrapper>::msSingleton = 0;
