Add JoltPhysics3D for a performance test

This commit is contained in:
SirLynix
2023-03-15 13:44:58 +01:00
committed by Jérôme Leclercq
parent bd4c2d6ee7
commit c5ac142888
40 changed files with 2555 additions and 95 deletions

View File

@@ -72,7 +72,7 @@ namespace Nz
std::size_t primitiveCount = list.GetSize();
if (primitiveCount > 1)
{
std::vector<CompoundCollider3D::ChildCollider> childColliders(primitiveCount);
std::vector<BulletCompoundCollider3D::ChildCollider> childColliders(primitiveCount);
for (unsigned int i = 0; i < primitiveCount; ++i)
{
@@ -81,12 +81,12 @@ namespace Nz
childColliders[i].offsetMatrix = primitive.matrix;
}
return std::make_shared<CompoundCollider3D>(std::move(childColliders));
return std::make_shared<BulletCompoundCollider3D>(std::move(childColliders));
}
else if (primitiveCount > 0)
return CreateGeomFromPrimitive(list.GetPrimitive(0));
else
return std::make_shared<NullCollider3D>();
return std::make_shared<BulletNullCollider3D>();
}
std::shared_ptr<BulletCollider3D> BulletCollider3D::CreateGeomFromPrimitive(const Primitive& primitive)
@@ -94,17 +94,17 @@ namespace Nz
switch (primitive.type)
{
case PrimitiveType::Box:
return std::make_shared<BoxCollider3D>(primitive.box.lengths);
return std::make_shared<BulletBoxCollider3D>(primitive.box.lengths);
case PrimitiveType::Cone:
return std::make_shared<ConeCollider3D>(primitive.cone.length, primitive.cone.radius);
return std::make_shared<BulletConeCollider3D>(primitive.cone.length, primitive.cone.radius);
case PrimitiveType::Plane:
return std::make_shared<BoxCollider3D>(Vector3f(primitive.plane.size.x, 0.01f, primitive.plane.size.y));
return std::make_shared<BulletBoxCollider3D>(Vector3f(primitive.plane.size.x, 0.01f, primitive.plane.size.y));
///TODO: PlaneGeom?
case PrimitiveType::Sphere:
return std::make_shared<SphereCollider3D>(primitive.sphere.size);
return std::make_shared<BulletSphereCollider3D>(primitive.sphere.size);
}
NazaraError("Primitive type not handled (0x" + NumberToString(UnderlyingCast(primitive.type), 16) + ')');
@@ -113,15 +113,15 @@ namespace Nz
/********************************** BoxCollider3D **********************************/
BoxCollider3D::BoxCollider3D(const Vector3f& lengths) :
BulletBoxCollider3D::BulletBoxCollider3D(const Vector3f& lengths) :
m_lengths(lengths)
{
m_shape = std::make_unique<btBoxShape>(ToBullet(m_lengths * 0.5f));
}
BoxCollider3D::~BoxCollider3D() = default;
BulletBoxCollider3D::~BulletBoxCollider3D() = default;
void BoxCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
void BulletBoxCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
{
auto InsertVertex = [&](float x, float y, float z) -> UInt16
{
@@ -163,59 +163,59 @@ namespace Nz
InsertEdge(v3, v7);
}
btCollisionShape* BoxCollider3D::GetShape() const
btCollisionShape* BulletBoxCollider3D::GetShape() const
{
return m_shape.get();
}
Vector3f BoxCollider3D::GetLengths() const
Vector3f BulletBoxCollider3D::GetLengths() const
{
return m_lengths;
}
ColliderType3D BoxCollider3D::GetType() const
ColliderType3D BulletBoxCollider3D::GetType() const
{
return ColliderType3D::Box;
}
/******************************** CapsuleCollider3D ********************************/
CapsuleCollider3D::CapsuleCollider3D(float length, float radius) :
BulletCapsuleCollider3D::BulletCapsuleCollider3D(float length, float radius) :
m_length(length),
m_radius(radius)
{
m_shape = std::make_unique<btCapsuleShape>(radius, length);
}
CapsuleCollider3D::~CapsuleCollider3D() = default;
BulletCapsuleCollider3D::~BulletCapsuleCollider3D() = default;
void CapsuleCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
void BulletCapsuleCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
{
}
float CapsuleCollider3D::GetLength() const
float BulletCapsuleCollider3D::GetLength() const
{
return m_length;
}
float CapsuleCollider3D::GetRadius() const
float BulletCapsuleCollider3D::GetRadius() const
{
return m_radius;
}
btCollisionShape* CapsuleCollider3D::GetShape() const
btCollisionShape* BulletCapsuleCollider3D::GetShape() const
{
return m_shape.get();
}
ColliderType3D CapsuleCollider3D::GetType() const
ColliderType3D BulletCapsuleCollider3D::GetType() const
{
return ColliderType3D::Capsule;
}
/******************************* CompoundCollider3D ********************************/
CompoundCollider3D::CompoundCollider3D(std::vector<ChildCollider> childs) :
BulletCompoundCollider3D::BulletCompoundCollider3D(std::vector<ChildCollider> childs) :
m_childs(std::move(childs))
{
m_shape = std::make_unique<btCompoundShape>();
@@ -226,67 +226,67 @@ namespace Nz
}
}
CompoundCollider3D::~CompoundCollider3D() = default;
BulletCompoundCollider3D::~BulletCompoundCollider3D() = default;
void CompoundCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
void BulletCompoundCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
{
for (const auto& child : m_childs)
child.collider->BuildDebugMesh(vertices, indices, offsetMatrix * child.offsetMatrix);
}
auto CompoundCollider3D::GetGeoms() const -> const std::vector<ChildCollider>&
auto BulletCompoundCollider3D::GetGeoms() const -> const std::vector<ChildCollider>&
{
return m_childs;
}
btCollisionShape* CompoundCollider3D::GetShape() const
btCollisionShape* BulletCompoundCollider3D::GetShape() const
{
return m_shape.get();
}
ColliderType3D CompoundCollider3D::GetType() const
ColliderType3D BulletCompoundCollider3D::GetType() const
{
return ColliderType3D::Compound;
}
/********************************* ConeCollider3D **********************************/
ConeCollider3D::ConeCollider3D(float length, float radius) :
BulletConeCollider3D::BulletConeCollider3D(float length, float radius) :
m_length(length),
m_radius(radius)
{
m_shape = std::make_unique<btConeShape>(radius, length);
}
ConeCollider3D::~ConeCollider3D() = default;
BulletConeCollider3D::~BulletConeCollider3D() = default;
void ConeCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
void BulletConeCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
{
}
float ConeCollider3D::GetLength() const
float BulletConeCollider3D::GetLength() const
{
return m_length;
}
float ConeCollider3D::GetRadius() const
float BulletConeCollider3D::GetRadius() const
{
return m_radius;
}
btCollisionShape* ConeCollider3D::GetShape() const
btCollisionShape* BulletConeCollider3D::GetShape() const
{
return m_shape.get();
}
ColliderType3D ConeCollider3D::GetType() const
ColliderType3D BulletConeCollider3D::GetType() const
{
return ColliderType3D::Cone;
}
/****************************** ConvexCollider3D *******************************/
ConvexCollider3D::ConvexCollider3D(SparsePtr<const Vector3f> vertices, unsigned int vertexCount)
BulletConvexCollider3D::BulletConvexCollider3D(SparsePtr<const Vector3f> vertices, unsigned int vertexCount)
{
static_assert(std::is_same_v<btScalar, float>);
@@ -294,9 +294,9 @@ namespace Nz
m_shape->optimizeConvexHull();
}
ConvexCollider3D::~ConvexCollider3D() = default;
BulletConvexCollider3D::~BulletConvexCollider3D() = default;
void ConvexCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
void BulletConvexCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
{
tsl::ordered_map<Vector3f, UInt16> vertexCache;
auto InsertVertex = [&](const Vector3f& position) -> UInt16
@@ -324,19 +324,19 @@ namespace Nz
}
}
btCollisionShape* ConvexCollider3D::GetShape() const
btCollisionShape* BulletConvexCollider3D::GetShape() const
{
return m_shape.get();
}
ColliderType3D ConvexCollider3D::GetType() const
ColliderType3D BulletConvexCollider3D::GetType() const
{
return ColliderType3D::ConvexHull;
}
/******************************* CylinderCollider3D ********************************/
CylinderCollider3D::CylinderCollider3D(float length, float radius) :
BulletCylinderCollider3D::BulletCylinderCollider3D(float length, float radius) :
m_length(length),
m_radius(radius)
{
@@ -344,85 +344,85 @@ namespace Nz
m_shape = std::make_unique<btCylinderShape>(btVector3{ radius, length, radius });
}
CylinderCollider3D::~CylinderCollider3D() = default;
BulletCylinderCollider3D::~BulletCylinderCollider3D() = default;
void CylinderCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
void BulletCylinderCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
{
}
float CylinderCollider3D::GetLength() const
float BulletCylinderCollider3D::GetLength() const
{
return m_length;
}
float CylinderCollider3D::GetRadius() const
float BulletCylinderCollider3D::GetRadius() const
{
return m_radius;
}
btCollisionShape* CylinderCollider3D::GetShape() const
btCollisionShape* BulletCylinderCollider3D::GetShape() const
{
return m_shape.get();
}
ColliderType3D CylinderCollider3D::GetType() const
ColliderType3D BulletCylinderCollider3D::GetType() const
{
return ColliderType3D::Cylinder;
}
/********************************* NullCollider3D **********************************/
NullCollider3D::NullCollider3D()
BulletNullCollider3D::BulletNullCollider3D()
{
m_shape = std::make_unique<btEmptyShape>();
}
NullCollider3D::~NullCollider3D() = default;
BulletNullCollider3D::~BulletNullCollider3D() = default;
void NullCollider3D::BuildDebugMesh(std::vector<Vector3f>& /*vertices*/, std::vector<UInt16>& /*indices*/, const Matrix4f& /*offsetMatrix*/) const
void BulletNullCollider3D::BuildDebugMesh(std::vector<Vector3f>& /*vertices*/, std::vector<UInt16>& /*indices*/, const Matrix4f& /*offsetMatrix*/) const
{
}
void NullCollider3D::ComputeInertia(float /*mass*/, Vector3f* inertia) const
void BulletNullCollider3D::ComputeInertia(float /*mass*/, Vector3f* inertia) const
{
inertia->Set(1.f, 1.f, 1.f);
}
btCollisionShape* NullCollider3D::GetShape() const
btCollisionShape* BulletNullCollider3D::GetShape() const
{
return m_shape.get();
}
ColliderType3D NullCollider3D::GetType() const
ColliderType3D BulletNullCollider3D::GetType() const
{
return ColliderType3D::Null;
}
/******************************** SphereCollider3D *********************************/
SphereCollider3D::SphereCollider3D(float radius) :
BulletSphereCollider3D::BulletSphereCollider3D(float radius) :
m_radius(radius)
{
m_shape = std::make_unique<btSphereShape>(radius);
}
SphereCollider3D::~SphereCollider3D() = default;
BulletSphereCollider3D::~BulletSphereCollider3D() = default;
void SphereCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
void BulletSphereCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
{
}
float SphereCollider3D::GetRadius() const
float BulletSphereCollider3D::GetRadius() const
{
return m_radius;
}
btCollisionShape* SphereCollider3D::GetShape() const
btCollisionShape* BulletSphereCollider3D::GetShape() const
{
return m_shape.get();
}
ColliderType3D SphereCollider3D::GetType() const
ColliderType3D BulletSphereCollider3D::GetType() const
{
return ColliderType3D::Sphere;
}

