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;