osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode.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 "OSGTerrainEngineNode"
00020 #include "MultiPassTerrainTechnique"
00021 #include "ParallelKeyNodeFactory"
00022 #include "SinglePassTerrainTechnique"
00023 #include "Terrain"
00024 #include "StreamingTerrain"
00025 #include "TileBuilder"
00026 #include "TransparentLayer"
00027 
00028 #include <osgEarth/ImageUtils>
00029 #include <osgEarth/Registry>
00030 #include <osgEarth/ShaderComposition>
00031 #include <osg/TexEnv>
00032 #include <osg/TexEnvCombine>
00033 #include <osg/PagedLOD>
00034 #include <osg/Timer>
00035 
00036 #define LC "[OSGTerrainEngine] "
00037 
00038 using namespace osgEarth;
00039 
00040 //------------------------------------------------------------------------
00041 
00042 // adapter that lets OSGTerrainEngineNode listen to Map events
00043 struct OSGTerrainEngineNodeMapCallbackProxy : public MapCallback
00044 {
00045     OSGTerrainEngineNodeMapCallbackProxy(OSGTerrainEngineNode* node) : _node(node) { }
00046     osg::observer_ptr<OSGTerrainEngineNode> _node;
00047 
00048     void onMapInfoEstablished( const MapInfo& mapInfo ) {
00049         _node->onMapInfoEstablished( mapInfo );
00050     }
00051 
00052     void onMapModelChanged( const MapModelChange& change ) {
00053         _node->onMapModelChanged( change );
00054     }
00055 };
00056 
00057 //---------------------------------------------------------------------------
00058 
00059 //static
00060 //static OpenThreads::ReentrantMutex s_engineNodeCacheMutex;
00061 static Threading::ReadWriteMutex s_engineNodeCacheMutex;
00062 //Caches the MapNodes that have been created
00063 typedef std::map<UID, osg::observer_ptr<OSGTerrainEngineNode> > EngineNodeCache;
00064 
00065 static
00066 EngineNodeCache& getEngineNodeCache()
00067 {
00068     static EngineNodeCache s_cache;
00069     return s_cache;
00070 }
00071 
00072 void
00073 OSGTerrainEngineNode::registerEngine(OSGTerrainEngineNode* engineNode)
00074 {
00075     Threading::ScopedWriteLock exclusiveLock( s_engineNodeCacheMutex );
00076     getEngineNodeCache()[engineNode->_uid] = engineNode;
00077     OE_DEBUG << LC << "Registered engine " << engineNode->_uid << std::endl;
00078 }
00079 
00080 void
00081 OSGTerrainEngineNode::unregisterEngine( UID uid )
00082 {
00083     Threading::ScopedWriteLock exclusiveLock( s_engineNodeCacheMutex );
00084     EngineNodeCache::iterator k = getEngineNodeCache().find( uid );
00085     if (k != getEngineNodeCache().end())
00086     {
00087         getEngineNodeCache().erase(k);
00088         OE_DEBUG << LC << "Unregistered engine " << uid << std::endl;
00089     }
00090 }
00091 
00092 // since this method is called in a database pager thread, we use a ref_ptr output
00093 // parameter to avoid the engine node being destructed between the time we 
00094 // return it and the time it's accessed; this could happen if the user removed the
00095 // MapNode from the scene during paging.
00096 void
00097 OSGTerrainEngineNode::getEngineByUID( UID uid, osg::ref_ptr<OSGTerrainEngineNode>& output )
00098 {
00099     Threading::ScopedReadLock sharedLock( s_engineNodeCacheMutex );
00100     EngineNodeCache::const_iterator k = getEngineNodeCache().find( uid );
00101     if (k != getEngineNodeCache().end())
00102         output = k->second.get();
00103 }
00104 
00105 UID
00106 OSGTerrainEngineNode::getUID() const
00107 {
00108     return _uid;
00109 }
00110 
00111 //------------------------------------------------------------------------
00112 
00113 OSGTerrainEngineNode::OSGTerrainEngineNode() :
00114 TerrainEngineNode(),
00115 _terrain( 0L ),
00116 _update_mapf( 0L ),
00117 _cull_mapf( 0L ),
00118 _tileCount( 0 ),
00119 _tileCreationTime( 0.0 )
00120 {
00121     _uid = Registry::instance()->createUID();
00122     _taskServiceMgr = Registry::instance()->getTaskServiceManager();
00123 }
00124 
00125 OSGTerrainEngineNode::OSGTerrainEngineNode( const OSGTerrainEngineNode& rhs, const osg::CopyOp& op ) :
00126 TerrainEngineNode( rhs, op )
00127 {
00128     //nop - this copy ctor will never get called since this is a plugin instance.
00129     OE_WARN << LC << "ILLEGAL STATE in OSGTerrainEngineNode Copy CTOR" << std::endl;
00130 }
00131 
00132 OSGTerrainEngineNode::~OSGTerrainEngineNode()
00133 {
00134     unregisterEngine( _uid );
00135 
00136     if ( _update_mapf )
00137     {
00138         delete _update_mapf;
00139     }
00140 
00141     if ( _cull_mapf )
00142     {
00143         delete _cull_mapf;
00144     }
00145 }
00146 
00147 void
00148 OSGTerrainEngineNode::preInitialize( const Map* map, const TerrainOptions& options )
00149 {
00150     TerrainEngineNode::preInitialize( map, options );
00151 
00152     _isStreaming =
00153         options.loadingPolicy()->mode() == LoadingPolicy::MODE_PREEMPTIVE ||
00154         options.loadingPolicy()->mode() == LoadingPolicy::MODE_SEQUENTIAL;
00155 
00156     // in standard mode, try to set the number of OSG DatabasePager threads to use.
00157     if ( options.loadingPolicy().isSet() && !_isStreaming )
00158     {
00159         int numThreads = -1;
00160 
00161         if ( options.loadingPolicy()->numLoadingThreads().isSet() )
00162         {
00163             numThreads = osg::maximum( 1, *options.loadingPolicy()->numLoadingThreads() );
00164         }
00165         else if ( options.loadingPolicy()->numLoadingThreadsPerCore().isSet() )
00166         {
00167             float numThreadsPerCore = *options.loadingPolicy()->numLoadingThreadsPerCore();
00168             numThreads = osg::maximum( (int)1, (int)osg::round( 
00169                 numThreadsPerCore * (float)OpenThreads::GetNumberOfProcessors() ) );
00170         }
00171 
00172         if ( numThreads > 0 )
00173         {
00174             OE_INFO << LC << "Requesting " << numThreads << " database pager threads in STANDARD mode" << std::endl;
00175             osg::DisplaySettings::instance()->setNumOfDatabaseThreadsHint( numThreads );
00176             //osg::DisplaySettings::instance()->setNumOfHttpDatabaseThreadsHint( numThreads );
00177         }
00178     }
00179 }
00180 
00181 void
00182 OSGTerrainEngineNode::postInitialize( const Map* map, const TerrainOptions& options )
00183 {
00184     TerrainEngineNode::postInitialize( map, options );
00185 
00186     // Initialize the map frames. We need one for the update thread and one for the
00187     // cull thread. Someday we can detect whether these are actually the same thread
00188     // (depends on the viewer's threading mode).
00189     _update_mapf = new MapFrame( map, Map::MASKED_TERRAIN_LAYERS, "osgterrain-update" );
00190     _cull_mapf   = new MapFrame( map, Map::TERRAIN_LAYERS, "osgterrain-cull" );
00191 
00192     // merge in the custom options:
00193     _terrainOptions.merge( options );
00194 
00195     // handle an already-established map profile:
00196     if ( _update_mapf->getProfile() )
00197     {
00198         // NOTE: this will initialize the map with the startup layers
00199         onMapInfoEstablished( MapInfo(map) );
00200     }
00201 
00202     // populate the terrain with whatever data is in the map to begin with:
00203     if ( _terrain )
00204     {
00205         // update the terrain revision in threaded mode
00206         if ( _isStreaming )
00207         {
00208             static_cast<StreamingTerrain*>(_terrain)->updateTaskServiceThreads( *_update_mapf );
00209         }
00210 
00211         updateTextureCombining();
00212     }
00213 
00214     // install a layer callback for processing further map actions:
00215     map->addMapCallback( new OSGTerrainEngineNodeMapCallbackProxy(this) );
00216 
00217     // register me.
00218     registerEngine( this );
00219 
00220     // now that we have a map, set up to recompute the bounds
00221     dirtyBound();
00222 }
00223 
00224 osg::BoundingSphere
00225 OSGTerrainEngineNode::computeBound() const
00226 {
00227     if ( _terrain && _terrain->getNumChildren() > 0 )
00228     {
00229         return _terrain->getBound();
00230     }
00231     else
00232     {
00233         return TerrainEngineNode::computeBound();
00234     }
00235 }
00236 
00237 void
00238 OSGTerrainEngineNode::onMapInfoEstablished( const MapInfo& mapInfo )
00239 {
00240     OE_INFO << LC << "Map profile established" << std::endl;
00241     
00242     LoadingPolicy::Mode mode = *_terrainOptions.loadingPolicy()->mode();
00243     OE_INFO << LC << "Loading policy mode = " <<
00244         ( mode == LoadingPolicy::MODE_PREEMPTIVE ? "PREEMPTIVE" :
00245           mode == LoadingPolicy::MODE_SEQUENTIAL ? "SEQUENTIAL" :
00246           mode == LoadingPolicy::MODE_PARALLEL   ? "PARALLEL" :
00247           "SERIAL/STANDARD" )
00248         << std::endl;
00249 
00250     // create a factory for creating actual tile data
00251     _tileFactory = new OSGTileFactory( _uid, *_cull_mapf, _terrainOptions );
00252 
00253     // go through and build the root nodesets.
00254     if ( !_isStreaming )
00255     {
00256         _terrain = new Terrain(
00257             *_update_mapf, *_cull_mapf, _tileFactory.get(), *_terrainOptions.quickReleaseGLObjects() );
00258     }
00259     else
00260     {
00261         _terrain = new StreamingTerrain(
00262             *_update_mapf, *_cull_mapf, _tileFactory.get(), *_terrainOptions.quickReleaseGLObjects() );
00263     }
00264 
00265     this->addChild( _terrain );
00266 
00267     // set the initial properties from the options structure:
00268     _terrain->setVerticalScale( _terrainOptions.verticalScale().value() );
00269     _terrain->setSampleRatio  ( _terrainOptions.heightFieldSampleRatio().value() );
00270 
00271     OE_INFO << LC << "Sample ratio = " << _terrainOptions.heightFieldSampleRatio().value() << std::endl;
00272 
00273     // install the proper layer composition technique:
00274 
00275     if ( _texCompositor->getTechnique() == TerrainOptions::COMPOSITING_MULTIPASS )
00276     {
00277         _terrain->setTechniquePrototype( new MultiPassTerrainTechnique( _texCompositor.get() ) );
00278         OE_INFO << LC << "Compositing technique = MULTIPASS" << std::endl;
00279     }
00280 
00281     else 
00282     {
00283         CustomTerrainTechnique* tech = new SinglePassTerrainTechnique( _texCompositor.get() );
00284 
00285         // prepare the interpolation technique for generating triangles:
00286         if ( _terrainOptions.elevationInterpolation() == INTERP_TRIANGULATE )
00287             tech->setOptimizeTriangleOrientation( false );
00288 
00289         _terrain->setTechniquePrototype( tech );
00290     }
00291 
00292     // install the shader program, if applicable:
00293     installShaders();
00294 
00295     // calculate a good thread pool size for non-streaming parallel processing
00296     if ( !_isStreaming )
00297     {
00298         unsigned num = 2 * OpenThreads::GetNumberOfProcessors();
00299         if ( _terrainOptions.loadingPolicy().isSet() )
00300         {
00301             if ( _terrainOptions.loadingPolicy()->numLoadingThreads().isSet() )
00302             {
00303                 num = *_terrainOptions.loadingPolicy()->numLoadingThreads();
00304             }
00305             else if ( _terrainOptions.loadingPolicy()->numLoadingThreadsPerCore().isSet() )
00306             {
00307                 num = (unsigned)(*_terrainOptions.loadingPolicy()->numLoadingThreadsPerCore() * OpenThreads::GetNumberOfProcessors());
00308             }
00309         }
00310         _tileService = new TaskService( "TileBuilder", num );
00311 
00312         // initialize the tile builder
00313         _tileBuilder = new TileBuilder( getMap(), _terrainOptions, _tileService.get() );
00314 
00315 
00316         // initialize a key node factory.
00317         switch( mode )
00318         {
00319         case LoadingPolicy::MODE_SERIAL:
00320             _keyNodeFactory = new SerialKeyNodeFactory( _tileBuilder.get(), _terrainOptions, mapInfo, _terrain, _uid );
00321             break;
00322 
00323         case LoadingPolicy::MODE_PARALLEL:
00324             _keyNodeFactory = new ParallelKeyNodeFactory( _tileBuilder.get(), _terrainOptions, mapInfo, _terrain, _uid );
00325             break;
00326 
00327         default:
00328             break;
00329         }
00330     }
00331 
00332     // Build the first level of the terrain.
00333     // Collect the tile keys comprising the root tiles of the terrain.
00334     std::vector< TileKey > keys;
00335     _update_mapf->getProfile()->getRootKeys( keys );
00336 
00337     for( unsigned i=0; i<keys.size(); ++i )
00338     {
00339         osg::Node* node;
00340         if ( _keyNodeFactory.valid() )
00341             node = _keyNodeFactory->createNode( keys[i] );
00342         else
00343             node = _tileFactory->createSubTiles( *_update_mapf, _terrain, keys[i], true );
00344 
00345         if ( node )
00346             _terrain->addChild( node );
00347         else
00348             OE_WARN << LC << "Couldn't make tile for root key: " << keys[i].str() << std::endl;
00349     }
00350 
00351     // we just added the root tiles, so mark the bound in need of recomputation.
00352     dirtyBound();
00353 }
00354 
00355 osg::Node*
00356 OSGTerrainEngineNode::createNode( const TileKey& key )
00357 {
00358     // if the engine has been disconnected from the scene graph, bail out and don't
00359     // create any more tiles
00360     if ( getNumParents() == 0 )
00361         return 0L;
00362 
00363 #ifdef PROFILING
00364     osg::Timer_t start = _timer.tick();
00365 #endif
00366 
00367     osg::Node* result = 0L;
00368 
00369     if ( _isStreaming )
00370     {
00371         // sequential or preemptive mode only.
00372         // create a map frame so we can safely create tiles from this dbpager thread
00373         MapFrame mapf( getMap(), Map::TERRAIN_LAYERS, "dbpager::earth plugin" );
00374         result = getTileFactory()->createSubTiles( mapf, _terrain, key, false );
00375     }
00376     else
00377     {
00378         result = _keyNodeFactory->createNode( key );
00379     }
00380 
00381 #ifdef PROFILING
00382     osg::Timer_t end = osg::Timer::instance()->tick();
00383     if ( result )
00384     {
00385         _tileCount++;
00386         _tileCreationTime += _timer.delta_s(start,_timer.tick());
00387         if ( _tileCount % 60 == 0 )
00388         {
00389             OE_INFO << LC << "Avg tile = " << 1000.0*(_tileCreationTime/(double)_tileCount)
00390                 << " ms, tiles per sec = " << (double)_tileCount/_timer.time_s() << std::endl;
00391         }
00392     }
00393 #endif
00394 
00395     return result;
00396 }
00397 
00398 void
00399 OSGTerrainEngineNode::onMapModelChanged( const MapModelChange& change )
00400 {
00401     _update_mapf->sync();
00402 
00403     // dispatch the change handler
00404     if ( change.getLayer() )
00405     {
00406         // first inform the texture compositor with the new model changes:
00407         if ( _texCompositor.valid() )
00408             _texCompositor->applyMapModelChange( change );
00409 
00410         // then apply the actual change:
00411         switch( change.getAction() )
00412         {
00413         case MapModelChange::ADD_IMAGE_LAYER:
00414             addImageLayer( change.getImageLayer() );
00415             break;
00416         case MapModelChange::REMOVE_IMAGE_LAYER:
00417             removeImageLayer( change.getImageLayer() );
00418             break;
00419         case MapModelChange::ADD_ELEVATION_LAYER:
00420             addElevationLayer( change.getElevationLayer() );
00421             break;
00422         case MapModelChange::REMOVE_ELEVATION_LAYER:
00423             removeElevationLayer( change.getElevationLayer() );
00424             break;
00425         case MapModelChange::MOVE_IMAGE_LAYER:
00426             moveImageLayer( change.getFirstIndex(), change.getSecondIndex() );
00427             break;
00428         case MapModelChange::MOVE_ELEVATION_LAYER:
00429             moveElevationLayer( change.getFirstIndex(), change.getSecondIndex() );
00430             break;
00431         }
00432     }
00433 
00434     // update the terrain revision in threaded mode
00435     if ( _isStreaming )
00436     {
00437         //getTerrain()->incrementRevision();
00438         static_cast<StreamingTerrain*>(_terrain)->updateTaskServiceThreads( *_update_mapf );
00439     }
00440 }
00441 
00442 void
00443 OSGTerrainEngineNode::addImageLayer( ImageLayer* layerAdded )
00444 {
00445     if ( !layerAdded || !layerAdded->getTileSource() )
00446         return;
00447 
00448     // visit all existing terrain tiles and inform each one of the new image layer:
00449     TileVector tiles;
00450     _terrain->getTiles( tiles );
00451 
00452     for( TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr )
00453     {
00454         Tile* tile = itr->get();
00455 
00456         StreamingTile* streamingTile = 0L;
00457 
00458         GeoImage geoImage;
00459         bool needToUpdateImagery = false;
00460         int imageLOD = -1;
00461 
00462         if ( !_isStreaming || tile->getKey().getLevelOfDetail() == 1 )
00463         {
00464             // in standard mode, or at the first LOD in seq/pre mode, fetch the image immediately.
00465             TileKey geoImageKey = tile->getKey();
00466             _tileFactory->createValidGeoImage( layerAdded, tile->getKey(), geoImage, geoImageKey );
00467             imageLOD = tile->getKey().getLevelOfDetail();
00468         }
00469         else
00470         {
00471             // in seq/pre mode, set up a placeholder and mark the tile as dirty.
00472             geoImage = GeoImage(ImageUtils::createEmptyImage(), tile->getKey().getExtent() );
00473             needToUpdateImagery = true;
00474             streamingTile = static_cast<StreamingTile*>(tile);
00475         }
00476 
00477         if (geoImage.valid())
00478         {
00479             const MapInfo& mapInfo = _update_mapf->getMapInfo();
00480 
00481             double img_min_lon, img_min_lat, img_max_lon, img_max_lat;
00482             geoImage.getExtent().getBounds(img_min_lon, img_min_lat, img_max_lon, img_max_lat);
00483 
00484             //Specify a new locator for the color with the coordinates of the TileKey that was actually used to create the image
00485             osg::ref_ptr<GeoLocator> img_locator = tile->getKey().getProfile()->getSRS()->createLocator( 
00486                 img_min_lon, img_min_lat, img_max_lon, img_max_lat, 
00487                 !mapInfo.isGeocentric() );
00488             
00489             //Set the CS to geocentric if we are dealing with a geocentric map
00490             if ( mapInfo.isGeocentric() )
00491             {
00492                 img_locator->setCoordinateSystemType( osgTerrain::Locator::GEOCENTRIC );
00493             }
00494 
00495             tile->setCustomColorLayer( CustomColorLayer(
00496                 layerAdded,
00497                 geoImage.getImage(),
00498                 img_locator.get(), imageLOD,  tile->getKey() ) );
00499 
00500             // if necessary, tell the tile to queue up a new imagery request (since we
00501             // just installed a placeholder)
00502             if ( needToUpdateImagery )
00503             {
00504                 streamingTile->updateImagery( layerAdded, *_update_mapf, _tileFactory.get() );
00505             }
00506         }
00507         else
00508         {
00509             // this can happen if there's no data in the new layer for the given tile.
00510             // we will rely on the driver to dump out a warning if this is an error.
00511         }
00512 
00513         tile->applyImmediateTileUpdate( TileUpdate::ADD_IMAGE_LAYER, layerAdded->getUID() );
00514     }
00515 
00516     updateTextureCombining();
00517 }
00518 
00519 void
00520 OSGTerrainEngineNode::removeImageLayer( ImageLayer* layerRemoved )
00521 {
00522     // make a thread-safe copy of the tile table
00523     TileVector tiles;
00524     _terrain->getTiles( tiles );
00525 
00526     for (TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr)
00527     {
00528         Tile* tile = itr->get();
00529 
00530         // critical section
00531         tile->removeCustomColorLayer( layerRemoved->getUID() );
00532     }
00533     
00534     updateTextureCombining();
00535 }
00536 
00537 void
00538 OSGTerrainEngineNode::moveImageLayer( unsigned int oldIndex, unsigned int newIndex )
00539 {
00540     // take a thread-safe copy of the tile table
00541     TileVector tiles;
00542     _terrain->getTiles( tiles );
00543 
00544     for (TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr)
00545     {
00546         Tile* tile = itr->get();
00547         tile->applyImmediateTileUpdate( TileUpdate::MOVE_IMAGE_LAYER );
00548     }     
00549 
00550     updateTextureCombining();
00551 }
00552 
00553 void
00554 OSGTerrainEngineNode::updateElevation( Tile* tile )
00555 {
00556     Threading::ScopedWriteLock exclusiveLock( tile->getTileLayersMutex() );
00557 
00558     const TileKey& key = tile->getKey();
00559 
00560     bool hasElevation = _update_mapf->elevationLayers().size() > 0;
00561 
00562     osgTerrain::HeightFieldLayer* heightFieldLayer = dynamic_cast<osgTerrain::HeightFieldLayer*>(tile->getElevationLayer());
00563     if (heightFieldLayer)
00564     {
00565         // In standard mode, just load the elevation data and dirty the tile.
00566         if ( !_isStreaming )
00567         {
00568             osg::ref_ptr<osg::HeightField> hf;
00569 
00570             if (hasElevation)
00571                 _update_mapf->getHeightField( key, true, hf, 0L, _terrainOptions.elevationInterpolation().value());
00572 
00573             if (!hf.valid()) 
00574                 hf = OSGTileFactory::createEmptyHeightField( key );
00575 
00576             heightFieldLayer->setHeightField( hf.get() );
00577             hf->setSkirtHeight( tile->getBound().radius() * _terrainOptions.heightFieldSkirtRatio().value() );
00578 
00579             //TODO: review this in favor of a tile update...
00580             tile->setDirty( true );
00581         }
00582 
00583         else // if ( isStreaming )
00584         {
00585             StreamingTile* stile = static_cast<StreamingTile*>(tile);
00586 
00587             //Update the elevation hint
00588             stile->setHasElevationHint( hasElevation );
00589 
00590             //In seq/pre mode, if there is no elevation, just clear out all the elevation on the tiles
00591             if ( !hasElevation )
00592             {
00593                 osg::ref_ptr<osg::HeightField> hf = OSGTileFactory::createEmptyHeightField( key );
00594                 heightFieldLayer->setHeightField( hf.get() );
00595                 hf->setSkirtHeight( stile->getBound().radius() * _terrainOptions.heightFieldSkirtRatio().value() );
00596                 stile->setElevationLOD( key.getLevelOfDetail() );
00597                 stile->resetElevationRequests( *_update_mapf );
00598                 stile->queueTileUpdate( TileUpdate::UPDATE_ELEVATION );
00599             }
00600             else
00601             {
00602                 //Always load the first LOD so the children tiles can have something to use for placeholders
00603                 if (stile->getKey().getLevelOfDetail() == 1)
00604                 {
00605                     osg::ref_ptr<osg::HeightField> hf;
00606                     _update_mapf->getHeightField( key, true, hf, 0L, _terrainOptions.elevationInterpolation().value());
00607                     if (!hf.valid()) 
00608                         hf = OSGTileFactory::createEmptyHeightField( key );
00609                     heightFieldLayer->setHeightField( hf.get() );
00610                     hf->setSkirtHeight( stile->getBound().radius() * _terrainOptions.heightFieldSkirtRatio().value() );
00611                     stile->setElevationLOD(tile->getKey().getLevelOfDetail());
00612                     stile->queueTileUpdate( TileUpdate::UPDATE_ELEVATION );
00613                 }
00614                 else
00615                 {
00616                     //Set the elevation LOD to -1
00617                     stile->setElevationLOD(-1);
00618                     stile->resetElevationRequests( *_update_mapf );
00619                 }
00620             }
00621         }
00622     }
00623 }
00624 
00625 
00626 void
00627 OSGTerrainEngineNode::addElevationLayer( ElevationLayer* layer )
00628 {
00629     if ( !layer || !layer->getTileSource() )
00630         return;
00631     
00632     TileVector tiles;
00633     _terrain->getTiles( tiles );
00634 
00635     OE_DEBUG << LC << "Found " << tiles.size() << std::endl;
00636 
00637     for (TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr)
00638     {
00639         updateElevation( itr->get() );
00640     }
00641 }
00642 
00643 void
00644 OSGTerrainEngineNode::removeElevationLayer( ElevationLayer* layerRemoved )
00645 {
00646     TileVector tiles;
00647     _terrain->getTiles( tiles );
00648 
00649     for (TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr)
00650     {
00651         updateElevation( itr->get() );
00652     }
00653 }
00654 
00655 void
00656 OSGTerrainEngineNode::moveElevationLayer( unsigned int oldIndex, unsigned int newIndex )
00657 {
00658     TileVector tiles;
00659     _terrain->getTiles( tiles );
00660 
00661     OE_DEBUG << "Found " << tiles.size() << std::endl;
00662 
00663     for (TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr)
00664     {
00665         updateElevation( itr->get() );
00666     }
00667 }
00668 
00669 void
00670 OSGTerrainEngineNode::validateTerrainOptions( TerrainOptions& options )
00671 {
00672     TerrainEngineNode::validateTerrainOptions( options );
00673     
00674     //nop for now.
00675     //note: to validate plugin-specific features, we would create an OSGTerrainOptions
00676     // and do the validation on that. You would then re-integrate it by calling
00677     // options.mergeConfig( osgTerrainOptions ).
00678 }
00679 
00680 void
00681 OSGTerrainEngineNode::traverse( osg::NodeVisitor& nv )
00682 {
00683     if ( _cull_mapf ) // ensures initialize() has been called
00684     {
00685         if ( nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR )
00686         {
00687             // update the cull-thread map frame if necessary. (We don't need to sync the
00688             // update_mapf becuase that happens in response to a map callback.)
00689             _cull_mapf->sync();
00690         }
00691     }
00692 
00693     TerrainEngineNode::traverse( nv );
00694 }
00695 
00696 void
00697 OSGTerrainEngineNode::installShaders()
00698 {
00699     // This method installs a default shader setup on the engine node itself. The texture compositor
00700     // can then override parts of the program by using a VirtualProgram on the _terrain node. We do
00701     // it this way so that the developer has the option of removing this top-level shader program,
00702     // replacing it, or migrating it higher up the scene graph if necessary.
00703 
00704     if ( _texCompositor.valid() && _texCompositor->usesShaderComposition() )
00705     {
00706         const ShaderFactory* sf = Registry::instance()->getShaderFactory();
00707 
00708         int numLayers = osg::maximum( 1, (int)_update_mapf->imageLayers().size() );
00709 
00710         VirtualProgram* vp = new VirtualProgram();
00711 
00712         // note. this stuff should probably happen automatically in VirtualProgram. gw
00713 
00714         //vp->setShader( "osgearth_vert_main",     sf->createVertexShaderMain() ); // happens in VirtualProgram now
00715         vp->setShader( "osgearth_vert_setupLighting", sf->createDefaultLightingVertexShader() );
00716         vp->setShader( "osgearth_vert_setupTexturing",  sf->createDefaultTextureVertexShader( numLayers ) );
00717 
00718         //vp->setShader( "osgearth_frag_main",     sf->createFragmentShaderMain() ); // happend in VirtualProgram now
00719         vp->setShader( "osgearth_frag_applyLighting", sf->createDefaultLightingFragmentShader() );
00720         vp->setShader( "osgearth_frag_applyTexturing",  sf->createDefaultTextureFragmentShader( numLayers ) );
00721 
00722         getOrCreateStateSet()->setAttributeAndModes( vp, osg::StateAttribute::ON );
00723     }
00724 }
00725 
00726 void
00727 OSGTerrainEngineNode::updateTextureCombining()
00728 {
00729     if ( _texCompositor.valid() )
00730     {
00731         int numImageLayers = _update_mapf->imageLayers().size();
00732         osg::StateSet* terrainStateSet = _terrain->getOrCreateStateSet();
00733 
00734         if ( _texCompositor->usesShaderComposition() )
00735         {
00736             // Creates or updates the shader components that are generated by the texture compositor.
00737             // These components reside in the CustomTerrain's stateset, and override the components
00738             // installed in the VP on the engine-node's stateset in installShaders().
00739 
00740             VirtualProgram* vp = dynamic_cast<VirtualProgram*>( terrainStateSet->getAttribute(osg::StateAttribute::PROGRAM) );
00741             if ( !vp )
00742             {
00743                 // create and add it the first time around..
00744                 vp = new VirtualProgram();
00745                 terrainStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON );
00746             }
00747 
00748             // first, update the default shader components based on the new layer count:
00749             const ShaderFactory* sf = Registry::instance()->getShaderFactory();
00750             vp->setShader( "osgearth_vert_setupTexturing",  sf->createDefaultTextureVertexShader( numImageLayers ) );
00751 
00752             // not this one, because the compositor always generates a new one.
00753             //vp->setShader( "osgearth_frag_applyTexturing",  lib.createDefaultTextureFragmentShader( numImageLayers ) );
00754         }
00755 
00756         // next, inform the compositor that it needs to update based on a new layer count:
00757         _texCompositor->updateMasterStateSet( terrainStateSet ); //, numImageLayers );
00758     }
00759 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines