Exemple #1
0
void
TerrainEngineNode::postInitialize( const Map* map, const TerrainOptions& options )
{
    if ( _map.valid() ) // i think this is always true [gw]
    {
        // manually trigger the map callbacks the first time:
        if ( _map->getProfile() )
            onMapInfoEstablished( MapInfo(_map.get()) );

        // create a layer controller. This object affects the uniforms that control layer appearance properties
        _imageLayerController = new ImageLayerController( _map.get(), this );

        // register the layer Controller it with all pre-existing image layers:
        MapFrame mapf( _map.get(), Map::IMAGE_LAYERS );
        for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i )
        {
            i->get()->addCallback( _imageLayerController.get() );
        }
    }

    _initStage = INIT_POSTINIT_COMPLETE;
}
void
StreamingTerrainNode::updateTaskServiceThreads( const MapFrame& mapf )
{
    //Get the maximum elevation weight
    float elevationWeight = 0.0f;
    for (ElevationLayerVector::const_iterator itr = mapf.elevationLayers().begin(); itr != mapf.elevationLayers().end(); ++itr)
    {
        ElevationLayer* layer = itr->get();
        float w = layer->getElevationLayerOptions().loadingWeight().value();
        if (w > elevationWeight) elevationWeight = w;
    }

    float totalImageWeight = 0.0f;
    for (ImageLayerVector::const_iterator itr = mapf.imageLayers().begin(); itr != mapf.imageLayers().end(); ++itr)
    {
        totalImageWeight += itr->get()->getImageLayerOptions().loadingWeight().value();
    }

    float totalWeight = elevationWeight + totalImageWeight;

    if (elevationWeight > 0.0f)
    {
        //Determine how many threads each layer gets
        int numElevationThreads = (int)osg::round((float)_numLoadingThreads * (elevationWeight / totalWeight ));
        OE_INFO << LC << "Elevation Threads = " << numElevationThreads << std::endl;
        getElevationTaskService()->setNumThreads( numElevationThreads );
    }

    for (ImageLayerVector::const_iterator itr = mapf.imageLayers().begin(); itr != mapf.imageLayers().end(); ++itr)
    {
        const TerrainLayerOptions& opt = itr->get()->getImageLayerOptions();
        int imageThreads = (int)osg::round((float)_numLoadingThreads * (opt.loadingWeight().value() / totalWeight ));
        OE_INFO << LC << "Image Threads for " << itr->get()->getName() << " = " << imageThreads << std::endl;
        getImageryTaskService( itr->get()->getUID() )->setNumThreads( imageThreads );
    }
}
Exemple #3
0
void
TileBuilder::createTile(const TileKey&      key, 
                        bool                parallelize, 
                        osg::ref_ptr<Tile>& out_tile, 
                        bool&               out_hasRealData,
                        bool&               out_hasLodBlendedLayers )
{
    MapFrame mapf( _map, Map::MASKED_TERRAIN_LAYERS );

    SourceRepo repo;

    // init this to false, then search for real data. "Real data" is data corresponding
    // directly to the key, as opposed to fallback data, which is derived from a lower
    // LOD key.
    out_hasRealData = false;
    out_hasLodBlendedLayers = false;

    const MapInfo& mapInfo = mapf.getMapInfo();

    // If we need more than one layer, fetch them in parallel.
    // TODO: change the test based on isKeyValid total.
    if ( parallelize && (mapf.imageLayers().size() + mapf.elevationLayers().size() > 1) )
    {
        // count the valid layers.
        int jobCount = 0;

        for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i )
        {
            if ( i->get()->isKeyValid( key ) )
                ++jobCount;

            if ( i->get()->getImageLayerOptions().lodBlending() == true )
                out_hasLodBlendedLayers = true;
        }

        if ( mapf.elevationLayers().size() > 0 )
            ++jobCount;

        // A thread job monitoring event:
        Threading::MultiEvent semaphore( jobCount );

        // Start the image layer jobs:
        for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i )
        {
            ImageLayer* layer = i->get();
            if ( layer->isKeyValid(key) )
            {
                ParallelTask<BuildColorLayer>* j = new ParallelTask<BuildColorLayer>( &semaphore );
                j->init( key, layer, mapInfo, _terrainOptions, repo );
                j->setPriority( -(float)key.getLevelOfDetail() );
                _service->add( j );
            }
        }

        // If we have elevation layers, start an elevation job as well. Otherwise just create an
        // empty one while we're waiting for the images to load.
        if ( mapf.elevationLayers().size() > 0 )
        {
            ParallelTask<BuildElevLayer>* ej = new ParallelTask<BuildElevLayer>( &semaphore );
            ej->init( key, mapf, _terrainOptions, repo );
            ej->setPriority( -(float)key.getLevelOfDetail() );
            _service->add( ej );
        }
        else
        {
            BuildElevLayer build;
            build.init( key, mapf, _terrainOptions, repo );
            build.execute();
        }

        // Wait for all the jobs to finish.
        semaphore.wait();
    }
    
    // Fetch the image data serially:
    else
    {
        // gather all the image layers serially.
        for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i )
        {
            ImageLayer* layer = i->get();
            //if ( layer->isKeyValid(key) )  // Wrong. no guarantee key is in the same profile.

            if ( layer->getEnabled() )
            {
                BuildColorLayer build;
                build.init( key, layer, mapInfo, _terrainOptions, repo );
                build.execute();

                if ( layer->getImageLayerOptions().lodBlending() == true )
                    out_hasLodBlendedLayers = true;
            }
        }
        
        // make an elevation layer.
        BuildElevLayer build;
        build.init( key, mapf, _terrainOptions, repo );
        build.execute();
    }

    // Bail out now if there's no data to be had.
    if ( repo._colorLayers.size() == 0 && !repo._elevLayer.getHFLayer() )
    {
        return;
    }

    // OK we are making a tile, so if there's no heightfield yet, make an empty one.
    if ( !repo._elevLayer.getHFLayer() )
    {
        osg::HeightField* hf = HeightFieldUtils::createReferenceHeightField( key.getExtent(), 8, 8 );
        //osg::HeightField* hf = key.getProfile()->getVerticalSRS()->createReferenceHeightField( key.getExtent(), 8, 8 );
        osgTerrain::HeightFieldLayer* hfLayer = new osgTerrain::HeightFieldLayer( hf );
        hfLayer->setLocator( GeoLocator::createForKey(key, mapInfo) );
        repo._elevLayer = CustomElevLayer( hfLayer, true );
    }

    // Now, if there are any color layers that did not get built, create them with an empty
    // image so the shaders have something to draw.
    osg::ref_ptr<osg::Image> emptyImage;
    osgTerrain::Locator* locator = repo._elevLayer.getHFLayer()->getLocator();

    for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i )
    {
        ImageLayer* layer = i->get();

        if ( layer->getEnabled() && !layer->isKeyValid(key) )
        {
            if ( !emptyImage.valid() )
                emptyImage = ImageUtils::createEmptyImage();

            repo.add( CustomColorLayer(
                layer,
                emptyImage.get(),
                locator,
                key.getLevelOfDetail(),
                key,
                true ) );
        }
    }

    //osg::Vec3dArray* maskBounds = 0L;
    //osgEarth::MaskLayer* mask = mapf.getTerrainMaskLayer();
    //if (mask)
    //  maskBounds = mask->getOrCreateBoundary();

    // Ready to create the actual tile.
    AssembleTile assemble;
    assemble.init( key, mapInfo, _terrainOptions, repo, mapf.terrainMaskLayers() );
    assemble.execute();

    if (!out_hasRealData)
    {
        // Check the results and see if we have any real data.
        for( ColorLayersByUID::const_iterator i = repo._colorLayers.begin(); i != repo._colorLayers.end(); ++i )
        {
            if ( !i->second.isFallbackData() ) 
            {
                out_hasRealData = true;
                break;
            }
        }
    }

    if ( !out_hasRealData && !repo._elevLayer.isFallbackData() )
    {
        out_hasRealData = true;
    }

    out_tile = assemble._tile;
}
Exemple #4
0
void
TileBuilder::finalizeJob(TileBuilder::Job*   job, 
                         osg::ref_ptr<Tile>& out_tile,
                         bool&               out_hasRealData,
                         bool&               out_hasLodBlending)
{
    SourceRepo& repo = job->_repo;

    out_hasRealData = false;
    out_hasLodBlending = false;

    // Bail out now if there's no data to be had.
    if ( repo._colorLayers.size() == 0 && !repo._elevLayer.getHFLayer() )
    {
        return;
    }

    const TileKey& key = job->_key;
    const MapInfo& mapInfo = job->_mapf.getMapInfo();

    // OK we are making a tile, so if there's no heightfield yet, make an empty one.
    if ( !repo._elevLayer.getHFLayer() )
    {
        osg::HeightField* hf = HeightFieldUtils::createReferenceHeightField( key.getExtent(), 8, 8 );
        osgTerrain::HeightFieldLayer* hfLayer = new osgTerrain::HeightFieldLayer( hf );
        hfLayer->setLocator( GeoLocator::createForKey(key, mapInfo) );
        repo._elevLayer = CustomElevLayer( hfLayer, true );
    }

    // Now, if there are any color layers that did not get built, create them with an empty
    // image so the shaders have something to draw.
    osg::ref_ptr<osg::Image> emptyImage;
    osgTerrain::Locator* locator = repo._elevLayer.getHFLayer()->getLocator();

    for( ImageLayerVector::const_iterator i = job->_mapf.imageLayers().begin(); i != job->_mapf.imageLayers().end(); ++i )
    {
        ImageLayer* layer = i->get();

        if ( layer->getEnabled() )
        {
            if ( !layer->isKeyValid(key) )
            {
                if ( !emptyImage.valid() )
                    emptyImage = ImageUtils::createEmptyImage();

                repo.add( CustomColorLayer(
                    i->get(), emptyImage.get(),
                    locator,
                    key.getLevelOfDetail(),
                    key,
                    true ) );
            }

            if ( i->get()->getImageLayerOptions().lodBlending() == true )
                out_hasLodBlending = true;
        }
    }

    // Ready to create the actual tile.
    AssembleTile assemble;
    assemble.init( key, mapInfo, _terrainOptions, repo );
    assemble.execute();

    // Check the results and see if we have any real data.
    for( ColorLayersByUID::const_iterator i = repo._colorLayers.begin(); i != repo._colorLayers.end(); ++i )
    {
        if ( !i->second.isFallbackData() ) 
        {
            out_hasRealData = true;
            break;
        }
    }
    if ( !out_hasRealData && !repo._elevLayer.isFallbackData() )
    {
        out_hasRealData = true;
    }

    out_tile = assemble._tile;
}
Exemple #5
0
void CacheSeed::seed( Map* map )
{
    if ( !map->getCache() )
    {
        OE_WARN << LC << "Warning: No cache defined; aborting." << std::endl;
        return;
    }

    std::vector<TileKey> keys;
    map->getProfile()->getRootKeys(keys);

    //Add the map's entire extent if we don't have one specified.
    if (_extents.empty())
    {
        addExtent( map->getProfile()->getExtent() );
    }

    bool hasCaches = false;
    int src_min_level = INT_MAX;
    unsigned int src_max_level = 0;

    MapFrame mapf( map, Map::TERRAIN_LAYERS, "CacheSeed::seed" );

    //Assumes the the TileSource will perform the caching for us when we call createImage
    for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); i++ )
    {
        ImageLayer* layer = i->get();
        TileSource* src   = layer->getTileSource();

        const ImageLayerOptions& opt = layer->getImageLayerOptions();

        if ( layer->isCacheOnly() )
        {
            OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" is set to cache-only; skipping." << std::endl;
        }
        else if ( !src )
        {
            OE_WARN << "Warning: Layer \"" << layer->getName() << "\" could not create TileSource; skipping." << std::endl;
        }
        //else if ( src->getCachePolicyHint(0L) == CachePolicy::NO_CACHE )
        //{
        //    OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" does not support seeding; skipping." << std::endl;
        //}
        else if ( !layer->getCache() )
        {
            OE_WARN << LC << "Notice: Layer \"" << layer->getName() << "\" has no cache defined; skipping." << std::endl;
        }
        else
        {
            hasCaches = true;

            if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level)
                src_min_level = opt.minLevel().get();
            if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level)
                src_max_level = opt.maxLevel().get();
        }
    }

    for( ElevationLayerVector::const_iterator i = mapf.elevationLayers().begin(); i != mapf.elevationLayers().end(); i++ )
    {
        ElevationLayer* layer = i->get();
        TileSource*     src   = layer->getTileSource();
        const ElevationLayerOptions& opt = layer->getElevationLayerOptions();

        if ( layer->isCacheOnly() )
        {
            OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" is set to cache-only; skipping." << std::endl;
        }
        else if (!src)
        {
            OE_WARN << "Warning: Layer \"" << layer->getName() << "\" could not create TileSource; skipping." << std::endl;
        }
        //else if ( src->getCachePolicyHint(0L) == CachePolicy::NO_CACHE )
        //{
        //    OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" does not support seeding; skipping." << std::endl;
        //}
        else if ( !layer->getCache() )
        {
            OE_WARN << LC << "Notice: Layer \"" << layer->getName() << "\" has no cache defined; skipping." << std::endl;
        }
        else
        {
            hasCaches = true;

            if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level)
                src_min_level = opt.minLevel().get();
            if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level)
                src_max_level = opt.maxLevel().get();
        }
    }

    if ( !hasCaches )
    {
        OE_WARN << LC << "There are either no caches defined in the map, or no sources to cache; aborting." << std::endl;
        return;
    }

    if ( src_max_level > 0 && src_max_level < _maxLevel )
    {
        _maxLevel = src_max_level;
    }

    OE_NOTICE << LC << "Maximum cache level will be " << _maxLevel << std::endl;

    osg::Timer_t startTime = osg::Timer::instance()->tick();
    //Estimate the number of tiles
    _total = 0;    

    for (unsigned int level = _minLevel; level <= _maxLevel; level++)
    {
        double coverageRatio = 0.0;

        if (_extents.empty())
        {
            unsigned int wide, high;
            map->getProfile()->getNumTiles( level, wide, high );
            _total += (wide * high);
        }
        else
        {
            for (std::vector< GeoExtent >::const_iterator itr = _extents.begin(); itr != _extents.end(); itr++)
            {
                const GeoExtent& extent = *itr;
                double boundsArea = extent.area();

                TileKey ll = map->getProfile()->createTileKey(extent.xMin(), extent.yMin(), level);
                TileKey ur = map->getProfile()->createTileKey(extent.xMax(), extent.yMax(), level);

                if (!ll.valid() || !ur.valid()) continue;
                
                int tilesWide = ur.getTileX() - ll.getTileX() + 1;
                int tilesHigh = ll.getTileY() - ur.getTileY() + 1;
                int tilesAtLevel = tilesWide * tilesHigh;
                //OE_NOTICE << "Tiles at level " << level << "=" << tilesAtLevel << std::endl;

                /*
                bool hasData = false;
                
                for (ImageLayerVector::const_iterator itr = mapf.imageLayers().begin(); itr != mapf.imageLayers().end(); itr++)
                {
                    TileSource* src = itr->get()->getTileSource();
                    if (src)
                    {
                        if (src->hasDataAtLOD( level ))
                        {
                            //Compute the percent coverage of this dataset on the current extent
                            if (src->getDataExtents().size() > 0)
                            {
                                double cov = 0.0;
                                for (unsigned int j = 0; j < src->getDataExtents().size(); j++)
                                {
                                    GeoExtent b = src->getDataExtents()[j].transform( extent.getSRS());
                                    GeoExtent intersection = b.intersectionSameSRS( extent );
                                    if (intersection.isValid())
                                    {
                                        double coverage = intersection.area() / boundsArea;
                                        cov += coverage; //Assumes the extents aren't overlapping                            
                                    }
                                }
                                if (coverageRatio < cov) coverageRatio = cov;
                            }
                            else
                            {
                                //We have no way of knowing how much coverage we have
                                coverageRatio = 1.0;
                            }
                            hasData = true;
                            break;
                        }
                    }
                }

                for (ElevationLayerVector::const_iterator itr = mapf.elevationLayers().begin(); itr != mapf.elevationLayers().end(); itr++)
                {
                    TileSource* src = itr->get()->getTileSource();
                    if (src)
                    {
                        if (src->hasDataAtLOD( level ))
                        {
                            //Compute the percent coverage of this dataset on the current extent
                            if (src->getDataExtents().size() > 0)
                            {
                                double cov = 0.0;
                                for (unsigned int j = 0; j < src->getDataExtents().size(); j++)
                                {
                                    GeoExtent b = src->getDataExtents()[j].transform( extent.getSRS());
                                    GeoExtent intersection = b.intersectionSameSRS( extent );
                                    if (intersection.isValid())
                                    {
                                        double coverage = intersection.area() / boundsArea;
                                        cov += coverage; //Assumes the extents aren't overlapping                            
                                    }
                                }
                                if (coverageRatio < cov) coverageRatio = cov;
                            }
                            else
                            {
                                //We have no way of knowing how much coverage we have
                                coverageRatio = 1.0;
                            }
                            hasData = true;
                            break;
                        }
                    }
                }

                //Adjust the coverage ratio by a fudge factor to try to keep it from being too small,
                //tiles are either processed or not and the ratio is exact so will cover tiles partially
                //and potentially be too small
                double adjust = 4.0;
                coverageRatio = osg::clampBetween(coverageRatio * adjust, 0.0, 1.0);                

                //OE_NOTICE << level <<  " CoverageRatio = " << coverageRatio << std::endl;

                if (hasData)
                {
                    _total += (int)ceil(coverageRatio * (double)tilesAtLevel );
                }
                */
                _total += tilesAtLevel;
            }
        }
    }

    osg::Timer_t endTime = osg::Timer::instance()->tick();
    //OE_NOTICE << "Counted tiles in " << osg::Timer::instance()->delta_s(startTime, endTime) << " s" << std::endl;

    OE_INFO << "Processing ~" << _total << " tiles" << std::endl;

    for (unsigned int i = 0; i < keys.size(); ++i)
    {
        processKey( mapf, keys[i] );
    }

    _total = _completed;

    if ( _progress.valid()) _progress->reportProgress(_completed, _total, 0, 1, "Finished");
}
Exemple #6
0
bool
Map::sync( MapFrame& frame ) const
{
    bool result = false;

    if ( frame._mapDataModelRevision != _dataModelRevision || !frame._initialized )
    {
        // hold the read lock while copying the layer lists.
        Threading::ScopedReadLock lock( const_cast<Map*>(this)->_mapDataMutex );

        if ( frame._parts & IMAGE_LAYERS )
        {
            if ( !frame._initialized )
                frame._imageLayers.reserve( _imageLayers.size() );
            frame._imageLayers.clear();
            if ( frame._copyValidDataOnly )
            {
                for( ImageLayerVector::const_iterator i = _imageLayers.begin(); i != _imageLayers.end(); ++i )
                    if ( i->get()->getProfile() )
                        frame._imageLayers.push_back( i->get() );
            }
            else
                std::copy( _imageLayers.begin(), _imageLayers.end(), std::back_inserter(frame._imageLayers) );
        }

        if ( frame._parts & ELEVATION_LAYERS )
        {
            if ( !frame._initialized )
                frame._elevationLayers.reserve( _elevationLayers.size() );
            frame._elevationLayers.clear();
            if ( frame._copyValidDataOnly )
            {
                for( ElevationLayerVector::const_iterator i = _elevationLayers.begin(); i != _elevationLayers.end(); ++i )
                    if ( i->get()->getProfile() )
                        frame._elevationLayers.push_back( i->get() );
            }
            else
                std::copy( _elevationLayers.begin(), _elevationLayers.end(), std::back_inserter(frame._elevationLayers) );
        }

        if ( frame._parts & MODEL_LAYERS )
        {
            if ( !frame._initialized )
                frame._modelLayers.reserve( _modelLayers.size() );
            frame._modelLayers.clear();
            std::copy( _modelLayers.begin(), _modelLayers.end(), std::back_inserter(frame._modelLayers) );
        }

        if ( frame._parts & MASK_LAYERS )
        {
          if ( !frame._initialized )
              frame._maskLayers.reserve( _terrainMaskLayers.size() );
          frame._maskLayers.clear();
          std::copy( _terrainMaskLayers.begin(), _terrainMaskLayers.end(), std::back_inserter(frame._maskLayers) );
        }

        // sync the revision numbers.
        frame._initialized = true;
        frame._mapDataModelRevision = _dataModelRevision;
            
        result = true;
    }    
    return result;
}
Exemple #7
0
void
MPGeometry::renderPrimitiveSets(osg::State& state,
                                bool        usingVBOs) const
{
    // check the map frame to see if it's up to date
    if ( _frame.needsSync() )
    {
        // this lock protects a MapFrame sync when we have multiple DRAW threads.
        Threading::ScopedMutexLock exclusive( _frameSyncMutex );

        if ( _frame.needsSync() && _frame.sync() ) // always double check
        {
            // This should only happen is the layer ordering changes;
            // If layers are added or removed, the Tile gets rebuilt and
            // the point is moot.
            std::vector<Layer> reordered;
            const ImageLayerVector& layers = _frame.imageLayers();
            reordered.reserve( layers.size() );
            for( ImageLayerVector::const_iterator i = layers.begin(); i != layers.end(); ++i )
            {
                std::vector<Layer>::iterator j = std::find( _layers.begin(), _layers.end(), i->get()->getUID() );
                if ( j != _layers.end() )
                    reordered.push_back( *j );
            }
            _layers.swap( reordered );
        }
    }

    unsigned layersDrawn = 0;


    osg::ref_ptr<osg::GL2Extensions> ext = osg::GL2Extensions::Get( state.getContextID(), true );
    const osg::Program::PerContextProgram* pcp = state.getLastAppliedProgramObject();

    GLint opacityLocation;
    GLint uidLocation;
    GLint orderLocation;
    GLint texMatParentLocation;

    // yes, it's possible that the PCP is not set up yet.
    // TODO: can we optimize this so we don't need to get uni locations every time?
    if ( pcp )
    {
        opacityLocation      = pcp->getUniformLocation( _opacityUniform->getNameID() );
        uidLocation          = pcp->getUniformLocation( _layerUIDUniform->getNameID() );
        orderLocation        = pcp->getUniformLocation( _layerOrderUniform->getNameID() );
        texMatParentLocation = pcp->getUniformLocation( _texMatParentUniform->getNameID() );
    }

    // activate the tile coordinate set - same for all layers
    state.setTexCoordPointer( _imageUnit+1, _tileCoords.get() );

    if ( _layers.size() > 0 )
    {
        float prev_opacity        = -1.0f;
        float prev_alphaThreshold = -1.0f;

        // first bind any shared layers
        // TODO: optimize by pre-storing shared indexes
        for(unsigned i=0; i<_layers.size(); ++i)
        {
            const Layer& layer = _layers[i];

            // a "shared" layer binds to a secondary texture unit so that other layers
            // can see it and use it.
            if ( layer._imageLayer->isShared() )
            {
                int sharedUnit = layer._imageLayer->shareImageUnit().get();
                {
                    state.setActiveTextureUnit( sharedUnit );
                    state.setTexCoordPointer( sharedUnit, layer._texCoords.get() );
                    // bind the texture for this layer to the active share unit.
                    layer._tex->apply( state );

                    // no texture LOD blending for shared layers for now. maybe later.
                }
            }
        }

        // track the active image unit.
        int activeImageUnit = -1;

        // interate over all the image layers
        for(unsigned i=0; i<_layers.size(); ++i)
        {
            const Layer& layer = _layers[i];

            if ( layer._imageLayer->getVisible() )
            {
                // activate the visible unit if necessary:
                if ( activeImageUnit != _imageUnit )
                {
                    state.setActiveTextureUnit( _imageUnit );
                    activeImageUnit = _imageUnit;
                }

                // bind the texture for this layer:
                layer._tex->apply( state );

                // if we're using a parent texture for blending, activate that now
                if ( layer._texParent.valid() )
                {
                    state.setActiveTextureUnit( _imageUnitParent );
                    activeImageUnit = _imageUnitParent;
                    layer._texParent->apply( state );
                }

                // bind the texture coordinates for this layer.
                // TODO: can probably optimize this by sharing or using texture matrixes.
                // State::setTexCoordPointer does some redundant work under the hood.
                state.setTexCoordPointer( _imageUnit, layer._texCoords.get() );

                // apply uniform values:
                if ( pcp )
                {
                    // apply opacity:
                    float opacity = layer._imageLayer->getOpacity();
                    if ( opacity != prev_opacity )
                    {
                        _opacityUniform->set( opacity );
                        _opacityUniform->apply( ext, opacityLocation );
                        prev_opacity = opacity;
                    }

                    // assign the layer UID:
                    _layerUIDUniform->set( layer._layerID );
                    _layerUIDUniform->apply( ext, uidLocation );

                    // assign the layer order:
                    _layerOrderUniform->set( (int)layersDrawn );
                    _layerOrderUniform->apply( ext, orderLocation );

                    // assign the parent texture matrix
                    if ( layer._texParent.valid() )
                    {
                        _texMatParentUniform->set( layer._texMatParent );
                        _texMatParentUniform->apply( ext, texMatParentLocation );
                    }
                }

                // draw the primitive sets.
                for(unsigned int primitiveSetNum=0; primitiveSetNum!=_primitives.size(); ++primitiveSetNum)
                {
                    const osg::PrimitiveSet* primitiveset = _primitives[primitiveSetNum].get();
                    primitiveset->draw(state, usingVBOs);
                }

                ++layersDrawn;
            }
        }

        // prevent texture leakage
        glBindTexture( GL_TEXTURE_2D, 0 );
    }

    // if we didn't draw anything, draw the raw tiles anyway with no texture.
    if ( layersDrawn == 0 )
    {
        _opacityUniform->set( 1.0f );
        _opacityUniform->apply( ext, opacityLocation );

        _layerUIDUniform->set( (int)-1 ); // indicates a non-textured layer
        _layerUIDUniform->apply( ext, uidLocation );

        _layerOrderUniform->set( (int)0 );
        _layerOrderUniform->apply( ext, orderLocation );

        // draw the primitives themselves.
        for(unsigned int primitiveSetNum=0; primitiveSetNum!=_primitives.size(); ++primitiveSetNum)
        {
            const osg::PrimitiveSet* primitiveset = _primitives[primitiveSetNum].get();
            primitiveset->draw(state, usingVBOs);
        }
    }
}
osg::Image*
CompositeTileSource::createImage(const TileKey&    key,
                                 ProgressCallback* progress )
{    
    ImageMixVector images;
    images.reserve(_imageLayers.size());

    // Try to get an image from each of the layers for the given key.
    for (ImageLayerVector::const_iterator itr = _imageLayers.begin(); itr != _imageLayers.end(); ++itr)
    {
        ImageLayer* layer = itr->get();
        ImageInfo imageInfo;
        imageInfo.dataInExtents = layer->getTileSource()->hasDataInExtent( key.getExtent() );
        imageInfo.opacity = layer->getOpacity();

        if (imageInfo.dataInExtents)
        {
            GeoImage image = layer->createImage(key, progress);
            if (image.valid())
            {
                imageInfo.image = image.getImage();
            }

            // If the progress got cancelled or it needs a retry then return NULL to prevent this tile from being built and cached with incomplete or partial data.
            if (progress && (progress->isCanceled() || progress->needsRetry()))
            {
                OE_DEBUG << LC << " createImage was cancelled or needs retry for " << key.str() << std::endl;
                return 0L;
            }
        }

        images.push_back(imageInfo);
    }

    // Determine the output texture size to use based on the image that were creatd.
    unsigned numValidImages = 0;
    osg::Vec2s textureSize;
    for (unsigned int i = 0; i < images.size(); i++)
    {
        ImageInfo& info = images[i];
        if (info.image.valid())
        {
            if (numValidImages == 0)
            {
                textureSize.set( info.image->s(), info.image->t());
            }
            numValidImages++;        
        }
    } 

    // Create fallback images if we have some valid data but not for all the layers
    if (numValidImages > 0 && numValidImages < images.size())
    {
        for (unsigned int i = 0; i < images.size(); i++)
        {
            ImageInfo& info = images[i];
            ImageLayer* layer = _imageLayers[i].get();
            if (!info.image.valid() && info.dataInExtents)
            {                      
                TileKey parentKey = key.createParentKey();

                GeoImage image;
                while (!image.valid() && parentKey.valid())
                {
                    image = layer->createImage(parentKey, progress);
                    if (image.valid())
                    {
                        break;
                    }

                    // If the progress got cancelled or it needs a retry then return NULL to prevent this tile from being built and cached with incomplete or partial data.
                    if (progress && (progress->isCanceled() || progress->needsRetry()))
                    {
                        OE_DEBUG << LC << " createImage was cancelled or needs retry for " << key.str() << std::endl;
                        return 0L;
                    }

                    parentKey = parentKey.createParentKey();
                }

                if (image.valid())
                {                                        
                    // TODO:  Bilinear options?
                    bool bilinear = layer->isCoverage() ? false : true;
                    GeoImage cropped = image.crop( key.getExtent(), true, textureSize.x(), textureSize.y(), bilinear);
                    info.image = cropped.getImage();
                }                    
            }
        }
    }

    // Now finally create the output image.
    //Recompute the number of valid images
    numValidImages = 0;
    for (unsigned int i = 0; i < images.size(); i++)
    {
        ImageInfo& info = images[i];
        if (info.image.valid()) numValidImages++;        
    }    

    if ( progress && progress->isCanceled() )
    {
        return 0L;
    }
    else if ( numValidImages == 0 )
    {
        return 0L;
    }
    else if ( numValidImages == 1 )
    {
        //We only have one valid image, so just return it and don't bother with compositing
        for (unsigned int i = 0; i < images.size(); i++)
        {
            ImageInfo& info = images[i];
            if (info.image.valid())
                return info.image.release();
        }
        return 0L;
    }
    else
    {
        osg::Image* result = 0;
        for (unsigned int i = 0; i < images.size(); i++)
        {
            ImageInfo& imageInfo = images[i];
            if (!result)
            {
                if (imageInfo.image.valid())
                {
                    result = new osg::Image( *imageInfo.image.get());
                }
            }
            else
            {
                if (imageInfo.image.valid())
                {
                    ImageUtils::mix( result, imageInfo.image.get(), imageInfo.opacity );
                }
            }            
        }        
        return result;
    }



}
void
TileModelFactory::createTileModel(const TileKey&           key, 
                                  const MapFrame&          frame,
                                  bool                     accumulate,
                                  osg::ref_ptr<TileModel>& out_model,
                                  ProgressCallback*        progress)
{

    osg::ref_ptr<TileModel> model = new TileModel( frame.getRevision(), frame.getMapInfo() );

    model->_useParentData = _terrainReqs->parentTexturesRequired();

    model->_tileKey = key;
    model->_tileLocator = GeoLocator::createForKey(key, frame.getMapInfo());

    OE_START_TIMER(fetch_imagery);

    // Fetch the image data and make color layers.
    unsigned index = 0;
    unsigned order = 0;
    for( ImageLayerVector::const_iterator i = frame.imageLayers().begin(); i != frame.imageLayers().end(); ++i )
    {
        ImageLayer* layer = i->get();

        if ( layer->getEnabled() && layer->isKeyInRange(key) )
        {
            BuildColorData build;
            build.init( key, layer, order, frame.getMapInfo(), _terrainOptions, _liveTiles.get(), model.get() );

            bool addedToModel = build.execute(progress);
            if ( addedToModel )
            {
                // only bump the order if we added something to the data model.
                order++;
            }
        }
    }

    if (progress)
        progress->stats()["fetch_imagery_time"] += OE_STOP_TIMER(fetch_imagery);

    
    // make an elevation layer.
    OE_START_TIMER(fetch_elevation);
    buildElevation(key, frame, accumulate, _terrainReqs->elevationTexturesRequired(), model.get(), progress);
    if (progress)
        progress->stats()["fetch_elevation_time"] += OE_STOP_TIMER(fetch_elevation);
    
    // make a normal map layer (if necessary)
    if ( _terrainReqs->normalTexturesRequired() )
    {
        OE_START_TIMER(fetch_normalmap);
        buildNormalMap(key, frame, accumulate, model.get(), progress);
        if (progress)
            progress->stats()["fetch_normalmap_time"] += OE_STOP_TIMER(fetch_normalmap);
    }

    // If nothing was added, not even a fallback heightfield, something went
    // horribly wrong. Leave without a tile model. Chances are that a parent tile
    // not not found in the live-tile registry.
    if ( model->_colorData.size() == 0 && !model->_elevationData.getHeightField() )
    {
        return;
    }

    // OK we are making a tile, so if there's no heightfield yet, make an empty one (and mark it
    // as fallback data of course)
    if ( !model->_elevationData.getHeightField() )
    {
        osg::HeightField* hf = HeightFieldUtils::createReferenceHeightField( key.getExtent(), 15, 15 );
        model->_elevationData = TileModel::ElevationData(
            hf,
            GeoLocator::createForKey(key, frame.getMapInfo()),
            true );
    }

    // look up the parent model and cache it.
    osg::ref_ptr<TileNode> parentTile;
    if ( _liveTiles->get(key.createParentKey(), parentTile) )
    {
        model->_parentModel = parentTile->getTileModel();
    }

    out_model = model.release();
}
Exemple #10
0
void
TileModelFactory::createTileModel(const TileKey&           key, 
                                  osg::ref_ptr<TileModel>& out_model,
                                  bool&                    out_hasRealData,
                                  bool&                    out_hasLodBlendedLayers )
{
    MapFrame mapf( _map, Map::MASKED_TERRAIN_LAYERS );
    
    const MapInfo& mapInfo = mapf.getMapInfo();

    osg::ref_ptr<TileModel> model = new TileModel();
    model->_tileKey = key;
    model->_tileLocator = GeoLocator::createForKey(key, mapInfo);

    // init this to false, then search for real data. "Real data" is data corresponding
    // directly to the key, as opposed to fallback data, which is derived from a lower
    // LOD key.
    out_hasRealData = false;
    out_hasLodBlendedLayers = false;
    
    // Fetch the image data and make color layers.
    for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i )
    {
        ImageLayer* layer = i->get();

        if ( layer->getEnabled() )
        {
            BuildColorData build;
            build.init( key, layer, mapInfo, _terrainOptions, model.get() );
            build.execute();

            if ( layer->getImageLayerOptions().lodBlending() == true )
            {
                out_hasLodBlendedLayers = true;
            }
        }
    }

    // make an elevation layer.
    BuildElevationData build;
    build.init( key, mapf, _terrainOptions, model.get(), _hfCache );
    build.execute();


    // Bail out now if there's no data to be had.
    if ( model->_colorData.size() == 0 && !model->_elevationData.getHFLayer() )
    {
        return;
    }

    // OK we are making a tile, so if there's no heightfield yet, make an empty one.
    if ( !model->_elevationData.getHFLayer() )
    {
        osg::HeightField* hf = HeightFieldUtils::createReferenceHeightField( key.getExtent(), 8, 8 );
        osgTerrain::HeightFieldLayer* hfLayer = new osgTerrain::HeightFieldLayer( hf );
        hfLayer->setLocator( GeoLocator::createForKey(key, mapInfo) );
        model->_elevationData = TileModel::ElevationData( hfLayer, true );
    }

    // Now, if there are any color layers that did not get built, create them with an empty
    // image so the shaders have something to draw.
    osg::ref_ptr<osg::Image> emptyImage;
    osgTerrain::Locator* locator = model->_elevationData.getHFLayer()->getLocator();

    for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i )
    {
        ImageLayer* layer = i->get();

        if ( layer->getEnabled() && !layer->isKeyValid(key) )
        {
            if ( !emptyImage.valid() )
                emptyImage = ImageUtils::createEmptyImage();

            model->_colorData[i->get()->getUID()] = TileModel::ColorData(
                layer,
                emptyImage.get(),
                locator,
                key.getLevelOfDetail(),
                key,
                true );
        }
    }

    // Ready to create the actual tile.
    //AssembleTile assemble;
    //assemble.init( key, mapInfo, _terrainOptions, model.get(), mapf.terrainMaskLayers() );
    //assemble.execute();

    // if we're using LOD blending, find and add the parent's state set.
    if ( out_hasLodBlendedLayers && key.getLevelOfDetail() > 0 && _liveTiles.valid() )
    {
        osg::ref_ptr<TileNode> parent;
        if ( _liveTiles->get( key.createParentKey(), parent ) )
        {
            model->_parentStateSet = parent->getPublicStateSet();
        }
    }

    if (!out_hasRealData)
    {
        // Check the results and see if we have any real data.
        for( TileModel::ColorDataByUID::const_iterator i = model->_colorData.begin(); i != model->_colorData.end(); ++i )
        {
            if ( !i->second.isFallbackData() ) 
            {
                out_hasRealData = true;
                break;
            }
        }
    }

    if ( !out_hasRealData && !model->_elevationData.isFallbackData() )
    {
        out_hasRealData = true;
    }

    out_model = model.release();
    //out_tile = assemble._node;
}
Exemple #11
0
osg::Node*
OSGTileFactory::createPopulatedTile(const MapFrame&  mapf, 
                                    Terrain*         terrain, 
                                    const TileKey&   key, 
                                    bool             wrapInPagedLOD, 
                                    bool             fallback, 
                                    bool&            validData )
{
    const MapInfo& mapInfo = mapf.getMapInfo();
    bool isPlateCarre = !mapInfo.isGeocentric() && mapInfo.isGeographicSRS();

    typedef std::vector<GeoImageData> GeoImageDataVector;
    GeoImageDataVector image_tiles;

    // Collect the image layers
    bool empty_map = mapf.imageLayers().size() == 0 && mapf.elevationLayers().size() == 0;

    // Create the images for the tile
    for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i )
    {
        ImageLayer* layer = i->get();
        GeoImageData imageData;

        // Only try to create images if the key is valid
        if ( layer->isKeyValid( key ) )
        {
            imageData._image = layer->createImage( key );
            imageData._layerUID = layer->getUID();
            imageData._imageTileKey = key;
        }

        // always push images, even it they are empty, so that the image_tiles vector is one-to-one
        // with the imageLayers() vector.
        image_tiles.push_back( imageData );
    }

    bool hasElevation = false;

    //Create the heightfield for the tile
    osg::ref_ptr<osg::HeightField> hf;
    if ( mapf.elevationLayers().size() > 0 )
    {
        mapf.getHeightField( key, false, hf, 0L, _terrainOptions.elevationInterpolation().value());     
    }

    //If we are on the first LOD and we couldn't get a heightfield tile, just create an empty one.  Otherwise you can run into the situation
    //where you could have an inset heightfield on one hemisphere and the whole other hemisphere won't show up.
    if ( mapInfo.isGeocentric() && key.getLevelOfDetail() <= 1 && !hf.valid())
    {
        hf = createEmptyHeightField( key );
    }
    hasElevation = hf.valid();

    //Determine if we've created any images
    unsigned int numValidImages = 0;
    for (unsigned int i = 0; i < image_tiles.size(); ++i)
    {
        if (image_tiles[i]._image.valid()) numValidImages++;
    }


    //If we couldn't create any imagery or heightfields, bail out
    if (!hf.valid() && (numValidImages == 0) && !empty_map)
    {
        OE_DEBUG << LC << "Could not create any imagery or heightfields for " << key.str() <<".  Not building tile" << std::endl;
        validData = false;

        //If we're not asked to fallback on previous LOD's and we have no data, return NULL
        if (!fallback)
        {
            return NULL;
        }
    }
    else
    {
        validData = true;
    }

    //Try to interpolate any missing image layers from parent tiles
    for (unsigned int i = 0; i < mapf.imageLayers().size(); i++ )
    {
        if (!image_tiles[i]._image.valid())
        {
            if (mapf.getImageLayerAt(i)->isKeyValid(key))
            {
                //If the key was valid and we have no image, then something possibly went wrong with the image creation such as a server being busy.
                createValidGeoImage(mapf.getImageLayerAt(i), key, image_tiles[i]._image, image_tiles[i]._imageTileKey);
            }

            //If we still couldn't create an image, either something is really wrong or the key wasn't valid, so just create a transparent placeholder image
            if (!image_tiles[i]._image.valid())
            {
                //If the image is not valid, create an empty texture as a placeholder
                image_tiles[i]._image = GeoImage(ImageUtils::createEmptyImage(), key.getExtent());
                image_tiles[i]._imageTileKey = key;
            }
        }
    }

    //Fill in missing heightfield information from parent tiles
    if (!hf.valid())
    {
        //We have no heightfield sources, 
        if ( mapf.elevationLayers().size() == 0 )
        {
            hf = createEmptyHeightField( key );
        }
        else
        {
            //Try to get a heightfield again, but this time fallback on parent tiles
            if ( mapf.getHeightField( key, true, hf, 0L, _terrainOptions.elevationInterpolation().value() ) )
            {
                hasElevation = true;
            }
            else
            {
                //We couldn't get any heightfield, so just create an empty one.
                hf = createEmptyHeightField( key );
            }
        }
    }


    // In a Plate Carre tesselation, scale the heightfield elevations from meters to degrees
    if ( isPlateCarre )
    {
        HeightFieldUtils::scaleHeightFieldToDegrees( hf.get() );
    }

    osg::ref_ptr<GeoLocator> locator = GeoLocator::createForKey( key, mapInfo );
    osgTerrain::HeightFieldLayer* hf_layer = new osgTerrain::HeightFieldLayer();
    hf_layer->setLocator( locator.get() );
    hf_layer->setHeightField( hf.get() );

    bool isStreaming = 
        _terrainOptions.loadingPolicy()->mode() == LoadingPolicy::MODE_SEQUENTIAL ||
        _terrainOptions.loadingPolicy()->mode() == LoadingPolicy::MODE_PREEMPTIVE;

    Tile* tile = terrain->createTile( key, locator.get() );
    tile->setTerrainTechnique( terrain->cloneTechnique() );
    tile->setVerticalScale( _terrainOptions.verticalScale().value() );
    //tile->setLocator( locator.get() );
    tile->setElevationLayer( hf_layer );
    //tile->setRequiresNormals( true );
    tile->setDataVariance(osg::Object::DYNAMIC);

#if 0
    //Attach an updatecallback to normalize the edges of TerrainTiles.
    if (hasElevation && _terrainOptions.normalizeEdges().get() )
    {
        tile->setUpdateCallback(new TerrainTileEdgeNormalizerUpdateCallback());
        tile->setDataVariance(osg::Object::DYNAMIC);
    }
#endif

    //Assign the terrain system to the TerrainTile.
    //It is very important the terrain system is set while the MapConfig's sourceMutex is locked.
    //This registers the terrain tile so that adding/removing layers are always in sync.  If you don't do this
    //you can end up with a situation where the database pager is waiting to merge a tile, then a layer is added, then
    //the tile is finally merged and is out of sync.

    double min_units_per_pixel = DBL_MAX;

#if 0
    // create contour layer:
    if (map->getContourTransferFunction() != NULL)
    {
        osgTerrain::ContourLayer* contourLayer(new osgTerrain::ContourLayer(map->getContourTransferFunction()));

        contourLayer->setMagFilter(_terrainOptions.getContourMagFilter().value());
        contourLayer->setMinFilter(_terrainOptions.getContourMinFilter().value());
        tile->setCustomColorLayer(layer,contourLayer); //TODO: need layerUID, not layer index here -GW
        ++layer;
    }
#endif

    for (unsigned int i = 0; i < image_tiles.size(); ++i)
    {
        if (image_tiles[i]._image.valid())
        {
            const GeoImage& geo_image = image_tiles[i]._image;

            double img_xmin, img_ymin, img_xmax, img_ymax;
            geo_image.getExtent().getBounds( img_xmin, img_ymin, img_xmax, img_ymax );

            //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 = key.getProfile()->getSRS()->createLocator( 
                img_xmin, img_ymin, img_xmax, img_ymax,
                isPlateCarre );

            if ( mapInfo.isGeocentric() )
                img_locator->setCoordinateSystemType( osgTerrain::Locator::GEOCENTRIC );

            tile->setCustomColorLayer( CustomColorLayer(
                mapf.getImageLayerAt(i),
                geo_image.getImage(),
                img_locator.get(),
                key.getLevelOfDetail(),
                key) );

            double upp = geo_image.getUnitsPerPixel();

            // Scale the units per pixel to degrees if the image is mercator (and the key is geo)
            if ( geo_image.getSRS()->isMercator() && key.getExtent().getSRS()->isGeographic() )
                upp *= 1.0f/111319.0f;

            min_units_per_pixel = osg::minimum(upp, min_units_per_pixel);
        }
    }

    osg::BoundingSphere bs = tile->getBound();
    double max_range = 1e10;
    double radius = bs.radius();

#if 1
    double min_range = radius * _terrainOptions.minTileRangeFactor().get();
    //osg::LOD::RangeMode mode = osg::LOD::DISTANCE_FROM_EYE_POINT;
#else
    double width = key.getExtent().width();	
    if (min_units_per_pixel == DBL_MAX) min_units_per_pixel = width/256.0;
    double min_range = (width / min_units_per_pixel) * _terrainOptions.getMinTileRangeFactor(); 
    //osg::LOD::RangeMode mode = osg::LOD::PIXEL_SIZE_ON_SCREEN;
#endif


    // a skirt hides cracks when transitioning between LODs:
    hf->setSkirtHeight(radius * _terrainOptions.heightFieldSkirtRatio().get() );

    // for now, cluster culling does not work for CUBE rendering
    //bool isCube = mapInfo.isCube(); //map->getMapOptions().coordSysType() == MapOptions::CSTYPE_GEOCENTRIC_CUBE;
    if ( mapInfo.isGeocentric() && !mapInfo.isCube() )
    {
        //TODO:  Work on cluster culling computation for cube faces
        osg::ClusterCullingCallback* ccc = createClusterCullingCallback(tile, locator->getEllipsoidModel() );
        tile->setCullCallback( ccc );
    }

    // Wait until now, when the tile is fully baked, to assign the terrain to the tile.
    // Placeholder tiles might try to locate this tile as an ancestor, and access its layers
    // and locators...so they must be intact before making this tile available via setTerrain.
    //
    // If there's already a placeholder tile registered, this will be ignored. If there isn't,
    // this will register the new tile.
    tile->attachToTerrain( terrain );
    //tile->setTerrain( terrain );
    //terrain->registerTile( tile );

    if ( isStreaming && key.getLevelOfDetail() > 0 )
    {
        static_cast<StreamingTile*>(tile)->setHasElevationHint( hasElevation );
    }

    osg::Node* result = 0L;

    if (wrapInPagedLOD)
    {
        // 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 );

        std::string filename = createURI( _engineId, key ); //map->getId(), key );

        //Only add the next tile if it hasn't been blacklisted
        bool isBlacklisted = osgEarth::Registry::instance()->isBlacklisted( filename );
        if (!isBlacklisted && key.getLevelOfDetail() < (unsigned int)getTerrainOptions().maxLOD().value() && validData )
        {
            plod->setFileName( 1, filename  );
            plod->setRange( 1, 0.0, min_range );
        }
        else
        {
            plod->setRange( 0, 0, FLT_MAX );
        }

#if USE_FILELOCATIONCALLBACK
        osgDB::Options* options = new osgDB::Options;
        options->setFileLocationCallback( new FileLocationCallback() );
        plod->setDatabaseOptions( options );
#endif
        result = plod;

        if ( isStreaming )
            result->addCullCallback( new PopulateStreamingTileDataCallback( _cull_thread_mapf ) );
    }
    else
    {
        result = tile;
    }

    return result;
}
void
TerrainEngineNode::updateImageUniforms()
{
    // don't bother if this is a hurting old card
    if ( !Registry::instance()->getCapabilities().supportsGLSL() )
        return;

    // update the layer uniform arrays:
    osg::StateSet* stateSet = this->getOrCreateStateSet();

    // get a copy of the image layer stack:
    MapFrame mapf( _map.get(), Map::IMAGE_LAYERS );

    _imageLayerController->_layerEnabledUniform.detach();
    _imageLayerController->_layerOpacityUniform.detach();
    _imageLayerController->_layerRangeUniform.detach();

#if 0
    if ( _imageLayerController->_layerEnabledUniform.valid() )
        _imageLayerController->_layerEnabledUniform->removeFrom( stateSet );

    if ( _imageLayerController->_layerOpacityUniform.valid() )
        _imageLayerController->_layerOpacityUniform->removeFrom( stateSet );

    if ( _imageLayerController->_layerRangeUniform.valid() )
        _imageLayerController->_layerRangeUniform->removeFrom( stateSet );
#endif

    //stateSet->removeUniform( "osgearth_ImageLayerAttenuation" );
    
    if ( mapf.imageLayers().size() > 0 )
    {
        // the "enabled" uniform is fixed size. this is handy to account for layers that are in flux...i.e., their source
        // layer count has changed, but the shader has not yet caught up. In the future we might use this to disable
        // "ghost" layers that used to exist at a given index, but no longer do.
        
        _imageLayerController->_layerEnabledUniform.attach( "osgearth_ImageLayerEnabled", osg::Uniform::BOOL,  stateSet, 64 );
        _imageLayerController->_layerOpacityUniform.attach( "osgearth_ImageLayerOpacity", osg::Uniform::FLOAT, stateSet, mapf.imageLayers().size() );
        _imageLayerController->_layerRangeUniform.attach  ( "osgearth_ImageLayerRange",   osg::Uniform::FLOAT, stateSet, 2 * mapf.imageLayers().size() );

        //_imageLayerController->_layerEnabledUniform  = new ArrayUniform( osg::Uniform::BOOL,  "osgearth_ImageLayerEnabled", 64 ); //mapf.imageLayers().size() );
        //_imageLayerController->_layerOpacityUniform  = new ArrayUniform( osg::Uniform::FLOAT, "osgearth_ImageLayerOpacity", mapf.imageLayers().size() );
        //_imageLayerController->_layerRangeUniform    = new ArrayUniform( osg::Uniform::FLOAT, "osgearth_ImageLayerRange", 2 * mapf.imageLayers().size() );

        for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i )
        {
            ImageLayer* layer = i->get();
            int index = (int)(i - mapf.imageLayers().begin());

            _imageLayerController->_layerOpacityUniform.setElement( index, layer->getOpacity() );
            _imageLayerController->_layerEnabledUniform.setElement( index, layer->getEnabled() );
            _imageLayerController->_layerRangeUniform.setElement( (2*index), layer->getImageLayerOptions().minVisibleRange().value() );
            _imageLayerController->_layerRangeUniform.setElement( (2*index)+1, layer->getImageLayerOptions().maxVisibleRange().value() );
        }

        // set the remainder of the layers to disabled 
        for( int j=mapf.imageLayers().size(); j<64; ++j )
            _imageLayerController->_layerEnabledUniform.setElement( j, false );

        //_imageLayerController->_layerOpacityUniform->addTo( stateSet );
        //_imageLayerController->_layerEnabledUniform->addTo( stateSet );
        //_imageLayerController->_layerRangeUniform->addTo( stateSet );
    }
}
Exemple #13
0
// called from the UPDATE TRAVERSAL, because this method can potentially alter
// the scene graph.
bool
StreamingTile::serviceCompletedRequests( const MapFrame& mapf, bool tileTableLocked )
{
    //Don't do anything until we have been added to the scene graph
    if (!_hasBeenTraversed) return false;

    bool tileModified = false;

    if ( !_requestsInstalled )
        return false;

    // First service the tile generator:
    if ( _tileGenRequest.valid() && _tileGenRequest->isCompleted() )
    {
        CustomTerrainTechnique* tech = dynamic_cast<CustomTerrainTechnique*>( getTerrainTechnique() );
        if ( tech )
        {
            //TODO: consider waiting to apply if there are still more tile updates in the queue.
            if ( _tileUpdates.size() == 0 )
            {
                tileModified = tech->applyTileUpdates();
            }
        }
        _tileGenRequest = 0L;
    }


    // now deal with imagery.
    const LoadingPolicy& lp = getStreamingTerrain()->getLoadingPolicy();

    StreamingTerrainNode* terrain = getStreamingTerrain();

    //Check each layer independently.
    for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i )
    {
        ImageLayer* imageLayer = i->get();

        bool checkForFinalImagery = false;

        CustomColorLayer colorLayer;
        if ( getCustomColorLayer( imageLayer->getUID(), colorLayer ) )
        {
            if ( lp.mode() == LoadingPolicy::MODE_PREEMPTIVE )
            {
                // in preemptive mode, always check for the final imagery - there are no intermediate
                // placeholders.
                checkForFinalImagery = true;
            }
            else if (lp.mode() == LoadingPolicy::MODE_SEQUENTIAL && 
                     readyForNewImagery(imageLayer, colorLayer.getLevelOfDetail()) )
            {
                // in sequential mode, we have to incrementally increase imagery resolution by
                // creating placeholders based of parent tiles, one LOD at a time.
                if ( colorLayer.getLevelOfDetail() + 1 < (int)_key.getLevelOfDetail() )
                {
                    // if the parent's image LOD is higher than ours, replace ours with the parent's
                    // since it is a higher-resolution placeholder:
                    if ( _family[Relative::PARENT].getImageLOD(colorLayer.getUID()) > colorLayer.getLevelOfDetail() )
                    {
                        osg::ref_ptr<Tile> parentTile;
                        getStreamingTerrain()->getTile( _family[Relative::PARENT].tileID, parentTile, !tileTableLocked );

                        // Set the color layer to the parent color layer as a placeholder.
                        CustomColorLayer parentColorLayer;
                        if ( parentTile->getCustomColorLayer( colorLayer.getUID(), parentColorLayer ) )
                        {
                            this->setCustomColorLayer( parentColorLayer );
                        }

                        // ... and queue up an update request.
                        queueTileUpdate( TileUpdate::UPDATE_IMAGE_LAYER, colorLayer.getUID() );
                    }
                }
                else
                {
                    // we've gone as far as we can with placeholders; time to check for the
                    // final imagery tile.
                    checkForFinalImagery = true;
                }
            }
        }

        if ( checkForFinalImagery )
        {
            // Then the image requests:
            for( TaskRequestList::iterator itr = _requests.begin(); itr != _requests.end(); )
            {
                bool increment = true;
                TileColorLayerRequest* r = static_cast<TileColorLayerRequest*>( itr->get() );
                //We only care about the current layer we are checking
                if ( r->_layerUID == imageLayer->getUID() )
                {
                    if ( itr->get()->isCompleted() )
                    {
                        if ( r->wasCanceled() )
                        {
                            //Reset the cancelled task to IDLE and give it a new progress callback.
                            r->setState( TaskRequest::STATE_IDLE );
                            r->setProgressCallback( new StampedProgressCallback(
                                r, terrain->getImageryTaskService( r->_layerUID )));
                            r->reset();
                        }
                        else // success..
                        {
                            //See if we even care about the request
                            if ( !mapf.getImageLayerByUID( r->_layerUID ) )
                            {
                                //The maplayer was probably deleted
                                OE_DEBUG << "Layer uid=" << r->_layerUID << " no longer exists, ignoring TileColorLayerRequest " << std::endl;
                                itr = _requests.erase(itr);
                                increment = false;
                            }
                            else
                            {
                                CustomColorLayerRef* result = static_cast<CustomColorLayerRef*>( r->getResult() );
                                if ( result )
                                {
                                    this->setCustomColorLayer( result->_layer );

                                    queueTileUpdate( TileUpdate::UPDATE_IMAGE_LAYER, r->_layerUID );

                                    //OE_NOTICE << "Complete IR (" << _key.str() << ") layer=" << r->_layerId << std::endl;

                                    // remove from the list (don't reference "r" after this!)
                                    itr = _requests.erase( itr );
                                    increment = false;
                                }
                                else
                                {  
                                    if (r->_numTries > r->_maxTries)
                                    {
                                        CustomColorLayer oldLayer;
                                        if ( this->getCustomColorLayer( r->_layerUID, oldLayer ) )
                                        {
                                            // apply the old color layer but with a new LOD.
                                            this->setCustomColorLayer( CustomColorLayer(
                                                oldLayer.getMapLayer(),
                                                oldLayer.getImage(),
                                                oldLayer.getLocator(),
                                                _key.getLevelOfDetail(),
                                                _key ));

                                            itr = _requests.erase( itr );
                                            increment = false;
                                            OE_DEBUG << "Tried (" << _key.str() << ") (layer uid=" << r->_layerUID << "), too many times, moving on...." << std::endl;
                                        }
                                    }
                                    else
                                    {
                                        OE_DEBUG << "IReq error (" << _key.str() << ") (layer uid=" << r->_layerUID << "), retrying" << std::endl;

                                        //The color layer request failed, probably due to a server error. Reset it.
                                        r->setState( TaskRequest::STATE_IDLE );
                                        r->reset();
                                    }
                                }
                            }
                        }
                    }
                }

                if ( increment )
                    ++itr;
            }
        }
    }

    // Finally, the elevation requests:
    if ( _hasElevation && !_elevationLayerUpToDate && _elevRequest.valid() && _elevPlaceholderRequest.valid() )
    {
        // First, check is the Main elevation request is done. If so, we will now have the final HF data
        // and can shut down the elevation requests for this tile.
        if ( _elevRequest->isCompleted() )
        {
            if ( _elevRequest->wasCanceled() )
            {
                // If the request was canceled, reset it to IDLE and reset the callback. On the next
                _elevRequest->setState( TaskRequest::STATE_IDLE );
                _elevRequest->setProgressCallback( new ProgressCallback() );            
                _elevRequest->reset();
            }
            else // success:
            {
                // if the elevation request succeeded, install the new elevation layer!
                TileElevationLayerRequest* r = static_cast<TileElevationLayerRequest*>( _elevRequest.get() );
                osg::ref_ptr<osgTerrain::HeightFieldLayer> newHFLayer = static_cast<osgTerrain::HeightFieldLayer*>( r->getResult() );
                if ( newHFLayer.valid() && newHFLayer->getHeightField() != NULL )
                {
                    newHFLayer->getHeightField()->setSkirtHeight( 
                        terrain->getTileFactory()->getTerrainOptions().heightFieldSkirtRatio().get() *
                        this->getBound().radius() );

                    // need to write-lock the layer data since we'll be changing it:
                    {
                        Threading::ScopedWriteLock lock( _tileLayersMutex );
                        this->setElevationLayer( newHFLayer.get() );
                        this->dirtyBound();
                    }

                    // the tile needs rebuilding. This will kick off a TileGenRequest.
                    queueTileUpdate( TileUpdate::UPDATE_ELEVATION );

                    // finalize the LOD marker for this tile, so other tiles can see where we are.
                    _elevationLOD = _key.getLevelOfDetail();

    #ifdef PREEMPTIVE_DEBUG
                    OE_NOTICE << "Tile (" << _key.str() << ") final HF, LOD (" << _elevationLOD << ")" << std::endl;
    #endif
                    // this was the final elev request, so mark elevation as DONE.
                    _elevationLayerUpToDate = true;

                    // GW- just reset these and leave them alone and let cancelRequests() take care of cleanup later.
                    // done with our Elevation requests!
                    //_elevRequest = 0L;
                    //_elevPlaceholderRequest = 0L;
                }
                else
                {
                    //We've tried to get the tile's elevation but couldn't.  Just mark the elevation layer as up to date and move on.
                    _elevationLOD = _key.getLevelOfDetail();
                    _elevationLayerUpToDate = true;

                    //This code will retry indefinitely.  We need to have a way to limit the number of retries since
                    //it will block neighbor tiles from loading.
                    //_elevRequest->setState( TaskRequest::STATE_IDLE );
                    //_elevRequest->reset();
                }
            }
        }

        else if ( _elevPlaceholderRequest->isCompleted() )
        {
            TileElevationPlaceholderLayerRequest* r = 
                static_cast<TileElevationPlaceholderLayerRequest*>(_elevPlaceholderRequest.get());

            if ( r->wasCanceled() )
            {
                r->setState( TaskRequest::STATE_IDLE );
                r->setProgressCallback( new ProgressCallback() );
                r->reset();
            }
            else // success:
            {
                osg::ref_ptr<osgTerrain::HeightFieldLayer> newPhLayer = static_cast<osgTerrain::HeightFieldLayer*>( r->getResult() );
                if ( newPhLayer.valid() && newPhLayer->getHeightField() != NULL )
                {
                    // install the new elevation layer.
                    {
                        Threading::ScopedWriteLock lock( _tileLayersMutex );
                        this->setElevationLayer( newPhLayer.get() );
                        this->dirtyBound();
                    }

                    // tile needs to be recompiled.
                    queueTileUpdate( TileUpdate::UPDATE_ELEVATION );

                    // update the elevation LOD for this tile, now that the new HF data is installed. This will
                    // allow other tiles to see where this tile's HF data is.
                    _elevationLOD = r->_nextLOD;

    #ifdef PREEMPTIVE_DEBUG
                    OE_NOTICE << "..tile (" << _key.str() << ") is now at (" << _elevationLOD << ")" << std::endl;
    #endif
                }
                _elevPlaceholderRequest->setState( TaskRequest::STATE_IDLE );
                _elevPlaceholderRequest->reset();
            }
        }
    }

    // if we have a new TileGenRequest, queue it up now.
    if ( _tileUpdates.size() > 0 && !_tileGenRequest.valid() ) // _tileGenNeeded && !_tileGenRequest.valid())
    {
        _tileGenRequest = new TileGenRequest( this, _tileUpdates.front() );
        _tileUpdates.pop();
        //OE_NOTICE << "tile (" << _key.str() << ") queuing new tile gen" << std::endl;
        getStreamingTerrain()->getTileGenerationTaskService()->add( _tileGenRequest.get() );
    }

    return tileModified;
}
Exemple #14
0
void
TileModelFactory::createTileModel(const TileKey&           key, 
                                  osg::ref_ptr<TileModel>& out_model,
                                  bool&                    out_hasRealData)
{
    MapFrame mapf( _map, Map::MASKED_TERRAIN_LAYERS );
    
    const MapInfo& mapInfo = mapf.getMapInfo();

    osg::ref_ptr<TileModel> model = new TileModel();
    model->_map         = _map;
    model->_tileKey     = key;
    model->_tileLocator = GeoLocator::createForKey(key, mapInfo);

    // init this to false, then search for real data. "Real data" is data corresponding
    // directly to the key, as opposed to fallback data, which is derived from a lower
    // LOD key.
    out_hasRealData = false;
    
    // Fetch the image data and make color layers.
    unsigned order = 0;
    for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i )
    {
        ImageLayer* layer = i->get();

        if ( layer->getEnabled() )
        {
            BuildColorData build;
            build.init( key, layer, order, mapInfo, _terrainOptions, model.get() );
            
            bool addedToModel = build.execute();
            if ( addedToModel )
            {
                // only bump the order if we added something to the data model.
                order++;
            }
        }
    }

    // make an elevation layer.
    BuildElevationData build;
    build.init( key, mapf, _terrainOptions, model.get(), _hfCache );
    build.execute();


    // Bail out now if there's no data to be had.
    if ( model->_colorData.size() == 0 && !model->_elevationData.getHeightField() )
    {
        return;
    }

    // OK we are making a tile, so if there's no heightfield yet, make an empty one.
    if ( !model->_elevationData.getHeightField() )
    {
        osg::HeightField* hf = HeightFieldUtils::createReferenceHeightField( key.getExtent(), 8, 8 );
        model->_elevationData = TileModel::ElevationData(
            hf,
            GeoLocator::createForKey(key, mapInfo),
            true );
    }

    if (!out_hasRealData)
    {
        // Check the results and see if we have any real data.
        for( TileModel::ColorDataByUID::const_iterator i = model->_colorData.begin(); i != model->_colorData.end(); ++i )
        {
            if ( !i->second.isFallbackData() ) 
            {
                out_hasRealData = true;
                break;
            }
        }
    }

    if ( !out_hasRealData && !model->_elevationData.isFallbackData() )
    {
        out_hasRealData = true;
    }

    // look up the parent model and cache it.
    osg::ref_ptr<TileNode> parentTile;
    if ( _liveTiles->get(key.createParentKey(), parentTile) )
        model->_parentModel = parentTile->getTileModel();

    out_model = model.release();
}
Exemple #15
0
bool
MapFrame::isCached( const osgEarth::TileKey& key ) const
{
    const Profile* mapProfile = getProfile();

    //Check the imagery layers
    for( ImageLayerVector::const_iterator i = imageLayers().begin(); i != imageLayers().end(); i++ )
    {
        ImageLayer* layer = i->get();
        osg::ref_ptr< Cache > cache = layer->getCache();

        if ( !cache.valid() || !layer->getProfile() ) 
            return false;

        std::vector< TileKey > keys;

        if ( mapProfile->isEquivalentTo( layer->getProfile() ) )
        {
            keys.push_back( key );
        }
        else
        {
            layer->getProfile()->getIntersectingTiles( key, keys );
        }

        for (unsigned int j = 0; j < keys.size(); ++j)
        {
            if ( layer->isKeyValid( keys[j] ) )
            {
                if ( !cache->isCached( keys[j], layer->getCacheSpec() ) )
                {
                    return false;
                }
            }
        }
    }

    for( ElevationLayerVector::const_iterator i = elevationLayers().begin(); i != elevationLayers().end(); ++i )
    {
        ElevationLayer* layer = i->get();
        osg::ref_ptr< Cache > cache = layer->getCache();

        if ( !cache.valid() || !layer->getProfile() )
            return false;

        std::vector<TileKey> keys;

        if ( mapProfile->isEquivalentTo( layer->getProfile() ) )
        {
            keys.push_back( key );
        }
        else
        {
            layer->getProfile()->getIntersectingTiles( key, keys );
        }

        for (unsigned int j = 0; j < keys.size(); ++j)
        {
            if ( layer->isKeyValid( keys[j] ) )
            {
                if ( !cache->isCached( keys[j], layer->getCacheSpec() ) )
                {
                    return false;
                }
            }
        }
    }
    return true;
}
Config
EarthFileSerializer2::serialize(const MapNode* input, const std::string& referrer) const
{
    Config mapConf("map");
    mapConf.set("version", "2");

    if ( !input || !input->getMap() )
        return mapConf; 

    const Map* map = input->getMap();
    MapFrame mapf( map, Map::ENTIRE_MODEL );

    // the map and node options:
    Config optionsConf = map->getInitialMapOptions().getConfig();
    optionsConf.merge( input->getMapNodeOptions().getConfig() );
    mapConf.add( "options", optionsConf );

    // the layers
    for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i )
    {
        ImageLayer* layer = i->get();
        //Config layerConf = layer->getInitialOptions().getConfig();
        Config layerConf = layer->getImageLayerOptions().getConfig();
        layerConf.set("name", layer->getName());
        layerConf.set("driver", layer->getInitialOptions().driver()->getDriver());        
        mapConf.add( "image", layerConf );
    }

    for( ElevationLayerVector::const_iterator i = mapf.elevationLayers().begin(); i != mapf.elevationLayers().end(); ++i )
    {
        ElevationLayer* layer = i->get();
        //Config layerConf = layer->getInitialOptions().getConfig();
        Config layerConf = layer->getElevationLayerOptions().getConfig();
        layerConf.set("name", layer->getName());
        layerConf.set("driver", layer->getInitialOptions().driver()->getDriver());        
        mapConf.add( "elevation", layerConf );
    }

    for( ModelLayerVector::const_iterator i = mapf.modelLayers().begin(); i != mapf.modelLayers().end(); ++i )
    {
        ModelLayer* layer = i->get();
        Config layerConf = layer->getModelLayerOptions().getConfig();
        layerConf.set("name", layer->getName());
        layerConf.set("driver", layer->getModelLayerOptions().driver()->getDriver());
        mapConf.add( "model", layerConf );
    }

    Config ext = input->externalConfig();
    if ( !ext.empty() )
    {
        ext.key() = "extensions";
        mapConf.add( ext );
    }

