// 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; }