osg::Node* SingleKeyNodeFactory::createTile(TileModel* model, bool setupChildrenIfNecessary) { // compile the model into a node: TileNode* tileNode = _modelCompiler->compile( model, _frame ); // see if this tile might have children. bool prepareForChildren = setupChildrenIfNecessary && model->_tileKey.getLOD() < *_options.maxLOD(); osg::Node* result = 0L; if ( prepareForChildren ) { //Compute the min range based on the 2D size of the tile osg::BoundingSphere bs = tileNode->getBound(); 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()); TilePagedLOD* plod = new TilePagedLOD( _engineUID, _liveTiles, _deadTiles ); plod->setCenter ( bs.center() ); plod->addChild ( tileNode ); plod->setRange ( 0, minRange, FLT_MAX ); plod->setFileName( 1, Stringify() << tileNode->getKey().str() << "." << _engineUID << ".osgearth_engine_mp_tile" ); plod->setRange ( 1, 0, minRange ); #if USE_FILELOCATIONCALLBACK osgDB::Options* options = Registry::instance()->cloneOrCreateOptions(); options->setFileLocationCallback( new FileLocationCallback() ); plod->setDatabaseOptions( options ); #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; }
// The osgDB::DatabasePager will call this method when merging a new child // into the scene graph. bool TilePagedLOD::addChild(osg::Node* node) { if ( node ) { // if we see an invalid tile marker, disable the paged lod slot. if ( dynamic_cast<InvalidTileNode*>(node) ) { this->setFileName( 1, "" ); this->setRange( 1, 0, 0 ); this->setRange( 0, 0.0f, FLT_MAX ); return true; } // If it's a TileNode, this is the simple first addition of the // static TileNode child (not from the pager). TileNode* tilenode = dynamic_cast<TileNode*>( node ); if ( tilenode && _live.get() ) { _live->add( tilenode ); // Listen for out east and south neighbors. const TileKey& key = tilenode->getKey(); _live->listenFor( key.createNeighborKey(1, 0), tilenode ); _live->listenFor( key.createNeighborKey(0, 1), tilenode ); } return osg::PagedLOD::addChild( node ); } return false; }
osg::Node* SerialKeyNodeFactory::createTile(TileModel* model, bool tileHasRealData) { // compile the model into a node: TileNode* tileNode = _modelCompiler->compile( model ); // see if this tile might have children. bool prepareForChildren = (tileHasRealData || (_options.minLOD().isSet() && model->_tileKey.getLOD() < *_options.minLOD())) && model->_tileKey.getLOD() < *_options.maxLOD(); osg::Node* result = 0L; if ( prepareForChildren ) { //Compute the min range based on the 2D size of the tile osg::BoundingSphere bs = tileNode->getBound(); 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()); osgDB::Options* dbOptions = Registry::instance()->cloneOrCreateOptions(); TileGroup* plod = new TileGroup(tileNode, _engineUID, _liveTiles.get(), _deadTiles.get(), dbOptions); plod->setSubtileRange( minRange ); #if USE_FILELOCATIONCALLBACK dbOptions->setFileLocationCallback( new FileLocationCallback() ); #endif result = plod; } else { result = tileNode; } // this one rejects back-facing tiles: if ( _mapInfo.isGeocentric() && _options.clusterCulling() == true ) { osg::HeightField* hf = model->_elevationData.getHeightField(); result->addCullCallback( HeightFieldUtils::createClusterCullingCallback( hf, tileNode->getKey().getProfile()->getSRS()->getEllipsoid(), *_options.verticalScale() ) ); } return result; }
// The osgDB::DatabasePager will call this automatically to purge expired // tiles from the scene graph. bool TilePagedLOD::removeExpiredChildren(double expiryTime, unsigned expiryFrame, osg::NodeList& removedChildren) { if (_children.size()>_numChildrenThatCannotBeExpired) { unsigned cindex = _children.size() - 1; double minExpiryTime = 0.0; unsigned minExpiryFrames = 0; // these were added in osg 3.1.0+ #if OSG_VERSION_GREATER_OR_EQUAL(3,1,0) minExpiryTime = _perRangeDataList[cindex]._minExpiryTime; minExpiryFrames = _perRangeDataList[cindex]._minExpiryFrames; #endif if (!_perRangeDataList[cindex]._filename.empty() && _perRangeDataList[cindex]._timeStamp + minExpiryTime < expiryTime && _perRangeDataList[cindex]._frameNumber + minExpiryFrames < expiryFrame) { osg::Node* nodeToRemove = _children[cindex].get(); removedChildren.push_back(nodeToRemove); ExpirationCollector collector( _live.get() ); nodeToRemove->accept( collector ); _releaser->push( collector._toRelease ); if ( _debug ) { TileNode* tileNode = getTileNode(); std::string key = tileNode ? tileNode->getKey().str() : "unk"; OE_NOTICE << LC << "Tile " << key << " : expiring " << collector._count << " children; " << "TS = " << _perRangeDataList[cindex]._timeStamp << ", MET = " << minExpiryTime << ", ET = " << expiryTime << "; FN = " << _perRangeDataList[cindex]._frameNumber << ", MEF = " << minExpiryFrames << ", EF = " << expiryFrame << "\n"; } return Group::removeChildren(cindex,1); } } return false; }
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: 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; }