summaryrefslogtreecommitdiff
path: root/src/tests/test_physics.cc
blob: df21e704a76b5ea7eaa01c59999216fc59dcc766 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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 <cassert>
#include <cmath>
#include <iostream>

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<Object3D> 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<int> 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;
}