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( ['./Program','./FeatureStyle','./Tile','./RendererTileData'], function(Program,FeatureStyle,Tile,RendererTileData) { 21 22 /**************************************************************************************************************/ 23 24 25 /** @constructor 26 TiledVectorRenderer constructor 27 */ 28 var TiledVectorRenderer = function(tileManager) 29 { 30 this.tileManager = tileManager; 31 32 // Create a bucket with default style 33 // Bucket aggregate geometries that shares a common style 34 this.buckets = [ { style: new FeatureStyle(), geometries: [] } ]; 35 36 var vertexShader = "\ 37 attribute vec3 vertex; \n\ 38 uniform float zOffset; \n\ 39 uniform mat4 modelViewMatrix;\n\ 40 uniform mat4 projectionMatrix;\n\ 41 \n\ 42 void main(void) \n\ 43 { \n\ 44 gl_Position = projectionMatrix * modelViewMatrix * vec4(vertex.x, vertex.y, vertex.z + zOffset, 1.0); \n\ 45 } \n\ 46 "; 47 48 var fragmentShader = "\ 49 #ifdef GL_ES \n\ 50 precision highp float; \n\ 51 #endif \n\ 52 uniform vec4 color; \n\ 53 \n\ 54 void main(void) \n\ 55 { \n\ 56 gl_FragColor = color; \n\ 57 } \n\ 58 "; 59 60 this.program = new Program(this.tileManager.renderContext); 61 this.program.createFromSource(vertexShader, fragmentShader); 62 63 // Customization for different renderer : lineString or polygon 64 this.styleEquals = null; 65 this.renderableConstuctor = null; 66 this.id = "empty"; 67 } 68 69 /**************************************************************************************************************/ 70 71 /** 72 Get or create a bucket to store a feature with the given style 73 */ 74 TiledVectorRenderer.prototype.getOrCreateBucket = function( layer, style ) 75 { 76 for ( var i = 0; i < this.buckets.length; i++ ) 77 { 78 if ( this.buckets[i].layer == layer && this.styleEquals(style, this.buckets[i].style ) ) 79 { 80 return this.buckets[i]; 81 } 82 } 83 84 var bucket = { layer: layer, style: style, geometries: [] }; 85 this.buckets.push( bucket ); 86 return bucket; 87 } 88 89 /**************************************************************************************************************/ 90 91 /** 92 Remove a geometry from the tile 93 */ 94 TiledVectorRenderer.prototype.removeGeometryFromTile = function( bucket, geometry, tile ) 95 { 96 var renderable = this.findRenderable( bucket, tile ); 97 if ( renderable && renderable.removeGeometry( geometry ) && tile.children ) 98 { 99 // Remove the geometry from loaded children 100 for ( var i = 0; i < 4; i++ ) 101 { 102 if ( tile.children[i].state == Tile.State.LOADED ) 103 { 104 this.removeGeometryFromTile( bucket, geometry, tile.children[i] ); 105 } 106 } 107 } 108 } 109 110 /**************************************************************************************************************/ 111 112 /** 113 Remove a geometry from the renderer 114 */ 115 TiledVectorRenderer.prototype.removeGeometry = function( geometry, layer ) 116 { 117 var foundBucket = null; 118 119 // Remove geometry from buckets 120 for ( var i = 0; i < this.buckets.length && !foundBucket; i++ ) 121 { 122 var bucket = this.buckets[i]; 123 if ( bucket.layer == layer ) 124 { 125 var index = bucket.geometries.indexOf( geometry ); 126 if ( index != -1 ) 127 { 128 bucket = this.buckets[i]; 129 bucket.geometries.splice( index, 1 ); 130 foundBucket = bucket; 131 } 132 } 133 } 134 135 // Remove geometry 136 if ( foundBucket ) 137 { 138 for ( var i = 0; i < this.tileManager.level0Tiles.length; i++ ) 139 { 140 this.removeGeometryFromTile( foundBucket, geometry, this.tileManager.level0Tiles[i] ); 141 } 142 } 143 } 144 145 /**************************************************************************************************************/ 146 147 /** 148 Clean-up a tile 149 TODO : the method is only used by TileManager.removePostRenderer, maybe remove it, TileManager can use directly the extension id. 150 */ 151 TiledVectorRenderer.prototype.cleanupTile = function( tile ) 152 { 153 if ( tile.extension[this.id] ) 154 { 155 tile.extension[this.id].dispose(); 156 delete tile.extension[this.id]; 157 } 158 } 159 160 /**************************************************************************************************************/ 161 162 /** 163 Add a geometry to the renderer. 164 Public method to add geometry to the renderer 165 */ 166 TiledVectorRenderer.prototype.addGeometry = function( geometry, layer, style ) 167 { 168 var bucket = this.getOrCreateBucket( layer, style ); 169 bucket.geometries.push( geometry ); 170 171 for ( var i = 0; i < this.tileManager.level0Tiles.length; i++ ) 172 { 173 var tile = this.tileManager.level0Tiles[i]; 174 if ( tile.state == Tile.State.LOADED ) 175 this.addGeometryToTile( bucket, geometry, tile ); 176 } 177 } 178 179 /**************************************************************************************************************/ 180 181 /** 182 Add a geometry to the given tile. 183 The method is recursive, it will also add the geometry to children if exists 184 */ 185 TiledVectorRenderer.prototype.addGeometryToTile = function( bucket, geometry, tile ) 186 { 187 var isNewRenderable = false; 188 189 // Try to find an existing renderable on the tile 190 var renderable; 191 if ( tile.extension[this.id] ) 192 { 193 renderable = tile.extension[this.id].getRenderable( bucket ); 194 } 195 196 // If no renderable on the tile, create a new renderable (or reuse an existing one) 197 if ( !renderable ) 198 { 199 renderable = new this.renderableConstuctor(bucket,this.tileManager.renderContext.gl); 200 isNewRenderable = true; 201 } 202 203 if ( renderable.addGeometry( geometry, tile ) && tile.children ) 204 { 205 // Recursively add the geometry to loaded children 206 for ( var i = 0; i < 4; i++ ) 207 { 208 if ( tile.children[i].state == Tile.State.LOADED ) 209 { 210 this.addGeometryToTile( bucket, geometry, tile.children[i] ); 211 } 212 } 213 } 214 215 // If the renderable is new, add it to the tile 216 if (isNewRenderable) 217 { 218 this.addRenderableToTile(tile,renderable); 219 } 220 } 221 222 /**************************************************************************************************************/ 223 224 /** 225 Add a renderable to the tile 226 */ 227 TiledVectorRenderer.prototype.addRenderableToTile = function( tile, renderable ) 228 { 229 if ( renderable.vertices.length > 0 ) 230 { 231 if ( !tile.extension[this.id] ) 232 tile.extension[this.id] = new RendererTileData(); 233 234 tile.extension[this.id].renderables.push( renderable ); 235 } 236 } 237 238 /**************************************************************************************************************/ 239 240 /** 241 Generate renderable data on the tile 242 */ 243 TiledVectorRenderer.prototype.generate = function( tile ) 244 { 245 if ( tile.parent ) 246 { 247 // Only add geometry from parent tile (if any) 248 var ls = tile.parent.extension[this.id]; 249 var ll = ls ? ls.renderables.length : 0; 250 for ( var i = 0; i < ll; i++ ) 251 { 252 var parentRenderable = ls.renderables[i]; 253 var renderable = new this.renderableConstuctor(parentRenderable.bucket,this.tileManager.renderContext.gl); 254 255 var parentGeometryInfos = parentRenderable.geometryInfos; 256 for ( var j = 0; j < parentGeometryInfos.length; j++ ) 257 { 258 renderable.addGeometry( parentGeometryInfos[j].geometry, tile ); 259 } 260 261 this.addRenderableToTile(tile,renderable); 262 } 263 } 264 else 265 { 266 // No parent tile : traverse all geometries to generate data on tile 267 for ( var i = 0; i < this.buckets.length; i++ ) 268 { 269 var bucket = this.buckets[i]; 270 var renderable = new this.renderableConstuctor(bucket,this.tileManager.renderContext.gl); 271 272 for ( var j = 0; j < bucket.geometries.length; j++ ) 273 { 274 renderable.addGeometry( bucket.geometries[j], tile ); 275 } 276 277 this.addRenderableToTile(tile,renderable); 278 } 279 } 280 } 281 282 /**************************************************************************************************************/ 283 284 /** 285 Render all redenrable on the given tiles 286 */ 287 TiledVectorRenderer.prototype.render = function( visibleTiles ) 288 { 289 var renderContext = this.tileManager.renderContext; 290 var gl = renderContext.gl; 291 292 var modelViewMatrix = mat4.create(); 293 294 // Setup program 295 this.program.apply(); 296 297 gl.depthFunc(gl.LEQUAL); 298 gl.uniformMatrix4fv( this.program.uniforms["projectionMatrix"], false, renderContext.projectionMatrix); 299 300 var currentStyle = null; 301 302 for (var i = 0; i < visibleTiles.length; ++i) 303 { 304 var tile = visibleTiles[i]; 305 306 // If the tile is loaded and contains renderable, render them 307 if ( tile.extension[this.id] ) 308 { 309 mat4.multiply( renderContext.viewMatrix, tile.matrix, modelViewMatrix ); 310 gl.uniformMatrix4fv( this.program.uniforms["modelViewMatrix"], false, modelViewMatrix ); 311 gl.uniform1f( this.program.uniforms["zOffset"], tile.radius * 0.0007 ); 312 313 var renderables = tile.extension[this.id].renderables; 314 for (var j=0; j < renderables.length; j++) 315 { 316 var renderable = renderables[j]; 317 318 if ( renderable.bucket.layer._visible ) 319 { 320 if ( renderable.bucket.style != currentStyle ) 321 { 322 currentStyle = renderable.bucket.style; 323 gl.lineWidth( currentStyle.strokeWidth ); 324 gl.uniform4f( this.program.uniforms["color"], currentStyle.strokeColor[0], currentStyle.strokeColor[1], currentStyle.strokeColor[2], 325 currentStyle.strokeColor[3] * renderable.bucket.layer._opacity ); 326 327 // TODO : manage opacity 328 } 329 330 renderables[j].render( this.program.attributes ); 331 } 332 } 333 } 334 // If the tile is not loaded, but its parent contains some renderable, render them 'clipped' to the child tile to avoid 'glitch' when zooming 335 else if ( tile.state != Tile.State.LOADED && tile.parent.extension[this.id] ) 336 { 337 mat4.multiply( renderContext.viewMatrix, tile.parent.matrix, modelViewMatrix ); 338 gl.uniformMatrix4fv( this.program.uniforms["modelViewMatrix"], false, modelViewMatrix ); 339 gl.uniform1f( this.program.uniforms["zOffset"], tile.parent.radius * 0.0007 ); 340 341 var renderables = tile.parent.extension[this.id].renderables; 342 for (var j=0; j < renderables.length; j++) 343 { 344 var renderable = renderables[j]; 345 if ( renderable.bucket.layer._visible ) 346 { 347 if ( renderable.bucket.style != currentStyle ) 348 { 349 currentStyle = renderable.bucket.style; 350 gl.lineWidth( currentStyle.strokeWidth ); 351 gl.uniform4f( this.program.uniforms["color"], currentStyle.strokeColor[0], currentStyle.strokeColor[1], currentStyle.strokeColor[2], 352 currentStyle.strokeColor[3] * renderable.bucket.layer._opacity ); 353 354 // TODO : manage opacity 355 } 356 357 renderables[j].renderChild( this.program.attributes, tile.parentIndex ); 358 } 359 } 360 361 } 362 } 363 364 gl.depthFunc(gl.LESS); 365 } 366 367 /**************************************************************************************************************/ 368 369 return TiledVectorRenderer; 370 371 }); 372