osg::Node* SingleKeyNodeFactory::createTile(TileModel* model, bool setupChildrenIfNecessary, ProgressCallback* progress) { #ifdef EXPERIMENTAL_TILE_NODE_CACHE osg::ref_ptr<TileNode> tileNode; TileNodeCache::Record rec; cache.get(model->_tileKey, rec); if ( rec.valid() ) { tileNode = rec.value().get(); } else { tileNode = _modelCompiler->compile( model, _frame ); cache.insert(model->_tileKey, tileNode); } #else // compile the model into a node: TileNode* tileNode = _modelCompiler->compile(model, _frame, progress); #endif // see if this tile might have children. bool prepareForChildren = setupChildrenIfNecessary && model->_tileKey.getLOD() < *_options.maxLOD(); osg::Node* result = 0L; if ( prepareForChildren ) { osg::BoundingSphere bs = tileNode->getBound(); TilePagedLOD* plod = new TilePagedLOD( _engineUID, _liveTiles, _deadTiles ); plod->setCenter ( bs.center() ); plod->addChild ( tileNode ); plod->setFileName( 1, Stringify() << tileNode->getKey().str() << "." << _engineUID << ".osgearth_engine_mp_tile" ); if ( _options.rangeMode().value() == osg::LOD::DISTANCE_FROM_EYE_POINT ) { //Compute the min range based on the 2D size of the tile GeoExtent extent = model->_tileKey.getExtent(); GeoPoint lowerLeft(extent.getSRS(), extent.xMin(), extent.yMin(), 0.0, ALTMODE_ABSOLUTE); GeoPoint upperRight(extent.getSRS(), extent.xMax(), extent.yMax(), 0.0, ALTMODE_ABSOLUTE); osg::Vec3d ll, ur; lowerLeft.toWorld( ll ); upperRight.toWorld( ur ); double radius = (ur - ll).length() / 2.0; float minRange = (float)(radius * _options.minTileRangeFactor().value()); plod->setRange( 0, minRange, FLT_MAX ); plod->setRange( 1, 0, minRange ); plod->setRangeMode( osg::LOD::DISTANCE_FROM_EYE_POINT ); } else { plod->setRange( 0, 0.0f, _options.tilePixelSize().value() ); plod->setRange( 1, _options.tilePixelSize().value(), FLT_MAX ); plod->setRangeMode( osg::LOD::PIXEL_SIZE_ON_SCREEN ); } // DBPager will set a priority based on the ratio range/maxRange. // This will offset that number with a full LOD #, giving LOD precedence. // Experimental. //plod->setPriorityScale( 1, model->_tileKey.getLOD()+1 ); #if USE_FILELOCATIONCALLBACK osgDB::Options* options = plod->getOrCreateDBOptions(); options->setFileLocationCallback( new FileLocationCallback() ); #endif result = plod; // this one rejects back-facing tiles: if ( _frame.getMapInfo().isGeocentric() && _options.clusterCulling() == true ) { osg::HeightField* hf = model->_elevationData.getHeightField(); result->addCullCallback( HeightFieldUtils::createClusterCullingCallback( hf, tileNode->getKey().getProfile()->getSRS()->getEllipsoid(), *_options.verticalScale() ) ); } } else { result = tileNode; } return result; }
osg::Node* SingleKeyNodeFactory::createTile(TileModel* model, bool setupChildrenIfNecessary, ProgressCallback* progress) { #ifdef EXPERIMENTAL_TILE_NODE_CACHE osg::ref_ptr<TileNode> tileNode; TileNodeCache::Record rec; cache.get(model->_tileKey, rec); if ( rec.valid() ) { tileNode = rec.value().get(); } else { tileNode = _modelCompiler->compile( model, _frame ); cache.insert(model->_tileKey, tileNode); } #else // compile the model into a node: osg::ref_ptr<TileNode> tileNode = _modelCompiler->compile(model, _frame, progress); #endif // see if this tile might have children. bool prepareForChildren = setupChildrenIfNecessary && model->_tileKey.getLOD() < *_options.maxLOD(); osg::Node* result = 0L; if ( prepareForChildren ) { osg::BoundingSphere bs = tileNode->getBound(); TilePagedLOD* plod = new TilePagedLOD( _engine->getUID(), _liveTiles.get(), _releaser.get() ); plod->setCenter ( bs.center() ); plod->addChild ( tileNode.get() ); plod->setFileName( 1, Stringify() << tileNode->getKey().str() << "." << _engine->getUID() << ".osgearth_engine_mp_tile" ); double rangeFactor = _options.minTileRangeFactor().get(); //if (_options.adaptivePolarRangeFactor() == true) //{ // double lat = model->_tileKey.getExtent().yMin() < 0 ? -model->_tileKey.getExtent().yMax() : model->_tileKey.getExtent().yMin(); // double latRad = osg::DegreesToRadians(lat); // rangeFactor -= (rangeFactor - 1.0)*sin(latRad)*sin(latRad); //} plod->setRangeFactor(rangeFactor); // Setup expiration. if (_options.minExpiryFrames().isSet()) { plod->setMinimumExpiryFrames(1, *_options.minExpiryFrames()); } if (_options.minExpiryTime().isSet()) { plod->setMinimumExpiryTime(1, *_options.minExpiryTime()); } if ( _options.rangeMode().value() == osg::LOD::DISTANCE_FROM_EYE_POINT ) { //Compute the min range based on the 2D size of the tile GeoExtent extent = model->_tileKey.getExtent(); double radius = 0.0; GeoPoint lowerLeft(extent.getSRS(), extent.xMin(), extent.yMin(), 0.0, ALTMODE_ABSOLUTE); GeoPoint upperRight(extent.getSRS(), extent.xMax(), extent.yMax(), 0.0, ALTMODE_ABSOLUTE); osg::Vec3d ll, ur; lowerLeft.toWorld( ll ); upperRight.toWorld( ur ); double radiusDiag = (ur - ll).length() / 2.0; if (_options.adaptivePolarRangeFactor() == true ) { GeoPoint left(extent.getSRS(), extent.xMin(), extent.yMin()+extent.height()*0.5, 0.0, ALTMODE_ABSOLUTE); GeoPoint right(extent.getSRS(), extent.xMax(), extent.yMin()+extent.height()*0.5, 0.0, ALTMODE_ABSOLUTE); osg::Vec3d l, r; left.toWorld(l); right.toWorld(r); double radiusHoriz = 1.4142 * (r - l).length() / 2.0; double lat = model->_tileKey.getExtent().yMin() < 0 ? -model->_tileKey.getExtent().yMax() : model->_tileKey.getExtent().yMin(); double latRad = osg::DegreesToRadians(lat); // mix between diagonal radius and horizontal radius based on latitude double t = cos(latRad); t = 1.0-(1.0-t)*(1.0-t); // decelerate t to weight the mix in favor of equator (diag radius) radius = t*radiusDiag + (1.0-t)*radiusHoriz; } else { radius = radiusDiag; } float minRange = radius; plod->setRange( 0, minRange, FLT_MAX ); plod->setRange( 1, 0, minRange ); plod->setRangeMode( osg::LOD::DISTANCE_FROM_EYE_POINT ); } else { // the *2 is because we page in 4-tile sets, not individual tiles. float size = 2.0f * _options.tilePixelSize().value(); plod->setRange( 0, 0.0f, size ); plod->setRange( 1, size, FLT_MAX ); plod->setRangeMode( osg::LOD::PIXEL_SIZE_ON_SCREEN ); } // Install a tile-aligned bounding box in the pager node itself so we can do // visibility testing before paging in subtiles. plod->setChildBoundingBoxAndMatrix( 1, tileNode->getTerrainBoundingBox(), tileNode->getMatrix() ); // DBPager will set a priority based on the ratio range/maxRange. // This will offset that number with a full LOD #, giving LOD precedence. // Experimental. //plod->setPriorityScale( 1, model->_tileKey.getLOD()+1 ); #if USE_FILELOCATIONCALLBACK osgDB::Options* options = plod->getOrCreateDBOptions(); options->setFileLocationCallback( new FileLocationCallback() ); #endif result = plod; // this one rejects back-facing tiles: if ( _frame.getMapInfo().isGeocentric() && _options.clusterCulling() == true ) { osg::HeightField* hf = model->_elevationData.getHeightField(); result->addCullCallback( HeightFieldUtils::createClusterCullingCallback( hf, tileNode->getKey().getProfile()->getSRS()->getEllipsoid(), *_options.verticalScale() ) ); } } else { result = tileNode.release(); } return result; }
osg::Node* SingleKeyNodeFactory::createTile(TileModel* model, bool setupChildrenIfNecessary, ProgressCallback* progress) { #ifdef EXPERIMENTAL_TILE_NODE_CACHE osg::ref_ptr<TileNode> tileNode; TileNodeCache::Record rec; cache.get(model->_tileKey, rec); if ( rec.valid() ) { tileNode = rec.value().get(); } else { tileNode = _modelCompiler->compile( model, _frame ); cache.insert(model->_tileKey, tileNode); } #else // compile the model into a node: TileNode* tileNode = _modelCompiler->compile(model, _frame, progress); tileNode->setEngineUID( _engineUID ); #endif // see if this tile might have children. bool prepareForChildren = setupChildrenIfNecessary && model->_tileKey.getLOD() < *_options.maxLOD(); osg::Node* result = 0L; if ( prepareForChildren ) { osg::BoundingSphere bs = tileNode->getBound(); TilePagedLOD* plod = new TilePagedLOD( _engineUID, _liveTiles, _deadTiles ); plod->setCenter ( bs.center() ); plod->addChild ( tileNode ); plod->setFileName( 1, Stringify() << tileNode->getKey().str() << "." << _engineUID << ".osgearth_engine_mp_tile" ); plod->setDebug ( _debug ); if ( _options.rangeMode().value() == osg::LOD::DISTANCE_FROM_EYE_POINT ) { //Compute the min range based on the 2D size of the tile GeoExtent extent = model->_tileKey.getExtent(); double radius = 0.0; #if 0 // Test code to use the equitorial radius so that all of the tiles at the same level // have the same range. This will make the poles page in more appropriately. if (_frame.getMapInfo().isGeocentric()) { GeoExtent equatorialExtent( extent.getSRS(), extent.west(), -extent.height()/2.0, extent.east(), extent.height()/2.0 ); radius = equatorialExtent.getBoundingGeoCircle().getRadius(); } else #endif { GeoPoint lowerLeft(extent.getSRS(), extent.xMin(), extent.yMin(), 0.0, ALTMODE_ABSOLUTE); GeoPoint upperRight(extent.getSRS(), extent.xMax(), extent.yMax(), 0.0, ALTMODE_ABSOLUTE); osg::Vec3d ll, ur; lowerLeft.toWorld( ll ); upperRight.toWorld( ur ); radius = (ur - ll).length() / 2.0; } float minRange = (float)(radius * _options.minTileRangeFactor().value()); plod->setRange( 0, minRange, FLT_MAX ); plod->setRange( 1, 0, minRange ); plod->setRangeMode( osg::LOD::DISTANCE_FROM_EYE_POINT ); } else { // the *2 is because we page in 4-tile sets, not individual tiles. float size = 2.0f * _options.tilePixelSize().value(); plod->setRange( 0, 0.0f, size ); plod->setRange( 1, size, FLT_MAX ); plod->setRangeMode( osg::LOD::PIXEL_SIZE_ON_SCREEN ); } // Install a tile-aligned bounding box in the pager node itself so we can do // visibility testing before paging in subtiles. plod->setChildBoundingBoxAndMatrix( 1, tileNode->getTerrainBoundingBox(), tileNode->getMatrix() ); // DBPager will set a priority based on the ratio range/maxRange. // This will offset that number with a full LOD #, giving LOD precedence. // Experimental. //plod->setPriorityScale( 1, model->_tileKey.getLOD()+1 ); #if USE_FILELOCATIONCALLBACK osgDB::Options* options = plod->getOrCreateDBOptions(); options->setFileLocationCallback( new FileLocationCallback() ); #endif result = plod; // this one rejects back-facing tiles: if ( _frame.getMapInfo().isGeocentric() && _options.clusterCulling() == true ) { #if 1 osg::HeightField* hf = model->_elevationData.getHeightField(); result->addCullCallback( HeightFieldUtils::createClusterCullingCallback( hf, tileNode->getKey().getProfile()->getSRS()->getEllipsoid(), *_options.verticalScale() ) ); #else // This works, but isn't quite as tight at the cluster culler. // Re-evaluate down the road. result->addCullCallback( new HorizonTileCuller( _frame.getMapInfo().getSRS(), tileNode->getTerrainBoundingBox(), tileNode->getMatrix(), tileNode->getKey() ) ); #endif } } else { result = tileNode; } return result; }
TileNode* TileGroupFactory::createTileNodeGraph(TerrainTileModel* model, bool setupChildrenIfNecessary, ProgressCallback* progress) { // TODO: fix this const unsigned tileSize = 17; // Build the surface node. SurfaceNodeFactory factory(model, _frame, _renderBindings, _geometryPool, tileSize, _options); SurfaceNode* surfaceNode = factory.createSurfaceNode(); surfaceNode->setEngineUID( _terrainEngine->getUID() ); // see if this tile might have children. bool prepareForChildren = setupChildrenIfNecessary && model->getKey().getLOD() < *_options.maxLOD(); // Build the Tile Node that will hold all the textures and texture matrices. TileNode* tileNode = createTileNode( model, progress ); // Build the paging node that will load subtiles, if necessary: if ( prepareForChildren ) { osg::BoundingSphere bs = surfaceNode->getBound(); TilePagedLOD* plod = new TilePagedLOD( _terrainEngine->getUID(), _liveTiles, _deadTiles ); plod->setCenter ( bs.center() ); plod->addChild ( surfaceNode ); plod->setFileName( 1, Stringify() << model->getKey().str() << "." << _terrainEngine->getUID() << ".osgearth_engine_mp_tile" ); if ( _options.rangeMode().value() == osg::LOD::DISTANCE_FROM_EYE_POINT ) { //Compute the min range based on the 2D size of the tile GeoExtent extent = model->getKey().getExtent(); GeoPoint lowerLeft(extent.getSRS(), extent.xMin(), extent.yMin(), 0.0, ALTMODE_ABSOLUTE); GeoPoint upperRight(extent.getSRS(), extent.xMax(), extent.yMax(), 0.0, ALTMODE_ABSOLUTE); osg::Vec3d ll, ur; lowerLeft.toWorld( ll ); upperRight.toWorld( ur ); double radius = (ur - ll).length() / 2.0; float minRange = (float)(radius * _options.minTileRangeFactor().value()); plod->setRange( 0, minRange, FLT_MAX ); plod->setRange( 1, 0, minRange ); plod->setRangeMode( osg::LOD::DISTANCE_FROM_EYE_POINT ); } else { plod->setRange( 0, 0.0f, _options.tilePixelSize().value() ); plod->setRange( 1, _options.tilePixelSize().value(), FLT_MAX ); plod->setRangeMode( osg::LOD::PIXEL_SIZE_ON_SCREEN ); } #if 0 // TODO: reinstate this! // Install a tile-aligned bounding box in the pager node itself so we can do // visibility testing before paging in subtiles. plod->setChildBoundingBoxAndMatrix( 1, surfaceNode->getTerrainBoundingBox(), surfaceNode->getLocalToWorldMatrix() ); #endif #if USE_FILELOCATIONCALLBACK osgDB::Options* options = plod->getOrCreateDBOptions(); options->setFileLocationCallback( new FileLocationCallback() ); #endif tileNode->addChild( plod ); // Install a callback to reject back-facing tiles. if ( _frame.getMapInfo().isGeocentric() && _options.clusterCulling() == true ) { const osg::HeightField* heightField = model->elevationModel()->getHeightField(); if ( heightField ) { tileNode->addCullCallback( HeightFieldUtils::createClusterCullingCallback( heightField, tileNode->getKey().getProfile()->getSRS()->getEllipsoid(), *_options.verticalScale() ) ); } } } else { tileNode->addChild( surfaceNode ); } return tileNode; }