View File

@@ -121,11 +121,19 @@ namespace Nz
}
}
#define HACK 0
btRigidBody* BulletPhysWorld3D::AddRigidBody(std::size_t& rigidBodyIndex, FunctionRef<void(btRigidBody* body)> constructor)
{
#if HACK
static std::size_t index = 0;
rigidBodyIndex = index++;
btRigidBody* rigidBody = (btRigidBody*) ::operator new(sizeof(btRigidBody));
constructor(rigidBody);
m_world->dynamicWorld.addRigidBody(rigidBody);
#else
btRigidBody* rigidBody = m_world->rigidBodyPool.Allocate(m_world->rigidBodyPool.DeferConstruct, rigidBodyIndex);
constructor(rigidBody);
m_world->dynamicWorld.addRigidBody(rigidBody);
// Small hack to order rigid bodies to make it cache friendly
@@ -144,6 +152,7 @@ namespace Nz
*it = rigidBody;
}
}
#endif
return rigidBody;
}
@@ -152,6 +161,10 @@ namespace Nz
{
// TODO: Improve deletion (since rigid bodies are sorted)
m_world->dynamicWorld.removeRigidBody(rigidBody); //< this does a linear search
#if HACK
::operator delete(rigidBody);
#else
m_world->rigidBodyPool.Free(rigidBodyIndex);
#endif
}
}

View File

