osgEarth 2.1.1
|
00001 /* -*-c++-*- */ 00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph 00003 * Copyright 2008-2010 Pelican Mapping 00004 * http://osgearth.org 00005 * 00006 * osgEarth is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU Lesser General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public License 00017 * along with this program. If not, see <http://www.gnu.org/licenses/> 00018 */ 00019 #include "TileBuilder" 00020 #include "TransparentLayer" 00021 #include <osgEarth/ImageUtils> 00022 #include <osgEarth/TaskService> 00023 00024 using namespace osgEarth; 00025 using namespace OpenThreads; 00026 00027 #define LC "[TileBuilder] " 00028 00029 //------------------------------------------------------------------------ 00030 00031 struct BuildColorLayer 00032 { 00033 void init( const TileKey& key, ImageLayer* layer, const MapInfo& mapInfo, 00034 const OSGTerrainOptions& opt, TileBuilder::SourceRepo& repo ) 00035 { 00036 _key = key; 00037 _layer = layer; 00038 _mapInfo = &mapInfo; 00039 _opt = &opt; 00040 _repo = &repo; 00041 } 00042 00043 void execute() 00044 { 00045 GeoImage geoImage; 00046 bool isFallbackData = false; 00047 00048 // fetch the image from the layer, falling back on parent keys utils we are 00049 // able to find one that works. 00050 TileKey imageKey( _key ); 00051 while( !geoImage.valid() && imageKey.valid() && _layer->isKeyValid(imageKey) ) 00052 { 00053 geoImage = _layer->createImage( imageKey, 0L ); // TODO: include a progress callback? 00054 if ( !geoImage.valid() ) 00055 { 00056 imageKey = imageKey.createParentKey(); 00057 isFallbackData = true; 00058 } 00059 } 00060 00061 GeoLocator* locator = 0L; 00062 00063 if ( !geoImage.valid() ) 00064 { 00065 // no image found, so make an empty one (one pixel alpha). 00066 geoImage = GeoImage( ImageUtils::createEmptyImage(), _key.getExtent() ); 00067 locator = GeoLocator::createForKey( _key, *_mapInfo ); 00068 isFallbackData = true; 00069 } 00070 else 00071 { 00072 locator = GeoLocator::createForExtent(geoImage.getExtent(), *_mapInfo); 00073 } 00074 // add the color layer to the repo. 00075 _repo->add( CustomColorLayer( 00076 _layer, 00077 geoImage.getImage(), 00078 locator, 00079 _key.getLevelOfDetail(), 00080 _key, 00081 isFallbackData ) ); 00082 } 00083 00084 TileKey _key; 00085 const MapInfo* _mapInfo; 00086 ImageLayer* _layer; 00087 const OSGTerrainOptions* _opt; 00088 TileBuilder::SourceRepo* _repo; 00089 }; 00090 00091 //------------------------------------------------------------------------ 00092 00093 struct BuildElevLayer 00094 { 00095 void init(const TileKey& key, const MapFrame& mapf, const OSGTerrainOptions& opt, TileBuilder::SourceRepo& repo) 00096 { 00097 _key = key; 00098 _mapf = &mapf; 00099 _opt = &opt; 00100 _repo = &repo; 00101 } 00102 00103 void execute() 00104 { 00105 const MapInfo& mapInfo = _mapf->getMapInfo(); 00106 00107 // Request a heightfield from the map, falling back on lower resolution tiles 00108 // if necessary (fallback=true) 00109 osg::ref_ptr<osg::HeightField> hf; 00110 bool isFallback = false; 00111 00112 if ( _mapf->getHeightField( _key, true, hf, &isFallback, *_opt->elevationInterpolation() ) ) 00113 { 00114 // Treat Plate Carre specially by scaling the height values. (There is no need 00115 // to do this with an empty heightfield) 00116 if ( mapInfo.isPlateCarre() ) 00117 { 00118 HeightFieldUtils::scaleHeightFieldToDegrees( hf.get() ); 00119 } 00120 00121 // Put it in the repo 00122 osgTerrain::HeightFieldLayer* hfLayer = new osgTerrain::HeightFieldLayer( hf.get() ); 00123 00124 // Generate a locator. 00125 hfLayer->setLocator( GeoLocator::createForKey( _key, mapInfo ) ); 00126 00127 _repo->set( CustomElevLayer(hfLayer, isFallback) ); 00128 } 00129 } 00130 00131 TileKey _key; 00132 const MapFrame* _mapf; 00133 const OSGTerrainOptions* _opt; 00134 TileBuilder::SourceRepo* _repo; 00135 }; 00136 00137 //------------------------------------------------------------------------ 00138 00139 struct AssembleTile 00140 { 00141 void init(const TileKey& key, const MapInfo& mapInfo, const OSGTerrainOptions& opt, TileBuilder::SourceRepo& repo, const MaskLayerVector& masks=MaskLayerVector() ) 00142 { 00143 _key = key; 00144 _mapInfo = &mapInfo; 00145 _opt = &opt; 00146 _repo = &repo; 00147 _tile = 0L; 00148 _masks.clear(); 00149 std::copy( masks.begin(), masks.end(), std::back_inserter(_masks) ); 00150 } 00151 00152 void execute() 00153 { 00154 _tile = new Tile( _key, GeoLocator::createForKey(_key, *_mapInfo), *_opt->quickReleaseGLObjects() ); 00155 _tile->setVerticalScale( *_opt->verticalScale() ); 00156 00157 //_tile->setRequiresNormals( true ); 00158 _tile->setDataVariance( osg::Object::DYNAMIC ); 00159 _tile->setTerrainMasks(_masks); 00160 00161 // copy over the source data. 00162 _tile->setCustomColorLayers( _repo->_colorLayers ); 00163 _tile->setElevationLayer( _repo->_elevLayer.getHFLayer() ); 00164 00165 osg::BoundingSphere bs = _tile->getBound(); 00166 00167 // a skirt hides cracks when transitioning between LODs: 00168 osg::HeightField* hf = _repo->_elevLayer.getHFLayer()->getHeightField(); 00169 hf->setSkirtHeight(bs.radius() * _opt->heightFieldSkirtRatio().get() ); 00170 } 00171 00172 TileKey _key; 00173 const MapInfo* _mapInfo; 00174 const OSGTerrainOptions* _opt; 00175 TileBuilder::SourceRepo* _repo; 00176 Tile* _tile; 00177 MaskLayerVector _masks; 00178 }; 00179 00180 //------------------------------------------------------------------------ 00181 00182 TileBuilder::TileBuilder(const Map* map, const OSGTerrainOptions& terrainOptions, TaskService* service) : 00183 _map( map ), 00184 _terrainOptions( terrainOptions ), 00185 _service( service ) 00186 { 00187 //nop 00188 } 00189 00190 TileBuilder::Job* 00191 TileBuilder::createJob( const TileKey& key, Threading::MultiEvent& semaphore ) 00192 { 00193 Job* job = new Job( key, _map ); 00194 00195 // create the image layer tasks: 00196 for( ImageLayerVector::const_iterator i = job->_mapf.imageLayers().begin(); i != job->_mapf.imageLayers().end(); ++i ) 00197 { 00198 ImageLayer* layer = i->get(); 00199 if ( layer->isKeyValid(key) ) 00200 { 00201 ParallelTask<BuildColorLayer>* j = new ParallelTask<BuildColorLayer>( &semaphore ); 00202 j->init( key, layer, job->_mapf.getMapInfo(), _terrainOptions, job->_repo ); 00203 j->setPriority( -(float)key.getLevelOfDetail() ); 00204 job->_tasks.push_back( j ); 00205 } 00206 } 00207 00208 // If we have elevation layers, start an elevation job as well. Otherwise just create an 00209 // empty one while we're waiting for the images to load. 00210 if ( job->_mapf.elevationLayers().size() > 0 ) 00211 { 00212 ParallelTask<BuildElevLayer>* ej = new ParallelTask<BuildElevLayer>( &semaphore ); 00213 ej->init( key, job->_mapf, _terrainOptions, job->_repo ); 00214 ej->setPriority( -(float)key.getLevelOfDetail() ); 00215 job->_tasks.push_back( ej ); 00216 } 00217 00218 return job; 00219 } 00220 00221 void 00222 TileBuilder::runJob( TileBuilder::Job* job ) 00223 { 00224 for( TaskRequestVector::iterator i = job->_tasks.begin(); i != job->_tasks.end(); ++i ) 00225 _service->add( i->get() ); 00226 } 00227 00228 void 00229 TileBuilder::finalizeJob(TileBuilder::Job* job, 00230 osg::ref_ptr<Tile>& out_tile, 00231 bool& out_hasRealData, 00232 bool& out_hasLodBlending) 00233 { 00234 SourceRepo& repo = job->_repo; 00235 00236 out_hasRealData = false; 00237 out_hasLodBlending = false; 00238 00239 // Bail out now if there's no data to be had. 00240 if ( repo._colorLayers.size() == 0 && !repo._elevLayer.getHFLayer() ) 00241 { 00242 return; 00243 } 00244 00245 const TileKey& key = job->_key; 00246 const MapInfo& mapInfo = job->_mapf.getMapInfo(); 00247 00248 // OK we are making a tile, so if there's no heightfield yet, make an empty one. 00249 if ( !repo._elevLayer.getHFLayer() ) 00250 { 00251 osg::HeightField* hf = key.getProfile()->getVerticalSRS()->createReferenceHeightField( key.getExtent(), 8, 8 ); 00252 osgTerrain::HeightFieldLayer* hfLayer = new osgTerrain::HeightFieldLayer( hf ); 00253 hfLayer->setLocator( GeoLocator::createForKey(key, mapInfo) ); 00254 repo._elevLayer = CustomElevLayer( hfLayer, true ); 00255 } 00256 00257 // Now, if there are any color layers that did not get built, create them with an empty 00258 // image so the shaders have something to draw. 00259 osg::ref_ptr<osg::Image> emptyImage; 00260 osgTerrain::Locator* locator = repo._elevLayer.getHFLayer()->getLocator(); 00261 00262 for( ImageLayerVector::const_iterator i = job->_mapf.imageLayers().begin(); i != job->_mapf.imageLayers().end(); ++i ) 00263 { 00264 if ( !i->get()->isKeyValid(key) ) 00265 { 00266 if ( !emptyImage.valid() ) 00267 emptyImage = ImageUtils::createEmptyImage(); 00268 00269 repo.add( CustomColorLayer( 00270 i->get(), emptyImage.get(), 00271 locator, 00272 key.getLevelOfDetail(), 00273 key, 00274 true ) ); 00275 } 00276 00277 if ( i->get()->getImageLayerOptions().lodBlending() == true ) 00278 out_hasLodBlending = true; 00279 } 00280 00281 // Ready to create the actual tile. 00282 AssembleTile assemble; 00283 assemble.init( key, mapInfo, _terrainOptions, repo ); 00284 assemble.execute(); 00285 00286 // Check the results and see if we have any real data. 00287 for( ColorLayersByUID::const_iterator i = repo._colorLayers.begin(); i != repo._colorLayers.end(); ++i ) 00288 { 00289 if ( !i->second.isFallbackData() ) 00290 { 00291 out_hasRealData = true; 00292 break; 00293 } 00294 } 00295 if ( !out_hasRealData && !repo._elevLayer.isFallbackData() ) 00296 { 00297 out_hasRealData = true; 00298 } 00299 00300 out_tile = assemble._tile; 00301 } 00302 00303 void 00304 TileBuilder::createTile(const TileKey& key, 00305 bool parallelize, 00306 osg::ref_ptr<Tile>& out_tile, 00307 bool& out_hasRealData, 00308 bool& out_hasLodBlendedLayers ) 00309 { 00310 MapFrame mapf( _map, Map::MASKED_TERRAIN_LAYERS ); 00311 00312 SourceRepo repo; 00313 00314 // init this to false, then search for real data. "Real data" is data corresponding 00315 // directly to the key, as opposed to fallback data, which is derived from a lower 00316 // LOD key. 00317 out_hasRealData = false; 00318 out_hasLodBlendedLayers = false; 00319 00320 const MapInfo& mapInfo = mapf.getMapInfo(); 00321 00322 // If we need more than one layer, fetch them in parallel. 00323 // TODO: change the test based on isKeyValid total. 00324 if ( parallelize && (mapf.imageLayers().size() + mapf.elevationLayers().size() > 1) ) 00325 { 00326 // count the valid layers. 00327 int jobCount = 0; 00328 00329 for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i ) 00330 { 00331 if ( i->get()->isKeyValid( key ) ) 00332 ++jobCount; 00333 00334 if ( i->get()->getImageLayerOptions().lodBlending() == true ) 00335 out_hasLodBlendedLayers = true; 00336 } 00337 00338 if ( mapf.elevationLayers().size() > 0 ) 00339 ++jobCount; 00340 00341 // A thread job monitoring event: 00342 Threading::MultiEvent semaphore( jobCount ); 00343 00344 // Start the image layer jobs: 00345 for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i ) 00346 { 00347 ImageLayer* layer = i->get(); 00348 if ( layer->isKeyValid(key) ) 00349 { 00350 ParallelTask<BuildColorLayer>* j = new ParallelTask<BuildColorLayer>( &semaphore ); 00351 j->init( key, layer, mapInfo, _terrainOptions, repo ); 00352 j->setPriority( -(float)key.getLevelOfDetail() ); 00353 _service->add( j ); 00354 } 00355 } 00356 00357 // If we have elevation layers, start an elevation job as well. Otherwise just create an 00358 // empty one while we're waiting for the images to load. 00359 if ( mapf.elevationLayers().size() > 0 ) 00360 { 00361 ParallelTask<BuildElevLayer>* ej = new ParallelTask<BuildElevLayer>( &semaphore ); 00362 ej->init( key, mapf, _terrainOptions, repo ); 00363 ej->setPriority( -(float)key.getLevelOfDetail() ); 00364 _service->add( ej ); 00365 } 00366 else 00367 { 00368 BuildElevLayer build; 00369 build.init( key, mapf, _terrainOptions, repo ); 00370 build.execute(); 00371 } 00372 00373 // Wait for all the jobs to finish. 00374 semaphore.wait(); 00375 } 00376 00377 // Fetch the image data serially: 00378 else 00379 { 00380 // gather all the image layers serially. 00381 for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i ) 00382 { 00383 ImageLayer* layer = i->get(); 00384 if ( layer->isKeyValid(key) ) 00385 { 00386 BuildColorLayer build; 00387 build.init( key, layer, mapInfo, _terrainOptions, repo ); 00388 build.execute(); 00389 } 00390 00391 if ( layer->getImageLayerOptions().lodBlending() == true ) 00392 out_hasLodBlendedLayers = true; 00393 } 00394 00395 // make an elevation layer. 00396 BuildElevLayer build; 00397 build.init( key, mapf, _terrainOptions, repo ); 00398 build.execute(); 00399 } 00400 00401 // Bail out now if there's no data to be had. 00402 if ( repo._colorLayers.size() == 0 && !repo._elevLayer.getHFLayer() ) 00403 { 00404 return; 00405 } 00406 00407 // OK we are making a tile, so if there's no heightfield yet, make an empty one. 00408 if ( !repo._elevLayer.getHFLayer() ) 00409 { 00410 osg::HeightField* hf = key.getProfile()->getVerticalSRS()->createReferenceHeightField( key.getExtent(), 8, 8 ); 00411 osgTerrain::HeightFieldLayer* hfLayer = new osgTerrain::HeightFieldLayer( hf ); 00412 hfLayer->setLocator( GeoLocator::createForKey(key, mapInfo) ); 00413 repo._elevLayer = CustomElevLayer( hfLayer, true ); 00414 } 00415 00416 // Now, if there are any color layers that did not get built, create them with an empty 00417 // image so the shaders have something to draw. 00418 osg::ref_ptr<osg::Image> emptyImage; 00419 osgTerrain::Locator* locator = repo._elevLayer.getHFLayer()->getLocator(); 00420 00421 for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i ) 00422 { 00423 if ( !i->get()->isKeyValid(key) ) 00424 { 00425 if ( !emptyImage.valid() ) 00426 emptyImage = ImageUtils::createEmptyImage(); 00427 00428 repo.add( CustomColorLayer( 00429 i->get(), emptyImage.get(), 00430 locator, 00431 key.getLevelOfDetail(), 00432 key, 00433 true ) ); 00434 } 00435 } 00436 00437 //osg::Vec3dArray* maskBounds = 0L; 00438 //osgEarth::MaskLayer* mask = mapf.getTerrainMaskLayer(); 00439 //if (mask) 00440 // maskBounds = mask->getOrCreateBoundary(); 00441 00442 // Ready to create the actual tile. 00443 AssembleTile assemble; 00444 assemble.init( key, mapInfo, _terrainOptions, repo, mapf.terrainMaskLayers() ); 00445 assemble.execute(); 00446 00447 if (!out_hasRealData) 00448 { 00449 // Check the results and see if we have any real data. 00450 for( ColorLayersByUID::const_iterator i = repo._colorLayers.begin(); i != repo._colorLayers.end(); ++i ) 00451 { 00452 if ( !i->second.isFallbackData() ) 00453 { 00454 out_hasRealData = true; 00455 break; 00456 } 00457 } 00458 } 00459 00460 if ( !out_hasRealData && !repo._elevLayer.isFallbackData() ) 00461 { 00462 out_hasRealData = true; 00463 } 00464 00465 out_tile = assemble._tile; 00466 }