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