feat: add more collider types + camera snapping fix
This commit is contained in:
@@ -178,6 +178,9 @@ namespace {
|
||||
"BoxCollider",
|
||||
"SphereCollider",
|
||||
"CapsuleCollider",
|
||||
"CylinderCollider",
|
||||
"ConvexHullCollider",
|
||||
"MeshCollider",
|
||||
"Canvas",
|
||||
"RectTransform",
|
||||
"UIImage",
|
||||
|
||||
@@ -59,10 +59,24 @@ namespace {
|
||||
|
||||
SceneManager::GetInstance().Destroy();
|
||||
}
|
||||
|
||||
void testCameraKeysDoNotEndEditGestures()
|
||||
{
|
||||
check(!EndsEditGesture({.keyReleased = true, .keyboardDrivesCamera = true}),
|
||||
"releasing a key while flying the editor camera must not capture an undo snapshot");
|
||||
check(EndsEditGesture({.keyReleased = true}), "releasing a key outside the viewport still ends an edit");
|
||||
check(EndsEditGesture({.keyboardDrivesCamera = true, .editPending = true}),
|
||||
"viewport shortcuts that edit (delete, duplicate) still end an edit");
|
||||
check(EndsEditGesture({.mouseReleased = true, .keyboardDrivesCamera = true}),
|
||||
"releasing the mouse after a viewport drag still ends an edit");
|
||||
check(!EndsEditGesture({.itemWasActive = true, .itemActive = true, .mouseReleased = true}),
|
||||
"nothing ends while an item is still being edited");
|
||||
}
|
||||
}
|
||||
|
||||
void RunEditorTests()
|
||||
{
|
||||
testUndoRedoRestoresEdits();
|
||||
testUndoHistoryIsCapped();
|
||||
testCameraKeysDoNotEndEditGestures();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user