osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarthDrivers/engine_osgterrain/TileBuilder.cpp

Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines