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 "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 }