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