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( function() {
 21 
 22 /**************************************************************************************************************/
 23 
 24 
 25 /** @constructor
 26  *	TiledVectorRenderable constructor
 27  */
 28 var TiledVectorRenderable = function( bucket, gl )
 29 {
 30 	this.gl = gl;
 31 	this.bucket = bucket;
 32  	this.vertexBuffer = null;
 33  	this.indexBuffer = null;
 34 	this.vertices = [];
 35 	this.indices = [];
 36 	this.geometryInfos = [];
 37 	this.dirtyVB = true;
 38 	this.dirtyIB = true;
 39 	this.childrenIndexBuffers = null;
 40 	this.childrenIndices = null;
 41 	this.glMode = -1;
 42 }
 43 
 44 
 45 /**************************************************************************************************************/
 46 
 47 /**
 48  * Build children indices.
 49  * Children indices are used to render a tile children when it is not completely loaded.
 50  */
 51 TiledVectorRenderable.prototype.buildChildrenIndices = function( )
 52 {
 53 	// Default method : nothing is done
 54 	this.childrenIndices = [ [], [], [], [] ];
 55 	this.childrenIndexBuffers = [ null, null, null, null ];
 56 }
 57 
 58 
 59 /**************************************************************************************************************/
 60 
 61 /**
 62  *	Remove a geometry from the renderable
 63  */
 64 TiledVectorRenderable.prototype.removeGeometry = function( geometry )
 65 {
 66 	var fiIndex = -1;
 67 
 68 	// Find the feature
 69 	for ( var i = 0; i < this.geometryInfos.length; i++ )
 70 	{
 71 		var fi = this.geometryInfos[i];
 72 		if ( fi.geometry == geometry )
 73 		{
 74 			// Remove feature from vertex and index buffer
 75 			this.vertices.splice( fi.startVertices, fi.vertexCount );
 76 			this.indices.splice( fi.startIndices, fi.indexCount );
 77 		
 78 			// Update index buffer
 79 			var vertexOffset = fi.vertexCount / 3;
 80 			for ( var n = fi.startIndices; n < this.indices.length; n++ )
 81 			{
 82 				this.indices[n] -= vertexOffset;
 83 			}
 84 			
 85 			fiIndex = i;
 86 			
 87 			break;
 88 		}
 89 	}
 90 	
 91 	if ( fiIndex >= 0 )
 92 	{
 93 		this.dirtyVB = true;
 94 		this.dirtyIB = true;
 95 		
 96 		// Update feature infos
 97 		for ( var i = fiIndex + 1; i < this.geometryInfos.length; i++ )
 98 		{
 99 			var fi = this.geometryInfos[i];
100 			fi.startVertices -= this.geometryInfos[fiIndex].vertexCount;
101 			fi.startIndices -= this.geometryInfos[fiIndex].indexCount;
102 		}
103 			
104 		// Remove the feature from the infos array
105 		this.geometryInfos.splice( fiIndex, 1 );
106 		
107 		// Erase children buffers : need to be rebuild
108 		this.disposeChildrenIndexBuffers();
109 		this.childrenIndices = null;
110 		
111 		return true;
112 	}
113 	else
114 	{
115 		return false;
116 	}	
117 }
118 
119 /**************************************************************************************************************/
120 
121 /** 
122   Check if a geometry crosses the date line
123 */
124 TiledVectorRenderable.prototype._fixDateLine = function( tile, coords ) 
125 {		
126 	var crossDateLine = false;
127 	var startLon = coords[0][0];
128 	for ( var i = 1; i < coords.length && !crossDateLine; i++) {
129 		var deltaLon = Math.abs( coords[i][0] - startLon );
130 		if ( deltaLon > 180 ) {
131 			// DateLine!
132 			crossDateLine =  true;
133 		}
134 	}
135 	
136 	if ( crossDateLine )
137 	{
138 		var fixCoords = [];
139 		
140 		if ( tile.geoBound.west < 0.0 )
141 		{
142 			// Ensure coordinates are always negative
143 			for ( var n = 0; n < coords.length; n++) {
144 				if ( coords[n][0] > 0 ) {
145 					fixCoords[n] = [ coords[n][0] - 360, coords[n][1] ];
146 				} else {
147 					fixCoords[n] = [ coords[n][0], coords[n][1] ];
148 				}
149 			}
150 		}
151 		else
152 		{
153 			// Ensure coordinates are always positive
154 			for ( var n = 0; n < coords.length; n++) {
155 				if ( coords[n][0] < 0 ) {
156 					fixCoords[n] = [ coords[n][0] + 360, coords[n][1] ];
157 				} else {
158 					fixCoords[n] = [ coords[n][0], coords[n][1] ];
159 				}
160 			}
161 		}
162 		
163 		return fixCoords;
164 	}
165 	else
166 	{
167 		return coords;
168 	}
169 };
170 
171 /**************************************************************************************************************/
172 
173 /**
174  *	Add a feature to the renderable
175  */
176 TiledVectorRenderable.prototype.addGeometry = function( geometry, tile )
177 {		
178 	var geometryInfo = { geometry: geometry,
179 						startVertices: this.vertices.length,
180 						startIndices: this.indices.length,
181 						vertexCount: 0,
182 						indexCount: 0 };
183 						
184 	var coords = geometry['coordinates'];
185 	switch (geometry['type'])
186 	{
187 		case "LineString":
188 			this.buildVerticesAndIndices( tile, coords );
189 			break;
190 		case "Polygon":
191 			for ( var i = 0; i < coords.length; i++ )
192 				this.buildVerticesAndIndices( tile, coords[i] );
193 			break;
194 		case "MultiLineString":
195 			for ( var i = 0; i < coords.length; i++ )
196 				this.buildVerticesAndIndices( tile, coords[i] );
197 			break;
198 		case "MultiPolygon":
199 			for ( var j = 0; j < coords.length; j++ )
200 				for ( var i = 0; i < coords[j].length; i++ )
201 					this.buildVerticesAndIndices( tile, coords[j][i] );
202 			break;
203 	}
204 	
205 	geometryInfo.vertexCount = this.vertices.length - geometryInfo.startVertices;
206 	geometryInfo.indexCount = this.indices.length - geometryInfo.startIndices;
207 		
208 	if ( geometryInfo.vertexCount > 0 )
209 	{
210 		this.geometryInfos.push( geometryInfo );
211 		this.dirtyVB = true;
212 		this.dirtyIB = true;
213 		
214 		// Erase children buffers : need to be rebuild
215 		this.disposeChildrenIndexBuffers();
216 		this.childrenIndices = null;
217 		
218 		return true;
219 	}
220 	else
221 	{
222 		// Feature not in the tile
223 		return false;
224 	}
225 }
226 
227 /**************************************************************************************************************/
228 
229 /**
230  *	Dispose children index buffers
231  */
232 TiledVectorRenderable.prototype.disposeChildrenIndexBuffers = function()
233 {
234 	var gl = this.gl;
235 
236 	if ( this.childrenIndexBuffers )
237 	{
238 		if ( this.childrenIndexBuffers[0] )
239 			gl.deleteBuffer(this.childrenIndexBuffers[0]);
240 		if ( this.childrenIndexBuffers[1] )
241 			gl.deleteBuffer(this.childrenIndexBuffers[1]);
242 		if ( this.childrenIndexBuffers[2] )
243 			gl.deleteBuffer(this.childrenIndexBuffers[2]);
244 		if ( this.childrenIndexBuffers[3] )
245 			gl.deleteBuffer(this.childrenIndexBuffers[3]);
246 	}
247 	
248 	this.childrenIndexBuffers = null;
249 }
250 
251 /**************************************************************************************************************/
252 
253 /**
254  *	Dispose graphics data 
255  */
256 TiledVectorRenderable.prototype.dispose = function()
257 {
258 	var gl = this.gl;
259 	
260 	if ( this.indexBuffer )
261 		gl.deleteBuffer(this.indexBuffer);
262 	if ( this.vertexBuffer )
263 		gl.deleteBuffer(this.vertexBuffer);
264 	
265 	this.disposeChildrenIndexBuffers();
266 	
267 	this.indexBuffer = null;
268 	this.vertexBuffer = null;
269 }
270 
271 /**************************************************************************************************************/
272 
273 /**
274  *	Render the line string for a child tile
275  *  Used for loading tiles
276  */
277 TiledVectorRenderable.prototype.renderChild = function(attributes, childIndex)
278 {
279 	if ( this.childrenIndices == null )
280 		this.buildChildrenIndices();
281 	
282 	var childIndices = this.childrenIndices[childIndex];
283 	if ( childIndices.length == 0 )
284 		return;
285 		
286 	var gl = this.gl;
287 			
288 	// Bind and update VertexBuffer
289 	if ( this.vertexBuffer == null )
290 		this.vertexBuffer = gl.createBuffer();
291 	gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
292 	if ( this.dirtyVB )
293 	{
294 		gl.bufferData(gl.ARRAY_BUFFER,	new Float32Array(this.vertices), gl.STATIC_DRAW);
295 		this.dirtyVB = false;
296 	}
297 
298 	// Warning : use quoted strings to access properties of the attributes, to work correclty in advanced mode with closure compiler
299 	gl.vertexAttribPointer(attributes['vertex'], 3, gl.FLOAT, false, 0, 0);
300 
301 	// Bind IndexBuffer
302 	var ib = this.childrenIndexBuffers[childIndex];
303 	if ( !ib ) 
304 	{
305 		var gl = this.gl;
306 		ib = gl.createBuffer();
307 		gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ib);
308 		gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(childIndices), gl.STATIC_DRAW);
309 		this.childrenIndexBuffers[childIndex] = ib;
310 	}
311 	else
312 	{
313 		gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,ib);
314 	}
315 
316 		
317 	gl.drawElements( this.glMode, childIndices.length, gl.UNSIGNED_SHORT, 0);
318 }
319 
320 /**************************************************************************************************************/
321 
322 /**
323  *	Render the line string
324  */
325 TiledVectorRenderable.prototype.render = function(attributes)
326 {
327 	var gl = this.gl;
328 			
329 	// Bind and update VertexBuffer
330 	if ( this.vertexBuffer == null )
331 		this.vertexBuffer = gl.createBuffer();
332 	gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
333 	if ( this.dirtyVB )
334 	{
335 		gl.bufferData(gl.ARRAY_BUFFER,	new Float32Array(this.vertices), gl.STATIC_DRAW);
336 		this.dirtyVB = false;
337 	}
338 
339 	// Warning : use quoted strings to access properties of the attributes, to work correclty in advanced mode with closure compiler
340 	gl.vertexAttribPointer(attributes['vertex'], 3, gl.FLOAT, false, 0, 0);
341 
342 	// Bind and update IndexBuffer
343 	if ( this.indexBuffer == null )
344 		this.indexBuffer = gl.createBuffer();
345 	gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
346 	if ( this.dirtyIB )
347 	{
348 		gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(this.indices), gl.STATIC_DRAW);
349 		this.dirtyIB = false;
350 	}
351 		
352 	gl.drawElements( this.glMode, this.indices.length, gl.UNSIGNED_SHORT, 0);
353 }
354 
355 /**************************************************************************************************************/
356 
357 return TiledVectorRenderable;
358 
359 });
360