1 module unecht.core.math.ray;
2 
3 import gl3n.linalg;
4 import gl3n.aabb;
5 
6 ///
7 struct Ray(T)
8 {
9     alias Vector!(T,3) VecType;
10     VecType  origin = VecType(0,0,0);
11     VecType  direction = VecType(0,0,0);
12 
13     /// note: fails for axis aligned rays (see: https://tavianator.com/fast-branchless-raybounding-box-intersections-part-2-nans/)
14     public @nogc pure nothrow bool intersects(AABBT!T aabb, ref T distance) const
15     {
16         import std.algorithm:min,max;
17         import std.math:abs;
18 
19         T tmin = -T.infinity;
20         T tmax = T.infinity;
21         
22         if (direction.x != 0) 
23         {
24             auto t1 = (aabb.min.x - origin.x)/direction.x;
25             auto t2 = (aabb.max.x - origin.x)/direction.x;
26             
27             tmin = max(tmin, min(t1, t2));
28             tmax = min(tmax, max(t1, t2));
29         }
30         
31         if (direction.y != 0) 
32         {
33             auto t1 = (aabb.min.y - origin.y)/direction.y;
34             auto t2 = (aabb.max.y - origin.y)/direction.y;
35             
36             tmin = max(tmin, min(t1, t2));
37             tmax = min(tmax, max(t1, t2));
38         }
39 
40         if (direction.z != 0) 
41         {
42             auto t1 = (aabb.min.z - origin.z)/direction.z;
43             auto t2 = (aabb.max.z - origin.z)/direction.z;
44             
45             tmin = max(tmin, min(t1, t2));
46             tmax = min(tmax, max(t1, t2));
47         }
48 
49         distance = tmin;
50         return tmax >= tmin && tmax >= 0;
51     }
52 
53     unittest
54     {
55         ray r = ray(vec3(0,0,0), vec3(1,0.0001,0.0001));
56         float distance;
57 
58         assert(r.intersects(AABB(vec3(-1,-1,-1), vec3(-0.5f,-0.5f,-0.5f)), distance) == false);
59         assert(r.intersects(AABB(vec3(10,10,10), vec3(100,100,100)), distance) == false);
60         assert(r.intersects(AABB(vec3(0.5f,-1,-1), vec3(1,1,1)), distance) == true);
61         assert(r.intersects(AABB(vec3(1000.5f,-10,-1), vec3(1,1,1)), distance) == true);
62     }
63 
64     unittest
65     {
66         //note: exactly aligned rays do not work
67         ray r = ray(vec3(0,0,0), vec3(1,0,0));
68         float distance;
69         assert(r.intersects(AABB(vec3(10,10,10), vec3(100,100,100)), distance) == true);
70     }
71 }
72 
73 alias Ray!(float) ray;