From eff8d43479e7704df65fae2a80eefa787213f502 Mon Sep 17 00:00:00 2001 From: skal Date: Mon, 9 Feb 2026 20:27:04 +0100 Subject: refactor: Reorganize tests into subsystem subdirectories Restructured test suite for better organization and targeted testing: **Structure:** - src/tests/audio/ - 15 audio system tests - src/tests/gpu/ - 12 GPU/shader tests - src/tests/3d/ - 6 3D rendering tests - src/tests/assets/ - 2 asset system tests - src/tests/util/ - 3 utility tests - src/tests/common/ - 3 shared test helpers - src/tests/scripts/ - 2 bash test scripts (moved conceptually, not physically) **CMake changes:** - Updated add_demo_test macro to accept LABEL parameter - Applied CTest labels to all 36 tests for subsystem filtering - Updated all test file paths in CMakeLists.txt - Fixed common helper paths (webgpu_test_fixture, etc.) - Added custom targets for subsystem testing: - run_audio_tests, run_gpu_tests, run_3d_tests - run_assets_tests, run_util_tests, run_all_tests **Include path updates:** - Fixed relative includes in GPU tests to reference ../common/ **Documentation:** - Updated doc/HOWTO.md with subsystem test commands - Updated doc/CONTRIBUTING.md with new test organization - Updated scripts/check_all.sh to reflect new structure **Verification:** - All 36 tests passing (100%) - ctest -L filters work correctly - make run__tests targets functional - scripts/check_all.sh passes Backward compatible: make test and ctest continue to work unchanged. handoff(Gemini): Test reorganization complete. 36/36 tests passing. --- src/tests/3d/test_physics.cc | 150 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 src/tests/3d/test_physics.cc (limited to 'src/tests/3d/test_physics.cc') diff --git a/src/tests/3d/test_physics.cc b/src/tests/3d/test_physics.cc new file mode 100644 index 0000000..df21e70 --- /dev/null +++ b/src/tests/3d/test_physics.cc @@ -0,0 +1,150 @@ +// This file is part of the 64k demo project. +// It tests the CPU-side SDF library and BVH for physics and collision. + +#include "3d/bvh.h" +#include "3d/physics.h" +#include "3d/sdf_cpu.h" +#include +#include +#include + +bool near(float a, float b, float e = 0.001f) { + return std::abs(a - b) < e; +} + +void test_sdf_sphere() { + std::cout << "Testing sdSphere..." << std::endl; + float r = 1.0f; + assert(near(sdf::sdSphere({0, 0, 0}, r), -1.0f)); + assert(near(sdf::sdSphere({1, 0, 0}, r), 0.0f)); + assert(near(sdf::sdSphere({2, 0, 0}, r), 1.0f)); +} + +void test_sdf_box() { + std::cout << "Testing sdBox..." << std::endl; + vec3 b(1, 1, 1); + assert(near(sdf::sdBox({0, 0, 0}, b), -1.0f)); + assert(near(sdf::sdBox({1, 1, 1}, b), 0.0f)); + assert(near(sdf::sdBox({2, 0, 0}, b), 1.0f)); +} + +void test_sdf_torus() { + std::cout << "Testing sdTorus..." << std::endl; + vec2 t(1.0f, 0.2f); + // Point on the ring: length(p.xz) = 1.0, p.y = 0 + assert(near(sdf::sdTorus({1, 0, 0}, t), -0.2f)); + assert(near(sdf::sdTorus({1.2f, 0, 0}, t), 0.0f)); +} + +void test_sdf_plane() { + std::cout << "Testing sdPlane..." << std::endl; + vec3 n(0, 1, 0); + float h = 1.0f; // Plane is at y = -1 (dot(p,n) + 1 = 0 => y = -1) + assert(near(sdf::sdPlane({0, 0, 0}, n, h), 1.0f)); + assert(near(sdf::sdPlane({0, -1, 0}, n, h), 0.0f)); +} + +void test_calc_normal() { + std::cout << "Testing calc_normal..." << std::endl; + + // Sphere normal at (1,0,0) should be (1,0,0) + auto sphere_sdf = [](vec3 p) { return sdf::sdSphere(p, 1.0f); }; + vec3 n = sdf::calc_normal({1, 0, 0}, sphere_sdf); + assert(near(n.x, 1.0f) && near(n.y, 0.0f) && near(n.z, 0.0f)); + + // Box normal at side + auto box_sdf = [](vec3 p) { return sdf::sdBox(p, {1, 1, 1}); }; + n = sdf::calc_normal({1, 0, 0}, box_sdf); + assert(near(n.x, 1.0f) && near(n.y, 0.0f) && near(n.z, 0.0f)); + + // Plane normal should be n + vec3 plane_n(0, 1, 0); + auto plane_sdf = [plane_n](vec3 p) { return sdf::sdPlane(p, plane_n, 1.0f); }; + n = sdf::calc_normal({0, 0, 0}, plane_sdf); + assert(near(n.x, plane_n.x) && near(n.y, plane_n.y) && near(n.z, plane_n.z)); +} + +void test_bvh() { + std::cout << "Testing BVH..." << std::endl; + std::vector objects; + + // Object 0: Left side + Object3D obj0(ObjectType::BOX); + obj0.position = {-10, 0, 0}; + objects.push_back(obj0); + + // Object 1: Right side + Object3D obj1(ObjectType::BOX); + obj1.position = {10, 0, 0}; + objects.push_back(obj1); + + BVH bvh; + BVHBuilder::build(bvh, objects); + + assert(bvh.nodes.size() == 3); // 1 root + 2 leaves + + // Query left side + std::vector results; + bvh.query({{-12, -2, -2}, {-8, 2, 2}}, results); + assert(results.size() == 1); + assert(results[0] == 0); + + // Query right side + results.clear(); + bvh.query({{8, -2, -2}, {12, 2, 2}}, results); + assert(results.size() == 1); + assert(results[0] == 1); + + // Query center (should miss both) + results.clear(); + bvh.query({{-2, -2, -2}, {2, 2, 2}}, results); + assert(results.size() == 0); + + // Query both + results.clear(); + bvh.query({{-12, -2, -2}, {12, 2, 2}}, results); + assert(results.size() == 2); +} + +void test_physics_falling() { + std::cout << "Testing Physics falling..." << std::endl; + Scene scene; + + // Plane at y = -1 + Object3D plane(ObjectType::PLANE); + plane.position = {0, -1, 0}; + plane.is_static = true; + scene.add_object(plane); + + // Sphere at y = 5 + Object3D sphere(ObjectType::SPHERE); + sphere.position = {0, 5, 0}; + sphere.velocity = {0, 0, 0}; + sphere.restitution = 0.0f; // No bounce for simple test + scene.add_object(sphere); + + PhysicsSystem physics; + float dt = 0.016f; + for (int i = 0; i < 100; ++i) { + physics.update(scene, dt); + } + + // Sphere should be above or at plane (y >= 0 because sphere radius is 1, + // plane is at -1) + assert(scene.objects[1].position.y >= -0.01f); + // Also should have slowed down + assert(scene.objects[1].velocity.y > -1.0f); +} + +int main() { + test_sdf_sphere(); + test_sdf_box(); + test_sdf_torus(); + test_sdf_plane(); + test_calc_normal(); + test_bvh(); + test_physics_falling(); + + std::cout << "--- ALL PHYSICS TESTS PASSED ---" << std::endl; + return 0; +} -- cgit v1.2.3