Files
ogre-jolt-prototype/physics.cpp
2026-01-23 19:09:00 +03:00

1805 lines
60 KiB
C++

// 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; // Non moving only collides with moving
case Layers::MOVING:
return true; // Moving collides with everything
case Layers::SENSORS:
return inObject2 ==
Layers::MOVING; // Non moving only collides with moving
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;
}
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;
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
{
Ogre::Vector3 d = mCameraNode->_getDerivedOrientation() *
Ogre::Vector3(0, 0, -1);
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]);
#if 0
float dproj1 = p1.dotProduct(d);
float dproj2 = p2.dotProduct(d);
float dproj3 = p3.dotProduct(d);
if (dproj1 < 0 && dproj2 < 0 && dproj3 < 0)
return;
if (dproj1 > 50 && dproj2 > 50 && dproj3 > 50)
return;
#endif
mLines.push_back({ p1, p2, cv });
#if 0
mTriangles.push_back({ { { inV1[0], inV1[1], inV1[2] },
{ inV2[0], inV2[1], inV2[2] },
{ inV3[0], inV3[1], inV3[2] } },
Ogre::ColourValue(color[0], color[1],
color[2], color[3]) });
#endif
}
#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 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] };
}
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 {
JPH::PhysicsSystem physics_system;
// 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;
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;
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;
};
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))
, debugDraw(false)
{
// 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();
// 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 = 1024;
// 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 = 1024;
// 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 = 1024;
// 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(JPH::Vec3(0, -0.1f, 0));
}
~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(dt, cCollisionSteps,
&temp_allocator, &job_system);
timeAccumulator -= fixedDeltaTime;
}
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;
body_interface.GetPositionAndRotation(bID, p, q);
Ogre::SceneNode *node = id2node[bID];
node->_setDerivedPosition(JoltPhysics::convert(p));
node->_setDerivedOrientation(JoltPhysics::convert(q));
}
for (JPH::Character *ch : characters) {
if (body_interface.IsAdded(ch->GetBodyID()))
ch->PostSimulation(0.1f);
}
if (debugDraw)
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;
}
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::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] };
JPH::MeshShapeSettings mesh_shape_settings(vertices, triangles);
JPH::ShapeSettings::ShapeResult result =
mesh_shape_settings.Create();
OgreAssert(result.Get(), "Can not create mesh shape");
return result.Get();
}
JPH::ShapeRefC createMeshShape(Ogre::String meshName)
{
Ogre::DefaultHardwareBufferManager dmgr;
Ogre::MeshPtr mesh =
Ogre::MeshManager::getSingleton().getByName(meshName);
if (!mesh->isLoaded()) {
mesh->setHardwareBufferManager(&dmgr);
mesh->load();
}
return createMeshShape(mesh);
}
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);
}
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
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;
}
};
void physics()
{
// Physics physics;
// physics.update(1.0f / 60.0f);
}
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;)
phys = new Physics(scnMgr, cameraNode, nullptr, &contacts);
}
JoltPhysicsWrapper::~JoltPhysicsWrapper()
{
if (phys)
delete phys;
}
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::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);
}
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);
}
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);
}
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);
}
template <>
JoltPhysicsWrapper *Ogre::Singleton<JoltPhysicsWrapper>::msSingleton = 0;