1 /*************************************** 2 * Copyright 2011, 2012 GlobWeb contributors. 3 * 4 * This file is part of GlobWeb. 5 * 6 * GlobWeb is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU Lesser General Public License as published by 8 * the Free Software Foundation, version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * GlobWeb is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with GlobWeb. If not, see <http://www.gnu.org/licenses/>. 18 ***************************************/ 19 20 define( ['./glMatrix'], function() { 21 22 /**************************************************************************************************************/ 23 24 /** @constructor 25 Plane constructor 26 */ 27 var Plane = function() 28 { 29 this.normal = vec3.create( [0.0, 0.0, 0.0] ); 30 this.d = 0.0; 31 } 32 33 /**************************************************************************************************************/ 34 35 /** 36 Plane init from 3 points 37 */ 38 Plane.prototype.init = function( v1, v2, v3 ) 39 { 40 var vu = []; 41 var vv = []; 42 vec3.subtract( v2, v1, vu ); 43 vec3.subtract( v3, v1, vv ); 44 vec3.cross( vu, vv, this.normal ); 45 vec3.normalize( this.normal ); 46 this.d = - vec3.dot( v1, this.normal ); 47 } 48 49 50 /**************************************************************************************************************/ 51 52 /** 53 Transform the plane with the given matrix 54 */ 55 Plane.prototype.transform = function(matrix) 56 { 57 var vec = [ this.normal[0], this.normal[1], this.normal[2], this.d ]; 58 mat4.multiplyVec4(matrix,vec); 59 this.normal[0] = vec[0]; 60 this.normal[1] = vec[1]; 61 this.normal[2] = vec[2]; 62 this.d = vec[3]; 63 } 64 65 /**************************************************************************************************************/ 66 67 /** 68 Intersection test between plane and bounding sphere. 69 return 1 if the bs is completely above plane, 70 return 0 if the bs intersects the plane, 71 return -1 if the bs is completely below the plane. 72 */ 73 Plane.prototype.intersectSphere = function( center, radius ) 74 { 75 var dist = vec3.dot( center, this.normal ) + this.d; 76 if (dist > radius) return 1; 77 else if ( dist < - radius ) return -1; 78 else return 0; 79 } 80 81 /**************************************************************************************************************/ 82 83 /** 84 Return the distance between a point and the plane 85 */ 86 Plane.prototype.distance = function( point ) 87 { 88 return point[0] * this.normal[0] + point[1] * this.normal[1] + point[2] * this.normal[2] + this.d 89 } 90 91 92 /**************************************************************************************************************/ 93 94 /** 95 Intersection test between plane and bounding box. 96 return 1 if the bbox is completely above plane, 97 return 0 if the bbox intersects the plane, 98 return -1 if the bbox is completely below the plane. 99 */ 100 Plane.prototype.intersectBoundingBox = function( bbox ) 101 { 102 var upperBBCorner = (this.normal[0]>=0.0?1:0) | 103 (this.normal[1]>=0.0?2:0) | 104 (this.normal[2]>=0.0?4:0); 105 106 var lowerBBCorner = (~upperBBCorner)&7; 107 108 // if lowest point above plane than all above. 109 if ( this.distance(bbox.getCorner(lowerBBCorner)) > 0.0) return 1; 110 111 // if highest point is below plane then all below. 112 if ( this.distance(bbox.getCorner(upperBBCorner)) < 0.0) return -1; 113 114 // d_lower<=0.0f && d_upper>=0.0f 115 // therefore must be crossing plane. 116 return 0; 117 } 118 119 /**************************************************************************************************************/ 120 121 /** @constructor 122 Frustum constructor 123 */ 124 var Frustum = function() 125 { 126 // The frustum does not contains near and far plane, because near and far are computed during rendering. 127 // Some tests have been done with a near plane but are not really useful 128 this.planes = [ new Plane(), new Plane(), new Plane(), new Plane(), new Plane() ]; 129 //this.planes = [ new Plane(), new Plane(), new Plane(), new Plane() ]; 130 } 131 132 /**************************************************************************************************************/ 133 134 /** 135 Compute the frustum from the given projection matrix 136 */ 137 Frustum.prototype.compute = function(projectionMatrix) 138 { 139 var inverseProjectionMatrix = mat4.create(); 140 mat4.inverse( projectionMatrix, inverseProjectionMatrix ) 141 142 var bottomleft = mat4.project( inverseProjectionMatrix, [-1.0,-1.0,-1.0,1.0] ); 143 var topleft = mat4.project( inverseProjectionMatrix, [-1.0,1.0,-1.0,1.0] ); 144 var topright = mat4.project( inverseProjectionMatrix, [1.0,1.0,-1.0,1.0] ); 145 var bottomright = mat4.project( inverseProjectionMatrix, [1.0,-1.0,-1.0,1.0] ); 146 147 this.planes[0].init( [0.0,0.0,0.0], bottomleft, topleft ); 148 this.planes[1].init( [0.0,0.0,0.0], topleft, topright ); 149 this.planes[2].init( [0.0,0.0,0.0], topright, bottomright ); 150 this.planes[3].init( [0.0,0.0,0.0], bottomright, bottomleft ); 151 152 // A plane for near plane if needed 153 this.planes[4].init( bottomleft, topleft, topright ); 154 } 155 156 /**************************************************************************************************************/ 157 158 /** 159 Transform the frustum with the given matrix 160 */ 161 Frustum.prototype.transform = function(frustum,matrix) 162 { 163 var mat = mat4.create(); 164 mat4.inverse(matrix,mat); 165 this.inverseTransform(frustum,mat); 166 } 167 168 /**************************************************************************************************************/ 169 170 /** 171 Inverse transform the frustum with the given matrix 172 */ 173 Frustum.prototype.inverseTransform = function(frustum,matrix) 174 { 175 // Optimized implementation 176 for ( var i = 0; i < frustum.planes.length; i++ ) 177 { 178 var plane = frustum.planes[i]; 179 180 var x = plane.normal[0]; 181 var y = plane.normal[1]; 182 var z = plane.normal[2]; 183 var w = plane.d; 184 185 plane = this.planes[i]; 186 187 plane.normal[0] = matrix[0]*x + matrix[1]*y + matrix[2]*z + matrix[3]*w; 188 plane.normal[1] = matrix[4]*x + matrix[5]*y + matrix[6]*z + matrix[7]*w; 189 plane.normal[2] = matrix[8]*x + matrix[9]*y + matrix[10]*z + matrix[11]*w; 190 plane.d = matrix[12]*x + matrix[13]*y + matrix[14]*z + matrix[15]*w; 191 } 192 } 193 194 /**************************************************************************************************************/ 195 196 /** 197 Intersection test between frustum and bounding sphere. 198 return 1 if the bs is completely outside the frustum, 199 return 0 if the bs intersects the frustum, 200 return -1 if the bs is completely inside the frustum. 201 */ 202 Frustum.prototype.containsSphere = function( center, radius ) 203 { 204 var flag = 1; 205 206 for (var i = 0; i < this.planes.length; i++) 207 { 208 var pn = this.planes[i].normal; 209 210 // Compute distance between center and plane (inline to be more efficient) 211 var dist = center[0]*pn[0] + center[1]*pn[1] + center[2]*pn[2] + this.planes[i].d; 212 213 if (dist <= radius) 214 { 215 if ( dist < - radius ) 216 return -1; 217 else 218 flag = 0; 219 } 220 } 221 222 return flag; 223 } 224 225 /**************************************************************************************************************/ 226 227 /** 228 Test if the frustum contains the given bounding box 229 */ 230 Frustum.prototype.containsBoundingBox = function( bbox ) 231 { 232 // Optimized implementation 233 for (var i = 0; i < this.planes.length; i++) 234 { 235 var plane = this.planes[i]; 236 237 // Get the closest point on the bbox 238 var bbx = plane.normal[0]>=0.0 ? bbox.max[0] : bbox.min[0]; 239 var bby = plane.normal[1]>=0.0 ? bbox.max[1] : bbox.min[1]; 240 var bbz = plane.normal[2]>=0.0 ? bbox.max[2] : bbox.min[2]; 241 242 // Compute the distance 243 var distance = bbx * plane.normal[0] + bby * plane.normal[1] + bbz * plane.normal[2] + plane.d 244 245 // if highest point is below plane then all below. 246 if ( distance < 0.0) return false; 247 } 248 249 /* for (var i = 0; i < 4; i++) 250 { 251 if ( this.planes[i].intersectBoundingBox( bbox ) < 0 ) 252 { 253 return false; 254 } 255 }*/ 256 257 return true; 258 } 259 260 /**************************************************************************************************************/ 261 262 return Frustum; 263 264 }); 265