@@ -27,7 +27,7 @@ namespace Nz
NazaraAssert(m_world, "Invalid world");
if (!m_geom)
m_geom = std::make_shared<NullCollider3D>();
m_geom = std::make_shared<BulletNullCollider3D>();
Vector3f inertia;
m_geom->ComputeInertia(1.f, &inertia);
@@ -214,7 +214,7 @@ namespace Nz
if (geom)
m_geom = std::move(geom);
else
m_geom = std::make_shared<NullCollider3D>();
m_geom = std::make_shared<BulletNullCollider3D>();
m_body->setCollisionShape(m_geom->GetShape());
if (recomputeInertia)
@@ -267,6 +267,15 @@ namespace Nz
m_body->setWorldTransform(worldTransform);
}
void BulletRigidBody3D::SetPositionAndRotation(const Vector3f& position, const Quaternionf& rotation)
{
btTransform worldTransform;
worldTransform.setOrigin(ToBullet(position));
worldTransform.setRotation(ToBullet(rotation));
m_body->setWorldTransform(worldTransform);
}
void BulletRigidBody3D::SetRotation(const Quaternionf& rotation)
{
btTransform worldTransform = m_body->getWorldTransform();

View File

@@ -4,6 +4,7 @@
#include <Nazara/BulletPhysics3D/Systems/BulletPhysics3DSystem.hpp>
#include <Nazara/Utility/Components/NodeComponent.hpp>
#include <iostream>
#include <Nazara/BulletPhysics3D/Debug.hpp>
namespace Nz
@@ -14,6 +15,10 @@ namespace Nz
{
m_constructConnection = registry.on_construct<BulletRigidBody3DComponent>().connect<&BulletPhysics3DSystem::OnConstruct>(this);
m_destructConnection = registry.on_destroy<BulletRigidBody3DComponent>().connect<&BulletPhysics3DSystem::OnDestruct>(this);
m_stepCount = 0;
m_physicsTime = Time::Zero();
m_updateTime = Time::Zero();
}
BulletPhysics3DSystem::~BulletPhysics3DSystem()
@@ -26,6 +31,20 @@ namespace Nz
rigidBodyComponent.Destroy();
}
void BulletPhysics3DSystem::Dump()
{
if (m_stepCount == 0)
m_stepCount = 1;
std::cout << "Physics time: " << (m_physicsTime / Time::Nanoseconds(m_stepCount)) << std::endl;
std::cout << "Update time: " << (m_updateTime / Time::Nanoseconds(m_stepCount)) << std::endl;
std::cout << "--" << std::endl;
m_stepCount = 0;
m_physicsTime = Time::Zero();
m_updateTime = Time::Zero();
}
bool BulletPhysics3DSystem::RaycastQueryFirst(const Vector3f& from, const Vector3f& to, RaycastHit* hitInfo)
{
if (!m_physWorld.RaycastQueryFirst(from, to, hitInfo))
@@ -49,12 +68,16 @@ namespace Nz
BulletRigidBody3DComponent& entityPhysics = m_registry.get<BulletRigidBody3DComponent>(entity);
NodeComponent& entityNode = m_registry.get<NodeComponent>(entity);
entityPhysics.SetPosition(entityNode.GetPosition(CoordSys::Global));
entityPhysics.SetRotation(entityNode.GetRotation(CoordSys::Global));
entityPhysics.SetPositionAndRotation(entityNode.GetPosition(CoordSys::Global), entityNode.GetRotation(CoordSys::Global));
});
Time t1 = GetElapsedNanoseconds();
// Update the physics world
m_physWorld.Step(elapsedTime);
m_stepCount++;
Time t2 = GetElapsedNanoseconds();
// Replicate rigid body position to their node components
// TODO: Only replicate active entities
@@ -67,6 +90,11 @@ namespace Nz
nodeComponent.SetPosition(rigidBodyComponent.GetPosition(), CoordSys::Global);
nodeComponent.SetRotation(rigidBodyComponent.GetRotation(), CoordSys::Global);
}
Time t3 = GetElapsedNanoseconds();
m_physicsTime += (t2 - t1);
m_updateTime += (t3 - t2);
}
void BulletPhysics3DSystem::OnConstruct(entt::registry& registry, entt::entity entity)

View File

@@ -0,0 +1,11 @@
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - JoltPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/JoltPhysics3D/Components/JoltRigidBody3DComponent.hpp>
#include <Nazara/BulletPhysics3D/Components/BulletRigidBody3DComponent.hpp>
#include <Nazara/JoltPhysics3D/Debug.hpp>
namespace Nz
{
}

View File

@@ -0,0 +1,214 @@
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - JoltPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/JoltPhysics3D/JoltCollider3D.hpp>
#include <Nazara/Core/PrimitiveList.hpp>
#include <Nazara/JoltPhysics3D/JoltHelper.hpp>
#include <Nazara/Utility/Algorithm.hpp>
#include <Nazara/Utility/IndexBuffer.hpp>
#include <Nazara/Utility/SoftwareBuffer.hpp>
#include <Nazara/Utility/StaticMesh.hpp>
#include <Nazara/Utility/VertexBuffer.hpp>
#include <Nazara/Utils/MemoryHelper.hpp>
#include <Jolt/Core/Core.h>
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
#include <Nazara/JoltPhysics3D/Debug.hpp>
namespace Nz
{
JoltCollider3D::~JoltCollider3D() = default;
std::shared_ptr<StaticMesh> JoltCollider3D::GenerateDebugMesh() const
{
std::vector<Vector3f> colliderVertices;
std::vector<UInt16> colliderIndices;
BuildDebugMesh(colliderVertices, colliderIndices);
std::shared_ptr<VertexBuffer> colliderVB = std::make_shared<VertexBuffer>(VertexDeclaration::Get(VertexLayout::XYZ), SafeCast<UInt32>(colliderVertices.size()), BufferUsage::Write, SoftwareBufferFactory, colliderVertices.data());
std::shared_ptr<IndexBuffer> colliderIB = std::make_shared<IndexBuffer>(IndexType::U16, colliderIndices.size(), BufferUsage::Write, SoftwareBufferFactory, colliderIndices.data());
std::shared_ptr<StaticMesh> colliderSubMesh = std::make_shared<StaticMesh>(std::move(colliderVB), std::move(colliderIB));
colliderSubMesh->GenerateAABB();
colliderSubMesh->SetPrimitiveMode(PrimitiveMode::LineList);
return colliderSubMesh;
}
std::shared_ptr<JoltCollider3D> JoltCollider3D::Build(const PrimitiveList& list)
{
std::size_t primitiveCount = list.GetSize();
if (primitiveCount > 1)
{
std::vector<JoltCompoundCollider3D::ChildCollider> childColliders(primitiveCount);
for (unsigned int i = 0; i < primitiveCount; ++i)
{
const Primitive& primitive = list.GetPrimitive(i);
childColliders[i].collider = CreateGeomFromPrimitive(primitive);
childColliders[i].offset = primitive.matrix.GetTranslation();
childColliders[i].rotation = primitive.matrix.GetRotation();
}
return std::make_shared<JoltCompoundCollider3D>(std::move(childColliders));
}
else if (primitiveCount > 0)
return CreateGeomFromPrimitive(list.GetPrimitive(0));
else
return nullptr;// std::make_shared<NullCollider3D>(); //< TODO
}
std::shared_ptr<JoltCollider3D> JoltCollider3D::CreateGeomFromPrimitive(const Primitive& primitive)
{
switch (primitive.type)
{
case PrimitiveType::Box:
return std::make_shared<JoltBoxCollider3D>(primitive.box.lengths);
case PrimitiveType::Cone:
return nullptr; //< TODO
//return std::make_shared<ConeCollider3D>(primitive.cone.length, primitive.cone.radius);
case PrimitiveType::Plane:
return std::make_shared<JoltBoxCollider3D>(Vector3f(primitive.plane.size.x, 0.01f, primitive.plane.size.y));
///TODO: PlaneGeom?
case PrimitiveType::Sphere:
return std::make_shared<JoltSphereCollider3D>(primitive.sphere.size);
}
NazaraError("Primitive type not handled (0x" + NumberToString(UnderlyingCast(primitive.type), 16) + ')');
return std::shared_ptr<JoltCollider3D>();
}
/********************************** BoxCollider3D **********************************/
JoltBoxCollider3D::JoltBoxCollider3D(const Vector3f& lengths, float convexRadius) :
m_lengths(lengths)
{
m_shapeSettings = std::make_unique<JPH::BoxShapeSettings>(ToJolt(m_lengths * 0.5f), convexRadius);
}
JoltBoxCollider3D::~JoltBoxCollider3D() = default;
void JoltBoxCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
{
auto InsertVertex = [&](float x, float y, float z) -> UInt16
{
UInt16 index = SafeCast<UInt16>(vertices.size());
vertices.push_back(offsetMatrix * Vector3f(x, y, z));
return index;
};
Vector3f halfLengths = m_lengths * 0.5f;
UInt16 v0 = InsertVertex(-halfLengths.x, -halfLengths.y, -halfLengths.z);
UInt16 v1 = InsertVertex(halfLengths.x, -halfLengths.y, -halfLengths.z);
UInt16 v2 = InsertVertex(halfLengths.x, -halfLengths.y, halfLengths.z);
UInt16 v3 = InsertVertex(-halfLengths.x, -halfLengths.y, halfLengths.z);
UInt16 v4 = InsertVertex(-halfLengths.x, halfLengths.y, -halfLengths.z);
UInt16 v5 = InsertVertex(halfLengths.x, halfLengths.y, -halfLengths.z);
UInt16 v6 = InsertVertex(halfLengths.x, halfLengths.y, halfLengths.z);
UInt16 v7 = InsertVertex(-halfLengths.x, halfLengths.y, halfLengths.z);
auto InsertEdge = [&](UInt16 from, UInt16 to)
{
indices.push_back(from);
indices.push_back(to);
};
InsertEdge(v0, v1);
InsertEdge(v1, v2);
InsertEdge(v2, v3);
InsertEdge(v3, v0);
InsertEdge(v4, v5);
InsertEdge(v5, v6);
InsertEdge(v6, v7);
InsertEdge(v7, v4);
InsertEdge(v0, v4);
InsertEdge(v1, v5);
InsertEdge(v2, v6);
InsertEdge(v3, v7);
}
JPH::ShapeSettings* JoltBoxCollider3D::GetShapeSettings() const
{
return m_shapeSettings.get();
}
Vector3f JoltBoxCollider3D::GetLengths() const
{
return m_lengths;
}
JoltColliderType3D JoltBoxCollider3D::GetType() const
{
return JoltColliderType3D::Box;
}
/******************************* JoltCompoundCollider3D ********************************/
JoltCompoundCollider3D::JoltCompoundCollider3D(std::vector<ChildCollider> childs) :
m_childs(std::move(childs))
{
m_shapeSettings = std::make_unique<JPH::StaticCompoundShapeSettings>();
for (const auto& child : m_childs)
m_shapeSettings->AddShape(ToJolt(child.offset), ToJolt(child.rotation), child.collider->GetShapeSettings());
}
JoltCompoundCollider3D::~JoltCompoundCollider3D() = default;
void JoltCompoundCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
{
for (const auto& child : m_childs)
child.collider->BuildDebugMesh(vertices, indices, offsetMatrix * Matrix4f::Transform(child.offset, child.rotation));
}
auto JoltCompoundCollider3D::GetGeoms() const -> const std::vector<ChildCollider>&
{
return m_childs;
}
JPH::ShapeSettings* JoltCompoundCollider3D::GetShapeSettings() const
{
return m_shapeSettings.get();
}
JoltColliderType3D JoltCompoundCollider3D::GetType() const
{
return JoltColliderType3D::Compound;
}
/******************************** JoltSphereCollider3D *********************************/
JoltSphereCollider3D::JoltSphereCollider3D(float radius) :
m_radius(radius)
{
m_shapeSettings = std::make_unique<JPH::SphereShapeSettings>(radius);
}
JoltSphereCollider3D::~JoltSphereCollider3D() = default;
void JoltSphereCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
{
}
float JoltSphereCollider3D::GetRadius() const
{
return m_radius;
}
JPH::ShapeSettings* JoltSphereCollider3D::GetShapeSettings() const
{
return m_shapeSettings.get();
}
JoltColliderType3D JoltSphereCollider3D::GetType() const
{
return JoltColliderType3D::Sphere;
}
}

View File

@@ -0,0 +1,125 @@
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - JoltPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#if 0
#include <Nazara/JoltPhysics3D/JoltConstraint3D.hpp>
#include <Nazara/BulletPhysics3D/BulletConstraint3D.hpp>
#include <Nazara/BulletPhysics3D/BulletHelper.hpp>
#include <BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h>
#include <BulletDynamics/Dynamics/btDynamicsWorld.h>
#include <Nazara/JoltPhysics3D/Debug.hpp>
namespace Nz
{
BulletConstraint3D::BulletConstraint3D(std::unique_ptr<btTypedConstraint> constraint, bool disableCollisions) :
m_constraint(std::move(constraint)),
m_bodyCollisionEnabled(!disableCollisions)
{
btDynamicsWorld* world = GetWorld().GetDynamicsWorld();
world->addConstraint(m_constraint.get(), disableCollisions);
}
BulletConstraint3D::BulletConstraint3D(BulletConstraint3D&& constraint) noexcept :
m_constraint(std::move(constraint.m_constraint)),
m_bodyCollisionEnabled(constraint.m_bodyCollisionEnabled)
{
if (m_constraint)
m_constraint->setUserConstraintPtr(this);
}
BulletConstraint3D::~BulletConstraint3D()
{
if (m_constraint)
{
btDynamicsWorld* world = GetWorld().GetDynamicsWorld();
world->removeConstraint(m_constraint.get());
}
}
BulletRigidBody3D& BulletConstraint3D::GetBodyA()
{
return *static_cast<BulletRigidBody3D*>(m_constraint->getRigidBodyA().getUserPointer());
}
const BulletRigidBody3D& BulletConstraint3D::GetBodyA() const
{
return *static_cast<BulletRigidBody3D*>(m_constraint->getRigidBodyA().getUserPointer());
}
BulletRigidBody3D& BulletConstraint3D::GetBodyB()
{
NazaraAssert(!IsSingleBody(), "constraint is not attached to a second body");
return *static_cast<BulletRigidBody3D*>(m_constraint->getRigidBodyB().getUserPointer());
}
const BulletRigidBody3D& BulletConstraint3D::GetBodyB() const
{
NazaraAssert(!IsSingleBody(), "constraint is not attached to a second body");
return *static_cast<BulletRigidBody3D*>(m_constraint->getRigidBodyB().getUserPointer());
}
BulletPhysWorld3D& BulletConstraint3D::GetWorld()
{
return *GetBodyA().GetWorld();
}
const BulletPhysWorld3D& BulletConstraint3D::GetWorld() const
{
return *GetBodyA().GetWorld();
}
bool BulletConstraint3D::IsSingleBody() const
{
return &m_constraint->getRigidBodyB() == &btTypedConstraint::getFixedBody();
}
BulletConstraint3D& BulletConstraint3D::operator=(BulletConstraint3D&& constraint) noexcept
{
m_constraint.reset();
m_constraint = std::move(constraint.m_constraint);
m_bodyCollisionEnabled = constraint.m_bodyCollisionEnabled;
if (m_constraint)
m_constraint->setUserConstraintPtr(this);
return *this;
}
BulletPivotConstraint3D::BulletPivotConstraint3D(BulletRigidBody3D& first, const Vector3f& pivot) :
BulletConstraint3D(std::make_unique<btPoint2PointConstraint>(*first.GetRigidBody(), ToBullet(first.ToLocal(pivot))))
{
}
BulletPivotConstraint3D::BulletPivotConstraint3D(BulletRigidBody3D& first, BulletRigidBody3D& second, const Vector3f& pivot, bool disableCollisions) :
BulletConstraint3D(std::make_unique<btPoint2PointConstraint>(*first.GetRigidBody(), *second.GetRigidBody(), ToBullet(first.ToLocal(pivot)), ToBullet(second.ToLocal(pivot))), disableCollisions)
{
}
BulletPivotConstraint3D::BulletPivotConstraint3D(BulletRigidBody3D& first, BulletRigidBody3D& second, const Vector3f& firstAnchor, const Vector3f& secondAnchor, bool disableCollisions) :
BulletConstraint3D(std::make_unique<btPoint2PointConstraint>(*first.GetRigidBody(), *second.GetRigidBody(), ToBullet(firstAnchor), ToBullet(secondAnchor)), disableCollisions)
{
}
Vector3f BulletPivotConstraint3D::GetFirstAnchor() const
{
return FromBullet(GetConstraint<btPoint2PointConstraint>()->getPivotInA());
}
Vector3f BulletPivotConstraint3D::GetSecondAnchor() const
{
return FromBullet(GetConstraint<btPoint2PointConstraint>()->getPivotInB());
}
void BulletPivotConstraint3D::SetFirstAnchor(const Vector3f& firstAnchor)
{
GetConstraint<btPoint2PointConstraint>()->setPivotA(ToBullet(firstAnchor));
}
void BulletPivotConstraint3D::SetSecondAnchor(const Vector3f& secondAnchor)
{
GetConstraint<btPoint2PointConstraint>()->setPivotB(ToBullet(secondAnchor));
}
}
#endif

View File

@@ -0,0 +1,31 @@
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - JoltPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_JOLTPHYSICS3D_JOLTHELPER_HPP
#define NAZARA_JOLTPHYSICS3D_JOLTHELPER_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/BulletPhysics3D/Config.hpp>
#include <Nazara/Math/Matrix4.hpp>
#include <Nazara/Math/Quaternion.hpp>
#include <Nazara/Math/Vector3.hpp>
#include <Jolt/Jolt.h>
namespace Nz
{
inline Quaternionf FromJolt(const JPH::Quat& quat);
inline Matrix4f FromJolt(const JPH::Mat44& matrix);
inline Vector3f FromJolt(const JPH::Vec3& vec);
inline JPH::Mat44 ToJolt(const Matrix4f& transformMatrix);
inline JPH::Quat ToJolt(const Quaternionf& quaternion);
inline JPH::Vec3 ToJolt(const Vector3f& vec);
inline JPH::Vec4 ToJolt(const Vector4f& vec);
}
#include <Nazara/JoltPhysics3D/JoltHelper.inl>
#endif // NAZARA_JOLTPHYSICS3D_JOLTHELPER_HPP

View File

@@ -0,0 +1,60 @@
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - JoltPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/JoltPhysics3D/Debug.hpp>
namespace Nz
{
Quaternionf FromJolt(const JPH::Quat& quat)
{
return Quaternionf(quat.GetW(), quat.GetX(), quat.GetY(), quat.GetZ());
}
Matrix4f FromJolt(const JPH::Mat44& transform)
{
const JPH::Vec4& row1 = transform.GetColumn4(0);
const JPH::Vec4& row2 = transform.GetColumn4(1);
const JPH::Vec4& row3 = transform.GetColumn4(2);
const JPH::Vec4& row4 = transform.GetColumn4(3);
return Matrix4f(
row1.GetX(), row1.GetY(), row1.GetZ(), row1.GetW(),
row2.GetX(), row2.GetY(), row2.GetZ(), row2.GetW(),
row3.GetX(), row3.GetY(), row3.GetZ(), row3.GetW(),
row4.GetX(), row4.GetY(), row4.GetZ(), row4.GetW());
}
inline Vector3f FromJolt(const JPH::Vec3& vec)
{
return Vector3f(vec.GetX(), vec.GetY(), vec.GetZ());
}
inline JPH::Mat44 ToJolt(const Matrix4f& transformMatrix)
{
JPH::Mat44 transform;
transform.SetColumn4(0, JPH::Vec4{ transformMatrix.m11, transformMatrix.m12, transformMatrix.m13, transformMatrix.m14 });
transform.SetColumn4(1, JPH::Vec4{ transformMatrix.m21, transformMatrix.m22, transformMatrix.m23, transformMatrix.m24 });
transform.SetColumn4(2, JPH::Vec4{ transformMatrix.m31, transformMatrix.m32, transformMatrix.m33, transformMatrix.m34 });
transform.SetColumn4(3, JPH::Vec4{ transformMatrix.m41, transformMatrix.m42, transformMatrix.m43, transformMatrix.m44 });
return transform;
}
inline JPH::Quat ToJolt(const Quaternionf& quaternion)
{
return JPH::Quat(quaternion.x, quaternion.y, quaternion.z, quaternion.w);
}
inline JPH::Vec3 ToJolt(const Vector3f& vec)
{
return JPH::Vec3(vec.x, vec.y, vec.z);
}
inline JPH::Vec4 ToJolt(const Vector4f& vec)
{
return JPH::Vec4(vec.x, vec.y, vec.z, vec.w);
}
}
#include <Nazara/JoltPhysics3D/DebugOff.hpp>

View File

