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([ './Frustum', './Numeric', './CoordinateSystem', './glMatrix' ], 21 function( Frustum, Numeric, CoordinateSystem ) { 22 23 /**************************************************************************************************************/ 24 25 /** 26 @constructor 27 Function constructor for RencerContext 28 */ 29 var RenderContext = function(options) 30 { 31 /** 32 * Private properties 33 */ 34 35 /** 36 * Private method 37 */ 38 39 40 /** 41 * Constructor 42 */ 43 this.shadersPath = options['shadersPath'] || "../shaders/"; 44 this.tileErrorTreshold = options['tileErrorTreshold'] || 4; 45 this.lighting = options['lighting'] || false; 46 this.continuousRendering = options['continuousRendering'] || false; 47 this.stats = null; 48 49 // Init GL 50 var canvas = null; 51 52 // Check canvas options 53 if (!options['canvas']) 54 throw "GlobWeb : no canvas in options"; 55 56 57 if (typeof options['canvas'] == "string") 58 { 59 canvas = document.getElementById(options['canvas']); 60 } 61 else 62 { 63 canvas = options['canvas']; 64 } 65 66 // Check canvas is valid 67 if (!canvas instanceof HTMLCanvasElement) 68 throw "GlobWeb : invalid canvas"; 69 70 // Create the webl context 71 var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"]; 72 var gl = null; 73 for (var ii = 0; ii < names.length && gl == null; ++ii) 74 { 75 try 76 { 77 gl = canvas.getContext(names[ii], options['contextAttribs']); 78 } 79 catch(e) {} 80 } 81 82 if ( gl == null ) 83 throw "GlobWeb : WebGL context cannot be initialized"; 84 85 86 if ( options['backgroundColor'] ) 87 { 88 var color = options['backgroundColor']; 89 gl.clearColor(color[0],color[1],color[2],color[3]); 90 } 91 else 92 { 93 gl.clearColor(0.0, 0.0, 0.0, 1.0); 94 } 95 96 gl.pixelStorei( gl['UNPACK_COLORSPACE_CONVERSION_WEBGL'], gl.NONE ); 97 gl.enable(gl.DEPTH_TEST); 98 gl.enable(gl.CULL_FACE); 99 100 // Store local variable into static object 101 this.viewMatrix = mat4.create(); 102 this.modelViewMatrix = mat4.create(); 103 this.projectionMatrix = mat4.create(); 104 this.gl = gl; 105 this.canvas = canvas; 106 this.frustum = new Frustum(); 107 this.worldFrustum = new Frustum(); 108 this.localFrustum = new Frustum(); 109 this.eyePosition = vec3.create(); 110 this.eyeDirection = vec3.create(); 111 this.minNear = 0.00001; 112 this.near = RenderContext.minNear; 113 this.far = 6.0; 114 this.numActiveAttribArray = 0; 115 this.frameRequested = false; 116 this.fov = 45; 117 118 119 // Initialize the window requestAnimationFrame 120 if ( !window.requestAnimationFrame ) 121 { 122 window.requestAnimationFrame = ( function() { 123 return window.webkitRequestAnimationFrame || 124 window.mozRequestAnimationFrame || 125 window.oRequestAnimationFrame || 126 window.msRequestAnimationFrame || 127 function( callback, element ) { window.setTimeout( callback, 1000 / 60 );}; 128 } )(); 129 } 130 } 131 132 /**************************************************************************************************************/ 133 134 /** 135 Request a frame 136 */ 137 RenderContext.prototype.requestFrame = function() 138 { 139 if (!this.frameRequested) 140 { 141 window.requestAnimationFrame(this.frame); 142 this.frameRequested = true; 143 } 144 } 145 /**************************************************************************************************************/ 146 147 /** 148 Update properies that depends on the view matrix 149 */ 150 RenderContext.prototype.updateViewDependentProperties = function() 151 { 152 var inverseViewMatrix = mat4.create(); 153 mat4.inverse( this.viewMatrix, inverseViewMatrix ); 154 155 vec3.set( [ 0.0, 0.0, 0.0 ], this.eyePosition ); 156 mat4.multiplyVec3( inverseViewMatrix, this.eyePosition ); 157 158 vec3.set( [ 0.0, 0.0, -1.0 ], this.eyeDirection ); 159 mat4.rotateVec3( inverseViewMatrix, this.eyeDirection ); 160 161 this.pixelSizeVector = this.computePixelSizeVector(); 162 163 // Init projection matrix 164 mat4.perspective(this.fov, this.canvas.width / this.canvas.height, this.minNear, this.far, this.projectionMatrix); 165 166 // Compute the frustum from the projection matrix 167 this.frustum.compute(this.projectionMatrix); 168 169 // Compute the world frustum 170 this.worldFrustum.inverseTransform( this.frustum, this.viewMatrix ); 171 172 // Init near and far to 'invalid' values 173 this.near = 1e9; 174 this.far = 0.0; 175 } 176 177 /**************************************************************************************************************/ 178 179 /** 180 Get mouse coordinates relative to the canvas element 181 */ 182 RenderContext.prototype.getXYRelativeToCanvas = function(event) 183 { 184 // cf. http://stackoverflow.com/questions/55677/how-do-i-get-the-coordinates-of-a-mouse-click-on-a-canvas-element 185 var pos = []; 186 if (event.pageX || event.pageY) 187 { 188 pos[0] = event.pageX; 189 pos[1] = event.pageY; 190 } 191 else 192 { 193 pos[0] = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; 194 pos[1] = event.clientY + document.body.scrollTop + document.documentElement.scrollTop; 195 } 196 197 var element = this.canvas; 198 while (element) 199 { 200 pos[0] -= element.offsetLeft; 201 pos[1] -= element.offsetTop; 202 element = element.offsetParent; 203 } 204 205 return pos; 206 } 207 208 209 /**************************************************************************************************************/ 210 211 /** 212 Compute the pixel size vector 213 */ 214 RenderContext.prototype.computePixelSizeVector = function( mv ) 215 { 216 // pre adjust P00,P20,P23,P33 by multiplying them by the viewport window matrix. 217 // here we do it in short hand with the knowledge of how the window matrix is formed 218 // note P23,P33 are multiplied by an implicit 1 which would come from the window matrix. 219 // Robert Osfield, June 2002. 220 221 var width = this.canvas.width; 222 var height = this.canvas.height; 223 var P = this.projectionMatrix; 224 var V = mv || this.viewMatrix; 225 226 // scaling for horizontal pixels 227 var P00 = P[0]*width*0.5; 228 var P20_00 = P[8]*width*0.5 + P[11]*width*0.5; 229 var scale_00 = [ V[0]*P00 + V[2]*P20_00, 230 V[4]*P00 + V[6]*P20_00, 231 V[8]*P00 + V[10]*P20_00 ]; 232 233 // scaling for vertical pixels 234 var P10 = P[5]*height*0.5; 235 var P20_10 = P[9]*height*0.5 + P[11]*height*0.5; 236 var scale_10 = [ V[1]*P10 + V[2]*P20_10, 237 V[5]*P10 + V[6]*P20_10, 238 V[9]*P10 + V[10]*P20_10 ]; 239 240 var P23 = P[11]; 241 var P33 = P[15]; 242 var pixelSizeVector = [V[2]*P23, 243 V[6]*P23, 244 V[10]*P23, 245 V[14]*P23 + V[15]*P33]; 246 247 var scaleRatio = 0.7071067811 / Math.sqrt( vec3.dot(scale_00,scale_00)+ vec3.dot(scale_10,scale_10) ); 248 pixelSizeVector[0] *= scaleRatio; 249 pixelSizeVector[1] *= scaleRatio; 250 pixelSizeVector[2] *= scaleRatio; 251 pixelSizeVector[3] *= scaleRatio; 252 253 return pixelSizeVector; 254 } 255 /**************************************************************************************************************/ 256 257 /** 258 Get 3D from a pixel 259 */ 260 RenderContext.prototype.get3DFromPixel = function(x,y) 261 { 262 // reverse y because (0,0) is top left but opengl's normalized 263 // device coordinate (-1,-1) is bottom left 264 var nx = ((x / this.canvas.width) * 2.0) - 1.0; 265 var ny = -(((y / this.canvas.height) * 2.0) - 1.0); 266 267 var tmpMat = mat4.create(); 268 mat4.multiply(this.projectionMatrix, this.viewMatrix, tmpMat); 269 mat4.inverse(tmpMat); 270 // Transform pos to world using inverse viewProjection matrix 271 var worldPick = mat4.multiplyVec4(tmpMat, [ nx, ny, -1, 1]); 272 worldPick[0] /= worldPick[3]; 273 worldPick[1] /= worldPick[3]; 274 worldPick[2] /= worldPick[3]; 275 276 // Shoot a ray from the camera to the picked point 277 // Get camera position 278 mat4.inverse(this.viewMatrix, tmpMat); 279 var worldCam = [tmpMat[12], tmpMat[13], tmpMat[14]]; 280 var rayDirection = vec3.create(); 281 vec3.subtract(worldPick, worldCam, rayDirection); 282 vec3.normalize(rayDirection); 283 284 // Intersect earth sphere 285 var t = Numeric.raySphereIntersection(worldCam, rayDirection, [0.0, 0.0, 0.0], CoordinateSystem.radius); 286 if (t >= 0) 287 { 288 var pos3d = Numeric.pointOnRay(worldCam, rayDirection, t); 289 return pos3d; 290 } 291 292 return null; 293 } 294 295 /**************************************************************************************************************/ 296 297 /** 298 Get pixel from 3D 299 */ 300 RenderContext.prototype.getPixelFrom3D = function(x,y,z) 301 { 302 var viewProjectionMatrix = mat4.create(); 303 mat4.multiply(this.projectionMatrix, this.viewMatrix, viewProjectionMatrix); 304 305 // transform world to clipping coordinates 306 var point3D = [x,y,z,1]; 307 mat4.project(viewProjectionMatrix, point3D); 308 309 // transform clipping to window coordinates 310 var winX = Math.round( ( 1 + point3D[0] ) * 0.5 * this.canvas.width ); 311 312 // reverse y because (0,0) is top left but opengl's normalized 313 // device coordinate (-1,-1) is bottom left 314 var winY = Math.round( ( 1 - point3D[1] ) * 0.5 * this.canvas.height ); 315 316 return [winX, winY]; 317 } 318 319 /**************************************************************************************************************/ 320 321 /** 322 Create a non power of two texture from an image 323 */ 324 RenderContext.prototype.createNonPowerOfTwoTextureFromImage = function(image) 325 { 326 var gl = this.gl; 327 var tex = gl.createTexture(); 328 gl.bindTexture(gl.TEXTURE_2D, tex); 329 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); 330 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 331 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 332 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 333 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 334 return tex; 335 } 336 337 /**************************************************************************************************************/ 338 339 return RenderContext; 340 341 }); 342