Remove BulletPhysics3D module

Having two physics engine seems redundant, Bullet3 development seems to have halted and JoltPhysics seems to be a better fit to Nazara
This commit is contained in:
Lynix
2024-02-06 20:45:06 +01:00
committed by Jérôme Leclercq
parent cb484a2432
commit 139bed2b0a
40 changed files with 31 additions and 2503 deletions

View File

@@ -1,470 +0,0 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - BulletPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/BulletPhysics3D/BulletCollider3D.hpp>
#include <Nazara/BulletPhysics3D/BulletHelper.hpp>
#include <Nazara/Core/PrimitiveList.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 <NazaraUtils/MemoryHelper.hpp>
#include <BulletCollision/CollisionShapes/btBoxShape.h>
#include <BulletCollision/CollisionShapes/btCapsuleShape.h>
#include <BulletCollision/CollisionShapes/btCompoundShape.h>
#include <BulletCollision/CollisionShapes/btConeShape.h>
#include <BulletCollision/CollisionShapes/btConvexHullShape.h>
#include <BulletCollision/CollisionShapes/btCylinderShape.h>
#include <BulletCollision/CollisionShapes/btEmptyShape.h>
#include <BulletCollision/CollisionShapes/btSphereShape.h>
#include <BulletCollision/CollisionShapes/btStaticPlaneShape.h>
#include <unordered_map>
#include <Nazara/BulletPhysics3D/Debug.hpp>
namespace Nz
{
BulletCollider3D::~BulletCollider3D() = default;
Boxf BulletCollider3D::ComputeAABB(const Vector3f& translation, const Quaternionf& rotation, const Vector3f& scale) const
{
return ComputeAABB(Matrix4f::Transform(translation, rotation), scale);
}
Boxf BulletCollider3D::ComputeAABB(const Matrix4f& offsetMatrix, const Vector3f& scale) const
{
btTransform transform = ToBullet(offsetMatrix);
btVector3 min, max;
GetShape()->getAabb(transform, min, max);
return Boxf(scale * FromBullet(min), scale * FromBullet(max));
}
void BulletCollider3D::ComputeInertia(float mass, Vector3f* inertia) const
{
NazaraAssert(inertia, "invalid inertia pointer");
btVector3 inertiaVec;
GetShape()->calculateLocalInertia(mass, inertiaVec);
*inertia = FromBullet(inertiaVec);
}
std::shared_ptr<StaticMesh> BulletCollider3D::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, SafeCast<UInt32>(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<BulletCollider3D> BulletCollider3D::Build(const PrimitiveList& list)
{
std::size_t primitiveCount = list.GetSize();
if (primitiveCount > 1)
{
std::vector<BulletCompoundCollider3D::ChildCollider> childColliders(primitiveCount);
for (unsigned int i = 0; i < primitiveCount; ++i)
{
const Primitive& primitive = list.GetPrimitive(i);
childColliders[i].collider = CreateGeomFromPrimitive(primitive);
childColliders[i].offsetMatrix = primitive.matrix;
}
return std::make_shared<BulletCompoundCollider3D>(std::move(childColliders));
}
else if (primitiveCount > 0)
return CreateGeomFromPrimitive(list.GetPrimitive(0));
else
return std::make_shared<BulletNullCollider3D>();
}
std::shared_ptr<BulletCollider3D> BulletCollider3D::CreateGeomFromPrimitive(const Primitive& primitive)
{
switch (primitive.type)
{
case PrimitiveType::Box:
return std::make_shared<BulletBoxCollider3D>(primitive.box.lengths);
case PrimitiveType::Cone:
return std::make_shared<BulletConeCollider3D>(primitive.cone.length, primitive.cone.radius);
case PrimitiveType::Plane:
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<BulletSphereCollider3D>(primitive.sphere.size);
}
NazaraErrorFmt("Primitive type not handled ({0:#x})", UnderlyingCast(primitive.type));
return std::shared_ptr<BulletCollider3D>();
}
/********************************** BoxCollider3D **********************************/
BulletBoxCollider3D::BulletBoxCollider3D(const Vector3f& lengths) :
m_lengths(lengths)
{
m_shape = std::make_unique<btBoxShape>(ToBullet(m_lengths * 0.5f));
}
BulletBoxCollider3D::~BulletBoxCollider3D() = default;
void BulletBoxCollider3D::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);
}
btCollisionShape* BulletBoxCollider3D::GetShape() const
{
return m_shape.get();
}
Vector3f BulletBoxCollider3D::GetLengths() const
{
return m_lengths;
}
BulletColliderType3D BulletBoxCollider3D::GetType() const
{
return BulletColliderType3D::Box;
}
/******************************** CapsuleCollider3D ********************************/
BulletCapsuleCollider3D::BulletCapsuleCollider3D(float length, float radius) :
m_length(length),
m_radius(radius)
{
m_shape = std::make_unique<btCapsuleShape>(radius, length);
}
BulletCapsuleCollider3D::~BulletCapsuleCollider3D() = default;
void BulletCapsuleCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
{
}
float BulletCapsuleCollider3D::GetLength() const
{
return m_length;
}
float BulletCapsuleCollider3D::GetRadius() const
{
return m_radius;
}
btCollisionShape* BulletCapsuleCollider3D::GetShape() const
{
return m_shape.get();
}
BulletColliderType3D BulletCapsuleCollider3D::GetType() const
{
return BulletColliderType3D::Capsule;
}
/******************************* CompoundCollider3D ********************************/
BulletCompoundCollider3D::BulletCompoundCollider3D(std::vector<ChildCollider> childs) :
m_childs(std::move(childs))
{
m_shape = std::make_unique<btCompoundShape>();
for (const auto& child : m_childs)
{
btTransform transform = ToBullet(child.offsetMatrix);
m_shape->addChildShape(transform, child.collider->GetShape());
}
}
BulletCompoundCollider3D::~BulletCompoundCollider3D() = default;
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 BulletCompoundCollider3D::GetGeoms() const -> const std::vector<ChildCollider>&
{
return m_childs;
}
btCollisionShape* BulletCompoundCollider3D::GetShape() const
{
return m_shape.get();
}
BulletColliderType3D BulletCompoundCollider3D::GetType() const
{
return BulletColliderType3D::Compound;
}
/********************************* ConeCollider3D **********************************/
BulletConeCollider3D::BulletConeCollider3D(float length, float radius) :
m_length(length),
m_radius(radius)
{
m_shape = std::make_unique<btConeShape>(radius, length);
}
BulletConeCollider3D::~BulletConeCollider3D() = default;
void BulletConeCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
{
}
float BulletConeCollider3D::GetLength() const
{
return m_length;
}
float BulletConeCollider3D::GetRadius() const
{
return m_radius;
}
btCollisionShape* BulletConeCollider3D::GetShape() const
{
return m_shape.get();
}
BulletColliderType3D BulletConeCollider3D::GetType() const
{
return BulletColliderType3D::Cone;
}
/****************************** ConvexCollider3D *******************************/
BulletConvexCollider3D::BulletConvexCollider3D(SparsePtr<const Vector3f> vertices, unsigned int vertexCount)
{
static_assert(std::is_same_v<btScalar, float>);
m_shape = std::make_unique<btConvexHullShape>(reinterpret_cast<const float*>(vertices.GetPtr()), vertexCount, vertices.GetStride());
m_shape->optimizeConvexHull();
}
BulletConvexCollider3D::~BulletConvexCollider3D() = default;
void BulletConvexCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
{
std::unordered_map<Vector3f, UInt16> vertexCache;
auto InsertVertex = [&](const Vector3f& position) -> UInt16
{
auto it = vertexCache.find(position);
if (it != vertexCache.end())
return it->second;
UInt16 index = SafeCast<UInt16>(vertices.size());
vertices.push_back(offsetMatrix * position);
vertexCache.emplace(position, index);
return index;
};
int edgeCount = m_shape->getNumEdges();
for (int i = 0; i < edgeCount; ++i)
{
btVector3 from, to;
m_shape->getEdge(i, from, to);
indices.push_back(InsertVertex(FromBullet(from)));
indices.push_back(InsertVertex(FromBullet(to)));
}
}
btCollisionShape* BulletConvexCollider3D::GetShape() const
{
return m_shape.get();
}
BulletColliderType3D BulletConvexCollider3D::GetType() const
{
return BulletColliderType3D::ConvexHull;
}
/******************************* CylinderCollider3D ********************************/
BulletCylinderCollider3D::BulletCylinderCollider3D(float length, float radius) :
m_length(length),
m_radius(radius)
{
// TODO: Allow to use two different radius
m_shape = std::make_unique<btCylinderShape>(btVector3{ radius, length, radius });
}
BulletCylinderCollider3D::~BulletCylinderCollider3D() = default;
void BulletCylinderCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
{
}
float BulletCylinderCollider3D::GetLength() const
{
return m_length;
}
float BulletCylinderCollider3D::GetRadius() const
{
return m_radius;
}
btCollisionShape* BulletCylinderCollider3D::GetShape() const
{
return m_shape.get();
}
BulletColliderType3D BulletCylinderCollider3D::GetType() const
{
return BulletColliderType3D::Cylinder;
}
/********************************* NullCollider3D **********************************/
BulletNullCollider3D::BulletNullCollider3D()
{
m_shape = std::make_unique<btEmptyShape>();
}
BulletNullCollider3D::~BulletNullCollider3D() = default;
void BulletNullCollider3D::BuildDebugMesh(std::vector<Vector3f>& /*vertices*/, std::vector<UInt16>& /*indices*/, const Matrix4f& /*offsetMatrix*/) const
{
}
void BulletNullCollider3D::ComputeInertia(float /*mass*/, Vector3f* inertia) const
{
*inertia = Vector3f::Unit();
}
btCollisionShape* BulletNullCollider3D::GetShape() const
{
return m_shape.get();
}
BulletColliderType3D BulletNullCollider3D::GetType() const
{
return BulletColliderType3D::Null;
}
/******************************** SphereCollider3D *********************************/
BulletSphereCollider3D::BulletSphereCollider3D(float radius) :
m_radius(radius)
{
m_shape = std::make_unique<btSphereShape>(radius);
}
BulletSphereCollider3D::~BulletSphereCollider3D() = default;
void BulletSphereCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
{
}
float BulletSphereCollider3D::GetRadius() const
{
return m_radius;
}
btCollisionShape* BulletSphereCollider3D::GetShape() const
{
return m_shape.get();
}
BulletColliderType3D BulletSphereCollider3D::GetType() const
{
return BulletColliderType3D::Sphere;
}
/******************************** StaticPlaneCollider3D *********************************/
BulletStaticPlaneCollider3D::BulletStaticPlaneCollider3D(const Planef& plane) :
BulletStaticPlaneCollider3D(plane.normal, plane.distance)
{
}
BulletStaticPlaneCollider3D::BulletStaticPlaneCollider3D(const Vector3f& normal, float distance) :
m_normal(normal),
m_distance(distance)
{
m_shape = std::make_unique<btStaticPlaneShape>(ToBullet(m_normal), m_distance);
}
BulletStaticPlaneCollider3D::~BulletStaticPlaneCollider3D() = default;
void BulletStaticPlaneCollider3D::BuildDebugMesh(std::vector<Vector3f>& vertices, std::vector<UInt16>& indices, const Matrix4f& offsetMatrix) const
{
}
float BulletStaticPlaneCollider3D::GetDistance() const
{
return m_distance;
}
const Vector3f& BulletStaticPlaneCollider3D::GetNormal() const
{
return m_normal;
}
btCollisionShape* BulletStaticPlaneCollider3D::GetShape() const
{
return m_shape.get();
}
BulletColliderType3D BulletStaticPlaneCollider3D::GetType() const
{
return BulletColliderType3D::StaticPlane;
}
}

View File

@@ -1,152 +0,0 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - BulletPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/BulletPhysics3D/BulletConstraint3D.hpp>
#include <Nazara/BulletPhysics3D/BulletHelper.hpp>
#include <BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h>
#include <BulletDynamics/Dynamics/btDynamicsWorld.h>
#include <Nazara/BulletPhysics3D/Debug.hpp>
namespace Nz
{
BulletConstraint3D::BulletConstraint3D(std::unique_ptr<btTypedConstraint> constraint, bool disableCollisions) :
m_constraint(std::move(constraint)),
m_bodyCollisionEnabled(!disableCollisions)
{
m_constraint->setUserConstraintPtr(this);
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()
{
Destroy();
}
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
{
Destroy();
m_constraint = std::move(constraint.m_constraint);
m_bodyCollisionEnabled = constraint.m_bodyCollisionEnabled;
if (m_constraint)
m_constraint->setUserConstraintPtr(this);
return *this;
}
void BulletConstraint3D::Destroy()
{
if (m_constraint)
{
btDynamicsWorld* world = GetWorld().GetDynamicsWorld();
world->removeConstraint(m_constraint.get());
m_constraint.reset();
}
}
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());
}
float BulletPivotConstraint3D::GetDamping() const
{
return GetConstraint<btPoint2PointConstraint>()->m_setting.m_damping;
}
float BulletPivotConstraint3D::GetImpulseClamp() const
{
return GetConstraint<btPoint2PointConstraint>()->m_setting.m_impulseClamp;
}
void BulletPivotConstraint3D::SetFirstAnchor(const Vector3f& firstAnchor)
{
GetConstraint<btPoint2PointConstraint>()->setPivotA(ToBullet(firstAnchor));
}
void BulletPivotConstraint3D::SetSecondAnchor(const Vector3f& secondAnchor)
{
GetConstraint<btPoint2PointConstraint>()->setPivotB(ToBullet(secondAnchor));
}
void BulletPivotConstraint3D::SetDamping(float damping)
{
GetConstraint<btPoint2PointConstraint>()->m_setting.m_damping = damping;
}
void BulletPivotConstraint3D::SetImpulseClamp(float impulseClamp)
{
GetConstraint<btPoint2PointConstraint>()->m_setting.m_impulseClamp = impulseClamp;
}
}

View File

@@ -1,32 +0,0 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - BulletPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_BULLETPHYSICS3D_BULLETHELPER_HPP
#define NAZARA_BULLETPHYSICS3D_BULLETHELPER_HPP
#include <NazaraUtils/Prerequisites.hpp>
#include <Nazara/BulletPhysics3D/Config.hpp>
#include <Nazara/Math/Matrix4.hpp>
#include <Nazara/Math/Quaternion.hpp>
#include <Nazara/Math/Vector3.hpp>
#include <LinearMath/btQuaternion.h>
#include <LinearMath/btTransform.h>
#include <LinearMath/btVector3.h>
namespace Nz
{
inline Quaternionf FromBullet(const btQuaternion& quat);
inline Matrix4f FromBullet(const btTransform& transform);
inline Vector3f FromBullet(const btVector3& vec);
inline btTransform ToBullet(const Matrix4f& transformMatrix);
inline btQuaternion ToBullet(const Quaternionf& rotation);
inline btVector3 ToBullet(const Vector3f& vec);
}
#include <Nazara/BulletPhysics3D/BulletHelper.inl>
#endif // NAZARA_BULLETPHYSICS3D_BULLETHELPER_HPP

View File

@@ -1,60 +0,0 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - BulletPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/BulletPhysics3D/Debug.hpp>
namespace Nz
{
Quaternionf FromBullet(const btQuaternion& quat)
{
return Quaternionf(quat.w(), quat.x(), quat.y(), quat.z());
}
Matrix4f FromBullet(const btTransform& transform)
{
const btMatrix3x3& basisMatrix = transform.getBasis();
const btVector3& origin = transform.getOrigin();
const btVector3& row1 = basisMatrix.getRow(0);
const btVector3& row2 = basisMatrix.getRow(1);
const btVector3& row3 = basisMatrix.getRow(2);
return Matrix4f(
row1.x(), row1.y(), row1.z(), 0.f,
row2.x(), row2.y(), row2.z(), 0.f,
row3.x(), row3.y(), row3.z(), 0.f,
origin.x(), origin.y(), origin.z(), 1.f);
}
inline Vector3f FromBullet(const btVector3& vec)
{
return Vector3f(vec.x(), vec.y(), vec.z());
}
btTransform ToBullet(const Matrix4f& transformMatrix)
{
btTransform transform;
transform.setBasis(btMatrix3x3(
transformMatrix.m11, transformMatrix.m12, transformMatrix.m13,
transformMatrix.m21, transformMatrix.m22, transformMatrix.m23,
transformMatrix.m31, transformMatrix.m32, transformMatrix.m33
));
transform.setOrigin(btVector3(transformMatrix.m41, transformMatrix.m42, transformMatrix.m43));
return transform;
}
btQuaternion ToBullet(const Quaternionf& rotation)
{
return btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w);
}
inline btVector3 ToBullet(const Vector3f& vec)
{
return btVector3(vec.x, vec.y, vec.z);
}
}
#include <Nazara/BulletPhysics3D/DebugOff.hpp>

View File

@@ -1,215 +0,0 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - BulletPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/BulletPhysics3D/BulletPhysWorld3D.hpp>
#include <Nazara/BulletPhysics3D/BulletHelper.hpp>
#include <NazaraUtils/MemoryPool.hpp>
#include <NazaraUtils/StackVector.hpp>
#include <BulletCollision/BroadphaseCollision/btDbvtBroadphase.h>
#include <BulletCollision/CollisionDispatch/btCollisionDispatcher.h>
#include <BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h>
#include <BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h>
#include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
#include <cassert>
#include <Nazara/BulletPhysics3D/Debug.hpp>
namespace Nz
{
namespace NAZARA_ANONYMOUS_NAMESPACE
{
class CallbackHitResult : public btCollisionWorld::RayResultCallback
{
public:
CallbackHitResult(const Vector3f& from, const Vector3f& to, const FunctionRef<std::optional<float>(const BulletPhysWorld3D::RaycastHit& hitInfo)>& callback) :
m_callback(callback),
m_from(from),
m_to(to)
{
}
btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) override
{
m_collisionObject = rayResult.m_collisionObject;
BulletPhysWorld3D::RaycastHit hitInfo;
hitInfo.fraction = rayResult.m_hitFraction;
hitInfo.hitPosition = Lerp(m_from, m_to, rayResult.m_hitFraction);
hitInfo.hitNormal = FromBullet((normalInWorldSpace) ? rayResult.m_hitNormalLocal : m_collisionObject->getWorldTransform().getBasis() * rayResult.m_hitNormalLocal);
if (const btRigidBody* body = btRigidBody::upcast(m_collisionObject))
hitInfo.hitBody = static_cast<BulletRigidBody3D*>(body->getUserPointer());
m_closestHitFraction = std::max(m_callback(hitInfo).value_or(m_closestHitFraction), 0.f);
return m_closestHitFraction;
}
private:
const FunctionRef<std::optional<float>(const BulletPhysWorld3D::RaycastHit& hitInfo)>& m_callback;
Vector3f m_from;
Vector3f m_to;
};
}
struct BulletPhysWorld3D::BulletWorld
{
btDefaultCollisionConfiguration collisionConfiguration;
btCollisionDispatcher dispatcher;
btDbvtBroadphase broadphase;
btSequentialImpulseConstraintSolver constraintSolver;
btDiscreteDynamicsWorld dynamicWorld;
MemoryPool<btRigidBody> rigidBodyPool;
BulletWorld() :
dispatcher(&collisionConfiguration),
dynamicWorld(&dispatcher, &broadphase, &constraintSolver, &collisionConfiguration),
rigidBodyPool(256)
{
}
BulletWorld(const BulletWorld&) = delete;
BulletWorld(BulletWorld&&) = delete;
BulletWorld& operator=(const BulletWorld&) = delete;
BulletWorld& operator=(BulletWorld&&) = delete;
};
BulletPhysWorld3D::BulletPhysWorld3D() :
m_maxStepCount(50),
m_gravity(Vector3f::Zero()),
m_stepSize(Time::TickDuration(120)),
m_timestepAccumulator(Time::Zero())
{
m_world = std::make_unique<BulletWorld>();
}
BulletPhysWorld3D::~BulletPhysWorld3D() = default;
btDynamicsWorld* BulletPhysWorld3D::GetDynamicsWorld()
{
return &m_world->dynamicWorld;
}
Vector3f BulletPhysWorld3D::GetGravity() const
{
return FromBullet(m_world->dynamicWorld.getGravity());
}
std::size_t BulletPhysWorld3D::GetMaxStepCount() const
{
return m_maxStepCount;
}
Time BulletPhysWorld3D::GetStepSize() const
{
return m_stepSize;
}
bool BulletPhysWorld3D::RaycastQuery(const Vector3f& from, const Vector3f& to, const FunctionRef<std::optional<float>(const RaycastHit& hitInfo)>& callback)
{
NAZARA_USE_ANONYMOUS_NAMESPACE
CallbackHitResult resultHandler(from, to, callback);
m_world->dynamicWorld.rayTest(ToBullet(from), ToBullet(to), resultHandler);
return resultHandler.hasHit();
}
bool BulletPhysWorld3D::RaycastQueryFirst(const Vector3f& from, const Vector3f& to, RaycastHit* hitInfo)
{
btCollisionWorld::ClosestRayResultCallback resultHandler(ToBullet(from), ToBullet(to));
m_world->dynamicWorld.rayTest(ToBullet(from), ToBullet(to), resultHandler);
if (!resultHandler.hasHit())
return false;
if (hitInfo)
{
hitInfo->fraction = resultHandler.m_closestHitFraction;
hitInfo->hitNormal = FromBullet(resultHandler.m_hitNormalWorld);
hitInfo->hitPosition = FromBullet(resultHandler.m_hitPointWorld);
if (const btRigidBody* body = btRigidBody::upcast(resultHandler.m_collisionObject))
hitInfo->hitBody = static_cast<BulletRigidBody3D*>(body->getUserPointer());
}
return true;
}
void BulletPhysWorld3D::SetGravity(const Vector3f& gravity)
{
m_world->dynamicWorld.setGravity(ToBullet(gravity));
}
void BulletPhysWorld3D::SetMaxStepCount(std::size_t maxStepCount)
{
m_maxStepCount = maxStepCount;
}
void BulletPhysWorld3D::SetStepSize(Time stepSize)
{
m_stepSize = stepSize;
}
void BulletPhysWorld3D::Step(Time timestep)
{
m_timestepAccumulator += timestep;
btScalar stepSize = m_stepSize.AsSeconds<btScalar>();
std::size_t stepCount = 0;
while (m_timestepAccumulator >= m_stepSize && stepCount < m_maxStepCount)
{
m_world->dynamicWorld.stepSimulation(stepSize, 0, stepSize);
m_timestepAccumulator -= m_stepSize;
stepCount++;
}
}
#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
auto& rigidBodies = m_world->dynamicWorld.getNonStaticRigidBodies();
if (rigidBodies.size() >= 2 && rigidBodies[rigidBodies.size() - 1] == rigidBody)
{
// Sort rigid bodies
btRigidBody** startPtr = &rigidBodies[0];
btRigidBody** endPtr = startPtr + rigidBodies.size();
btRigidBody** lastPtr = endPtr - 1;
auto it = std::lower_bound(startPtr, endPtr, rigidBody);
if (it != lastPtr)
{
std::move_backward(it, lastPtr, endPtr);
*it = rigidBody;
}
}
#endif
return rigidBody;
}
void BulletPhysWorld3D::RemoveRigidBody(btRigidBody* rigidBody, std::size_t rigidBodyIndex)
{
// 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

@@ -1,21 +0,0 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - BulletPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/BulletPhysics3D/BulletPhysics3D.hpp>
#include <Nazara/BulletPhysics3D/BulletCollider3D.hpp>
#include <Nazara/BulletPhysics3D/Config.hpp>
#include <Nazara/Core/Core.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/Log.hpp>
#include <Nazara/BulletPhysics3D/Debug.hpp>
namespace Nz
{
BulletPhysics3D::BulletPhysics3D(Config /*config*/) :
ModuleBase("BulletPhysics3D", this)
{
}
BulletPhysics3D* BulletPhysics3D::s_instance;
}

View File

@@ -1,339 +0,0 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - BulletPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/BulletPhysics3D/BulletRigidBody3D.hpp>
#include <Nazara/BulletPhysics3D/BulletHelper.hpp>
#include <Nazara/BulletPhysics3D/BulletPhysWorld3D.hpp>
#include <NazaraUtils/MemoryHelper.hpp>
#include <BulletDynamics/Dynamics/btDynamicsWorld.h>
#include <BulletDynamics/Dynamics/btRigidBody.h>
#include <algorithm>
#include <array>
#include <cmath>
#include <Nazara/BulletPhysics3D/Debug.hpp>
namespace Nz
{
BulletRigidBody3D::BulletRigidBody3D(BulletPhysWorld3D* world, const Matrix4f& mat) :
BulletRigidBody3D(world, nullptr, mat)
{
}
BulletRigidBody3D::BulletRigidBody3D(BulletPhysWorld3D* world, std::shared_ptr<BulletCollider3D> 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<BulletNullCollider3D>();
Vector3f inertia;
m_geom->ComputeInertia(1.f, &inertia);
btRigidBody::btRigidBodyConstructionInfo constructionInfo(1.f, nullptr, m_geom->GetShape(), ToBullet(inertia));
constructionInfo.m_startWorldTransform = ToBullet(mat);
m_body = m_world->AddRigidBody(m_bodyPoolIndex, [&](btRigidBody* body)
{
PlacementNew(body, constructionInfo);
});
m_body->setUserPointer(this);
}
BulletRigidBody3D::BulletRigidBody3D(BulletRigidBody3D&& object) noexcept :
m_geom(std::move(object.m_geom)),
m_bodyPoolIndex(object.m_bodyPoolIndex),
m_body(object.m_body),
m_world(object.m_world)
{
if (m_body)
m_body->setUserPointer(this);
object.m_body = nullptr;
}
BulletRigidBody3D::~BulletRigidBody3D()
{
Destroy();
}
void BulletRigidBody3D::AddForce(const Vector3f& force, CoordSys coordSys)
{
switch (coordSys)
{
case CoordSys::Global:
WakeUp();
m_body->applyCentralForce(ToBullet(force));
break;
case CoordSys::Local:
WakeUp();
m_body->applyCentralForce(ToBullet(GetRotation() * force));
break;
}
}
void BulletRigidBody3D::AddForce(const Vector3f& force, const Vector3f& point, CoordSys coordSys)
{
switch (coordSys)
{
case CoordSys::Global:
WakeUp();
m_body->applyForce(ToBullet(force), ToBullet(point));
break;
case CoordSys::Local:
{
Matrix4f transformMatrix = GetMatrix();
return AddForce(transformMatrix.Transform(force, 0.f), point, CoordSys::Global);
}
}
}
void BulletRigidBody3D::AddTorque(const Vector3f& torque, CoordSys coordSys)
{
switch (coordSys)
{
case CoordSys::Global:
WakeUp();
m_body->applyTorque(ToBullet(torque));
break;
case CoordSys::Local:
Matrix4f transformMatrix = GetMatrix();
WakeUp();
m_body->applyTorque(ToBullet(transformMatrix.Transform(torque, 0.f)));
break;
}
}
void BulletRigidBody3D::EnableSleeping(bool enable)
{
if (enable)
{
if (m_body->getActivationState() == DISABLE_DEACTIVATION)
m_body->setActivationState(ACTIVE_TAG);
}
else
{
if (m_body->getActivationState() != DISABLE_DEACTIVATION)
m_body->setActivationState(DISABLE_DEACTIVATION);
}
}
void BulletRigidBody3D::FallAsleep()
{
if (m_body->getActivationState() != DISABLE_DEACTIVATION)
m_body->setActivationState(ISLAND_SLEEPING);
}
Boxf BulletRigidBody3D::GetAABB() const
{
btVector3 min, max;
m_body->getAabb(min, max);
return Boxf(FromBullet(min), FromBullet(max));
}
float BulletRigidBody3D::GetAngularDamping() const
{
return m_body->getAngularDamping();
}
Vector3f BulletRigidBody3D::GetAngularVelocity() const
{
return FromBullet(m_body->getAngularVelocity());
}
float BulletRigidBody3D::GetLinearDamping() const
{
return m_body->getLinearDamping();
}
Vector3f BulletRigidBody3D::GetLinearVelocity() const
{
return FromBullet(m_body->getLinearVelocity());
}
float BulletRigidBody3D::GetMass() const
{
return m_body->getMass();
}
Vector3f BulletRigidBody3D::GetMassCenter(CoordSys coordSys) const
{
return FromBullet(m_body->getCenterOfMassPosition());
}
Matrix4f BulletRigidBody3D::GetMatrix() const
{
return FromBullet(m_body->getWorldTransform());
}
Vector3f BulletRigidBody3D::GetPosition() const
{
return FromBullet(m_body->getWorldTransform().getOrigin());
}
Quaternionf BulletRigidBody3D::GetRotation() const
{
return FromBullet(m_body->getWorldTransform().getRotation());
}
bool BulletRigidBody3D::IsSimulationEnabled() const
{
return m_body->isActive();
}
bool BulletRigidBody3D::IsSleeping() const
{
return m_body->getActivationState() == ISLAND_SLEEPING;
}
bool BulletRigidBody3D::IsSleepingEnabled() const
{
return m_body->getActivationState() != DISABLE_DEACTIVATION;
}
void BulletRigidBody3D::SetAngularDamping(float angularDamping)
{
m_body->setDamping(m_body->getLinearDamping(), angularDamping);
}
void BulletRigidBody3D::SetAngularVelocity(const Vector3f& angularVelocity)
{
m_body->setAngularVelocity(ToBullet(angularVelocity));
}
void BulletRigidBody3D::SetGeom(std::shared_ptr<BulletCollider3D> geom, bool recomputeInertia)
{
if (m_geom != geom)
{
if (geom)
m_geom = std::move(geom);
else
m_geom = std::make_shared<BulletNullCollider3D>();
m_body->setCollisionShape(m_geom->GetShape());
if (recomputeInertia)
{
float mass = GetMass();
Vector3f inertia;
m_geom->ComputeInertia(mass, &inertia);
m_body->setMassProps(mass, ToBullet(inertia));
}
}
}
void BulletRigidBody3D::SetLinearDamping(float damping)
{
m_body->setDamping(damping, m_body->getAngularDamping());
}
void BulletRigidBody3D::SetLinearVelocity(const Vector3f& velocity)
{
m_body->setLinearVelocity(ToBullet(velocity));
}
void BulletRigidBody3D::SetMass(float mass)
{
NazaraAssert(mass >= 0.f, "Mass must be positive and finite");
NazaraAssert(std::isfinite(mass), "Mass must be positive and finite");
Vector3f inertia;
m_geom->ComputeInertia(mass, &inertia);
m_body->setMassProps(mass, ToBullet(inertia));
}
void BulletRigidBody3D::SetMassCenter(const Vector3f& center)
{
btTransform centerTransform;
centerTransform.setIdentity();
centerTransform.setOrigin(ToBullet(center));
m_body->setCenterOfMassTransform(centerTransform);
}
void BulletRigidBody3D::SetPosition(const Vector3f& position)
{
btTransform worldTransform = m_body->getWorldTransform();
worldTransform.setOrigin(ToBullet(position));
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();
worldTransform.setRotation(ToBullet(rotation));
m_body->setWorldTransform(worldTransform);
}
Quaternionf BulletRigidBody3D::ToLocal(const Quaternionf& worldRotation)
{
return GetRotation().Conjugate() * worldRotation;
}
Vector3f BulletRigidBody3D::ToLocal(const Vector3f& worldPosition)
{
return GetMatrix().InverseTransform() * worldPosition;
}
Quaternionf BulletRigidBody3D::ToWorld(const Quaternionf& localRotation)
{
return GetRotation() * localRotation;
}
Vector3f BulletRigidBody3D::ToWorld(const Vector3f& localPosition)
{
return GetMatrix() * localPosition;
}
void BulletRigidBody3D::WakeUp()
{
m_body->activate();
}
BulletRigidBody3D& BulletRigidBody3D::operator=(BulletRigidBody3D&& object) noexcept
{
Destroy();
m_body = object.m_body;
m_bodyPoolIndex = object.m_bodyPoolIndex;
m_geom = std::move(object.m_geom);
m_world = object.m_world;
if (m_body)
m_body->setUserPointer(this);
object.m_body = nullptr;
return *this;
}
void BulletRigidBody3D::Destroy()
{
if (m_body)
{
m_world->RemoveRigidBody(m_body, m_bodyPoolIndex);
m_body = nullptr;
}
m_geom.reset();
}
}

View File

@@ -1,145 +0,0 @@
// Copyright (C) 2024 Jérôme "SirLynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - BulletPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/BulletPhysics3D/Systems/BulletPhysics3DSystem.hpp>
#include <Nazara/Core/Components/DisabledComponent.hpp>
#include <Nazara/Utility/Components/NodeComponent.hpp>
#include <Nazara/BulletPhysics3D/Debug.hpp>
namespace Nz
{
BulletPhysics3DSystem::BulletPhysics3DSystem(entt::registry& registry) :
m_registry(registry),
m_physicsConstructObserver(m_registry, entt::collector.group<BulletRigidBody3DComponent, NodeComponent>())
{
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()
{
m_physicsConstructObserver.disconnect();
// Ensure every RigidBody3D is destroyed before world is
auto rigidBodyView = m_registry.view<BulletRigidBody3DComponent>();
for (auto [entity, rigidBodyComponent] : rigidBodyView.each())
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 << "Active body count: " << m_activeObjectCount << std::endl;
std::cout << "--" << std::endl;
*/
m_stepCount = 0;
m_physicsTime = Time::Zero();
m_updateTime = Time::Zero();
}
bool BulletPhysics3DSystem::RaycastQuery(const Vector3f& from, const Vector3f& to, const FunctionRef<std::optional<float>(const RaycastHit& hitInfo)>& callback)
{
return m_physWorld.RaycastQuery(from, to, [&](const BulletPhysWorld3D::RaycastHit& hitInfo)
{
RaycastHit hitWithEntity;
static_cast<BulletPhysWorld3D::RaycastHit&>(hitWithEntity) = hitInfo;
if (hitWithEntity.hitBody)
{
std::size_t uniqueIndex = hitWithEntity.hitBody->GetUniqueIndex();
if (uniqueIndex < m_physicsEntities.size())
hitWithEntity.hitEntity = entt::handle(m_registry, m_physicsEntities[uniqueIndex]);
}
return callback(hitWithEntity);
});
}
bool BulletPhysics3DSystem::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 BulletPhysics3DSystem::Update(Time elapsedTime)
{
// Move newly-created physics entities to their node position/rotation
m_physicsConstructObserver.each([&](entt::entity entity)
{
BulletRigidBody3DComponent& entityPhysics = m_registry.get<BulletRigidBody3DComponent>(entity);
NodeComponent& entityNode = m_registry.get<NodeComponent>(entity);
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
m_activeObjectCount = 0;
auto view = m_registry.view<NodeComponent, const BulletRigidBody3DComponent>(entt::exclude<DisabledComponent>);
for (auto entity : view)
{
auto& rigidBodyComponent = view.get<const BulletRigidBody3DComponent>(entity);
if (rigidBodyComponent.IsSleeping())
continue;
auto& nodeComponent = view.get<NodeComponent>(entity);
nodeComponent.SetTransform(rigidBodyComponent.GetPosition(), rigidBodyComponent.GetRotation());
m_activeObjectCount++;
}
Time t3 = GetElapsedNanoseconds();
m_physicsTime += (t2 - t1);
m_updateTime += (t3 - t2);
}
void BulletPhysics3DSystem::OnConstruct(entt::registry& registry, entt::entity entity)
{
// Register rigid body owning entity
BulletRigidBody3DComponent& rigidBody = registry.get<BulletRigidBody3DComponent>(entity);
std::size_t uniqueIndex = rigidBody.GetUniqueIndex();
if (uniqueIndex >= m_physicsEntities.size())
m_physicsEntities.resize(uniqueIndex + 1);
m_physicsEntities[uniqueIndex] = entity;
}
void BulletPhysics3DSystem::OnDestruct(entt::registry& registry, entt::entity entity)
{
// Unregister owning entity
BulletRigidBody3DComponent& rigidBody = registry.get<BulletRigidBody3DComponent>(entity);
std::size_t uniqueIndex = rigidBody.GetUniqueIndex();
assert(uniqueIndex <= m_physicsEntities.size());
m_physicsEntities[uniqueIndex] = entt::null;
}
}