1 /// Note: this module is not completly tested! 2 /// Use with special care, results might be wrong. 3 4 module gl3n.frustum; 5 6 private { 7 import gl3n.linalg : vec3, mat4, dot; 8 import gl3n.math : abs, cradians; 9 import gl3n.aabb : AABB; 10 import gl3n.plane : Plane; 11 } 12 13 enum { 14 OUTSIDE = 0, /// Used as flag to indicate if the object intersects with the frustum. 15 INSIDE, /// ditto 16 INTERSECT /// ditto 17 } 18 19 /// 20 struct Frustum { 21 enum { 22 LEFT, /// Used to access the planes array. 23 RIGHT, /// ditto 24 BOTTOM, /// ditto 25 TOP, /// ditto 26 NEAR, /// ditto 27 FAR /// ditto 28 } 29 30 Plane[6] planes; /// Holds all 6 planes of the frustum. 31 32 @safe pure nothrow: 33 34 @property ref Plane left() { return planes[LEFT]; } 35 @property ref Plane right() { return planes[RIGHT]; } 36 @property ref Plane bottom() { return planes[BOTTOM]; } 37 @property ref Plane top() { return planes[TOP]; } 38 @property ref Plane near() { return planes[NEAR]; } 39 @property ref Plane far() { return planes[FAR]; } 40 41 /// Constructs the frustum from a model-view-projection matrix. 42 /// Params: 43 /// mvp = a model-view-projection matrix 44 this(mat4 mvp) { 45 mvp.transpose(); // we store the matrix row-major 46 47 planes = [ 48 // left 49 Plane(mvp[0][3] + mvp[0][0], 50 mvp[1][3] + mvp[1][0], 51 mvp[2][3] + mvp[2][0], 52 mvp[3][3] + mvp[3][0]), 53 54 // right 55 Plane(mvp[0][3] - mvp[0][0], 56 mvp[1][3] - mvp[1][0], 57 mvp[2][3] - mvp[2][0], 58 mvp[3][3] - mvp[3][0]), 59 60 // bottom 61 Plane(mvp[0][3] + mvp[0][1], 62 mvp[1][3] + mvp[1][1], 63 mvp[2][3] + mvp[2][1], 64 mvp[3][3] + mvp[3][1]), 65 // top 66 Plane(mvp[0][3] - mvp[0][1], 67 mvp[1][3] - mvp[1][1], 68 mvp[2][3] - mvp[2][1], 69 mvp[3][3] - mvp[3][1]), 70 // near 71 Plane(mvp[0][3] + mvp[0][2], 72 mvp[1][3] + mvp[1][2], 73 mvp[2][3] + mvp[2][2], 74 mvp[3][3] + mvp[3][2]), 75 // far 76 Plane(mvp[0][3] - mvp[0][2], 77 mvp[1][3] - mvp[1][2], 78 mvp[2][3] - mvp[2][2], 79 mvp[3][3] - mvp[3][2]) 80 ]; 81 82 normalize(); 83 } 84 85 /// Constructs the frustum from 6 planes. 86 /// Params: 87 /// planes = the 6 frustum planes in the order: left, right, bottom, top, near, far. 88 this(Plane[6] planes) { 89 this.planes = planes; 90 normalize(); 91 } 92 93 private void normalize() { 94 foreach(ref e; planes) { 95 e.normalize(); 96 } 97 } 98 99 /// Checks if the $(I aabb) intersects with the frustum. 100 /// Returns OUTSIDE (= 0), INSIDE (= 1) or INTERSECT (= 2). 101 int intersects(AABB aabb) { 102 vec3 hextent = aabb.half_extent; 103 vec3 center = aabb.center; 104 105 int result = INSIDE; 106 foreach(plane; planes) { 107 float d = dot(center, plane.normal); 108 float r = dot(hextent, abs(plane.normal)); 109 110 if(d + r < -plane.d) { 111 // outside 112 return OUTSIDE; 113 } 114 if(d - r < -plane.d) { 115 result = INTERSECT; 116 } 117 } 118 119 return result; 120 } 121 122 /// Returns true if the $(I aabb) intersects with the frustum or is inside it. 123 bool opBinaryRight(string s : "in")(AABB aabb) { 124 return intersects(aabb) > 0; 125 } 126 }