import BoundingBox from './BoundingBox'; import Plane from './Plane'; import vec3 from '../glmatrix/vec3'; var vec3Set = vec3.set; var vec3Copy = vec3.copy; var vec3TranformMat4 = vec3.transformMat4; var mathMin = Math.min; var mathMax = Math.max; /** * @constructor * @alias clay.Frustum */ var Frustum = function() { /** * Eight planes to enclose the frustum * @type {clay.Plane[]} */ this.planes = []; for (var i = 0; i < 6; i++) { this.planes.push(new Plane()); } /** * Bounding box of frustum * @type {clay.BoundingBox} */ this.boundingBox = new BoundingBox(); /** * Eight vertices of frustum * @type {Float32Array[]} */ this.vertices = []; for (var i = 0; i < 8; i++) { this.vertices[i] = vec3.fromValues(0, 0, 0); } }; Frustum.prototype = { // http://web.archive.org/web/20120531231005/http://crazyjoke.free.fr/doc/3D/plane%20extraction.pdf /** * Set frustum from a projection matrix * @param {clay.Matrix4} projectionMatrix */ setFromProjection: function(projectionMatrix) { var planes = this.planes; var m = projectionMatrix.array; var m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3]; var m4 = m[4], m5 = m[5], m6 = m[6], m7 = m[7]; var m8 = m[8], m9 = m[9], m10 = m[10], m11 = m[11]; var m12 = m[12], m13 = m[13], m14 = m[14], m15 = m[15]; // Update planes vec3Set(planes[0].normal.array, m3 - m0, m7 - m4, m11 - m8); planes[0].distance = -(m15 - m12); planes[0].normalize(); vec3Set(planes[1].normal.array, m3 + m0, m7 + m4, m11 + m8); planes[1].distance = -(m15 + m12); planes[1].normalize(); vec3Set(planes[2].normal.array, m3 + m1, m7 + m5, m11 + m9); planes[2].distance = -(m15 + m13); planes[2].normalize(); vec3Set(planes[3].normal.array, m3 - m1, m7 - m5, m11 - m9); planes[3].distance = -(m15 - m13); planes[3].normalize(); vec3Set(planes[4].normal.array, m3 - m2, m7 - m6, m11 - m10); planes[4].distance = -(m15 - m14); planes[4].normalize(); vec3Set(planes[5].normal.array, m3 + m2, m7 + m6, m11 + m10); planes[5].distance = -(m15 + m14); planes[5].normalize(); // Perspective projection var boundingBox = this.boundingBox; var vertices = this.vertices; if (m15 === 0) { var aspect = m5 / m0; var zNear = -m14 / (m10 - 1); var zFar = -m14 / (m10 + 1); var farY = -zFar / m5; var nearY = -zNear / m5; // Update bounding box boundingBox.min.set(-farY * aspect, -farY, zFar); boundingBox.max.set(farY * aspect, farY, zNear); // update vertices //--- min z // min x vec3Set(vertices[0], -farY * aspect, -farY, zFar); vec3Set(vertices[1], -farY * aspect, farY, zFar); // max x vec3Set(vertices[2], farY * aspect, -farY, zFar); vec3Set(vertices[3], farY * aspect, farY, zFar); //-- max z vec3Set(vertices[4], -nearY * aspect, -nearY, zNear); vec3Set(vertices[5], -nearY * aspect, nearY, zNear); vec3Set(vertices[6], nearY * aspect, -nearY, zNear); vec3Set(vertices[7], nearY * aspect, nearY, zNear); } else { // Orthographic projection var left = (-1 - m12) / m0; var right = (1 - m12) / m0; var top = (1 - m13) / m5; var bottom = (-1 - m13) / m5; var near = (-1 - m14) / m10; var far = (1 - m14) / m10; boundingBox.min.set(Math.min(left, right), Math.min(bottom, top), Math.min(far, near)); boundingBox.max.set(Math.max(right, left), Math.max(top, bottom), Math.max(near, far)); var min = boundingBox.min.array; var max = boundingBox.max.array; //--- min z // min x vec3Set(vertices[0], min[0], min[1], min[2]); vec3Set(vertices[1], min[0], max[1], min[2]); // max x vec3Set(vertices[2], max[0], min[1], min[2]); vec3Set(vertices[3], max[0], max[1], min[2]); //-- max z vec3Set(vertices[4], min[0], min[1], max[2]); vec3Set(vertices[5], min[0], max[1], max[2]); vec3Set(vertices[6], max[0], min[1], max[2]); vec3Set(vertices[7], max[0], max[1], max[2]); } }, /** * Apply a affine transform matrix and set to the given bounding box * @function * @param {clay.BoundingBox} * @param {clay.Matrix4} * @return {clay.BoundingBox} */ getTransformedBoundingBox: (function() { var tmpVec3 = vec3.create(); return function(bbox, matrix) { var vertices = this.vertices; var m4 = matrix.array; var min = bbox.min; var max = bbox.max; var minArr = min.array; var maxArr = max.array; var v = vertices[0]; vec3TranformMat4(tmpVec3, v, m4); vec3Copy(minArr, tmpVec3); vec3Copy(maxArr, tmpVec3); for (var i = 1; i < 8; i++) { v = vertices[i]; vec3TranformMat4(tmpVec3, v, m4); minArr[0] = mathMin(tmpVec3[0], minArr[0]); minArr[1] = mathMin(tmpVec3[1], minArr[1]); minArr[2] = mathMin(tmpVec3[2], minArr[2]); maxArr[0] = mathMax(tmpVec3[0], maxArr[0]); maxArr[1] = mathMax(tmpVec3[1], maxArr[1]); maxArr[2] = mathMax(tmpVec3[2], maxArr[2]); } min._dirty = true; max._dirty = true; return bbox; }; }) () }; export default Frustum;