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; }
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; }
void SerialKeyNodeFactory::addTile(TileModel* model, bool tileHasRealData, bool tileHasLodBlending, osg::Group* parent ) { // create a node: TileNode* tileNode = new TileNode( model->_tileKey, model->_tileLocator ); // install the tile model and compile it: tileNode->setTileModel( model ); tileNode->compile( _modelCompiler.get() ); // assemble a URI for this tile's child group: std::string uri = Stringify() << model->_tileKey.str() << "." << _engineUID << ".osgearth_engine_mp_tile"; osg::Node* result = 0L; // Only add the next tile if all the following are true: // 1. Either there's real tile data, or a minLOD is explicity set in the options; // 2. The tile isn't blacklisted; and // 3. We are still below the maximim LOD. bool wrapInPagedLOD = (tileHasRealData || (_options.minLOD().isSet() && model->_tileKey.getLOD() < *_options.minLOD())) && !osgEarth::Registry::instance()->isBlacklisted( uri ) && model->_tileKey.getLOD() < *_options.maxLOD(); if ( wrapInPagedLOD ) { osg::BoundingSphere bs = tileNode->getBound(); float maxRange = FLT_MAX; //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()); // create a PLOD so we can keep subdividing: osg::PagedLOD* plod = new CustomPagedLOD( _liveTiles.get(), _deadTiles.get() ); plod->setCenter( bs.center() ); plod->addChild( tileNode ); plod->setRangeMode( *_options.rangeMode() ); plod->setFileName( 1, uri ); if (plod->getRangeMode() == osg::LOD::PIXEL_SIZE_ON_SCREEN) { static const float sqrt2 = sqrt(2.0f); minRange = 0; maxRange = (*_options.tilePixelSize()) * sqrt2; plod->setRange( 0, minRange, maxRange ); plod->setRange( 1, maxRange, FLT_MAX ); } else { plod->setRange( 0, minRange, maxRange ); plod->setRange( 1, 0, minRange ); } plod->setUserData( new MapNode::TileRangeData(minRange, maxRange) ); #if USE_FILELOCATIONCALLBACK osgDB::Options* options = Registry::instance()->cloneOrCreateOptions(); options->setFileLocationCallback( new FileLocationCallback() ); plod->setDatabaseOptions( options ); #endif result = plod; if ( tileHasLodBlending ) { // Make the LOD transition distance, and a measure of how // close the tile is to an LOD change, to shaders. result->addCullCallback(new LODFactorCallback); } } else { result = tileNode; } // this cull callback dynamically adjusts the LOD scale based on distance-to-camera: if ( _options.lodFallOff().isSet() && *_options.lodFallOff() > 0.0 ) { result->addCullCallback( new DynamicLODScaleCallback(*_options.lodFallOff()) ); } // this one rejects back-facing tiles: if ( _mapInfo.isGeocentric() && _options.clusterCulling() == true ) { osg::HeightField* hf = model->_elevationData.getHFLayer()->getHeightField(); result->addCullCallback( HeightFieldUtils::createClusterCullingCallback( hf, tileNode->getLocator()->getEllipsoidModel(), *_options.verticalScale() ) ); } parent->addChild( 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); #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; }