void
StreamingTerrainNode::updateTraversal( osg::NodeVisitor& nv )
{
    // this stamp keeps track of when requests are dispatched. If a request's stamp gets too
    // old, it is considered "expired" and subject to cancelation
    int stamp = nv.getFrameStamp()->getFrameNumber();

    // update the frame stamp on the task services. This is necessary to support 
    // automatic request cancelation for image requests.
    {
        ScopedLock<Mutex> lock( _taskServiceMutex );
        for (TaskServiceMap::iterator i = _taskServices.begin(); i != _taskServices.end(); ++i)
        {
            i->second->setStamp( stamp );
        }
    }

    // next, go through the live tiles and process update-traversal requests. This
    // requires a read-lock on the master tiles table.
    {
        Threading::ScopedReadLock tileTableReadLock( _tilesMutex );

        for( TileTable::const_iterator i = _tiles.begin(); i != _tiles.end(); ++i )
        {
            StreamingTile* tile = static_cast<StreamingTile*>( i->second.get() );

            // update the neighbor list for each tile.
            refreshFamily( _update_mapf.getMapInfo(), tile->getKey(), tile->getFamily(), true );

            tile->servicePendingElevationRequests( _update_mapf, stamp, true );                   
            tile->serviceCompletedRequests( _update_mapf, true );
        }
    }
}
示例#2
0
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 );
                }
            }
        }
    }
}
示例#3
0
void
OSGTerrainEngineNode::addImageLayer( ImageLayer* layerAdded )
{
    if ( !layerAdded )
        return;

    if (!_isStreaming)
    {
        refresh();
    }
    else
    {
        // visit all existing terrain tiles and inform each one of the new image layer:
        TileVector tiles;
        _terrain->getTiles( tiles );

        for( TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr )
        {
            Tile* tile = itr->get();

            StreamingTile* streamingTile = 0L;

            GeoImage geoImage;
            bool needToUpdateImagery = false;
            int imageLOD = -1;

            if ( !_isStreaming || tile->getKey().getLevelOfDetail() == 1 )
            {
                // in standard mode, or at the first LOD in seq/pre mode, fetch the image immediately.
                TileKey geoImageKey = tile->getKey();
                _tileFactory->createValidGeoImage( layerAdded, tile->getKey(), geoImage, geoImageKey );
                imageLOD = tile->getKey().getLevelOfDetail();
            }
            else
            {
                // in seq/pre mode, set up a placeholder and mark the tile as dirty.
                geoImage = GeoImage(ImageUtils::createEmptyImage(), tile->getKey().getExtent() );
                needToUpdateImagery = true;
                streamingTile = static_cast<StreamingTile*>(tile);
            }

            if (geoImage.valid())
            {
                const MapInfo& mapInfo = _update_mapf->getMapInfo();

                double img_min_lon, img_min_lat, img_max_lon, img_max_lat;
                geoImage.getExtent().getBounds(img_min_lon, img_min_lat, img_max_lon, img_max_lat);

                //Specify a new locator for the color with the coordinates of the TileKey that was actually used to create the image
                osg::ref_ptr<GeoLocator> img_locator = tile->getKey().getProfile()->getSRS()->createLocator( 
                    img_min_lon, img_min_lat, img_max_lon, img_max_lat, 
                    !mapInfo.isGeocentric() );

                //Set the CS to geocentric if we are dealing with a geocentric map
                if ( mapInfo.isGeocentric() )
                {
                    img_locator->setCoordinateSystemType( osgTerrain::Locator::GEOCENTRIC );
                }

                tile->setCustomColorLayer( CustomColorLayer(
                    layerAdded,
                    geoImage.getImage(),
                    img_locator.get(), imageLOD,  tile->getKey() ) );

                // if necessary, tell the tile to queue up a new imagery request (since we
                // just installed a placeholder)
                if ( needToUpdateImagery )
                {
                    streamingTile->updateImagery( layerAdded, *_update_mapf, _tileFactory.get() );
                }
            }
            else
            {
                // this can happen if there's no data in the new layer for the given tile.
                // we will rely on the driver to dump out a warning if this is an error.
            }

            tile->applyImmediateTileUpdate( TileUpdate::ADD_IMAGE_LAYER, layerAdded->getUID() );
        }

        updateTextureCombining();
    }
}
示例#4
0
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;
}