void OSGTerrainEngineNode::updateElevation( Tile* tile ) { Threading::ScopedWriteLock exclusiveLock( tile->getTileLayersMutex() ); const TileKey& key = tile->getKey(); bool hasElevation = _update_mapf->elevationLayers().size() > 0; osgTerrain::HeightFieldLayer* heightFieldLayer = dynamic_cast<osgTerrain::HeightFieldLayer*>(tile->getElevationLayer()); if (heightFieldLayer) { // In standard mode, just load the elevation data and dirty the tile. if ( !_isStreaming ) { osg::ref_ptr<osg::HeightField> hf; if (hasElevation) _update_mapf->getHeightField( key, true, hf, 0L); if (!hf.valid()) hf = OSGTileFactory::createEmptyHeightField( key ); heightFieldLayer->setHeightField( hf.get() ); hf->setSkirtHeight( tile->getBound().radius() * _terrainOptions.heightFieldSkirtRatio().value() ); //TODO: review this in favor of a tile update... tile->setDirty( true ); } else // if ( isStreaming ) { StreamingTile* stile = static_cast<StreamingTile*>(tile); //Update the elevation hint stile->setHasElevationHint( hasElevation ); //In seq/pre mode, if there is no elevation, just clear out all the elevation on the tiles if ( !hasElevation ) { osg::ref_ptr<osg::HeightField> hf = OSGTileFactory::createEmptyHeightField( key ); heightFieldLayer->setHeightField( hf.get() ); hf->setSkirtHeight( stile->getBound().radius() * _terrainOptions.heightFieldSkirtRatio().value() ); stile->setElevationLOD( key.getLevelOfDetail() ); stile->resetElevationRequests( *_update_mapf ); stile->queueTileUpdate( TileUpdate::UPDATE_ELEVATION ); } else { //Always load the first LOD so the children tiles can have something to use for placeholders if (stile->getKey().getLevelOfDetail() == 1) { osg::ref_ptr<osg::HeightField> hf; _update_mapf->getHeightField( key, true, hf, 0L); if (!hf.valid()) hf = OSGTileFactory::createEmptyHeightField( key ); heightFieldLayer->setHeightField( hf.get() ); hf->setSkirtHeight( stile->getBound().radius() * _terrainOptions.heightFieldSkirtRatio().value() ); stile->setElevationLOD(tile->getKey().getLevelOfDetail()); stile->queueTileUpdate( TileUpdate::UPDATE_ELEVATION ); } else { //Set the elevation LOD to -1 stile->setElevationLOD(-1); stile->resetElevationRequests( *_update_mapf ); } } } } }
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; }