@@ -0,0 +1,284 @@
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - JoltPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/JoltPhysics3D/JoltPhysWorld3D.hpp>
#include <Nazara/JoltPhysics3D/JoltHelper.hpp>
#include <Nazara/JoltPhysics3D/JoltPhysics3D.hpp>
#include <Nazara/JoltPhysics3D/JoltPhysWorld3D.hpp>
#include <Nazara/Utils/MemoryPool.hpp>
#include <Nazara/Utils/StackVector.hpp>
#include <Jolt/Jolt.h>
#include <Jolt/Core/TempAllocator.h>
#include <Jolt/Physics/Collision/ObjectLayer.h>
#include <Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h>
#include <Jolt/Physics/PhysicsSettings.h>
#include <Jolt/Physics/PhysicsSystem.h>
#include <Jolt/Physics/Body/BodyActivationListener.h>
#include <cassert>
#include <iostream>
#include <Nazara/JoltPhysics3D/Debug.hpp>
namespace DitchMeAsap
{
using namespace JPH;
using std::cout;
using std::endl;
// Layer that objects can be in, determines which other objects it can collide with
// Typically you at least want to have 1 layer for moving bodies and 1 layer for static bodies, but you can have more
// layers if you want. E.g. you could have a layer for high detail collision (which is not used by the physics simulation
// but only if you do collision testing).
namespace Layers
{
static constexpr uint8 NON_MOVING = 0;
static constexpr uint8 MOVING = 1;
static constexpr uint8 NUM_LAYERS = 2;
};
/// Class that determines if two object layers can collide
class ObjectLayerPairFilterImpl : public ObjectLayerPairFilter
{
public:
virtual bool ShouldCollide(ObjectLayer inObject1, 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
default:
JPH_ASSERT(false);
return false;
}
}
};
// Each broadphase layer results in a separate bounding volume tree in the broad phase. You at least want to have
// a layer for non-moving and moving objects to avoid having to update a tree full of static objects every frame.
// You can have a 1-on-1 mapping between object layers and broadphase layers (like in this case) but if you have
// many object layers you'll be creating many broad phase trees, which is not efficient. If you want to fine tune
// your broadphase layers define JPH_TRACK_BROADPHASE_STATS and look at the stats reported on the TTY.
namespace BroadPhaseLayers
{
static constexpr BroadPhaseLayer NON_MOVING(0);
static constexpr BroadPhaseLayer MOVING(1);
static constexpr uint NUM_LAYERS(2);
};
// BroadPhaseLayerInterface implementation
// This defines a mapping between object and broadphase layers.
class BPLayerInterfaceImpl final : public 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;
}
virtual uint GetNumBroadPhaseLayers() const override
{
return BroadPhaseLayers::NUM_LAYERS;
}
virtual BroadPhaseLayer GetBroadPhaseLayer(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(BroadPhaseLayer inLayer) const override
{
switch ((BroadPhaseLayer::Type)inLayer)
{
case (BroadPhaseLayer::Type)BroadPhaseLayers::NON_MOVING: return "NON_MOVING";
case (BroadPhaseLayer::Type)BroadPhaseLayers::MOVING: return "MOVING";
default: JPH_ASSERT(false); return "INVALID";
}
}
#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED
private:
BroadPhaseLayer mObjectToBroadPhase[Layers::NUM_LAYERS];
};
/// Class that determines if an object layer can collide with a broadphase layer
class ObjectVsBroadPhaseLayerFilterImpl : public ObjectVsBroadPhaseLayerFilter
{
public:
virtual bool ShouldCollide(ObjectLayer inLayer1, 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;
}
}
};
// An example contact listener
class MyContactListener : public ContactListener
{
public:
// See: ContactListener
virtual ValidateResult OnContactValidate(const Body& inBody1, const Body& inBody2, RVec3Arg inBaseOffset, const CollideShapeResult& inCollisionResult) override
{
cout << "Contact validate callback" << endl;
// Allows you to ignore a contact before it is created (using layers to not make objects collide is cheaper!)
return ValidateResult::AcceptAllContactsForThisBodyPair;
}
virtual void OnContactAdded(const Body& inBody1, const Body& inBody2, const ContactManifold& inManifold, ContactSettings& ioSettings) override
{
cout << "A contact was added" << endl;
}
virtual void OnContactPersisted(const Body& inBody1, const Body& inBody2, const ContactManifold& inManifold, ContactSettings& ioSettings) override
{
cout << "A contact was persisted" << endl;
}
virtual void OnContactRemoved(const SubShapeIDPair& inSubShapePair) override
{
cout << "A contact was removed" << endl;
}
};
// An example activation listener
class MyBodyActivationListener : public BodyActivationListener
{
public:
virtual void OnBodyActivated(const BodyID& inBodyID, uint64 inBodyUserData) override
{
cout << "A body got activated" << endl;
}
virtual void OnBodyDeactivated(const BodyID& inBodyID, uint64 inBodyUserData) override
{
cout << "A body went to sleep" << endl;
}
};
}
namespace Nz
{
struct JoltPhysWorld3D::JoltWorld
{
JPH::TempAllocatorMalloc tempAllocation;
JPH::PhysicsSystem physicsSystem;
DitchMeAsap::BPLayerInterfaceImpl layerInterface;
DitchMeAsap::ObjectLayerPairFilterImpl objectLayerFilter;
DitchMeAsap::ObjectVsBroadPhaseLayerFilterImpl objectBroadphaseLayerFilter;
JoltWorld() = default;
JoltWorld(const JoltWorld&) = delete;
JoltWorld(JoltWorld&&) = delete;
JoltWorld& operator=(const JoltWorld&) = delete;
JoltWorld& operator=(JoltWorld&&) = delete;
};
JoltPhysWorld3D::JoltPhysWorld3D() :
m_maxStepCount(50),
m_gravity(Vector3f::Zero()),
m_stepSize(Time::TickDuration(120)),
m_timestepAccumulator(Time::Zero())
{
m_world = std::make_unique<JoltWorld>();
m_world->physicsSystem.Init(0xFFFF, 0, 0xFFFF, 0xFFFF, m_world->layerInterface, m_world->objectBroadphaseLayerFilter, m_world->objectLayerFilter);
}
JoltPhysWorld3D::~JoltPhysWorld3D() = default;
Vector3f JoltPhysWorld3D::GetGravity() const
{
return FromJolt(m_world->physicsSystem.GetGravity());
}
std::size_t JoltPhysWorld3D::GetMaxStepCount() const
{
return m_maxStepCount;
}
JPH::PhysicsSystem* JoltPhysWorld3D::GetPhysicsSystem()
{
return &m_world->physicsSystem;
}
Time JoltPhysWorld3D::GetStepSize() const
{
return m_stepSize;
}
bool JoltPhysWorld3D::RaycastQueryFirst(const Vector3f& from, const Vector3f& to, RaycastHit* hitInfo)
{
return false;
/*
btCollisionWorld::ClosestRayResultCallback callback(ToBullet(from), ToBullet(to));
m_world->dynamicWorld.rayTest(ToBullet(from), ToBullet(to), callback);
if (!callback.hasHit())
return false;
if (hitInfo)
{
hitInfo->fraction = callback.m_closestHitFraction;
hitInfo->hitNormal = FromBullet(callback.m_hitNormalWorld);
hitInfo->hitPosition = FromBullet(callback.m_hitPointWorld);
if (const btRigidBody* body = btRigidBody::upcast(callback.m_collisionObject))
hitInfo->hitBody = static_cast<BulletRigidBody3D*>(body->getUserPointer());
}
return true;*/
}
void JoltPhysWorld3D::SetGravity(const Vector3f& gravity)
{
m_world->physicsSystem.SetGravity(ToJolt(gravity));
}
void JoltPhysWorld3D::SetMaxStepCount(std::size_t maxStepCount)
{
m_maxStepCount = maxStepCount;
}
void JoltPhysWorld3D::SetStepSize(Time stepSize)
{
m_stepSize = stepSize;
}
void JoltPhysWorld3D::Step(Time timestep)
{
JPH::JobSystem& jobSystem = JoltPhysics3D::Instance()->GetThreadPool();
m_timestepAccumulator += timestep;
float stepSize = m_stepSize.AsSeconds<float>();
static bool firstStep = true;
if (firstStep)
{
m_world->physicsSystem.OptimizeBroadPhase();
firstStep = false;
}
std::size_t stepCount = 0;
while (m_timestepAccumulator >= m_stepSize && stepCount < m_maxStepCount)
{
m_world->physicsSystem.Update(stepSize, 1, 1, &m_world->tempAllocation, &jobSystem);
m_timestepAccumulator -= m_stepSize;
stepCount++;
}
}
}

View File

@@ -0,0 +1,62 @@
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - JoltPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/JoltPhysics3D/JoltPhysics3D.hpp>
#include <Nazara/JoltPhysics3D/Config.hpp>
#include <Nazara/Core/Core.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/Log.hpp>
#include <Jolt/Jolt.h>
#include <Jolt/RegisterTypes.h>
#include <Jolt/Core/Factory.h>
#include <Jolt/Core/JobSystemThreadPool.h>
#include <Jolt/Physics/PhysicsSettings.h>
#include <cstdarg>
#include <iostream>
#include <Nazara/JoltPhysics3D/Debug.hpp>
// 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;
}
namespace Nz
{
JoltPhysics3D::JoltPhysics3D(Config /*config*/) :
ModuleBase("JoltPhysics3D", this)
{
JPH::RegisterDefaultAllocator();
JPH::Trace = TraceImpl;
JPH::Factory::sInstance = new JPH::Factory;
JPH::RegisterTypes();
m_threadPool = std::make_unique<JPH::JobSystemThreadPool>(JPH::cMaxPhysicsJobs, JPH::cMaxPhysicsBarriers, Core::Instance()->GetHardwareInfo().GetCpuThreadCount());
}
JoltPhysics3D::~JoltPhysics3D()
{
m_threadPool.reset();
// FIXME: Uncomment when next version of Jolt gets released
//JPH::UnregisterTypes();
delete JPH::Factory::sInstance;
JPH::Factory::sInstance = nullptr;
}
JPH::JobSystem& JoltPhysics3D::GetThreadPool()
{
return *m_threadPool;
}
JoltPhysics3D* JoltPhysics3D::s_instance;
}

View File

@@ -0,0 +1,325 @@
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - JoltPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/JoltPhysics3D/JoltRigidBody3D.hpp>
#include <Nazara/JoltPhysics3D/JoltHelper.hpp>
#include <Nazara/JoltPhysics3D/JoltPhysWorld3D.hpp>
#include <Nazara/JoltPhysics3D/JoltRigidBody3D.hpp>
#include <Nazara/Utils/MemoryHelper.hpp>
#include <Jolt/Jolt.h>
#include <Jolt/Physics/PhysicsSystem.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <algorithm>
#include <array>
#include <cmath>
#include <Nazara/JoltPhysics3D/Debug.hpp>
namespace Nz
{
JoltRigidBody3D::JoltRigidBody3D(JoltPhysWorld3D* world, const Matrix4f& mat) :
JoltRigidBody3D(world, nullptr, mat)
{
}
JoltRigidBody3D::JoltRigidBody3D(JoltPhysWorld3D* world, std::shared_ptr<JoltCollider3D> geom, const Matrix4f& mat) :
m_geom(std::move(geom)),
m_world(world)
{
NazaraAssert(m_world, "Invalid world");
if (!m_geom)
m_geom = std::make_shared<JoltSphereCollider3D>(1.f);
JPH::BodyInterface& body_interface = m_world->GetPhysicsSystem()->GetBodyInterface();
JPH::Vec3 position = ToJolt(mat.GetTranslation());
JPH::Quat rotation = ToJolt(mat.GetRotation().Normalize());
JPH::BodyCreationSettings settings(m_geom->GetShapeSettings(), position, rotation, JPH::EMotionType::Dynamic, 1);
settings.mAllowSleeping = false;
JPH::Body* body = body_interface.CreateBody(settings);
body->SetUserData(SafeCast<UInt64>(reinterpret_cast<std::uintptr_t>(this)));
body_interface.AddBody(body->GetID(), JPH::EActivation::Activate);
m_bodyIndex = body->GetID().GetIndexAndSequenceNumber();
}
JoltRigidBody3D::JoltRigidBody3D(JoltRigidBody3D&& object) noexcept :
m_geom(std::move(object.m_geom)),
m_bodyIndex(object.m_bodyIndex),
m_world(object.m_world)
{
object.m_bodyIndex = JPH::BodyID::cInvalidBodyID;
}
JoltRigidBody3D::~JoltRigidBody3D()
{
Destroy();
}
#if 0
void JoltRigidBody3D::AddForce(const Vector3f& force, CoordSys coordSys)
{
switch (coordSys)
{
case CoordSys::Global:
WakeUp();
m_body->applyCentralForce(ToJolt(force));
break;
case CoordSys::Local:
WakeUp();
m_body->applyCentralForce(ToJolt(GetRotation() * force));
break;
}
}
void JoltRigidBody3D::AddForce(const Vector3f& force, const Vector3f& point, CoordSys coordSys)
{
switch (coordSys)
{
case CoordSys::Global:
WakeUp();
m_body->applyForce(ToJolt(force), ToJolt(point));
break;
case CoordSys::Local:
{
Matrix4f transformMatrix = GetMatrix();
return AddForce(transformMatrix.Transform(force, 0.f), point, CoordSys::Global);
}
}
}
void JoltRigidBody3D::AddTorque(const Vector3f& torque, CoordSys coordSys)
{
switch (coordSys)
{
case CoordSys::Global:
WakeUp();
m_body->applyTorque(ToJolt(torque));
break;
case CoordSys::Local:
Matrix4f transformMatrix = GetMatrix();
WakeUp();
m_body->applyTorque(ToJolt(transformMatrix.Transform(torque, 0.f)));
break;
}
}
#endif
void JoltRigidBody3D::EnableSleeping(bool enable)
{
}
#if 0
void JoltRigidBody3D::FallAsleep()
{
if (m_body->getActivationState() != DISABLE_DEACTIVATION)
m_body->setActivationState(ISLAND_SLEEPING);
}
Boxf JoltRigidBody3D::GetAABB() const
{
btVector3 min, max;
m_body->getAabb(min, max);
return Boxf(FromJolt(min), FromJolt(max));
}
float JoltRigidBody3D::GetAngularDamping() const
{
return m_body->getAngularDamping();
}
Vector3f JoltRigidBody3D::GetAngularVelocity() const
{
return FromJolt(m_body->getAngularVelocity());
}
float JoltRigidBody3D::GetLinearDamping() const
{
return m_body->getLinearDamping();
}
Vector3f JoltRigidBody3D::GetLinearVelocity() const
{
return FromJolt(m_body->getLinearVelocity());
}
float JoltRigidBody3D::GetMass() const
{
return m_body->getMass();
}
Vector3f JoltRigidBody3D::GetMassCenter(CoordSys coordSys) const
{
return FromJolt(m_body->getCenterOfMassPosition());
}
Matrix4f JoltRigidBody3D::GetMatrix() const
{
return FromJolt(m_body->getWorldTransform());
}
#endif
Vector3f JoltRigidBody3D::GetPosition() const
{
JPH::BodyInterface& body_interface = m_world->GetPhysicsSystem()->GetBodyInterface();
return FromJolt(body_interface.GetPosition(JPH::BodyID(m_bodyIndex)));
}
Quaternionf JoltRigidBody3D::GetRotation() const
{
JPH::BodyInterface& body_interface = m_world->GetPhysicsSystem()->GetBodyInterface();
return FromJolt(body_interface.GetRotation(JPH::BodyID(m_bodyIndex)));
}
#if 0
bool JoltRigidBody3D::IsSimulationEnabled() const
{
return m_body->isActive();
}
#endif
bool JoltRigidBody3D::IsSleeping() const
{
JPH::BodyInterface& body_interface = m_world->GetPhysicsSystem()->GetBodyInterface();
return !body_interface.IsActive(JPH::BodyID(m_bodyIndex));
}
#if 0
bool JoltRigidBody3D::IsSleepingEnabled() const
{
return m_body->getActivationState() != DISABLE_DEACTIVATION;
}
void JoltRigidBody3D::SetAngularDamping(float angularDamping)
{
m_body->setDamping(m_body->getLinearDamping(), angularDamping);
}
void JoltRigidBody3D::SetAngularVelocity(const Vector3f& angularVelocity)
{
m_body->setAngularVelocity(ToJolt(angularVelocity));
}
void JoltRigidBody3D::SetGeom(std::shared_ptr<JoltCollider3D> geom, bool recomputeInertia)
{
if (m_geom != geom)
{
if (geom)
m_geom = std::move(geom);
else
m_geom = std::make_shared<NullCollider3D>();
m_body->setCollisionShape(m_geom->GetShape());
if (recomputeInertia)
{
float mass = GetMass();
Vector3f inertia;
m_geom->ComputeInertia(mass, &inertia);
m_body->setMassProps(mass, ToJolt(inertia));
}
}
}
void JoltRigidBody3D::SetLinearDamping(float damping)
{
m_body->setDamping(damping, m_body->getAngularDamping());
}
void JoltRigidBody3D::SetLinearVelocity(const Vector3f& velocity)
{
m_body->setLinearVelocity(ToJolt(velocity));
}
#endif 0
void JoltRigidBody3D::SetMass(float mass)
{
NazaraAssert(mass >= 0.f, "Mass must be positive and finite");
NazaraAssert(std::isfinite(mass), "Mass must be positive and finite");
JPH::BodyInterface& body_interface = m_world->GetPhysicsSystem()->GetBodyInterface();
body_interface.SetMotionType(JPH::BodyID(m_bodyIndex), (mass > 0.f) ? JPH::EMotionType::Dynamic : JPH::EMotionType::Static, JPH::EActivation::Activate);
}
#if 0
void JoltRigidBody3D::SetMassCenter(const Vector3f& center)
{
btTransform centerTransform;
centerTransform.setIdentity();
centerTransform.setOrigin(ToJolt(center));
m_body->setCenterOfMassTransform(centerTransform);
}
#endif 0
void JoltRigidBody3D::SetPosition(const Vector3f& position)
{
JPH::BodyInterface& body_interface = m_world->GetPhysicsSystem()->GetBodyInterface();
body_interface.SetPosition(JPH::BodyID(m_bodyIndex), ToJolt(position), JPH::EActivation::Activate);
}
void JoltRigidBody3D::SetRotation(const Quaternionf& rotation)
{
JPH::BodyInterface& body_interface = m_world->GetPhysicsSystem()->GetBodyInterface();
body_interface.SetRotation(JPH::BodyID(m_bodyIndex), ToJolt(rotation), JPH::EActivation::Activate);
}
#if 0
Quaternionf JoltRigidBody3D::ToLocal(const Quaternionf& worldRotation)
{
return GetRotation().Conjugate() * worldRotation;
}
Vector3f JoltRigidBody3D::ToLocal(const Vector3f& worldPosition)
{
btTransform worldTransform = m_body->getWorldTransform();
return GetMatrix().InverseTransform() * worldPosition;
}
Quaternionf JoltRigidBody3D::ToWorld(const Quaternionf& localRotation)
{
return GetRotation() * localRotation;
}
Vector3f JoltRigidBody3D::ToWorld(const Vector3f& localPosition)
{
return GetMatrix() * localPosition;
}
void JoltRigidBody3D::WakeUp()
{
m_body->activate();
}
#endif
JoltRigidBody3D& JoltRigidBody3D::operator=(JoltRigidBody3D&& object) noexcept
{
Destroy();
m_bodyIndex = object.m_bodyIndex;
m_geom = std::move(object.m_geom);
m_world = object.m_world;
object.m_bodyIndex = JPH::BodyID::cInvalidBodyID;
return *this;
}
void JoltRigidBody3D::Destroy()
{
if (m_bodyIndex != JPH::BodyID::cInvalidBodyID)
{
JPH::BodyInterface& body_interface = m_world->GetPhysicsSystem()->GetBodyInterface();
body_interface.RemoveBody(JPH::BodyID(m_bodyIndex));
body_interface.DestroyBody(JPH::BodyID(m_bodyIndex));
m_bodyIndex = JPH::BodyID::cInvalidBodyID;
}
m_geom.reset();
}
}

