osg::Node* OSGTileFactory::createPlaceholderTile(const MapFrame& mapf, StreamingTerrain* terrain, const TileKey& key ) { // Start out by finding the nearest registered ancestor tile, since the placeholder is // going to be based on inherited data. Note- the ancestor may not be the immediate // parent, b/c the parent may or may not be in the scene graph. TileKey ancestorKey = key.createParentKey(); osg::ref_ptr<StreamingTile> ancestorTile; while( !ancestorTile.valid() && ancestorKey.valid() ) { terrain->getTile( ancestorKey.getTileId(), ancestorTile ); if ( !ancestorTile.valid() ) ancestorKey = ancestorKey.createParentKey(); } if ( !ancestorTile.valid() ) { OE_WARN << LC << "cannot find ancestor tile for (" << key.str() << ")" <<std::endl; return 0L; } OE_DEBUG << LC << "Creating placeholder for " << key.str() << std::endl; const MapInfo& mapInfo = mapf.getMapInfo(); bool hasElevation = mapf.elevationLayers().size() > 0; // Build a "placeholder" tile. double xmin, ymin, xmax, ymax; key.getExtent().getBounds( xmin, ymin, xmax, ymax ); // A locator will place the tile on the globe: osg::ref_ptr<GeoLocator> locator = GeoLocator::createForKey( key, mapInfo ); // The empty tile: StreamingTile* tile = new StreamingTile( key, locator.get(), terrain->getQuickReleaseGLObjects() ); tile->setTerrainTechnique( terrain->cloneTechnique() ); tile->setVerticalScale( _terrainOptions.verticalScale().value() ); tile->setDataVariance( osg::Object::DYNAMIC ); //tile->setLocator( locator.get() ); // Attach an updatecallback to normalize the edges of TerrainTiles. #if 0 if ( hasElevation && _terrainOptions.normalizeEdges().get() ) { tile->setUpdateCallback(new TerrainTileEdgeNormalizerUpdateCallback()); tile->setDataVariance(osg::Object::DYNAMIC); } #endif // Generate placeholder imagery and elevation layers. These "inherit" data from an // ancestor tile. { //Threading::ScopedReadLock parentLock( ancestorTile->getTileLayersMutex() ); addPlaceholderImageLayers ( tile, ancestorTile.get() ); addPlaceholderHeightfieldLayer( tile, ancestorTile.get(), locator.get(), key, ancestorKey ); } // calculate the switching distances: osg::BoundingSphere bs = tile->getBound(); double max_range = 1e10; double radius = bs.radius(); double min_range = radius * _terrainOptions.minTileRangeFactor().get(); // Set the skirt height of the heightfield osgTerrain::HeightFieldLayer* hfLayer = static_cast<osgTerrain::HeightFieldLayer*>(tile->getElevationLayer()); if (!hfLayer) { OE_WARN << LC << "Warning: Couldn't get hfLayer for " << key.str() << std::endl; } hfLayer->getHeightField()->setSkirtHeight(radius * _terrainOptions.heightFieldSkirtRatio().get() ); // In a Plate Carre tesselation, scale the heightfield elevations from meters to degrees if ( mapInfo.isPlateCarre() && hfLayer->getHeightField() ) HeightFieldUtils::scaleHeightFieldToDegrees( hfLayer->getHeightField() ); bool markTileLoaded = false; if ( _terrainOptions.loadingPolicy()->mode().get() != LoadingPolicy::MODE_STANDARD ) { markTileLoaded = true; tile->setHasElevationHint( hasElevation ); } // install a tile switcher: tile->attachToTerrain( terrain ); //tile->setTerrain( terrain ); //terrain->registerTile( tile ); osg::Node* result = 0L; // create a PLOD so we can keep subdividing: osg::PagedLOD* plod = new osg::PagedLOD(); plod->setCenter( bs.center() ); plod->addChild( tile, min_range, max_range ); if ( key.getLevelOfDetail() < (unsigned int)getTerrainOptions().maxLOD().get() ) { plod->setFileName( 1, createURI( _engineId, key ) ); //map->getId(), key ) ); plod->setRange( 1, 0.0, min_range ); } else { plod->setRange( 0, 0, FLT_MAX ); } #if 0 //USE_FILELOCATIONCALLBACK osgDB::Options* options = new osgDB::Options; options->setFileLocationCallback( new FileLocationCallback); plod->setDatabaseOptions( options ); #endif result = plod; // Install a callback that will load the actual tile data via the pager. result->addCullCallback( new PopulateStreamingTileDataCallback( _cull_thread_mapf ) ); // Install a cluster culler (FIXME for cube mode) //bool isCube = map->getMapOptions().coordSysType() == MapOptions::CSTYPE_GEOCENTRIC_CUBE; if ( mapInfo.isGeocentric() && !mapInfo.isCube() ) { osg::ClusterCullingCallback* ccc = createClusterCullingCallback( tile, locator->getEllipsoidModel() ); result->addCullCallback( ccc ); } return result; }