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(['./Utils', './BaseLayer', './Program','./CoordinateSystem'], function(Utils,BaseLayer,Program,CoordinateSystem) {
 21 
 22 /** @name AtmosphereLayer
 23 	@class
 24 	A layer to BaseLayer an atmosphere on the globe.
 25 	@augments RasterLayer
 26 	@param options Configuration properties for the layer. See {@link BaseLayer} for base properties :
 27 		<ul>
 28 			<li>kr : the raylength parameter, default is 0.0025</li>
 29 			<li>km : the mie parameter, default is 0.0015</li>
 30 			<li>sunBrightness : the mie parameter, default is 0.0015</li>
 31 			<li>exposure : the exposure, use for basic high dynamic range, default is 2.0</li>
 32 			<li>wavelength : the RGB color of the sun, default is [0.650,0.570,0.475]</li>
 33 		</ul>
 34  */
 35 var AtmosphereLayer = function(options)
 36 {
 37 	BaseLayer.prototype.constructor.call( this, options );
 38 	if (!this.name)
 39 		this.name = "Atmosphere";
 40 
 41 	this.kr = (options && options['kr']) || 0.0025;
 42 	this.km = (options && options['km']) || 0.0015;
 43 	this.sunBrightness = (options && options['sunBrightness']) || 15.0;
 44 	this.exposure = (options && options['exposure']) || 2.0;
 45 	this.wavelength = (options && options['wavelength']) || [0.650,0.570,0.475];
 46 	
 47 	// internal properties
 48 	this._innerRadius = CoordinateSystem.radius;
 49 	this._outerRadius = this._innerRadius * 1.025;
 50 	this._skyProgram = null;
 51 	this._groundProgram = null;
 52 	this._originalProgram = null;
 53 	this._isValid = false;
 54 }
 55 
 56 /**************************************************************************************************************/
 57 
 58 Utils.inherits( BaseLayer,AtmosphereLayer );
 59 
 60 /**************************************************************************************************************/
 61 
 62 /** 
 63   Attach the atmosphere layer to the globe
 64  */
 65 AtmosphereLayer.prototype._attach = function( g )
 66 {
 67 	this.globe = g;
 68 	var renderContext = g.renderContext;
 69 	
 70 	// Setup program, uniform now that we have the render context
 71 		
 72     this._skyFromSpaceProgram = new Program(renderContext);
 73 	this._skyFromSpaceProgram.loadFromFile( "SkyFromSpaceVert.glsl", "SkyFrag.glsl" );
 74 	
 75 	this._skyFromAtmosphereProgram = new Program(renderContext);
 76 	this._skyFromAtmosphereProgram.loadFromFile( "SkyFromAtmosphereVert.glsl", "SkyFrag.glsl" );
 77 	
 78 	this._groundFromSpaceProgram = new Program(renderContext);
 79 	this._groundFromSpaceProgram.loadFromFile( "GroundFromSpaceVert.glsl", "GroundFrag.glsl" );
 80 	
 81     this._groundFromAtmosphereProgram = new Program(renderContext);
 82 	this._groundFromAtmosphereProgram.loadFromFile( "GroundFromAtmosphereVert.glsl", "GroundFrag.glsl" );
 83 	
 84 	// Check if the atmosphre is valid : all programs must be OK
 85 	this._isValid = this._skyFromSpaceProgram.glProgram != null
 86 	    && this._skyFromAtmosphereProgram.glProgram != null
 87 		&& this._groundFromSpaceProgram.glProgram != null
 88 		&& this._groundFromAtmosphereProgram.glProgram != null;
 89 		
 90 	if ( !this._isValid )
 91 		return;
 92 
 93 	this._skyFromSpaceProgram.apply();
 94 	this._initUniforms( this._skyFromSpaceProgram.uniforms );
 95 	this._skyFromAtmosphereProgram.apply();
 96 	this._initUniforms( this._skyFromAtmosphereProgram.uniforms );
 97 	this._groundFromSpaceProgram.apply();
 98 	this._initUniforms( this._groundFromSpaceProgram.uniforms );
 99 	this._groundFromAtmosphereProgram.apply();
100 	this._initUniforms( this._groundFromAtmosphereProgram.uniforms );
101 	
102 	// Create the sphere
103 	var vertices = [];
104 	var indices = [];
105 	
106 	var nbEl = 72;
107 	var nbAz = 144;
108 	
109 	// Create the vertices
110 	for (var el=-nbEl; el <= nbEl; el++)
111 	{
112 		var elevation = el * (Math.PI * 0.5) / nbEl;
113 		for (var az=-nbAz; az <= nbAz; az++)
114 		{
115 			var azimuth = az * Math.PI / nbAz;
116 
117 			var x = this._outerRadius * Math.cos(azimuth) * Math.cos(elevation);
118 			var y = this._outerRadius * Math.sin(azimuth) * Math.cos(elevation);
119 			var z = this._outerRadius * Math.sin(elevation);
120 
121 			vertices.push( x );
122 			vertices.push( y );
123 			vertices.push( z );
124 		}
125 	}
126 
127 	// build the sphere triangles
128 	for (var el=0; el < 2*nbEl; el++)
129 	{
130 		for (var az=0; az < 2*nbAz; az++)
131 		{
132 			indices.push( el * (2*nbAz+1) + az );
133 			indices.push( (el+1) * (2*nbAz+1) + az+1 );
134 			indices.push( el * (2*nbAz+1) + az + 1 );
135 			
136 			indices.push( (el+1) * (2*nbAz+1) + az+1 );
137 			indices.push( el * (2*nbAz+1) + az );
138 			indices.push( (el+1) * (2*nbAz+1) + az );
139 		}
140 	}
141 
142 	var gl = renderContext.gl;
143 	this._vertexBuffer = gl.createBuffer();
144 	gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
145 	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
146 	
147 	this._indexBuffer = gl.createBuffer();
148 	gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer);
149 	gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
150 	this._numIndices = indices.length;
151 	
152 	this._originalProgram = g.tileManager.program;
153 	
154 	g.preRenderers.push(this);
155 	g.tileManager.addPostRenderer(this);
156 }
157 
158 /**************************************************************************************************************/
159 
160 /*
161 	Initialize uniforms
162  */
163 AtmosphereLayer.prototype._initUniforms = function( uniforms )
164 {
165 	var gl = this.globe.renderContext.gl;
166 	
167 	var g = -0.95;		// The Mie phase asymmetry factor
168 	var scale = 1.0 / ( this.outerRadius - this.innerRadius );
169 	var rayleighScaleDepth = 0.25;
170 	var mieScaleDepth = 0.1;
171 
172 	var lightDir = [1.0,1.0,1.0];
173 	vec3.normalize( lightDir );
174 	
175 	gl.uniform1f( uniforms["fKrESun"], this.kr * this.sunBrightness );
176 	gl.uniform1f( uniforms["fKmESun"], this.kr * this.sunBrightness );
177 	gl.uniform1f( uniforms["fKr4PI"], this.kr * 4.0 * Math.PI );
178 	gl.uniform1f( uniforms["fKm4PI"], this.km * 4.0 * Math.PI );
179 	gl.uniform1f( uniforms["fExposure"], this.exposure );
180 
181 	var wavelength = [ Math.pow( this.wavelength[0], 4.0 ), Math.pow( this.wavelength[1], 4.0 ) ,Math.pow( this.wavelength[2], 4.0 ) ];
182 	gl.uniform3f( uniforms["v3InvWavelength"], 1.0 / wavelength[0], 1.0 / wavelength[1], 1.0 / wavelength[2] );
183 
184 	gl.uniform3f( uniforms["v3LightPos"],  lightDir[0], lightDir[1], lightDir[2] );
185 	gl.uniform1f( uniforms["fInnerRadius"], this._innerRadius );
186 	gl.uniform1f( uniforms["fInnerRadius2"], this._innerRadius*this._innerRadius );
187 	gl.uniform1f( uniforms["fOuterRadius"], this._outerRadius );
188 	gl.uniform1f( uniforms["fOuterRadius2"], this._outerRadius*this._outerRadius );
189 	gl.uniform1f( uniforms["fScale"], 1.0 / (this._outerRadius - this._innerRadius) );
190 	gl.uniform1f( uniforms["fScaleDepth"], rayleighScaleDepth );
191 	gl.uniform1f( uniforms["fScaleOverScaleDepth"], (1.0 / (this._outerRadius - this._innerRadius)) / rayleighScaleDepth );
192 	gl.uniform1f( uniforms["g"], g );
193 	gl.uniform1f( uniforms["g2"], g*g );
194 }
195 
196 /**************************************************************************************************************/
197 
198 /*
199 	Pre-render the atmoshpere
200  */
201 AtmosphereLayer.prototype.preRender = function()
202 {
203 	if ( !this._isValid )
204 		return;
205 		
206 	var tileManager = this.globe.tileManager;
207 	if ( !this._visible )
208 	{
209 		tileManager.program = this._originalProgram;
210 		return;
211 	}
212 		
213 	var rc = this.globe.renderContext;
214 	var gl = rc.gl;
215 
216 	// Compute the eye position from the view matrix : the eye position is equals to [0,0,0] * inv(viewMatrix)
217 	// Optimized to avoid to compute the view matrix inverse
218 	var vm = rc.viewMatrix;
219 	var x = vm[12], y = vm[13], z = vm[14];
220 	var eyePos = [ -( vm[0]*x + vm[1]*y + vm[2]*z ),
221 				 -( vm[4]*x + vm[5]*y + vm[6]*z ),
222 				  -( vm[8]*x + vm[9]*y + vm[10]*z ) ];
223 	var eyeHeight = vec3.length(eyePos);
224 
225 	this._skyProgram = eyeHeight < this._outerRadius ? this._skyFromAtmosphereProgram : this._skyFromSpaceProgram;
226 	this._groundProgram = eyeHeight < this._outerRadius ? this._groundFromAtmosphereProgram : this._groundFromSpaceProgram;
227 
228 	this._skyProgram.apply();
229 	
230 	gl.uniform3f( this._skyProgram.uniforms["v3CameraPos"], eyePos[0], eyePos[1], eyePos[2] );
231 	gl.uniform1f( this._skyProgram.uniforms["fCameraHeight2"], eyeHeight * eyeHeight );
232 	gl.uniform1f( this._skyProgram.uniforms["fCameraHeight"], eyeHeight );
233 
234 	this._groundProgram.apply();
235 	
236 	var earthCenter = [ 0.0, 0.0, 0.0 ];
237 	mat4.multiplyVec3( rc.viewMatrix, earthCenter );
238 	gl.uniform3f( this._groundProgram.uniforms["earthCenter"], earthCenter[0], earthCenter[1], earthCenter[2] );
239 
240 	var lightDir = [1.0,1.0,1.0];
241 	vec3.normalize( lightDir );
242 	var x = lightDir[0], y = lightDir[1], z = lightDir[2];
243 	var mat = rc.viewMatrix;
244 	lightDir[0] = mat[0]*x + mat[4]*y + mat[8]*z;
245 	lightDir[1] = mat[1]*x + mat[5]*y + mat[9]*z;
246 	lightDir[2] = mat[2]*x + mat[6]*y + mat[10]*z ;
247 	gl.uniform3f( this._groundProgram.uniforms["lightDir"], lightDir[0], lightDir[1], lightDir[2] );
248 
249 	gl.uniform3f( this._groundProgram.uniforms["v3CameraPos"], eyePos[0], eyePos[1], eyePos[2] );
250 	gl.uniform1f( this._groundProgram.uniforms["fCameraHeight2"], eyeHeight * eyeHeight );
251 	gl.uniform1f( this._groundProgram.uniforms["fCameraHeight"], eyeHeight );
252 	
253 	tileManager.program = this._groundProgram;
254 	
255 	rc.far = 2;
256 }
257 
258 /**************************************************************************************************************/
259 
260 /*
261 	Render the atmosphere
262  */
263 AtmosphereLayer.prototype.render = function()
264 {
265 	if ( !this._isValid || ! this._visible )
266 		return;
267 		
268 	var rc = this.globe.renderContext;
269 	var gl = rc.gl;
270 	
271 	gl.enable(gl.CULL_FACE);
272 
273 	this._skyProgram.apply();
274 	
275 	gl.uniformMatrix4fv(this._skyProgram.uniforms["projectionMatrix"], false, rc.projectionMatrix);
276 	gl.uniformMatrix4fv(this._skyProgram.uniforms["viewMatrix"], false, rc.viewMatrix);
277 
278 	gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
279 	gl.vertexAttribPointer(this._skyProgram.attributes['vertex'], 3, gl.FLOAT, false, 0, 0);
280 	gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer);
281 	gl.drawElements(gl.TRIANGLES, this._numIndices, gl.UNSIGNED_SHORT, 0);
282 	
283 	gl.disable(gl.CULL_FACE);
284 }
285 
286 /**************************************************************************************************************/
287 
288 return AtmosphereLayer;
289 
290 });
291 
292