View File

@@ -0,0 +1,108 @@
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - JoltPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/JoltPhysics3D/Systems/JoltPhysics3DSystem.hpp>
#include <Nazara/Utility/Components/NodeComponent.hpp>
#include <iostream>
#include <Nazara/JoltPhysics3D/Debug.hpp>
namespace Nz
{
JoltPhysics3DSystem::JoltPhysics3DSystem(entt::registry& registry) :
m_registry(registry),
m_physicsConstructObserver(m_registry, entt::collector.group<JoltRigidBody3DComponent, NodeComponent>())
{
m_constructConnection = registry.on_construct<JoltRigidBody3DComponent>().connect<&JoltPhysics3DSystem::OnConstruct>(this);
m_destructConnection = registry.on_destroy<JoltRigidBody3DComponent>().connect<&JoltPhysics3DSystem::OnDestruct>(this);
m_stepCount = 0;
m_physicsTime = Time::Zero();
m_updateTime = Time::Zero();
}
JoltPhysics3DSystem::~JoltPhysics3DSystem()
{
m_physicsConstructObserver.disconnect();
// Ensure every RigidBody3D is destroyed before world is
auto rigidBodyView = m_registry.view<JoltRigidBody3DComponent>();
for (auto [entity, rigidBodyComponent] : rigidBodyView.each())
rigidBodyComponent.Destroy();
}
void JoltPhysics3DSystem::Dump()
{
if (m_stepCount == 0)
m_stepCount = 1;
std::cout << "Physics time: " << (m_physicsTime / Time::Nanoseconds(m_stepCount)) << std::endl;
std::cout << "Update time: " << (m_updateTime / Time::Nanoseconds(m_stepCount)) << std::endl;
std::cout << "--" << std::endl;
m_stepCount = 0;
m_physicsTime = Time::Zero();
m_updateTime = Time::Zero();
}
bool JoltPhysics3DSystem::RaycastQueryFirst(const Vector3f& from, const Vector3f& to, RaycastHit* hitInfo)
{
if (!m_physWorld.RaycastQueryFirst(from, to, hitInfo))
return false;
/*if (hitInfo->hitBody)
{
std::size_t uniqueIndex = hitInfo->hitBody->GetUniqueIndex();
if (uniqueIndex < m_physicsEntities.size())
hitInfo->hitEntity = entt::handle(m_registry, m_physicsEntities[uniqueIndex]);
}*/
return true;
}
void JoltPhysics3DSystem::Update(Time elapsedTime)
{
// Move newly-created physics entities to their node position/rotation
m_physicsConstructObserver.each([&](entt::entity entity)
{
JoltRigidBody3DComponent& entityPhysics = m_registry.get<JoltRigidBody3DComponent>(entity);
NodeComponent& entityNode = m_registry.get<NodeComponent>(entity);
entityPhysics.SetPosition(entityNode.GetPosition(CoordSys::Global));
entityPhysics.SetRotation(entityNode.GetRotation(CoordSys::Global));
});
Time t1 = GetElapsedNanoseconds();
// Update the physics world
m_physWorld.Step(elapsedTime);
m_stepCount++;
Time t2 = GetElapsedNanoseconds();
// Replicate rigid body position to their node components
// TODO: Only replicate active entities
auto view = m_registry.view<NodeComponent, const JoltRigidBody3DComponent>();
for (auto [entity, nodeComponent, rigidBodyComponent] : view.each())
{
if (rigidBodyComponent.IsSleeping())
continue;
nodeComponent.SetPosition(rigidBodyComponent.GetPosition(), CoordSys::Global);
nodeComponent.SetRotation(rigidBodyComponent.GetRotation(), CoordSys::Global);
}
Time t3 = GetElapsedNanoseconds();
m_physicsTime += (t2 - t1);
m_updateTime += (t3 - t2);
}
void JoltPhysics3DSystem::OnConstruct(entt::registry& registry, entt::entity entity)
{
}
void JoltPhysics3DSystem::OnDestruct(entt::registry& registry, entt::entity entity)
{
}
}