osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory.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 "OSGTileFactory"
00020 #include "Terrain"
00021 #include "StreamingTerrain"
00022 #include "FileLocationCallback"
00023 #include "TransparentLayer"
00024 
00025 #include <osgEarth/Map>
00026 #include <osgEarth/Caching>
00027 #include <osgEarth/HeightFieldUtils>
00028 #include <osgEarth/Registry>
00029 #include <osgEarth/ImageUtils>
00030 #include <osgEarth/TileSource>
00031 
00032 #include <osg/Image>
00033 #include <osg/Notify>
00034 #include <osg/PagedLOD>
00035 #include <osg/ClusterCullingCallback>
00036 #include <osg/CoordinateSystemNode>
00037 #include <osgFX/MultiTextureControl>
00038 #include <osgDB/ReadFile>
00039 #include <osgDB/WriteFile>
00040 #include <osgTerrain/Locator>
00041 #include <osgTerrain/GeometryTechnique>
00042 #include <OpenThreads/ReentrantMutex>
00043 #include <sstream>
00044 #include <stdlib.h>
00045 
00046 using namespace OpenThreads;
00047 using namespace osgEarth;
00048 using namespace osgEarth::Drivers;
00049 
00050 #define LC "[OSGTileFactory] "
00051 
00052 /*****************************************************************************/
00053 
00054 namespace
00055 {
00056     //TODO: get rid of this, and move it to the CustomTerrain CULL traversal....?????
00057     struct PopulateStreamingTileDataCallback : public osg::NodeCallback
00058     {
00059         PopulateStreamingTileDataCallback( const MapFrame& mapf ) : _mapf(mapf) { }
00060 
00061         void operator()( osg::Node* node, osg::NodeVisitor* nv )
00062         {
00063             if ( nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR )
00064             {
00065                 if ( node->asGroup()->getNumChildren() > 0 )
00066                 {
00067                     StreamingTile* tile = static_cast<StreamingTile*>( node->asGroup()->getChild(0) );
00068                     tile->servicePendingImageRequests( _mapf, nv->getFrameStamp()->getFrameNumber() );
00069                 }
00070             }
00071             traverse( node, nv );
00072         }
00073 
00074         const MapFrame& _mapf;
00075     };
00076 }
00077 
00078 /*****************************************************************************/
00079 
00080 OSGTileFactory::OSGTileFactory(unsigned int engineId,
00081                                const MapFrame& cull_thread_mapf,
00082                                const OSGTerrainOptions& props ) :
00083 osg::Referenced( true ),
00084 _engineId( engineId ),
00085 _cull_thread_mapf( cull_thread_mapf ),
00086 _terrainOptions( props )
00087 {
00088     LoadingPolicy::Mode mode = _terrainOptions.loadingPolicy()->mode().value();
00089 }
00090 
00091 const OSGTerrainOptions& 
00092 OSGTileFactory::getTerrainOptions() const
00093 {
00094     return _terrainOptions;
00095 }
00096 
00097 std::string
00098 OSGTileFactory::createURI( unsigned int id, const TileKey& key )
00099 {
00100     std::stringstream ss;
00101     ss << key.str() << "." <<id<<".osgearth_osgterrain_tile";
00102     std::string ssStr;
00103     ssStr = ss.str();
00104     return ssStr;
00105 }
00106 
00107 // Make a MatrixTransform suitable for use with a Locator object based on the given extents.
00108 // Calling Locator::setTransformAsExtents doesn't work with OSG 2.6 due to the fact that the
00109 // _inverse member isn't updated properly.  Calling Locator::setTransform works correctly.
00110 osg::Matrixd
00111 OSGTileFactory::getTransformFromExtents(double minX, double minY, double maxX, double maxY) const
00112 {
00113     osg::Matrixd transform;
00114     transform.set(
00115         maxX-minX, 0.0,       0.0, 0.0,
00116         0.0,       maxY-minY, 0.0, 0.0,
00117         0.0,       0.0,       1.0, 0.0,
00118         minX,      minY,      0.0, 1.0); 
00119     return transform;
00120 }
00121 
00122 osg::Node*
00123 OSGTileFactory::createSubTiles( const MapFrame& mapf, Terrain* terrain, const TileKey& key, bool populateLayers )
00124 {
00125     TileKey k0 = key.createChildKey(0);
00126     TileKey k1 = key.createChildKey(1);
00127     TileKey k2 = key.createChildKey(2);
00128     TileKey k3 = key.createChildKey(3);
00129 
00130     bool hasValidData = false;
00131     bool validData;
00132 
00133     bool fallback = false;
00134     osg::ref_ptr<osg::Node> q0 = createTile( mapf, terrain, k0, populateLayers, true, fallback, validData);
00135     if (!hasValidData && validData) hasValidData = true;
00136 
00137     osg::ref_ptr<osg::Node> q1 = createTile( mapf, terrain, k1, populateLayers, true, fallback, validData );
00138     if (!hasValidData && validData) hasValidData = true;
00139 
00140     osg::ref_ptr<osg::Node> q2 = createTile( mapf, terrain, k2, populateLayers, true, fallback, validData );
00141     if (!hasValidData && validData) hasValidData = true;
00142 
00143     osg::ref_ptr<osg::Node> q3 = createTile( mapf, terrain, k3, populateLayers, true, fallback, validData );
00144     if (!hasValidData && validData) hasValidData = true;
00145 
00146     if (!hasValidData)
00147     {
00148         OE_DEBUG << LC << "Couldn't create any quadrants for " << key.str() << " time to stop subdividing!" << std::endl;
00149         return NULL;
00150     }
00151 
00152     osg::Group* tile_parent = new osg::Group();
00153 
00154     fallback = true;
00155     //Fallback on tiles if we couldn't create any
00156     if (!q0.valid())
00157     {
00158         q0 = createTile( mapf, terrain, k0, populateLayers, true, fallback, validData);
00159     }
00160 
00161     if (!q1.valid())
00162     {
00163         q1 = createTile( mapf, terrain, k1, populateLayers, true, fallback, validData);
00164     }
00165 
00166     if (!q2.valid())
00167     {
00168         q2 = createTile( mapf, terrain, k2, populateLayers, true, fallback, validData);
00169     }
00170 
00171     if (!q3.valid())
00172     {        
00173         q3 = createTile( mapf, terrain, k3, populateLayers, true, fallback, validData);
00174     }
00175 
00176     tile_parent->addChild( q0.get() );
00177     tile_parent->addChild( q1.get() );
00178     tile_parent->addChild( q2.get() );
00179     tile_parent->addChild( q3.get() );
00180     return tile_parent;
00181 }
00182 
00183 bool
00184 OSGTileFactory::createValidGeoImage(ImageLayer* layer,
00185                                     const TileKey& key,
00186                                     GeoImage& out_image,
00187                                     TileKey&  out_actualTileKey,
00188                                     ProgressCallback* progress)
00189 {
00190     //TODO:  Redo this to just grab images from the parent TerrainTiles
00191     //Try to create the image with the given key
00192     out_actualTileKey = key;
00193 
00194     while (out_actualTileKey.valid())
00195     {
00196         if ( layer->isKeyValid(out_actualTileKey) )
00197         {
00198             out_image = layer->createImage( out_actualTileKey, progress );
00199             if ( out_image.valid() )
00200             {
00201                 return true;
00202             }
00203         }
00204         out_actualTileKey = out_actualTileKey.createParentKey();
00205     }
00206     return false;
00207 }
00208 
00209 bool
00210 OSGTileFactory::hasMoreLevels( Map* map, const TileKey& key )
00211 {
00212     //Threading::ScopedReadLock lock( map->getMapDataMutex() );
00213 
00214     bool more_levels = false;
00215 
00216     ImageLayerVector imageLayers;
00217     map->getImageLayers( imageLayers );
00218 
00219     for ( ImageLayerVector::const_iterator i = imageLayers.begin(); i != imageLayers.end(); i++ )
00220     {
00221         const ImageLayerOptions& opt = i->get()->getImageLayerOptions();
00222 
00223         if ( !opt.maxLevel().isSet() || key.getLevelOfDetail() < (unsigned int)*opt.maxLevel() )
00224         {
00225             more_levels = true;
00226             break;
00227         }
00228     }
00229     if ( !more_levels )
00230     {
00231         ElevationLayerVector elevLayers;
00232         map->getElevationLayers( elevLayers );
00233 
00234         for( ElevationLayerVector::const_iterator j = elevLayers.begin(); j != elevLayers.end(); j++ )
00235         {
00236             const ElevationLayerOptions& opt = j->get()->getElevationLayerOptions();
00237 
00238             if ( !opt.maxLevel().isSet() || key.getLevelOfDetail() < (unsigned int)*opt.maxLevel() )
00239             //if ( !j->get()->maxLevel().isSet() || key.getLevelOfDetail() < j->get()->maxLevel().get() )
00240             {
00241                 more_levels = true;
00242                 break;
00243             }
00244         }
00245     }
00246 
00247     return more_levels;
00248 }
00249 
00250 osg::HeightField*
00251 OSGTileFactory::createEmptyHeightField( const TileKey& key, int numCols, int numRows )
00252 {
00253     osg::HeightField* hf = key.getProfile()->getVerticalSRS()->createReferenceHeightField(
00254         key.getExtent(), numCols, numRows );
00255 
00256     return hf;
00257 }
00258 
00259 void
00260 OSGTileFactory::addPlaceholderImageLayers(Tile* tile, Tile* ancestorTile )
00261 {
00262     if ( !ancestorTile )
00263     {
00264         //OE_NOTICE << "No ancestorTile for key " << key.str() << std::endl;
00265         return;
00266     }        
00267 
00268     // Now if we have a valid ancestor tile, go through and make a temporary tile consisting only of
00269     // layers that exist in the new map layer image list as well.
00270     //int layer = 0;
00271     ColorLayersByUID colorLayers;
00272     ancestorTile->getCustomColorLayers( colorLayers );
00273     tile->setCustomColorLayers( colorLayers );
00274 }
00275 
00276 
00277 void
00278 OSGTileFactory::addPlaceholderHeightfieldLayer(StreamingTile* tile,
00279                                                StreamingTile* ancestorTile,
00280                                                GeoLocator*    defaultLocator,
00281                                                const TileKey& key,
00282                                                const TileKey& ancestorKey)
00283 {
00284     osgTerrain::HeightFieldLayer* newHFLayer = 0L;
00285 
00286     if ( ancestorTile && ancestorKey.valid() )
00287     {
00288         osg::ref_ptr<osgTerrain::HeightFieldLayer> ancestorLayer;
00289         {
00290             Threading::ScopedReadLock sharedLock( ancestorTile->getTileLayersMutex() );
00291             ancestorLayer = dynamic_cast<osgTerrain::HeightFieldLayer*>(ancestorTile->getElevationLayer());
00292         }
00293 
00294         if ( ancestorLayer.valid() )
00295         {
00296             osg::ref_ptr<osg::HeightField> ancestorHF = ancestorLayer->getHeightField();
00297             if ( ancestorHF.valid() )
00298             {
00299                 osg::HeightField* newHF = HeightFieldUtils::createSubSample(
00300                     ancestorHF.get(),
00301                     ancestorKey.getExtent(),
00302                     key.getExtent());
00303 
00304                 newHFLayer = new osgTerrain::HeightFieldLayer( newHF );
00305                 newHFLayer->setLocator( defaultLocator );
00306 
00307                 // lock to set the elevation layerdata:
00308                 {
00309                     Threading::ScopedWriteLock exclusiveLock( tile->getTileLayersMutex() );
00310                     tile->setElevationLayer( newHFLayer );                
00311                     tile->setElevationLOD( ancestorTile->getElevationLOD() );
00312                 }
00313             }
00314         }
00315     }
00316 
00317     // lock the tile to write the elevation data.
00318     {
00319         Threading::ScopedWriteLock exclusiveLock( tile->getTileLayersMutex() );
00320 
00321         if ( !newHFLayer )
00322         {
00323             newHFLayer = new osgTerrain::HeightFieldLayer();
00324             newHFLayer->setHeightField( createEmptyHeightField( key, 8, 8 ) );
00325             newHFLayer->setLocator( defaultLocator );
00326             tile->setElevationLOD( -1 );
00327         }
00328 
00329         if ( newHFLayer )
00330         {
00331             tile->setElevationLayer( newHFLayer );
00332         }
00333     }
00334 }
00335 
00336 
00337 osgTerrain::HeightFieldLayer*
00338 OSGTileFactory::createPlaceholderHeightfieldLayer(osg::HeightField* ancestorHF,
00339                                                   const TileKey&    ancestorKey,
00340                                                   const TileKey&    key,
00341                                                   GeoLocator*       keyLocator )
00342 {
00343     osgTerrain::HeightFieldLayer* hfLayer = NULL;
00344 
00345     osg::HeightField* newHF = HeightFieldUtils::createSubSample(
00346         ancestorHF,
00347         ancestorKey.getExtent(),
00348         key.getExtent() );
00349 
00350     newHF->setSkirtHeight( ancestorHF->getSkirtHeight() / 2.0 );
00351 
00352     hfLayer = new osgTerrain::HeightFieldLayer( newHF );
00353     hfLayer->setLocator( keyLocator );
00354 
00355     return hfLayer;
00356 }
00357 
00358 osg::Node*
00359 OSGTileFactory::createTile(const MapFrame&  mapf, 
00360                            Terrain*         terrain, 
00361                            const TileKey&   key, 
00362                            bool             populateLayers, 
00363                            bool             wrapInPagedLOD, 
00364                            bool             fallback,
00365                            bool&            out_validData )
00366 {
00367     if ( populateLayers )
00368     {        
00369         return createPopulatedTile( mapf, terrain, key, wrapInPagedLOD, fallback, out_validData);
00370     }
00371     else
00372     {
00373         //Placeholders always contain valid data
00374         out_validData = true;
00375 
00376         return createPlaceholderTile(
00377             mapf, 
00378             static_cast<StreamingTerrain*>(terrain),
00379             key );
00380     }
00381 }
00382 
00383 
00384 
00385 osg::Node*
00386 OSGTileFactory::createPlaceholderTile(const MapFrame&   mapf,
00387                                       StreamingTerrain* terrain,
00388                                       const TileKey&    key )
00389 {
00390     // Start out by finding the nearest registered ancestor tile, since the placeholder is
00391     // going to be based on inherited data. Note- the ancestor may not be the immediate
00392     // parent, b/c the parent may or may not be in the scene graph.
00393     TileKey ancestorKey = key.createParentKey();
00394     osg::ref_ptr<StreamingTile> ancestorTile;
00395     while( !ancestorTile.valid() && ancestorKey.valid() )
00396     {
00397         terrain->getTile( ancestorKey.getTileId(), ancestorTile );
00398         if ( !ancestorTile.valid() )
00399             ancestorKey = ancestorKey.createParentKey();
00400     }
00401     if ( !ancestorTile.valid() )
00402     {
00403         OE_WARN << LC << "cannot find ancestor tile for (" << key.str() << ")" <<std::endl;
00404         return 0L;
00405     }
00406 
00407     OE_DEBUG << LC << "Creating placeholder for " << key.str() << std::endl;
00408 
00409     const MapInfo& mapInfo = mapf.getMapInfo();
00410 
00411     bool hasElevation = mapf.elevationLayers().size() > 0;
00412 
00413     // Build a "placeholder" tile.
00414     double xmin, ymin, xmax, ymax;
00415     key.getExtent().getBounds( xmin, ymin, xmax, ymax );
00416 
00417     // A locator will place the tile on the globe:
00418     osg::ref_ptr<GeoLocator> locator = GeoLocator::createForKey( key, mapInfo );
00419 
00420     // The empty tile:
00421     StreamingTile* tile = new StreamingTile( key, locator.get(), terrain->getQuickReleaseGLObjects() );
00422     tile->setTerrainTechnique( terrain->cloneTechnique() );
00423     tile->setVerticalScale( _terrainOptions.verticalScale().value() );
00424     tile->setDataVariance( osg::Object::DYNAMIC );
00425     //tile->setLocator( locator.get() );
00426 
00427     // Attach an updatecallback to normalize the edges of TerrainTiles.
00428 #if 0
00429     if ( hasElevation && _terrainOptions.normalizeEdges().get() )
00430     {
00431         tile->setUpdateCallback(new TerrainTileEdgeNormalizerUpdateCallback());
00432         tile->setDataVariance(osg::Object::DYNAMIC);
00433     }
00434 #endif
00435 
00436     // Generate placeholder imagery and elevation layers. These "inherit" data from an
00437     // ancestor tile.
00438     {
00439         //Threading::ScopedReadLock parentLock( ancestorTile->getTileLayersMutex() );
00440         addPlaceholderImageLayers     ( tile, ancestorTile.get() );
00441         addPlaceholderHeightfieldLayer( tile, ancestorTile.get(), locator.get(), key, ancestorKey );
00442     }
00443 
00444     // calculate the switching distances:
00445     osg::BoundingSphere bs = tile->getBound();
00446     double max_range = 1e10;
00447     double radius = bs.radius();
00448     double min_range = radius * _terrainOptions.minTileRangeFactor().get();
00449 
00450     // Set the skirt height of the heightfield
00451     osgTerrain::HeightFieldLayer* hfLayer = static_cast<osgTerrain::HeightFieldLayer*>(tile->getElevationLayer());
00452     if (!hfLayer)
00453     {
00454         OE_WARN << LC << "Warning: Couldn't get hfLayer for " << key.str() << std::endl;
00455     }
00456     hfLayer->getHeightField()->setSkirtHeight(radius * _terrainOptions.heightFieldSkirtRatio().get() );
00457 
00458     // In a Plate Carre tesselation, scale the heightfield elevations from meters to degrees
00459     if ( mapInfo.isPlateCarre() && hfLayer->getHeightField() )
00460         HeightFieldUtils::scaleHeightFieldToDegrees( hfLayer->getHeightField() );
00461 
00462     bool markTileLoaded = false;
00463 
00464     if ( _terrainOptions.loadingPolicy()->mode().get() != LoadingPolicy::MODE_STANDARD )
00465     {
00466         markTileLoaded = true;
00467         tile->setHasElevationHint( hasElevation );
00468     }
00469 
00470     // install a tile switcher:
00471     tile->attachToTerrain( terrain );
00472     //tile->setTerrain( terrain );
00473     //terrain->registerTile( tile );
00474 
00475     osg::Node* result = 0L;
00476 
00477     // create a PLOD so we can keep subdividing:
00478     osg::PagedLOD* plod = new osg::PagedLOD();
00479     plod->setCenter( bs.center() );
00480     plod->addChild( tile, min_range, max_range );
00481 
00482     if ( key.getLevelOfDetail() < (unsigned int)getTerrainOptions().maxLOD().get() )
00483     {
00484         plod->setFileName( 1, createURI( _engineId, key ) ); //map->getId(), key ) );
00485         plod->setRange( 1, 0.0, min_range );
00486     }
00487     else
00488     {
00489         plod->setRange( 0, 0, FLT_MAX );
00490     }
00491 
00492 #if 0 //USE_FILELOCATIONCALLBACK
00493     osgDB::Options* options = new osgDB::Options;
00494     options->setFileLocationCallback( new FileLocationCallback);
00495     plod->setDatabaseOptions( options );
00496 #endif
00497 
00498     result = plod;
00499 
00500     // Install a callback that will load the actual tile data via the pager.
00501     result->addCullCallback( new PopulateStreamingTileDataCallback( _cull_thread_mapf ) );
00502 
00503     // Install a cluster culler (FIXME for cube mode)
00504     //bool isCube = map->getMapOptions().coordSysType() == MapOptions::CSTYPE_GEOCENTRIC_CUBE;
00505     if ( mapInfo.isGeocentric() && !mapInfo.isCube() )
00506     {
00507         osg::ClusterCullingCallback* ccc = createClusterCullingCallback( tile, locator->getEllipsoidModel() );
00508         result->addCullCallback( ccc );
00509     }     
00510 
00511     return result;
00512 }
00513 
00514 namespace
00515 {
00516     struct GeoImageData
00517     {
00518         GeoImageData() : _layerUID(-1) , _imageTileKey(0,0,0,0L) { }
00519         GeoImage _image;
00520         UID      _layerUID;
00521         TileKey  _imageTileKey;
00522     };
00523 }
00524 
00525 
00526 osg::Node*
00527 OSGTileFactory::createPopulatedTile(const MapFrame&  mapf, 
00528                                     Terrain*         terrain, 
00529                                     const TileKey&   key, 
00530                                     bool             wrapInPagedLOD, 
00531                                     bool             fallback, 
00532                                     bool&            validData )
00533 {
00534     const MapInfo& mapInfo = mapf.getMapInfo();
00535     bool isPlateCarre = !mapInfo.isGeocentric() && mapInfo.isGeographicSRS();
00536 
00537     typedef std::vector<GeoImageData> GeoImageDataVector;
00538     GeoImageDataVector image_tiles;
00539 
00540     // Collect the image layers
00541     bool empty_map = mapf.imageLayers().size() == 0 && mapf.elevationLayers().size() == 0;
00542 
00543     // Create the images for the tile
00544     for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i )
00545     {
00546         ImageLayer* layer = i->get();
00547         GeoImageData imageData;
00548 
00549         // Only try to create images if the key is valid
00550         if ( layer->isKeyValid( key ) )
00551         {
00552             imageData._image = layer->createImage( key );
00553             imageData._layerUID = layer->getUID();
00554             imageData._imageTileKey = key;
00555         }
00556 
00557         // always push images, even it they are empty, so that the image_tiles vector is one-to-one
00558         // with the imageLayers() vector.
00559         image_tiles.push_back( imageData );
00560     }
00561 
00562     bool hasElevation = false;
00563 
00564     //Create the heightfield for the tile
00565     osg::ref_ptr<osg::HeightField> hf;
00566     if ( mapf.elevationLayers().size() > 0 )
00567     {
00568         mapf.getHeightField( key, false, hf, 0L, _terrainOptions.elevationInterpolation().value());     
00569     }
00570 
00571     //If we are on the first LOD and we couldn't get a heightfield tile, just create an empty one.  Otherwise you can run into the situation
00572     //where you could have an inset heightfield on one hemisphere and the whole other hemisphere won't show up.
00573     if ( mapInfo.isGeocentric() && key.getLevelOfDetail() <= 1 && !hf.valid())
00574     {
00575         hf = createEmptyHeightField( key );
00576     }
00577     hasElevation = hf.valid();
00578 
00579     //Determine if we've created any images
00580     unsigned int numValidImages = 0;
00581     for (unsigned int i = 0; i < image_tiles.size(); ++i)
00582     {
00583         if (image_tiles[i]._image.valid()) numValidImages++;
00584     }
00585 
00586 
00587     //If we couldn't create any imagery or heightfields, bail out
00588     if (!hf.valid() && (numValidImages == 0) && !empty_map)
00589     {
00590         OE_DEBUG << LC << "Could not create any imagery or heightfields for " << key.str() <<".  Not building tile" << std::endl;
00591         validData = false;
00592 
00593         //If we're not asked to fallback on previous LOD's and we have no data, return NULL
00594         if (!fallback)
00595         {
00596             return NULL;
00597         }
00598     }
00599     else
00600     {
00601         validData = true;
00602     }
00603 
00604     //Try to interpolate any missing image layers from parent tiles
00605     for (unsigned int i = 0; i < mapf.imageLayers().size(); i++ )
00606     {
00607         if (!image_tiles[i]._image.valid())
00608         {
00609             if (mapf.getImageLayerAt(i)->isKeyValid(key))
00610             {
00611                 //If the key was valid and we have no image, then something possibly went wrong with the image creation such as a server being busy.
00612                 createValidGeoImage(mapf.getImageLayerAt(i), key, image_tiles[i]._image, image_tiles[i]._imageTileKey);
00613             }
00614 
00615             //If we still couldn't create an image, either something is really wrong or the key wasn't valid, so just create a transparent placeholder image
00616             if (!image_tiles[i]._image.valid())
00617             {
00618                 //If the image is not valid, create an empty texture as a placeholder
00619                 image_tiles[i]._image = GeoImage(ImageUtils::createEmptyImage(), key.getExtent());
00620                 image_tiles[i]._imageTileKey = key;
00621             }
00622         }
00623     }
00624 
00625     //Fill in missing heightfield information from parent tiles
00626     if (!hf.valid())
00627     {
00628         //We have no heightfield sources, 
00629         if ( mapf.elevationLayers().size() == 0 )
00630         {
00631             hf = createEmptyHeightField( key );
00632         }
00633         else
00634         {
00635             //Try to get a heightfield again, but this time fallback on parent tiles
00636             if ( mapf.getHeightField( key, true, hf, 0L, _terrainOptions.elevationInterpolation().value() ) )
00637             {
00638                 hasElevation = true;
00639             }
00640             else
00641             {
00642                 //We couldn't get any heightfield, so just create an empty one.
00643                 hf = createEmptyHeightField( key );
00644             }
00645         }
00646     }
00647 
00648 
00649     // In a Plate Carre tesselation, scale the heightfield elevations from meters to degrees
00650     if ( isPlateCarre )
00651     {
00652         HeightFieldUtils::scaleHeightFieldToDegrees( hf.get() );
00653     }
00654 
00655     osg::ref_ptr<GeoLocator> locator = GeoLocator::createForKey( key, mapInfo );
00656     osgTerrain::HeightFieldLayer* hf_layer = new osgTerrain::HeightFieldLayer();
00657     hf_layer->setLocator( locator.get() );
00658     hf_layer->setHeightField( hf.get() );
00659 
00660     bool isStreaming = 
00661         _terrainOptions.loadingPolicy()->mode() == LoadingPolicy::MODE_SEQUENTIAL ||
00662         _terrainOptions.loadingPolicy()->mode() == LoadingPolicy::MODE_PREEMPTIVE;
00663 
00664     Tile* tile = terrain->createTile( key, locator.get() );
00665     tile->setTerrainTechnique( terrain->cloneTechnique() );
00666     tile->setVerticalScale( _terrainOptions.verticalScale().value() );
00667     //tile->setLocator( locator.get() );
00668     tile->setElevationLayer( hf_layer );
00669     //tile->setRequiresNormals( true );
00670     tile->setDataVariance(osg::Object::DYNAMIC);
00671 
00672 #if 0
00673     //Attach an updatecallback to normalize the edges of TerrainTiles.
00674     if (hasElevation && _terrainOptions.normalizeEdges().get() )
00675     {
00676         tile->setUpdateCallback(new TerrainTileEdgeNormalizerUpdateCallback());
00677         tile->setDataVariance(osg::Object::DYNAMIC);
00678     }
00679 #endif
00680 
00681     //Assign the terrain system to the TerrainTile.
00682     //It is very important the terrain system is set while the MapConfig's sourceMutex is locked.
00683     //This registers the terrain tile so that adding/removing layers are always in sync.  If you don't do this
00684     //you can end up with a situation where the database pager is waiting to merge a tile, then a layer is added, then
00685     //the tile is finally merged and is out of sync.
00686 
00687     double min_units_per_pixel = DBL_MAX;
00688 
00689 #if 0
00690     // create contour layer:
00691     if (map->getContourTransferFunction() != NULL)
00692     {
00693         osgTerrain::ContourLayer* contourLayer(new osgTerrain::ContourLayer(map->getContourTransferFunction()));
00694 
00695         contourLayer->setMagFilter(_terrainOptions.getContourMagFilter().value());
00696         contourLayer->setMinFilter(_terrainOptions.getContourMinFilter().value());
00697         tile->setCustomColorLayer(layer,contourLayer); //TODO: need layerUID, not layer index here -GW
00698         ++layer;
00699     }
00700 #endif
00701 
00702     for (unsigned int i = 0; i < image_tiles.size(); ++i)
00703     {
00704         if (image_tiles[i]._image.valid())
00705         {
00706             const GeoImage& geo_image = image_tiles[i]._image;
00707 
00708             double img_xmin, img_ymin, img_xmax, img_ymax;
00709             geo_image.getExtent().getBounds( img_xmin, img_ymin, img_xmax, img_ymax );
00710 
00711             //Specify a new locator for the color with the coordinates of the TileKey that was actually used to create the image
00712             osg::ref_ptr<GeoLocator> img_locator = key.getProfile()->getSRS()->createLocator( 
00713                 img_xmin, img_ymin, img_xmax, img_ymax,
00714                 isPlateCarre );
00715 
00716             if ( mapInfo.isGeocentric() )
00717                 img_locator->setCoordinateSystemType( osgTerrain::Locator::GEOCENTRIC );
00718 
00719             tile->setCustomColorLayer( CustomColorLayer(
00720                 mapf.getImageLayerAt(i),
00721                 geo_image.getImage(),
00722                 img_locator.get(),
00723                 key.getLevelOfDetail(),
00724                 key) );
00725 
00726             double upp = geo_image.getUnitsPerPixel();
00727 
00728             // Scale the units per pixel to degrees if the image is mercator (and the key is geo)
00729             if ( geo_image.getSRS()->isMercator() && key.getExtent().getSRS()->isGeographic() )
00730                 upp *= 1.0f/111319.0f;
00731 
00732             min_units_per_pixel = osg::minimum(upp, min_units_per_pixel);
00733         }
00734     }
00735 
00736     osg::BoundingSphere bs = tile->getBound();
00737     double max_range = 1e10;
00738     double radius = bs.radius();
00739 
00740 #if 1
00741     double min_range = radius * _terrainOptions.minTileRangeFactor().get();
00742     //osg::LOD::RangeMode mode = osg::LOD::DISTANCE_FROM_EYE_POINT;
00743 #else
00744     double width = key.getExtent().width();     
00745     if (min_units_per_pixel == DBL_MAX) min_units_per_pixel = width/256.0;
00746     double min_range = (width / min_units_per_pixel) * _terrainOptions.getMinTileRangeFactor(); 
00747     //osg::LOD::RangeMode mode = osg::LOD::PIXEL_SIZE_ON_SCREEN;
00748 #endif
00749 
00750 
00751     // a skirt hides cracks when transitioning between LODs:
00752     hf->setSkirtHeight(radius * _terrainOptions.heightFieldSkirtRatio().get() );
00753 
00754     // for now, cluster culling does not work for CUBE rendering
00755     //bool isCube = mapInfo.isCube(); //map->getMapOptions().coordSysType() == MapOptions::CSTYPE_GEOCENTRIC_CUBE;
00756     if ( mapInfo.isGeocentric() && !mapInfo.isCube() )
00757     {
00758         //TODO:  Work on cluster culling computation for cube faces
00759         osg::ClusterCullingCallback* ccc = createClusterCullingCallback(tile, locator->getEllipsoidModel() );
00760         tile->setCullCallback( ccc );
00761     }
00762 
00763     // Wait until now, when the tile is fully baked, to assign the terrain to the tile.
00764     // Placeholder tiles might try to locate this tile as an ancestor, and access its layers
00765     // and locators...so they must be intact before making this tile available via setTerrain.
00766     //
00767     // If there's already a placeholder tile registered, this will be ignored. If there isn't,
00768     // this will register the new tile.
00769     tile->attachToTerrain( terrain );
00770     //tile->setTerrain( terrain );
00771     //terrain->registerTile( tile );
00772 
00773     if ( isStreaming && key.getLevelOfDetail() > 0 )
00774     {
00775         static_cast<StreamingTile*>(tile)->setHasElevationHint( hasElevation );
00776     }
00777 
00778     osg::Node* result = 0L;
00779 
00780     if (wrapInPagedLOD)
00781     {
00782         // create a PLOD so we can keep subdividing:
00783         osg::PagedLOD* plod = new osg::PagedLOD();
00784         plod->setCenter( bs.center() );
00785         plod->addChild( tile, min_range, max_range );
00786 
00787         std::string filename = createURI( _engineId, key ); //map->getId(), key );
00788 
00789         //Only add the next tile if it hasn't been blacklisted
00790         bool isBlacklisted = osgEarth::Registry::instance()->isBlacklisted( filename );
00791         if (!isBlacklisted && key.getLevelOfDetail() < (unsigned int)getTerrainOptions().maxLOD().value() && validData )
00792         {
00793             plod->setFileName( 1, filename  );
00794             plod->setRange( 1, 0.0, min_range );
00795         }
00796         else
00797         {
00798             plod->setRange( 0, 0, FLT_MAX );
00799         }
00800 
00801 #if USE_FILELOCATIONCALLBACK
00802         osgDB::Options* options = new osgDB::Options;
00803         options->setFileLocationCallback( new FileLocationCallback() );
00804         plod->setDatabaseOptions( options );
00805 #endif
00806         result = plod;
00807 
00808         if ( isStreaming )
00809             result->addCullCallback( new PopulateStreamingTileDataCallback( _cull_thread_mapf ) );
00810     }
00811     else
00812     {
00813         result = tile;
00814     }
00815 
00816     return result;
00817 }
00818 
00819 CustomColorLayerRef*
00820 OSGTileFactory::createImageLayer(const MapInfo&    mapInfo,
00821                                  ImageLayer*       layer,
00822                                  const TileKey&    key,
00823                                  ProgressCallback* progress)
00824 {
00825     if ( !layer )
00826         return 0L;
00827 
00828     GeoImage geoImage;
00829 
00830     //If the key is valid, try to get the image from the MapLayer
00831     bool keyValid = layer->isKeyValid( key );
00832     if ( keyValid )
00833     {
00834         geoImage = layer->createImage(key, progress);
00835     }
00836     else
00837     {
00838         //If the key is not valid, simply make a transparent tile
00839         geoImage = GeoImage(ImageUtils::createEmptyImage(), key.getExtent());
00840     }
00841 
00842     if (geoImage.valid())
00843     {
00844         osg::ref_ptr<GeoLocator> imgLocator = GeoLocator::createForKey( key, mapInfo );
00845 
00846         if ( mapInfo.isGeocentric() )
00847             imgLocator->setCoordinateSystemType( osgTerrain::Locator::GEOCENTRIC );
00848 
00849         return new CustomColorLayerRef( CustomColorLayer(
00850             layer,
00851             geoImage.getImage(),
00852             imgLocator.get(),
00853             key.getLevelOfDetail(),
00854             key) );
00855     }
00856 
00857     return NULL;
00858 }
00859 
00860 osgTerrain::HeightFieldLayer* 
00861 OSGTileFactory::createHeightFieldLayer( const MapFrame& mapf, const TileKey& key, bool exactOnly )
00862 {
00863     const MapInfo& mapInfo = mapf.getMapInfo();
00864     bool isPlateCarre = !mapInfo.isGeocentric() && mapInfo.isGeographicSRS();
00865 
00866     // try to create a heightfield at native res:
00867     osg::ref_ptr<osg::HeightField> hf;
00868     if ( !mapf.getHeightField( key, !exactOnly, hf, 0L, _terrainOptions.elevationInterpolation().value() ) )
00869     {
00870         if ( exactOnly )
00871             return NULL;
00872         else
00873             hf = createEmptyHeightField( key );
00874     }
00875 
00876     // In a Plate Carre tesselation, scale the heightfield elevations from meters to degrees
00877     if ( isPlateCarre )
00878     {
00879         HeightFieldUtils::scaleHeightFieldToDegrees( hf.get() );
00880     }
00881 
00882     osgTerrain::HeightFieldLayer* hfLayer = new osgTerrain::HeightFieldLayer( hf.get() );
00883 
00884     GeoLocator* locator = GeoLocator::createForKey( key, mapInfo );
00885     hfLayer->setLocator( locator );
00886 
00887     return hfLayer;
00888 }
00889 
00890 osg::ClusterCullingCallback*
00891 OSGTileFactory::createClusterCullingCallback(Tile* tile, osg::EllipsoidModel* et)
00892 {
00893     //This code is a very slightly modified version of the DestinationTile::createClusterCullingCallback in VirtualPlanetBuilder.
00894     osg::HeightField* grid = ((osgTerrain::HeightFieldLayer*)tile->getElevationLayer())->getHeightField();
00895     if (!grid) return 0;
00896 
00897     float verticalScale = 1.0f;
00898     Tile* customTile = dynamic_cast<Tile*>(tile);
00899     if (customTile)
00900     {
00901         verticalScale = customTile->getVerticalScale();
00902     }
00903 
00904     double globe_radius = et ? et->getRadiusPolar() : 1.0;
00905     unsigned int numColumns = grid->getNumColumns();
00906     unsigned int numRows = grid->getNumRows();
00907 
00908     double midLong = grid->getOrigin().x()+grid->getXInterval()*((double)(numColumns-1))*0.5;
00909     double midLat = grid->getOrigin().y()+grid->getYInterval()*((double)(numRows-1))*0.5;
00910     double midZ = grid->getOrigin().z();
00911 
00912     double midX,midY;
00913     et->convertLatLongHeightToXYZ(osg::DegreesToRadians(midLat),osg::DegreesToRadians(midLong),midZ, midX,midY,midZ);
00914 
00915     osg::Vec3 center_position(midX,midY,midZ);
00916 
00917     osg::Vec3 center_normal(midX,midY,midZ);
00918     center_normal.normalize();
00919 
00920     osg::Vec3 transformed_center_normal = center_normal;
00921 
00922     unsigned int r,c;
00923 
00924     // populate the vertex/normal/texcoord arrays from the grid.
00925     double orig_X = grid->getOrigin().x();
00926     double delta_X = grid->getXInterval();
00927     double orig_Y = grid->getOrigin().y();
00928     double delta_Y = grid->getYInterval();
00929     double orig_Z = grid->getOrigin().z();
00930 
00931 
00932     float min_dot_product = 1.0f;
00933     float max_cluster_culling_height = 0.0f;
00934     float max_cluster_culling_radius = 0.0f;
00935 
00936     for(r=0;r<numRows;++r)
00937     {
00938         for(c=0;c<numColumns;++c)
00939         {
00940             double X = orig_X + delta_X*(double)c;
00941             double Y = orig_Y + delta_Y*(double)r;
00942             double Z = orig_Z + grid->getHeight(c,r) * verticalScale;
00943             double height = Z;
00944 
00945             et->convertLatLongHeightToXYZ(
00946                 osg::DegreesToRadians(Y), osg::DegreesToRadians(X), Z,
00947                 X, Y, Z);
00948 
00949             osg::Vec3d v(X,Y,Z);
00950             osg::Vec3 dv = v - center_position;
00951             double d = sqrt(dv.x()*dv.x() + dv.y()*dv.y() + dv.z()*dv.z());
00952             double theta = acos( globe_radius/ (globe_radius + fabs(height)) );
00953             double phi = 2.0 * asin (d*0.5/globe_radius); // d/globe_radius;
00954             double beta = theta+phi;
00955             double cutoff = osg::PI_2 - 0.1;
00956 
00957             //log(osg::INFO,"theta="<<theta<<"\tphi="<<phi<<" beta "<<beta);
00958             if (phi<cutoff && beta<cutoff)
00959             {
00960                 float local_dot_product = -sin(theta + phi);
00961                 float local_m = globe_radius*( 1.0/ cos(theta+phi) - 1.0);
00962                 float local_radius = static_cast<float>(globe_radius * tan(beta)); // beta*globe_radius;
00963                 min_dot_product = osg::minimum(min_dot_product, local_dot_product);
00964                 max_cluster_culling_height = osg::maximum(max_cluster_culling_height,local_m);      
00965                 max_cluster_culling_radius = osg::maximum(max_cluster_culling_radius,local_radius);
00966             }
00967             else
00968             {
00969                 //log(osg::INFO,"Turning off cluster culling for wrap around tile.");
00970                 return 0;
00971             }
00972         }
00973     }    
00974 
00975     osg::ClusterCullingCallback* ccc = new osg::ClusterCullingCallback;
00976 
00977     ccc->set(center_position + transformed_center_normal*max_cluster_culling_height ,
00978         transformed_center_normal, 
00979         min_dot_product,
00980         max_cluster_culling_radius);
00981 
00982     return ccc;
00983 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines