feat: add more collider types + camera snapping fix

This commit is contained in:
2026-10-24 02:34:09 +02:00
parent 355dfa7851
commit 118016bcc7
26 changed files with 1525 additions and 354 deletions
+124
View File
@@ -10,6 +10,9 @@
#include <destrum/Components/Physics/BoxCollider.h>
#include <destrum/Components/Physics/CapsuleCollider.h>
#include <destrum/Components/Physics/Collider.h>
#include <destrum/Components/Physics/ConvexHullCollider.h>
#include <destrum/Components/Physics/CylinderCollider.h>
#include <destrum/Components/Physics/MeshCollider.h>
#include <destrum/Components/LightComponent.h>
#include <destrum/Components/Physics/Rigidbody.h>
@@ -357,6 +360,124 @@ namespace {
SceneManager::GetInstance().Destroy();
}
std::shared_ptr<const PhysicsMeshData> makeCubeMesh(float halfExtent)
{
auto mesh = std::make_shared<PhysicsMeshData>();
for (int corner = 0; corner < 8; ++corner) {
mesh->points.push_back({
corner & 1 ? halfExtent : -halfExtent,
corner & 2 ? halfExtent : -halfExtent,
corner & 4 ? halfExtent : -halfExtent});
}
mesh->points.push_back(glm::vec3{0.0f});
mesh->indices = {
0, 2, 1, 1, 2, 3, 4, 5, 6, 5, 7, 6,
0, 1, 4, 1, 5, 4, 2, 6, 3, 3, 6, 7,
0, 4, 2, 2, 4, 6, 1, 3, 5, 3, 7, 5};
return mesh;
}
std::shared_ptr<const PhysicsMeshData> makeQuadMesh(float halfExtent)
{
auto mesh = std::make_shared<PhysicsMeshData>();
mesh->points = {
{-halfExtent, 0.0f, -halfExtent},
{halfExtent, 0.0f, -halfExtent},
{-halfExtent, 0.0f, halfExtent},
{halfExtent, 0.0f, halfExtent}};
mesh->indices = {0, 2, 1, 1, 2, 3};
return mesh;
}
void testCylinderCollider()
{
JoltPhysicsWorld world{{.workerThreadCount = 1, .gravity = glm::vec3{0.0f}}};
GameObject object{"cylinder"};
object.AddComponent<CylinderCollider>(1.0f, 4.0f);
Rigidbody* rigidbody = object.AddComponent<Rigidbody>();
rigidbody->SetType(RigidbodyType::Static);
world.RegisterRigidbody(*rigidbody);
PhysicsRaycastHit hit;
check(world.Raycast({-3.0f, 1.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, 5.0f, hit) && close(hit.distance, 2.0f),
"cylinder sides must be at its radius along the whole height");
check(world.Raycast({0.0f, 5.0f, 0.0f}, {0.0f, -1.0f, 0.0f}, 5.0f, hit) && close(hit.distance, 3.0f),
"cylinder caps must be flat at half its height");
}
void testConvexHullCollider()
{
JoltPhysicsWorld world{{.workerThreadCount = 1, .gravity = glm::vec3{0.0f}}};
GameObject object{"hull"};
object.GetTransform().SetLocalScale(glm::vec3{2.0f});
auto* collider = object.AddComponent<ConvexHullCollider>();
collider->SetCustomMesh(makeCubeMesh(1.0f));
check(collider->BuildPhysicsShape().type == PhysicsShapeType::ConvexHull,
"a cube mesh must build a convex hull");
check(collider->BuildPhysicsShape().mesh->points.size() == 8,
"interior points must not end up in the hull");
Rigidbody* rigidbody = object.AddComponent<Rigidbody>();
rigidbody->SetType(RigidbodyType::Static);
world.RegisterRigidbody(*rigidbody);
PhysicsRaycastHit hit;
check(world.Raycast({-5.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}, 10.0f, hit) && close(hit.distance, 3.0f),
"mesh based colliders must apply the transform scale");
GameObject flat{"flat-hull"};
auto* flatCollider = flat.AddComponent<ConvexHullCollider>();
flatCollider->SetCustomMesh(makeQuadMesh(1.0f));
check(flatCollider->BuildPhysicsShape().type == PhysicsShapeType::Box,
"a mesh without volume must fall back to a box");
GameObject empty{"empty-hull"};
empty.AddComponent<ConvexHullCollider>();
Rigidbody* emptyBody = empty.AddComponent<Rigidbody>();
world.RegisterRigidbody(*emptyBody);
check(!emptyBody->HasPhysicsBody(), "a mesh collider without a mesh must not create a body");
}
void testMeshCollider()
{
JoltPhysicsWorld world{{.workerThreadCount = 1, .gravity = {0.0f, -9.81f, 0.0f}}};
GameObject ground{"mesh-ground"};
ground.AddComponent<MeshCollider>()->SetCustomMesh(makeQuadMesh(10.0f));
Rigidbody* groundBody = ground.AddComponent<Rigidbody>();
groundBody->SetType(RigidbodyType::Static);
world.RegisterRigidbody(*groundBody);
check(groundBody->HasPhysicsBody(), "a static mesh collider must create a body");
GameObject ball{"ball"};
ball.GetTransform().SetLocalPosition({0.0f, 2.0f, 0.0f});
ball.AddComponent<SphereCollider>(0.5f);
Rigidbody* ballBody = ball.AddComponent<Rigidbody>();
world.RegisterRigidbody(*ballBody);
for (int step = 0; step < 180; ++step) {
world.Step(1.0f / 60.0f);
world.SyncDynamicBodiesToTransforms();
}
const float restHeight = ball.GetTransform().GetWorldPosition().y;
check(restHeight > 0.4f && restHeight < 0.6f, "a sphere must come to rest on a mesh collider");
GameObject kinematic{"kinematic-mesh"};
kinematic.GetTransform().SetLocalPosition({50.0f, 0.0f, 0.0f});
kinematic.AddComponent<MeshCollider>()->SetCustomMesh(makeCubeMesh(1.0f));
Rigidbody* kinematicBody = kinematic.AddComponent<Rigidbody>();
kinematicBody->SetType(RigidbodyType::Kinematic);
world.RegisterRigidbody(*kinematicBody);
check(kinematicBody->HasPhysicsBody(), "a kinematic mesh collider must create a body");
GameObject dynamic{"dynamic-mesh"};
auto* dynamicCollider = dynamic.AddComponent<MeshCollider>();
dynamicCollider->SetCustomMesh(makeCubeMesh(1.0f));
dynamic.AddComponent<Rigidbody>();
check(dynamicCollider->BuildPhysicsShape().type == PhysicsShapeType::ConvexHull,
"dynamic mesh colliders must fall back to a convex hull");
}
}
void RunPhysicsTests()
@@ -368,5 +489,8 @@ void RunPhysicsTests()
testKinematicBodiesMoveWithVelocity();
testColliderCenterOffset();
testFrozenRotationCapsule();
testCylinderCollider();
testConvexHullCollider();
testMeshCollider();
testTriggerZone();
}