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
|
#pragma once
#include "../Common/Lambda.hpp"
#include "AABB.hpp"
#include "Vector.hpp"
struct Ray {
struct RaycastResult {
Bool hit = false;
Vec3 point;
Vec3 normal;
};
// References:
// [1]: https://gdbooks.gitbooks.io/3dcollisions/content/Chapter3/raycast_aabb.html
// [2]: https://tavianator.com/2015/ray_box_nan.html
RaycastResult cast(AABB box, Real max_distance = 0) const {
RaycastResult hit{ true };
// `t` is the time at which the ray intersects a plane of the AABB.
// A single vector defines 3 planes, one for each axis.
// Our AABB ends up having 6 planes, taken from the `from` and `to` points,
// hence the 6 `t`s.
Vec3 t_from = (box.min - origin) / direction;
Vec3 t_to = (box.max - origin) / direction;
// NOTE: We have to use std::fmin and std::fmax here, since
// std::min and std::max are not IEEE 754 compliant. [2]
Vec3 smaller_t_values = t_from.zip(t_to, LAMBDA(std::fmin, 2));
Vec3 bigger_t_values = t_from.zip(t_to, LAMBDA(std::fmax, 2));
Real biggest_min_t = smaller_t_values.reduce(LAMBDA(std::fmax, 2));
Real smallest_max_t = bigger_t_values.reduce(LAMBDA(std::fmin, 2));
// The normal of the collision is a normal of the plane which is intersected
// by the ray at the biggest smaller `t` value.
// Since a plane always has two normals, we know which one to pick by looking
// at the direction of the ray.
hit.normal = smaller_t_values.zip(direction, [=](Real t, Real d) {
return Math::floats_equal(t, biggest_min_t) ? Math::sign(d) : 0;
});
// If the smallest `t` is negative, the ray is pointing away from the AABB.
// If the biggest `t` is smaller than the smallest `t`, the ray is missing the AABB,
// since that means we do not reach all the planes defined by `from` before we reach
// a plane defined by `to`, which only happens if there is no intersection.
if (smallest_max_t < 0 || biggest_min_t > smallest_max_t) return {};
// Get the intersection point from the collision time `t`
// with the formula `p = o + dt`.
hit.point = origin + direction * biggest_min_t;
if (max_distance > 0 && (hit.point - origin).magnitude() > max_distance)
return {};
return hit;
}
Vec3 origin;
Vec3 direction;
};
|