#if 1 // removed until it can be debugged.
    // Re-write pathnames in the Config so they are relative to the new referrer.
    if ( _rewritePaths && !referrer.empty() )
    {
        RewritePaths rewritePaths( referrer );
        rewritePaths.setRewriteAbsolutePaths( _rewriteAbsolutePaths );
        rewritePaths.apply( mapConf );
    }
#endif

    return mapConf;
}
Exemple #17
0
bool
MapFrame::isCached( const TileKey& key ) const
{
    // is there a map cache at all?
    if ( _map->getCache() == 0L )
        return false;

    //Check to see if the tile will load fast
    // Check the imagery layers
    for( ImageLayerVector::const_iterator i = imageLayers().begin(); i != imageLayers().end(); i++ )
    {   
        const ImageLayer* layer = i->get();

        if (!layer->getEnabled())
            continue;

        // If we're cache only we should be fast
        if (layer->isCacheOnly())
            continue;

        // no-cache mode? always slow
        if (layer->isNoCache())
            return false;

        // No tile source? skip it
        osg::ref_ptr< TileSource > source = layer->getTileSource();
        if (!source.valid())
            continue;

        //If the tile is blacklisted, it should also be fast.
        if ( source->getBlacklist()->contains( key ) )
            continue;

        //If no data is available on this tile, we'll be fast
        if ( !source->hasData( key ) )
            continue;

        if ( !layer->isCached(key) )
            return false;
    }

    for( ElevationLayerVector::const_iterator i = elevationLayers().begin(); i != elevationLayers().end(); ++i )
    {
        const ElevationLayer* layer = i->get();

        if (!layer->getEnabled())
            continue;

        //If we're cache only we should be fast
        if (layer->isCacheOnly())
            continue;

        // no-cache mode? always high-latency.
        if (layer->isNoCache())
            return false;

        osg::ref_ptr< TileSource > source = layer->getTileSource();
        if (!source.valid())
            continue;

        //If the tile is blacklisted, it should also be fast.
        if ( source->getBlacklist()->contains( key ) )
            continue;

        if ( !source->hasData( key ) )
            continue;

        if ( !i->get()->isCached( key ) )
            return false;
    }

    return true;
}
int
purge( osg::ArgumentParser& args )
{
    osg::ref_ptr<osg::Node> node = osgDB::readNodeFiles( args );
    if ( !node.valid() )
        return usage( "Failed to read .earth file." );

    MapNode* mapNode = MapNode::findMapNode( node.get() );
    if ( !mapNode )
        return usage( "Input file was not a .earth file" );

    Map* map = mapNode->getMap();

    if ( !map->getCache() )
        return message( "Earth file does not contain a cache." );

    std::vector<Entry> entries;


    ImageLayerVector imageLayers;
    map->getLayers( imageLayers );
    for( ImageLayerVector::const_iterator i = imageLayers.begin(); i != imageLayers.end(); ++i )
    {
        ImageLayer* layer = i->get();

        bool useMFP =
            layer->getProfile() &&
            layer->getProfile()->getSRS()->isSphericalMercator() &&
            mapNode->getMapNodeOptions().getTerrainOptions().enableMercatorFastPath() == true;

        const Profile* cacheProfile = useMFP ? layer->getProfile() : map->getProfile();

        CacheSettings* cacheSettings = layer->getCacheSettings();
        if (cacheSettings)
        {
            CacheBin* bin = cacheSettings->getCacheBin();
            if ( bin )
            {
                entries.push_back(Entry());
                entries.back()._isImage = true;
                entries.back()._name = i->get()->getName();
                entries.back()._bin = bin;
            }
        }
    }

    ElevationLayerVector elevationLayers;
    map->getLayers( elevationLayers );
    for( ElevationLayerVector::const_iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i )
    {
        ElevationLayer* layer = i->get();

        bool useMFP =
            layer->getProfile() &&
            layer->getProfile()->getSRS()->isSphericalMercator() &&
            mapNode->getMapNodeOptions().getTerrainOptions().enableMercatorFastPath() == true;

        const Profile* cacheProfile = useMFP ? layer->getProfile() : map->getProfile();
        
        CacheSettings* cacheSettings = layer->getCacheSettings();
        if (cacheSettings)
        {
            CacheBin* bin = cacheSettings->getCacheBin();
            if (bin)
            {
                entries.push_back(Entry());
                entries.back()._isImage = false;
                entries.back()._name = i->get()->getName();
                entries.back()._bin = bin;
            }
        }
    }

    if ( entries.size() > 0 )
    {
        std::cout << std::endl;

        for( unsigned i=0; i<entries.size(); ++i )
        {
            std::cout << (i+1) << ") " << entries[i]._name << " (" << (entries[i]._isImage? "image" : "elevation" ) << ")" << std::endl;
        }

        std::cout 
            << std::endl
            << "Enter number of cache to purge, or <enter> to quit: "
            << std::flush;

        std::string input;
        std::getline( std::cin, input );

        if ( !input.empty() )
        {
            unsigned k = as<unsigned>(input, 0L);
            if ( k > 0 && k <= entries.size() )
            {
                Config meta = entries[k-1]._bin->readMetadata();
                if ( !meta.empty() )
                {
                    std::cout
                        << std::endl
                        << "Cache METADATA:" << std::endl
                        << meta.toJSON() 
                        << std::endl << std::endl;
                }

                std::cout
                    << "Are you sure (y/N)? "
                    << std::flush;

                std::getline( std::cin, input );
                if ( input == "y" || input == "Y" )
                {
                    std::cout << "Purging.." << std::flush;
                    entries[k-1]._bin->clear();
                }
                else
                {
                    std::cout << "No action taken." << std::endl;
                }
            }
            else
            {
                std::cout << "Invalid choice." << std::endl;
            }
        }
        else
        {
            std::cout << "No action taken." << std::endl;
        }
    }

    return 0;
}
Exemple #19
0
void CacheSeed::seed( Map* map )
{
    if ( !map->getCache() )
    {
        OE_WARN << LC << "Warning: No cache defined; aborting." << std::endl;
        return;
    }

    std::vector<TileKey> keys;
    map->getProfile()->getRootKeys(keys);

    //Add the map's entire extent if we don't have one specified.
    if (_extents.empty())
    {
        addExtent( map->getProfile()->getExtent() );
    }

    bool hasCaches = false;
    int src_min_level = INT_MAX;
    unsigned int src_max_level = 0;

    MapFrame mapf( map, Map::TERRAIN_LAYERS, "CacheSeed::seed" );

    //Assumes the the TileSource will perform the caching for us when we call createImage
    for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); i++ )
    {
        ImageLayer* layer = i->get();
        TileSource* src   = layer->getTileSource();

        const ImageLayerOptions& opt = layer->getImageLayerOptions();

        if ( layer->isCacheOnly() )
        {
            OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" is set to cache-only; skipping." << std::endl;
        }
        else if ( !src )
        {
            OE_WARN << "Warning: Layer \"" << layer->getName() << "\" could not create TileSource; skipping." << std::endl;
        }
        //else if ( src->getCachePolicyHint(0L) == CachePolicy::NO_CACHE )
        //{
        //    OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" does not support seeding; skipping." << std::endl;
        //}
        else if ( !layer->getCache() )
        {
            OE_WARN << LC << "Notice: Layer \"" << layer->getName() << "\" has no cache defined; skipping." << std::endl;
        }
        else
        {
            hasCaches = true;

            if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level)
                src_min_level = opt.minLevel().get();
            if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level)
                src_max_level = opt.maxLevel().get();
        }
    }

    for( ElevationLayerVector::const_iterator i = mapf.elevationLayers().begin(); i != mapf.elevationLayers().end(); i++ )
    {
        ElevationLayer* layer = i->get();
        TileSource*     src   = layer->getTileSource();
        const ElevationLayerOptions& opt = layer->getElevationLayerOptions();

        if ( layer->isCacheOnly() )
        {
            OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" is set to cache-only; skipping." << std::endl;
        }
        else if (!src)
        {
            OE_WARN << "Warning: Layer \"" << layer->getName() << "\" could not create TileSource; skipping." << std::endl;
        }
        //else if ( src->getCachePolicyHint(0L) == CachePolicy::NO_CACHE )
        //{
        //    OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" does not support seeding; skipping." << std::endl;
        //}
        else if ( !layer->getCache() )
        {
            OE_WARN << LC << "Notice: Layer \"" << layer->getName() << "\" has no cache defined; skipping." << std::endl;
        }
        else
        {
            hasCaches = true;

            if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level)
                src_min_level = opt.minLevel().get();
            if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level)
                src_max_level = opt.maxLevel().get();
        }
    }

    if ( !hasCaches )
    {
        OE_WARN << LC << "There are either no caches defined in the map, or no sources to cache; aborting." << std::endl;
        return;
    }

    if ( src_max_level > 0 && src_max_level < _maxLevel )
    {
        _maxLevel = src_max_level;
    }

    OE_NOTICE << LC << "Maximum cache level will be " << _maxLevel << std::endl;

    //Estimate the number of tiles
    _total = 0;    
    CacheEstimator est;
    est.setMinLevel( _minLevel );
    est.setMaxLevel( _maxLevel );
    est.setProfile( map->getProfile() ); 
    for (unsigned int i = 0; i < _extents.size(); i++)
    {                
        est.addExtent( _extents[ i ] );
    } 
    _total = est.getNumTiles();

    OE_INFO << "Processing ~" << _total << " tiles" << std::endl;

    for (unsigned int i = 0; i < keys.size(); ++i)
    {
        processKey( mapf, keys[i] );
    }

    _total = _completed;

    if ( _progress.valid()) _progress->reportProgress(_completed, _total, 0, 1, "Finished");
}
unsigned int
ElevationQuery::getMaxLevel( double x, double y, const SpatialReference* srs, const Profile* profile ) const
{
    unsigned int maxLevel = 0;
    for( ElevationLayerVector::const_iterator i = _mapf.elevationLayers().begin(); i != _mapf.elevationLayers().end(); ++i )
    {
        unsigned int layerMax = 0;
        osgEarth::TileSource* ts = i->get()->getTileSource();
        if ( ts && ts->getDataExtents().size() > 0 )
        {
            osg::Vec3d tsCoord(x, y, 0);

            const SpatialReference* tsSRS = ts->getProfile() ? ts->getProfile()->getSRS() : 0L;
            if ( srs && tsSRS )
                srs->transform(tsCoord, tsSRS, tsCoord);
            else
                tsSRS = srs;
            
            for (osgEarth::DataExtentList::iterator j = ts->getDataExtents().begin(); j != ts->getDataExtents().end(); j++)
            {
                if (j->maxLevel().isSet() && j->maxLevel() > layerMax && j->contains( tsCoord.x(), tsCoord.y(), tsSRS ))
                {
                    layerMax = j->maxLevel().value();
                }
            }

            //Need to convert the layer max of this TileSource to that of the actual profile
            layerMax = profile->getEquivalentLOD( ts->getProfile(), layerMax );
        }
        else
        {
            layerMax = i->get()->getMaxDataLevel();
        }        

        if ( i->get()->getTerrainLayerRuntimeOptions().maxLevel().isSet() )
            layerMax = std::min( layerMax, *i->get()->getTerrainLayerRuntimeOptions().maxLevel() );

        if (layerMax > maxLevel) maxLevel = layerMax;
    }    

    // need to check the image layers too, because if image layers do deeper than elevation layers,
    // upsampling occurs that can change the formation of the terrain skin.
    // NOTE: this probably doesn't happen in "triangulation" interpolation mode.. -gw
    for( ImageLayerVector::const_iterator i = _mapf.imageLayers().begin(); i != _mapf.imageLayers().end(); ++i )
    {
        unsigned int layerMax = 0;
        osgEarth::TileSource* ts = i->get()->getTileSource();
        if ( ts && ts->getDataExtents().size() > 0 )
        {
            osg::Vec3d tsCoord(x, y, 0);
            const SpatialReference* tsSRS = ts->getProfile() ? ts->getProfile()->getSRS() : 0L;
            if ( srs && tsSRS )
                srs->transform(tsCoord, tsSRS, tsCoord);
            else
                tsSRS = srs;
            
            for (osgEarth::DataExtentList::iterator j = ts->getDataExtents().begin(); j != ts->getDataExtents().end(); j++)
            {
                if (j->maxLevel().isSet()  && j->maxLevel() > layerMax && j->contains( tsCoord.x(), tsCoord.y(), tsSRS ))
                {
                    layerMax = j->maxLevel().value();
                }
            }

            //Need to convert the layer max of this TileSource to that of the actual profile
            layerMax = profile->getEquivalentLOD( ts->getProfile(), layerMax );
        }
        else
        {
            layerMax = i->get()->getMaxDataLevel();
        }
        
        
        if ( i->get()->getTerrainLayerRuntimeOptions().maxLevel().isSet() )
            layerMax = std::min( layerMax, *i->get()->getTerrainLayerRuntimeOptions().maxLevel() );

        if (layerMax > maxLevel)
            maxLevel = layerMax;
    }    

    return maxLevel;
}
Exemple #21
0
void CacheSeed::seed( Map* map )
{
    // We must do this to avoid an error message in OpenSceneGraph b/c the findWrapper method doesn't appear to be threadsafe.
    // This really isn't a big deal b/c this only effects data that is already cached.
    osgDB::ObjectWrapper* wrapper = osgDB::Registry::instance()->getObjectWrapperManager()->findWrapper( "osg::Image" );

    osg::Timer_t startTime = osg::Timer::instance()->tick();
    if ( !map->getCache() )
    {
        OE_WARN << LC << "Warning: No cache defined; aborting." << std::endl;
        return;
    }

    std::vector<TileKey> keys;
    map->getProfile()->getRootKeys(keys);

    //Add the map's entire extent if we don't have one specified.
    if (_extents.empty())
    {
        addExtent( map->getProfile()->getExtent() );
    }

    bool hasCaches = false;
    int src_min_level = INT_MAX;
    unsigned int src_max_level = 0;

    MapFrame mapf( map, Map::TERRAIN_LAYERS, "CacheSeed::seed" );

    //Assumes the the TileSource will perform the caching for us when we call createImage
    for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); i++ )
    {
        ImageLayer* layer = i->get();
        TileSource* src   = layer->getTileSource();

        const ImageLayerOptions& opt = layer->getImageLayerOptions();

        if ( layer->isCacheOnly() )
        {
            OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" is set to cache-only; skipping." << std::endl;
        }
        else if ( !src )
        {
            OE_WARN << "Warning: Layer \"" << layer->getName() << "\" could not create TileSource; skipping." << std::endl;
        }
        //else if ( src->getCachePolicyHint(0L) == CachePolicy::NO_CACHE )
        //{
        //    OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" does not support seeding; skipping." << std::endl;
        //}
        else if ( !layer->getCache() )
        {
            OE_WARN << LC << "Notice: Layer \"" << layer->getName() << "\" has no cache defined; skipping." << std::endl;
        }
        else
        {
            hasCaches = true;

            if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level)
                src_min_level = opt.minLevel().get();
            if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level)
                src_max_level = opt.maxLevel().get();
        }
    }

    for( ElevationLayerVector::const_iterator i = mapf.elevationLayers().begin(); i != mapf.elevationLayers().end(); i++ )
    {
        ElevationLayer* layer = i->get();
        TileSource*     src   = layer->getTileSource();
        const ElevationLayerOptions& opt = layer->getElevationLayerOptions();

        if ( layer->isCacheOnly() )
        {
            OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" is set to cache-only; skipping." << std::endl;
        }
        else if (!src)
        {
            OE_WARN << "Warning: Layer \"" << layer->getName() << "\" could not create TileSource; skipping." << std::endl;
        }
        //else if ( src->getCachePolicyHint(0L) == CachePolicy::NO_CACHE )
        //{
        //    OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" does not support seeding; skipping." << std::endl;
        //}
        else if ( !layer->getCache() )
        {
            OE_WARN << LC << "Notice: Layer \"" << layer->getName() << "\" has no cache defined; skipping." << std::endl;
        }
        else
        {
            hasCaches = true;

            if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level)
                src_min_level = opt.minLevel().get();
            if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level)
                src_max_level = opt.maxLevel().get();
        }
    }

    if ( !hasCaches )
    {
        OE_WARN << LC << "There are either no caches defined in the map, or no sources to cache; aborting." << std::endl;
        return;
    }

    if ( src_max_level > 0 && src_max_level < _maxLevel )
    {
        _maxLevel = src_max_level;
    }

    OE_NOTICE << LC << "Maximum cache level will be " << _maxLevel << std::endl;

    //Estimate the number of tiles
    _total = 0;    
    CacheEstimator est;
    est.setMinLevel( _minLevel );
    est.setMaxLevel( _maxLevel );
    est.setProfile( map->getProfile() ); 
    for (unsigned int i = 0; i < _extents.size(); i++)
    {                
        est.addExtent( _extents[ i ] );
    } 
    _total = est.getNumTiles();

    OE_INFO << "Processing ~" << _total << " tiles" << std::endl;


    // Initialize the operations queue
    _queue = new osg::OperationQueue;

    osg::Timer_t endTime = osg::Timer::instance()->tick();

    // Start the threads
    std::vector< osg::ref_ptr< osg::OperationsThread > > threads;
    for (unsigned int i = 0; i < _numThreads; i++)
    {        
        osg::OperationsThread* thread = new osg::OperationsThread();
        thread->setOperationQueue(_queue.get());
        thread->start();
        threads.push_back( thread );
    }

    OE_NOTICE << "Startup time " << osg::Timer::instance()->delta_s( startTime, endTime ) << std::endl;

    
    // Add the root keys to the queue
    for (unsigned int i = 0; i < keys.size(); ++i)
    {
        //processKey( mapf, keys[i] );
        _queue.get()->add( new CacheTileOperation( mapf, *this, keys[i]) );
    }    

    bool done = false;
    while (!done)
    {
        OpenThreads::Thread::microSleep(500000); // sleep for half a second
        done = true;
        if (_queue->getNumOperationsInQueue() > 0)
        {
            done = false;
            continue;
        }
        else
        {
            // Make sure no threads are currently working on an operation, which actually might add MORE operations since we are doing a quadtree traversal
            for (unsigned int i = 0; i < threads.size(); i++)
            {
                if (threads[i]->getCurrentOperation())
                {
                    done = false;
                    continue;
                }
            }
        }
    }    

    _total = _completed;

    if ( _progress.valid()) _progress->reportProgress(_completed, _total, 0, 1, "Finished");
}
void
TileModelFactory::createTileModel(const TileKey&           key, 
                                  const MapFrame&          frame,
                                  osg::ref_ptr<TileModel>& out_model) //,
                                  //bool&                    out_hasRealData)
{

    osg::ref_ptr<TileModel> model = new TileModel( frame.getRevision(), frame.getMapInfo() );
    model->_tileKey = key;
    model->_tileLocator = GeoLocator::createForKey(key, frame.getMapInfo());
    
    // Fetch the image data and make color layers.
    unsigned order = 0;
    for( ImageLayerVector::const_iterator i = frame.imageLayers().begin(); i != frame.imageLayers().end(); ++i )
    {
        ImageLayer* layer = i->get();

        if ( layer->getEnabled() )
        {
            BuildColorData build;
            build.init( key, layer, order, frame.getMapInfo(), _terrainOptions, model.get() );
            
            bool addedToModel = build.execute();
            if ( addedToModel )
            {
                // only bump the order if we added something to the data model.
                order++;
            }
        }
    }

    // make an elevation layer.
    BuildElevationData build;
    build.init( key, frame, _terrainOptions, model.get(), _hfCache );
    build.execute();


    // Bail out now if there's no data to be had.
    if ( model->_colorData.size() == 0 && !model->_elevationData.getHeightField() )
    {
        return;
    }

    // OK we are making a tile, so if there's no heightfield yet, make an empty one (and mark it
    // as fallback data of course)
    if ( !model->_elevationData.getHeightField() )
    {
        osg::HeightField* hf = HeightFieldUtils::createReferenceHeightField( key.getExtent(), 15, 15 );
        model->_elevationData = TileModel::ElevationData(
            hf,
            GeoLocator::createForKey(key, frame.getMapInfo()),
            true );
    }

    // look up the parent model and cache it.
    osg::ref_ptr<TileNode> parentTile;
    if ( _liveTiles->get(key.createParentKey(), parentTile) )
        model->_parentModel = parentTile->getTileModel();

    out_model = model.release();
}
Exemple #23
0
void
MPGeometry::renderPrimitiveSets(osg::State& state,
                                bool        renderColor,
                                bool        usingVBOs) const
{
    // check the map frame to see if it's up to date
    if ( _frame.needsSync() )
    {
        // this lock protects a MapFrame sync when we have multiple DRAW threads.
        Threading::ScopedMutexLock exclusive( _frameSyncMutex );

        if ( _frame.needsSync() && _frame.sync() ) // always double check
        {
            // This should only happen is the layer ordering changes;
            // If layers are added or removed, the Tile gets rebuilt and
            // the point is moot.
            std::vector<Layer> reordered;
            const ImageLayerVector& layers = _frame.imageLayers();
            reordered.reserve( layers.size() );
            for( ImageLayerVector::const_iterator i = layers.begin(); i != layers.end(); ++i )
            {
                std::vector<Layer>::iterator j = std::find( _layers.begin(), _layers.end(), i->get()->getUID() );
                if ( j != _layers.end() )
                    reordered.push_back( *j );
            }
            _layers.swap( reordered );
        }
    }

    unsigned layersDrawn = 0;

    // access the GL extensions interface for the current GC:
    const osg::Program::PerContextProgram* pcp = 0L;

#if OSG_MIN_VERSION_REQUIRED(3,3,3)
	osg::ref_ptr<osg::GLExtensions> ext;
#else
    osg::ref_ptr<osg::GL2Extensions> ext;
#endif
    unsigned contextID;

    if (_supportsGLSL)
    {
        contextID = state.getContextID();
#if OSG_MIN_VERSION_REQUIRED(3,3,3)
		ext = osg::GLExtensions::Get(contextID, true);
#else
		ext = osg::GL2Extensions::Get( contextID, true );
#endif
        pcp = state.getLastAppliedProgramObject();
    }

    // cannot store these in the object since there could be multiple GCs (and multiple
    // PerContextPrograms) at large
    GLint tileKeyLocation       = -1;
    GLint birthTimeLocation     = -1;
    GLint opacityLocation       = -1;
    GLint uidLocation           = -1;
    GLint orderLocation         = -1;
    GLint texMatParentLocation  = -1;
    GLint minRangeLocation      = -1;
    GLint maxRangeLocation      = -1;

    // The PCP can change (especially in a VirtualProgram environment). So we do need to
    // requery the uni locations each time unfortunately. TODO: explore optimizations.
    if ( pcp )
    {
        tileKeyLocation      = pcp->getUniformLocation( _tileKeyUniformNameID );
        birthTimeLocation    = pcp->getUniformLocation( _birthTimeUniformNameID );
        opacityLocation      = pcp->getUniformLocation( _opacityUniformNameID );
        uidLocation          = pcp->getUniformLocation( _uidUniformNameID );
        orderLocation        = pcp->getUniformLocation( _orderUniformNameID );
        texMatParentLocation = pcp->getUniformLocation( _texMatParentUniformNameID );
        minRangeLocation = pcp->getUniformLocation( _minRangeUniformNameID );
        maxRangeLocation = pcp->getUniformLocation( _maxRangeUniformNameID );
    }
    
    // apply the tilekey uniform once.
    if ( tileKeyLocation >= 0 )
    {
        ext->glUniform4fv( tileKeyLocation, 1, _tileKeyValue.ptr() );
    }

    // set the "birth time" - i.e. the time this tile last entered the scene in the current GC.
    if ( birthTimeLocation >= 0 )
    {
        PerContextData& pcd = _pcd[contextID];
        if ( pcd.birthTime < 0.0f )
        {
            const osg::FrameStamp* stamp = state.getFrameStamp();
            if ( stamp )
            {
                pcd.birthTime = stamp->getReferenceTime();
            }
        }
        ext->glUniform1f( birthTimeLocation, pcd.birthTime );
    }

    // activate the tile coordinate set - same for all layers
    if ( renderColor )
    {
        state.setTexCoordPointer( _imageUnit+1, _tileCoords.get() );
    }

#ifndef OSG_GLES2_AVAILABLE
    if ( renderColor )
    {
        // emit a default terrain color since we're not binding a color array:
        glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
    }
#endif

    // activate the elevation texture if there is one. Same for all layers.
    //if ( _elevTex.valid() )
    //{
    //    state.setActiveTextureUnit( 2 );
    //    state.setTexCoordPointer( 1, _tileCoords.get() ); // necessary?? since we do it above
    //    _elevTex->apply( state );
    //    // todo: probably need an elev texture matrix as well. -gw
    //}
    

    // track the active image unit.
    int activeImageUnit = -1;

    // remember whether we applied a parent texture.
    bool usedTexParent = false;

    if ( _layers.size() > 0 )
    {
        float prev_opacity        = -1.0f;

        // first bind any shared layers. We still have to do this even if we are
        // in !renderColor mode b/c these textures could be used by vertex shaders
        // to alter the geometry.
        int sharedLayers = 0;
        if ( pcp )
        {
            for(unsigned i=0; i<_layers.size(); ++i)
            {
                const Layer& layer = _layers[i];

                // a "shared" layer binds to a secondary texture unit so that other layers
                // can see it and use it.
                if ( layer._imageLayer->isShared() )
                {
                    ++sharedLayers;
                    int sharedUnit = layer._imageLayer->shareImageUnit().get();
                    {
                        state.setActiveTextureUnit( sharedUnit );

                        state.setTexCoordPointer( sharedUnit, layer._texCoords.get() );
                        // bind the texture for this layer to the active share unit.
                        layer._tex->apply( state );

                        // Shared layers need a texture matrix since the terrain engine doesn't
                        // provide a "current texture coordinate set" uniform (i.e. oe_layer_texc)
                        GLint texMatLocation = 0;
                        texMatLocation = pcp->getUniformLocation( layer._texMatUniformID );
                        if ( texMatLocation >= 0 )
                        {
                            ext->glUniformMatrix4fv( texMatLocation, 1, GL_FALSE, layer._texMat.ptr() );
                        }
                    }
                }
            }
        }
        if (renderColor)
        {
            // find the first opaque layer, top-down, and start there:
            unsigned first = 0;
            for(first = _layers.size()-1; first > 0; --first)
            {
                const Layer& layer = _layers[first];
                if (layer._opaque &&
                    //Color filters can modify the opacity
                    layer._imageLayer->getColorFilters().empty() &&
                    layer._imageLayer->getVisible() &&
                    layer._imageLayer->getOpacity() >= 1.0f)
                {
                    break;
                }
            }

            // interate over all the image layers
            for(unsigned i=first; i<_layers.size(); ++i)
            {
                const Layer& layer = _layers[i];

                if ( layer._imageLayer->getVisible() && layer._imageLayer->getOpacity() > 0.0f )
                {       
                    // activate the visible unit if necessary:
                    if ( activeImageUnit != _imageUnit )
                    {
                        state.setActiveTextureUnit( _imageUnit );
                        activeImageUnit = _imageUnit;
                    }

                    // bind the texture for this layer:
                    layer._tex->apply( state );

                    // in FFP mode, we need to enable the GL mode for texturing:
                    if ( !pcp ) //!_supportsGLSL)
                    {
                        state.applyMode(GL_TEXTURE_2D, true);
                    }

                    // if we're using a parent texture for blending, activate that now
                    if ( texMatParentLocation >= 0 && layer._texParent.valid() )
                    {
                        state.setActiveTextureUnit( _imageUnitParent );
                        activeImageUnit = _imageUnitParent;
                        layer._texParent->apply( state );
                        usedTexParent = true;
                    }

                    // bind the texture coordinates for this layer.
                    // TODO: can probably optimize this by sharing or using texture matrixes.
                    // State::setTexCoordPointer does some redundant work under the hood.
                    state.setTexCoordPointer( _imageUnit, layer._texCoords.get() );

                    // apply uniform values:
                    if ( pcp )
                    {
                        // apply opacity:
                        if ( opacityLocation >= 0 )
                        {
                            float opacity = layer._imageLayer->getOpacity();
                            if ( opacity != prev_opacity )
                            {
                                ext->glUniform1f( opacityLocation, (GLfloat)opacity );
                                prev_opacity = opacity;
                            }
                        }

                        // assign the layer UID:
                        if ( uidLocation >= 0 )
                        {
                            ext->glUniform1i( uidLocation, (GLint)layer._layerID );
                        }

                        // assign the layer order:
                        if ( orderLocation >= 0 )
                        {
                            ext->glUniform1i( orderLocation, (GLint)layersDrawn );
                        }

                        // assign the parent texture matrix
                        if ( texMatParentLocation >= 0 && layer._texParent.valid() )
                        {
                            ext->glUniformMatrix4fv( texMatParentLocation, 1, GL_FALSE, layer._texMatParent.ptr() );
                        }

                        // assign the min range
                        if ( minRangeLocation >= 0 )
                        {
                            ext->glUniform1f( minRangeLocation, layer._imageLayer->getImageLayerOptions().minVisibleRange().get() );
                        }

                        // assign the max range
                        if ( maxRangeLocation >= 0 )
                        {
                            ext->glUniform1f( maxRangeLocation, layer._imageLayer->getImageLayerOptions().maxVisibleRange().get() );
                        }
                    }

                    // draw the primitive sets.
                    for(unsigned int primitiveSetNum=0; primitiveSetNum!=_primitives.size(); ++primitiveSetNum)
                    {
                        const osg::PrimitiveSet* primitiveset = _primitives[primitiveSetNum].get();
                        if ( primitiveset )
                        {
                            primitiveset->draw(state, usingVBOs);
                        }
                        else
                        {
                            OE_WARN << LC << "Strange, MPGeometry had a 0L primset" << std::endl;
                        }
                    }

                    ++layersDrawn;
                }
            }
        }
    }

    // if we didn't draw anything, draw the raw tiles anyway with no texture.
    if ( layersDrawn == 0 )
    {
        if ( pcp )
        {
            if ( opacityLocation >= 0 )
                ext->glUniform1f( opacityLocation, (GLfloat)1.0f );
            if ( uidLocation >= 0 )
                ext->glUniform1i( uidLocation, (GLint)-1 );
            if ( orderLocation >= 0 )
                ext->glUniform1i( orderLocation, (GLint)0 );
        }

        // draw the primitives themselves.
        for(unsigned int primitiveSetNum=0; primitiveSetNum!=_primitives.size(); ++primitiveSetNum)
        {
            const osg::PrimitiveSet* primitiveset = _primitives[primitiveSetNum].get();
            primitiveset->draw(state, usingVBOs);
        }
    }

    else // at least one textured layer was drawn:
    {
        // prevent texture leakage
        // TODO: find a way to remove this to speed things up
        if ( renderColor )
        {
            glBindTexture( GL_TEXTURE_2D, 0 );

            // if a parent texture was applied, need to disable both.
            if ( usedTexParent )
            {
                state.setActiveTextureUnit(
                    activeImageUnit != _imageUnitParent ? _imageUnitParent :
                    _imageUnit );

                glBindTexture( GL_TEXTURE_2D, 0);
            }
        }
    }
}
Exemple #24
0
void
MPGeometry::renderPrimitiveSets(osg::State& state,
                                bool        usingVBOs) const
{
    // check the map frame to see if it's up to date
    if ( _frame.needsSync() )
    {
        // this lock protects a MapFrame sync when we have multiple DRAW threads.
        Threading::ScopedMutexLock exclusive( _frameSyncMutex );

        if ( _frame.needsSync() && _frame.sync() ) // always double check
        {
            // This should only happen is the layer ordering changes;
            // If layers are added or removed, the Tile gets rebuilt and
            // the point is moot.
            std::vector<Layer> reordered;
            const ImageLayerVector& layers = _frame.imageLayers();
            reordered.reserve( layers.size() );
            for( ImageLayerVector::const_iterator i = layers.begin(); i != layers.end(); ++i )
            {
                std::vector<Layer>::iterator j = std::find( _layers.begin(), _layers.end(), i->get()->getUID() );
                if ( j != _layers.end() )
                    reordered.push_back( *j );
            }
            _layers.swap( reordered );
        }
    }

    unsigned layersDrawn = 0;


    // access the GL extensions interface for the current GC:
    osg::ref_ptr<osg::GL2Extensions> ext = osg::GL2Extensions::Get( state.getContextID(), true );
    const osg::Program::PerContextProgram* pcp = state.getLastAppliedProgramObject();

    // cannot store these in the object since there could be multiple GCs (and multiple
    // PerContextPrograms) at large
    GLint tileKeyLocation;
    GLint opacityLocation;
    GLint uidLocation;
    GLint orderLocation;
    GLint texMatParentLocation;

    // The PCP can change (especially in a VirtualProgram environment). So we do need to
    // requery the uni locations each time unfortunately. TODO: explore optimizations.
    if ( pcp )
    {
        tileKeyLocation      = pcp->getUniformLocation( _tileKeyUniformNameID );
        opacityLocation      = pcp->getUniformLocation( _opacityUniformNameID );
        uidLocation          = pcp->getUniformLocation( _uidUniformNameID );
        orderLocation        = pcp->getUniformLocation( _orderUniformNameID );
        texMatParentLocation = pcp->getUniformLocation( _texMatParentUniformNameID );
    }
    
    // apply the tilekey uniform once.
    ext->glUniform4fv( tileKeyLocation, 1, _tileKeyValue.ptr() );

    // activate the tile coordinate set - same for all layers
    state.setTexCoordPointer( _imageUnit+1, _tileCoords.get() );

    if ( _layers.size() > 0 )
    {
        float prev_opacity        = -1.0f;
        float prev_alphaThreshold = -1.0f;

        // first bind any shared layers
        // TODO: optimize by pre-storing shared indexes
        for(unsigned i=0; i<_layers.size(); ++i)
        {
            const Layer& layer = _layers[i];

            // a "shared" layer binds to a secondary texture unit so that other layers
            // can see it and use it.
            if ( layer._imageLayer->isShared() )
            {
                int sharedUnit = layer._imageLayer->shareImageUnit().get();
                {
                    state.setActiveTextureUnit( sharedUnit );
                    state.setTexCoordPointer( sharedUnit, layer._texCoords.get() );
                    // bind the texture for this layer to the active share unit.
                    layer._tex->apply( state );

                    // no texture LOD blending for shared layers for now. maybe later.
                }
            }
        }

        // track the active image unit.
        int activeImageUnit = -1;

        // interate over all the image layers
        //glDepthMask(GL_TRUE);
        for(unsigned i=0; i<_layers.size(); ++i)
        {
          //  if ( i > 0 )
            //    glDepthMask(GL_FALSE);

            const Layer& layer = _layers[i];

            if ( layer._imageLayer->getVisible() )
            {       
                // activate the visible unit if necessary:
                if ( activeImageUnit != _imageUnit )
                {
                    state.setActiveTextureUnit( _imageUnit );
                    activeImageUnit = _imageUnit;
                }

                // bind the texture for this layer:
                layer._tex->apply( state );

                // if we're using a parent texture for blending, activate that now
                if ( layer._texParent.valid() )
                {
                    state.setActiveTextureUnit( _imageUnitParent );
                    activeImageUnit = _imageUnitParent;
                    layer._texParent->apply( state );
                }

                // bind the texture coordinates for this layer.
                // TODO: can probably optimize this by sharing or using texture matrixes.
                // State::setTexCoordPointer does some redundant work under the hood.
                state.setTexCoordPointer( _imageUnit, layer._texCoords.get() );

                // apply uniform values:
                if ( pcp )
                {
                    // apply opacity:
                    float opacity = layer._imageLayer->getOpacity();
                    if ( opacity != prev_opacity )
                    {
                        ext->glUniform1f( opacityLocation, (GLfloat)opacity );
                        prev_opacity = opacity;
                    }

                    // assign the layer UID:
                    ext->glUniform1i( uidLocation, (GLint)layer._layerID );

                    // assign the layer order:
                    ext->glUniform1i( orderLocation, (GLint)layersDrawn );

                    // assign the parent texture matrix
                    if ( layer._texParent.valid() )
                    {
                        ext->glUniformMatrix4fv( texMatParentLocation, 1, GL_FALSE, layer._texMatParent.ptr() );
                    }
                }

                // draw the primitive sets.
                for(unsigned int primitiveSetNum=0; primitiveSetNum!=_primitives.size(); ++primitiveSetNum)
                {
                    const osg::PrimitiveSet* primitiveset = _primitives[primitiveSetNum].get();
                    primitiveset->draw(state, usingVBOs);
                }

                ++layersDrawn;
            }
        }

        // prevent texture leakage
        // TODO: find a way to remove this to speed things up
        glBindTexture( GL_TEXTURE_2D, 0 );
    }

    // if we didn't draw anything, draw the raw tiles anyway with no texture.
    if ( layersDrawn == 0 )
    {
        ext->glUniform1f( opacityLocation, (GLfloat)1.0f );
        ext->glUniform1i( uidLocation, (GLint)-1 );
        ext->glUniform1i( orderLocation, (GLint)0 );

        // draw the primitives themselves.
        for(unsigned int primitiveSetNum=0; primitiveSetNum!=_primitives.size(); ++primitiveSetNum)
        {
            const osg::PrimitiveSet* primitiveset = _primitives[primitiveSetNum].get();
            primitiveset->draw(state, usingVBOs);
        }
    }
}