1 #include "IntersectionTest.hpp"
3 #include "geometry/const.hpp"
4 #include "geometry/primitive.hpp"
7 #include <glm/gtx/io.hpp>
8 #include <glm/gtx/transform.hpp>
10 CPPUNIT_TEST_SUITE_REGISTRATION(gong::geometry::test::IntersectionTest);
17 void IntersectionTest::setUp() {
20 void IntersectionTest::tearDown() {
24 void IntersectionTest::testAABB() {
25 AABB a{ { -1, -1, -1 }, { 1, 1, 1 } };
28 CPPUNIT_ASSERT_MESSAGE(
29 "coincidental AABBs should intersect",
34 CPPUNIT_ASSERT_MESSAGE(
35 "AABBs should intersect",
40 CPPUNIT_ASSERT_MESSAGE(
41 "AABBs should not intersect",
46 CPPUNIT_ASSERT_MESSAGE(
47 "AABBs should intersect",
52 CPPUNIT_ASSERT_MESSAGE(
53 "AABBs should not intersect",
58 CPPUNIT_ASSERT_MESSAGE(
59 "AABBs should intersect",
64 CPPUNIT_ASSERT_MESSAGE(
65 "AABBs should not intersect",
70 CPPUNIT_ASSERT_MESSAGE(
71 "AABBs should not intersect",
76 CPPUNIT_ASSERT_MESSAGE(
77 "AABBs should not intersect",
82 void IntersectionTest::testSimpleRayBox() {
83 Ray ray{ { 0, 0, 0 }, { 1, 0, 0 }, { } }; // at origin, pointing right
85 AABB box{ { -1, -1, -1 }, { 1, 1, 1 } }; // 2x2x2 cube centered around origin
87 const float delta = std::numeric_limits<float>::epsilon();
91 CPPUNIT_ASSERT_MESSAGE(
92 "ray at origin not intersecting box at origin",
93 Intersection(ray, box, distance)
96 // move ray outside the box, but have it still point at it
97 // should be 4 units to the left now
99 CPPUNIT_ASSERT_MESSAGE(
100 "ray pointing at box doesn't intersect",
101 Intersection(ray, box, distance)
103 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
104 "intersection distance way off",
105 4.0f, distance, delta
108 // move ray to the other side, so it's pointing away now
110 CPPUNIT_ASSERT_MESSAGE(
111 "ray pointing away from box still intersects",
112 !Intersection(ray, box, distance)
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 };
119 CPPUNIT_ASSERT_MESSAGE(
120 "ray pointing at box doesn't intersect",
121 Intersection(ray, box, distance)
123 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
124 "intersection distance way off",
125 5.65685424949238019520f, distance, delta
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
134 const float delta = std::numeric_limits<float>::epsilon();
139 CPPUNIT_ASSERT_MESSAGE(
140 "ray at origin not intersecting box at origin",
141 Intersection(ray, box, M, &distance)
143 // normal undefined, so can't test
145 // move ray outside the box, but have it still point at it
146 // should be 4 units to the left now
148 CPPUNIT_ASSERT_MESSAGE(
149 "ray pointing at box to the right doesn't intersect",
150 Intersection(ray, box, M, &distance, &normal)
152 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
153 "intersection distance way off",
154 4.0f, distance, delta
156 CPPUNIT_ASSERT_EQUAL_MESSAGE(
157 "wrong surface normal at intersection point",
158 glm::vec3(-1, 0, 0), normal
161 // move ray to the other side, so it's pointing away now
163 CPPUNIT_ASSERT_MESSAGE(
164 "ray pointing away from box to the left still intersects",
165 !Intersection(ray, box, M)
170 CPPUNIT_ASSERT_MESSAGE(
171 "ray pointing at box to the left does not intersect",
172 Intersection(ray, box, M, &distance, &normal)
174 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
175 "intersection distance way off",
176 4.0f, distance, delta
178 CPPUNIT_ASSERT_EQUAL_MESSAGE(
179 "wrong surface normal at intersection point",
180 glm::vec3(1, 0, 0), normal
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)
190 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
191 "intersection distance way off",
192 4.0f, distance, delta
194 CPPUNIT_ASSERT_EQUAL_MESSAGE(
195 "wrong surface normal at intersection point",
196 glm::vec3(0, -1, 0), normal
201 CPPUNIT_ASSERT_MESSAGE(
202 "ray pointing away from box above still intersects",
203 !Intersection(ray, box, M)
208 CPPUNIT_ASSERT_MESSAGE(
209 "ray pointing at box below does not intersect",
210 Intersection(ray, box, M, &distance, &normal)
212 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
213 "intersection distance way off",
214 4.0f, distance, delta
216 CPPUNIT_ASSERT_EQUAL_MESSAGE(
217 "wrong surface normal at intersection point",
218 glm::vec3(0, 1, 0), normal
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)
228 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
229 "intersection distance way off",
230 4.0f, distance, delta
232 CPPUNIT_ASSERT_EQUAL_MESSAGE(
233 "wrong surface normal at intersection point",
234 glm::vec3(0, 0, -1), normal
239 CPPUNIT_ASSERT_MESSAGE(
240 "ray pointing away from box in front still intersects",
241 !Intersection(ray, box, M)
246 CPPUNIT_ASSERT_MESSAGE(
247 "ray pointing at box behind does not intersect",
248 Intersection(ray, box, M, &distance, &normal)
250 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
251 "intersection distance way off",
252 4.0f, distance, delta
254 CPPUNIT_ASSERT_EQUAL_MESSAGE(
255 "wrong surface normal at intersection point",
256 glm::vec3(0, 0, 1), normal
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)
266 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
267 "intersection distance way off",
268 5.65685424949238019520f, distance, delta
270 CPPUNIT_ASSERT_EQUAL_MESSAGE(
271 "wrong surface normal at intersection point",
272 glm::vec3(-1, 0, 0), normal
276 void IntersectionTest::testBoxBox() {
277 const float delta = std::numeric_limits<float>::epsilon();
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 ^^
286 CPPUNIT_ASSERT_MESSAGE(
287 "identical OBBs don't intersect",
288 Intersection(box, Ma, box, Mb, depth, normal)
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",
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)
303 // depth and normal undefined for non-intersecting objects
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)
312 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
313 "bad penetration depth (with rotation)",
314 0.01421356237309504880f, depth, delta
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()
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)
328 void IntersectionTest::testSphereSphere() {
329 const float delta = std::numeric_limits<float>::epsilon();
331 Sphere a{{ 0.0f, 0.0f, 0.0f }, 1.0f};
332 Sphere b{{ 0.0f, 0.0f, 0.0f }, 1.0f};
336 CPPUNIT_ASSERT_MESSAGE(
337 "coincidental spheres should intersect",
338 Intersection(a, b, depth, normal)
340 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
341 "bad intersection distance",
344 // normal can be just about anything
347 CPPUNIT_ASSERT_MESSAGE(
348 "spheres should intersect",
349 Intersection(a, b, depth, normal)
351 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
352 "bad intersection distance",
355 CPPUNIT_ASSERT_EQUAL_MESSAGE(
356 "bad intersection normal",
357 glm::vec3(1, 0, 0), normal
360 b.Position({ -1.5, 0, 0 });
361 CPPUNIT_ASSERT_MESSAGE(
362 "spheres should intersect",
363 Intersection(a, b, depth, normal)
365 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
366 "bad intersection distance",
369 CPPUNIT_ASSERT_EQUAL_MESSAGE(
370 "bad intersection normal",
371 glm::vec3(-1, 0, 0), normal
374 b.Move({ -1, 0, 0 });
375 CPPUNIT_ASSERT_MESSAGE(
376 "spheres should not intersect",
377 !Intersection(a, b, depth, normal)
381 void IntersectionTest::testSpherePlane() {
382 const float delta = std::numeric_limits<float>::epsilon();
384 // unit sphere at origin
385 Sphere sphere{{ 0.0f, 0.0f, 0.0f }, 1.0f};
386 // horizontal plane at origin (the XZ plane)
387 Plane plane{{ 0.0f, 1.0f, 0.0f }, 0.0f };
391 CPPUNIT_ASSERT_MESSAGE(
392 "sphere at origin does not intersect plane at origin",
393 Intersection(sphere, plane, depth, normal)
395 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
396 "bad penetration depth of sphere with plane",
399 // normal is actually undefined in this case, but either will work
400 CPPUNIT_ASSERT_MESSAGE(
401 "bad contact normal of sphere intersecting plane",
402 normal == glm::vec3(0.0f, 1.0f, 0.0f) || normal == glm::vec3(0.0f, -1.0f, 0.0f)
405 // center above, but still intersecting
406 sphere.origin.y = 0.5f;
407 CPPUNIT_ASSERT_MESSAGE(
408 "sphere does not intersect plane",
409 Intersection(sphere, plane, depth, normal)
411 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
412 "bad penetration depth of sphere with plane",
415 CPPUNIT_ASSERT_EQUAL_MESSAGE(
416 "bad contact normal of sphere intersecting plane",
417 glm::vec3(0.0f, 1.0f, 0.0f), normal
420 // center below, but still intersecting
421 sphere.origin.y = -0.5f;
422 CPPUNIT_ASSERT_MESSAGE(
423 "sphere does not intersect plane",
424 Intersection(sphere, plane, depth, normal)
426 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
427 "bad penetration depth of sphere with plane",
430 CPPUNIT_ASSERT_EQUAL_MESSAGE(
431 "bad contact normal of sphere intersecting plane",
432 glm::vec3(0.0f, -1.0f, 0.0f), normal
435 // sphere completely above
436 sphere.origin.y = 1.5f;
437 CPPUNIT_ASSERT_MESSAGE(
438 "sphere above plane intersects",
439 !Intersection(sphere, plane, depth, normal)
442 // sphere completely below
443 sphere.origin.y = -1.5f;
444 CPPUNIT_ASSERT_MESSAGE(
445 "sphere below plane intersects",
446 !Intersection(sphere, plane, depth, normal)
450 void IntersectionTest::testSphereHalfSpace() {
451 const float delta = std::numeric_limits<float>::epsilon();
453 // unit sphere at origin
454 Sphere sphere{{ 0.0f, 0.0f, 0.0f }, 1.0f};
455 // horizontal plane at origin (the XZ plane)
456 Plane plane{{ 0.0f, 1.0f, 0.0f }, 0.0f };
459 CPPUNIT_ASSERT_MESSAGE(
460 "sphere at origin does not intersect half space to origin",
461 Intersection(sphere, plane, depth)
463 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
464 "bad penetration depth of sphere with half space",
468 sphere.origin.y = 0.5f;
469 CPPUNIT_ASSERT_MESSAGE(
470 "sphere does not intersect half space",
471 Intersection(sphere, plane, depth)
473 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
474 "bad penetration depth of sphere with half space",
478 sphere.origin.y = -0.5f;
479 CPPUNIT_ASSERT_MESSAGE(
480 "sphere does not intersect half space",
481 Intersection(sphere, plane, depth)
483 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
484 "bad penetration depth of sphere with half space",
488 sphere.origin.y = -1.5f;
489 CPPUNIT_ASSERT_MESSAGE(
490 "sphere inside half space does not intersect",
491 Intersection(sphere, plane, depth)
493 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
494 "bad penetration depth of sphere with half space",
498 sphere.origin.y = 1.5f;
499 CPPUNIT_ASSERT_MESSAGE(
500 "sphere outside half space intersects",
501 !Intersection(sphere, plane, depth)