]> git.localhorst.tv Git - gong.git/blob - tst/geometry/IntersectionTest.cpp
36cacdfa8bd309293c8224a0da636f9dd544e34d
[gong.git] / tst / geometry / IntersectionTest.cpp
1 #include "IntersectionTest.hpp"
2
3 #include "geometry/const.hpp"
4 #include "geometry/primitive.hpp"
5
6 #include <limits>
7 #include <glm/gtx/io.hpp>
8 #include <glm/gtx/transform.hpp>
9
10 CPPUNIT_TEST_SUITE_REGISTRATION(gong::geometry::test::IntersectionTest);
11
12
13 namespace gong {
14 namespace geometry {
15 namespace test {
16
17 void IntersectionTest::setUp() {
18 }
19
20 void IntersectionTest::tearDown() {
21 }
22
23
24 void IntersectionTest::testAABB() {
25         AABB a{ { -1, -1, -1 }, { 1, 1, 1 } };
26         AABB b(a);
27
28         CPPUNIT_ASSERT_MESSAGE(
29                 "coincidental AABBs should intersect",
30                 Intersection(a, b)
31         );
32
33         b.Move({ 1, 0, 0 });
34         CPPUNIT_ASSERT_MESSAGE(
35                 "AABBs should intersect",
36                 Intersection(a, b)
37         );
38
39         b.Move({ 2, 0, 0 });
40         CPPUNIT_ASSERT_MESSAGE(
41                 "AABBs should not intersect",
42                 !Intersection(a, b)
43         );
44
45         b.Move({ -4, 0, 0 });
46         CPPUNIT_ASSERT_MESSAGE(
47                 "AABBs should intersect",
48                 Intersection(a, b)
49         );
50
51         b.Move({ -2, 0, 0 });
52         CPPUNIT_ASSERT_MESSAGE(
53                 "AABBs should not intersect",
54                 !Intersection(a, b)
55         );
56
57         b.Move({ 3, 1, 0 });
58         CPPUNIT_ASSERT_MESSAGE(
59                 "AABBs should intersect",
60                 Intersection(a, b)
61         );
62
63         b.Move({ 0, 2, 0 });
64         CPPUNIT_ASSERT_MESSAGE(
65                 "AABBs should not intersect",
66                 !Intersection(a, b)
67         );
68
69         b.Move({ 2, 0, 0 });
70         CPPUNIT_ASSERT_MESSAGE(
71                 "AABBs should not intersect",
72                 !Intersection(a, b)
73         );
74
75         b.Move({ 0, 0, 2 });
76         CPPUNIT_ASSERT_MESSAGE(
77                 "AABBs should not intersect",
78                 !Intersection(a, b)
79         );
80 }
81
82 void IntersectionTest::testSimpleRayBox() {
83         Ray ray{ { 0, 0, 0 }, { 1, 0, 0 }, { } }; // at origin, pointing right
84         ray.Update();
85         AABB box{ { -1, -1, -1 }, { 1, 1, 1 } }; // 2x2x2 cube centered around origin
86
87         const float delta = std::numeric_limits<float>::epsilon();
88
89         float distance = 0;
90
91         CPPUNIT_ASSERT_MESSAGE(
92                 "ray at origin not intersecting box at origin",
93                 Intersection(ray, box, distance)
94         );
95
96         // move ray outside the box, but have it still point at it
97         // should be 4 units to the left now
98         ray.orig.x = -5;
99         CPPUNIT_ASSERT_MESSAGE(
100                 "ray pointing at box doesn't intersect",
101                 Intersection(ray, box, distance)
102         );
103         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
104                 "intersection distance way off",
105                 4.0f, distance, delta
106         );
107
108         // move ray to the other side, so it's pointing away now
109         ray.orig.x = 5;
110         CPPUNIT_ASSERT_MESSAGE(
111                 "ray pointing away from box still intersects",
112                 !Intersection(ray, box, distance)
113         );
114
115         // 45 deg down from 4 units away, so should be about 4 * sqrt(2)
116         ray.orig = { -5.0f, 4.5f, 0.0f };
117         ray.dir = { 0.70710678118654752440f, -0.70710678118654752440f, 0.0f };
118         ray.Update();
119         CPPUNIT_ASSERT_MESSAGE(
120                 "ray pointing at box doesn't intersect",
121                 Intersection(ray, box, distance)
122         );
123         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
124                 "intersection distance way off",
125                 5.65685424949238019520f, distance, delta
126         );
127 }
128
129 void IntersectionTest::testRayBox() {
130         Ray ray{ { 0, 0, 0 }, { 1, 0, 0 }, { } }; // at origin, pointing right
131         AABB box{ { -1, -1, -1 }, { 1, 1, 1 } }; // 2x2x2 cube centered around origin
132         glm::mat4 M(1); // no transformation
133
134         const float delta = std::numeric_limits<float>::epsilon();
135
136         float distance = 0;
137         glm::vec3 normal(0);
138
139         CPPUNIT_ASSERT_MESSAGE(
140                 "ray at origin not intersecting box at origin",
141                 Intersection(ray, box, M, &distance)
142         );
143         // normal undefined, so can't test
144
145         // move ray outside the box, but have it still point at it
146         // should be 4 units to the left now
147         ray.orig.x = -5;
148         CPPUNIT_ASSERT_MESSAGE(
149                 "ray pointing at box to the right doesn't intersect",
150                 Intersection(ray, box, M, &distance, &normal)
151         );
152         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
153                 "intersection distance way off",
154                 4.0f, distance, delta
155         );
156         CPPUNIT_ASSERT_EQUAL_MESSAGE(
157                 "wrong surface normal at intersection point",
158                 glm::vec3(-1, 0, 0), normal
159         );
160
161         // move ray to the other side, so it's pointing away now
162         ray.orig.x = 5;
163         CPPUNIT_ASSERT_MESSAGE(
164                 "ray pointing away from box to the left still intersects",
165                 !Intersection(ray, box, M)
166         );
167
168         // turn ray around
169         ray.dir.x = -1;
170         CPPUNIT_ASSERT_MESSAGE(
171                 "ray pointing at box to the left does not intersect",
172                 Intersection(ray, box, M, &distance, &normal)
173         );
174         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
175                 "intersection distance way off",
176                 4.0f, distance, delta
177         );
178         CPPUNIT_ASSERT_EQUAL_MESSAGE(
179                 "wrong surface normal at intersection point",
180                 glm::vec3(1, 0, 0), normal
181         );
182
183         // ray below
184         ray.orig = { 0, -5, 0 };
185         ray.dir = { 0, 1, 0 };
186         CPPUNIT_ASSERT_MESSAGE(
187                 "ray pointing at box above does not intersect",
188                 Intersection(ray, box, M, &distance, &normal)
189         );
190         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
191                 "intersection distance way off",
192                 4.0f, distance, delta
193         );
194         CPPUNIT_ASSERT_EQUAL_MESSAGE(
195                 "wrong surface normal at intersection point",
196                 glm::vec3(0, -1, 0), normal
197         );
198
199         // turn ray around
200         ray.dir.y = -1;
201         CPPUNIT_ASSERT_MESSAGE(
202                 "ray pointing away from box above still intersects",
203                 !Intersection(ray, box, M)
204         );
205
206         // move ray above
207         ray.orig.y = 5;
208         CPPUNIT_ASSERT_MESSAGE(
209                 "ray pointing at box below does not intersect",
210                 Intersection(ray, box, M, &distance, &normal)
211         );
212         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
213                 "intersection distance way off",
214                 4.0f, distance, delta
215         );
216         CPPUNIT_ASSERT_EQUAL_MESSAGE(
217                 "wrong surface normal at intersection point",
218                 glm::vec3(0, 1, 0), normal
219         );
220
221         // ray behind
222         ray.orig = { 0, 0, -5 };
223         ray.dir = { 0, 0, 1 };
224         CPPUNIT_ASSERT_MESSAGE(
225                 "ray pointing at box in front does not intersect",
226                 Intersection(ray, box, M, &distance, &normal)
227         );
228         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
229                 "intersection distance way off",
230                 4.0f, distance, delta
231         );
232         CPPUNIT_ASSERT_EQUAL_MESSAGE(
233                 "wrong surface normal at intersection point",
234                 glm::vec3(0, 0, -1), normal
235         );
236
237         // turn ray around
238         ray.dir.z = -1;
239         CPPUNIT_ASSERT_MESSAGE(
240                 "ray pointing away from box in front still intersects",
241                 !Intersection(ray, box, M)
242         );
243
244         // move ray in front
245         ray.orig.z = 5;
246         CPPUNIT_ASSERT_MESSAGE(
247                 "ray pointing at box behind does not intersect",
248                 Intersection(ray, box, M, &distance, &normal)
249         );
250         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
251                 "intersection distance way off",
252                 4.0f, distance, delta
253         );
254         CPPUNIT_ASSERT_EQUAL_MESSAGE(
255                 "wrong surface normal at intersection point",
256                 glm::vec3(0, 0, 1), normal
257         );
258
259         // 45 deg down from 4 units away, so should be about 4 * sqrt(2)
260         ray.orig = { -5.0f, 4.5f, 0.0f };
261         ray.dir = { 0.70710678118654752440f, -0.70710678118654752440f, 0.0f };
262         CPPUNIT_ASSERT_MESSAGE(
263                 "ray pointing at box doesn't intersect",
264                 Intersection(ray, box, M, &distance, &normal)
265         );
266         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
267                 "intersection distance way off",
268                 5.65685424949238019520f, distance, delta
269         );
270         CPPUNIT_ASSERT_EQUAL_MESSAGE(
271                 "wrong surface normal at intersection point",
272                 glm::vec3(-1, 0, 0), normal
273         );
274 }
275
276 void IntersectionTest::testBoxBox() {
277         const float delta = std::numeric_limits<float>::epsilon();
278         float depth = 0;
279         glm::vec3 normal(0);
280
281         AABB box{ { -1, -1, -1 }, { 1, 1, 1 } }; // 2x2x2 cube centered around origin
282         glm::mat4 Ma(1); // identity
283         glm::mat4 Mb(1); // identity
284         // they're identical, so should probably intersect ^^
285
286         CPPUNIT_ASSERT_MESSAGE(
287                 "identical OBBs don't intersect",
288                 Intersection(box, Ma, box, Mb, depth, normal)
289         );
290         // depth is two, but normal can be any
291         // (will probably be the first axis of box a, but any is valid)
292         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
293                 "penetration depth of coincidental 2x2x2 boxes is not 2",
294                 2.0f, depth, delta
295         );
296
297         Ma = glm::translate(glm::vec3(-2, 0, 0)); // 2 to the left
298         Mb = glm::translate(glm::vec3(2, 0, 0)); // 2 to the right
299         CPPUNIT_ASSERT_MESSAGE(
300                 "distant OBBs intersect (2 apart, no rotation)",
301                 !Intersection(box, Ma, box, Mb, depth, normal)
302         );
303         // depth and normal undefined for non-intersecting objects
304
305         Ma = glm::rotate(PI_0p25, glm::vec3(0, 0, 1)); // rotated 45° around Z
306         Mb = glm::translate(glm::vec3(2.4, 0, 0)); // 2.4 to the right
307         // they should barely touch. intersect by about sqrt(2) - 1.4 if my head works
308         CPPUNIT_ASSERT_MESSAGE(
309                 "OBBs don't intersect (one rotated by 45°)",
310                 Intersection(box, Ma, box, Mb, depth, normal)
311         );
312         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
313                 "bad penetration depth (with rotation)",
314                 0.01421356237309504880f, depth, delta
315         );
316         CPPUNIT_ASSERT_EQUAL_MESSAGE(
317                 "bad intersection normal (with rotation)",
318                 glm::vec3(1, 0, 0), glm::abs(normal) // normal can be in + or - x, therefore abs()
319         );
320
321         Mb = glm::translate(glm::vec3(3, 0, 0)); // 3 to the right
322         CPPUNIT_ASSERT_MESSAGE(
323                 "OBBs intersect (one rotated by 45°)",
324                 !Intersection(box, Ma, box, Mb, depth, normal)
325         );
326 }
327
328 void IntersectionTest::testSpherePlane() {
329         const float delta = std::numeric_limits<float>::epsilon();
330
331         // unit sphere at origin
332         Sphere sphere{{ 0.0f, 0.0f, 0.0f }, 1.0f};
333         // horizontal plane at origin (the XZ plane)
334         Plane plane{{ 0.0f, 1.0f, 0.0f }, 0.0f };
335         float depth;
336         glm::vec3 normal;
337
338         CPPUNIT_ASSERT_MESSAGE(
339                 "sphere at origin does not intersect plane at origin",
340                 Intersection(sphere, plane, depth, normal)
341         );
342         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
343                 "bad penetration depth of sphere with plane",
344                 1.0f, depth, delta
345         );
346         // normal is actually undefined in this case, but either will work
347         CPPUNIT_ASSERT_MESSAGE(
348                 "bad contact normal of sphere intersecting plane",
349                 normal == glm::vec3(0.0f, 1.0f, 0.0f) || normal == glm::vec3(0.0f, -1.0f, 0.0f)
350         );
351
352         // center above, but still intersecting
353         sphere.origin.y = 0.5f;
354         CPPUNIT_ASSERT_MESSAGE(
355                 "sphere does not intersect plane",
356                 Intersection(sphere, plane, depth, normal)
357         );
358         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
359                 "bad penetration depth of sphere with plane",
360                 0.5f, depth, delta
361         );
362         CPPUNIT_ASSERT_EQUAL_MESSAGE(
363                 "bad contact normal of sphere intersecting plane",
364                 glm::vec3(0.0f, 1.0f, 0.0f), normal
365         );
366
367         // center below, but still intersecting
368         sphere.origin.y = -0.5f;
369         CPPUNIT_ASSERT_MESSAGE(
370                 "sphere does not intersect plane",
371                 Intersection(sphere, plane, depth, normal)
372         );
373         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
374                 "bad penetration depth of sphere with plane",
375                 0.5f, depth, delta
376         );
377         CPPUNIT_ASSERT_EQUAL_MESSAGE(
378                 "bad contact normal of sphere intersecting plane",
379                 glm::vec3(0.0f, -1.0f, 0.0f), normal
380         );
381
382         // sphere completely above
383         sphere.origin.y = 1.5f;
384         CPPUNIT_ASSERT_MESSAGE(
385                 "sphere above plane intersects",
386                 !Intersection(sphere, plane, depth, normal)
387         );
388
389         // sphere completely below
390         sphere.origin.y = -1.5f;
391         CPPUNIT_ASSERT_MESSAGE(
392                 "sphere below plane intersects",
393                 !Intersection(sphere, plane, depth, normal)
394         );
395 }
396
397 void IntersectionTest::testSphereHalfSpace() {
398         const float delta = std::numeric_limits<float>::epsilon();
399
400         // unit sphere at origin
401         Sphere sphere{{ 0.0f, 0.0f, 0.0f }, 1.0f};
402         // horizontal plane at origin (the XZ plane)
403         Plane plane{{ 0.0f, 1.0f, 0.0f }, 0.0f };
404         float depth;
405
406         CPPUNIT_ASSERT_MESSAGE(
407                 "sphere at origin does not intersect half space to origin",
408                 Intersection(sphere, plane, depth)
409         );
410         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
411                 "bad penetration depth of sphere with half space",
412                 1.0f, depth, delta
413         );
414
415         sphere.origin.y = 0.5f;
416         CPPUNIT_ASSERT_MESSAGE(
417                 "sphere does not intersect half space",
418                 Intersection(sphere, plane, depth)
419         );
420         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
421                 "bad penetration depth of sphere with half space",
422                 0.5f, depth, delta
423         );
424
425         sphere.origin.y = -0.5f;
426         CPPUNIT_ASSERT_MESSAGE(
427                 "sphere does not intersect half space",
428                 Intersection(sphere, plane, depth)
429         );
430         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
431                 "bad penetration depth of sphere with half space",
432                 1.5f, depth, delta
433         );
434
435         sphere.origin.y = -1.5f;
436         CPPUNIT_ASSERT_MESSAGE(
437                 "sphere inside half space does not intersect",
438                 Intersection(sphere, plane, depth)
439         );
440         CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
441                 "bad penetration depth of sphere with half space",
442                 2.5f, depth, delta
443         );
444
445         sphere.origin.y = 1.5f;
446         CPPUNIT_ASSERT_MESSAGE(
447                 "sphere outside half space intersects",
448                 !Intersection(sphere, plane, depth)
449         );
450 }
451
452 }
453 }
454 }