Esempio n. 1
0
void
PointDrawable::setupState()
{
    // Create the singleton state set for the shader. This stateset will be
    // shared by all PointDrawable instances so OSG will sort them together.
    if (!_sharedStateSet.valid())
    {
        static Threading::Mutex s_mutex;
        s_mutex.lock();
        if (!_sharedStateSet.valid())
        {
            _sharedStateSet = new osg::StateSet();

            _sharedStateSet->setTextureAttributeAndModes(0, new osg::PointSprite(), osg::StateAttribute::ON);

            if (_gpu)
            {
                VirtualProgram* vp = VirtualProgram::getOrCreate(_sharedStateSet.get());
                Shaders shaders;
                shaders.load(vp, shaders.PointDrawable);
                _sharedStateSet->setMode(GL_PROGRAM_POINT_SIZE, 1);
            }
            else
            {
                //todo
            }

            s_isCoreProfile = Registry::capabilities().isCoreProfile();
        }
        s_mutex.unlock();
    }
}
Esempio n. 2
0
void
ShaderGenerator::apply(osg::PagedLOD& node)
{
    if ( !_active )
        return;

    bool ignore;
    if ( node.getUserValue(SHADERGEN_HINT_IGNORE, ignore) && ignore )
        return;

    for( unsigned i=0; i<node.getNumFileNames(); ++i )
    {
        static Threading::Mutex s_mutex;
        s_mutex.lock();
        const std::string& filename = node.getFileName( i );
        if (!filename.empty() && 
            osgDB::getLowerCaseFileExtension(filename).compare(SHADERGEN_PL_EXTENSION) != 0 )
        {
            node.setFileName( i, Stringify() << filename << "." << SHADERGEN_PL_EXTENSION );
        }
        s_mutex.unlock();
    }

    apply( static_cast<osg::LOD&>(node) );
}
Esempio n. 3
0
void
PointDrawable::drawImplementation(osg::RenderInfo& ri) const
{
    // The mode validity for PointSprite might never get set since
    // the OSG GLObjectVisitor cannot traverse the shared stateset;
    // so this block of code will set it upon first draw.
    static bool s_sharedStateActivated = false;
    static Threading::Mutex s_mutex;

    if (!s_sharedStateActivated)
    {
        s_mutex.lock();
        if (!s_sharedStateActivated)
        {                
            osg::PointSprite* sprite = dynamic_cast<osg::PointSprite*>(
                _sharedStateSet->getTextureAttribute(0, osg::StateAttribute::POINTSPRITE));

            if (sprite)
                sprite->checkValidityOfAssociatedModes(*ri.getState());

            s_sharedStateActivated = true;
        }

        s_mutex.unlock();
    }

    osg::Geometry::drawImplementation(ri);
}
Esempio n. 4
0
osg::BoundingSphere
DepthOffsetGroup::computeBound() const
{
    if ( _adapter.supported() )
    {
        static Threading::Mutex s_mutex;
        s_mutex.lock();
        const_cast<DepthOffsetGroup*>(this)->scheduleUpdate();
        s_mutex.unlock();
    }
    return osg::Group::computeBound();
}
Esempio n. 5
0
void
DrapingTechnique::preCullTerrain(OverlayDecorator::TechRTTParams& params,
                                 osgUtil::CullVisitor*             cv )
{
    // allocate a texture image unit the first time through.
    if ( !_textureUnit.isSet() )
    {
        static Threading::Mutex m;
        m.lock();
        if ( !_textureUnit.isSet() )
        {
            // apply the user-request texture unit, if applicable:
            if ( _explicitTextureUnit.isSet() )
            {
                if ( !_textureUnit.isSet() || *_textureUnit != *_explicitTextureUnit )
                {
                    _textureUnit = *_explicitTextureUnit;
                }
            }

            // otherwise, automatically allocate a texture unit if necessary:
            else if ( !_textureUnit.isSet() )
            {
                int texUnit;
                if ( params._terrainResources->reserveTextureImageUnit(texUnit, "Draping") )
                {
                    _textureUnit = texUnit;
                    OE_INFO << LC << "Reserved texture image unit " << *_textureUnit << std::endl;
                }
                else
                {
                    OE_WARN << LC << "No texture image units available." << std::endl;
                }
            }
        }
        m.unlock();
    }

    if ( !params._rttCamera.valid() && _textureUnit.isSet() )
    {
        setUpCamera( params );
    }
}
osg::Node*
GeometryCompiler::compile(FeatureList&          workingSet,
                          const Style&          style,
                          const FilterContext&  context)
{
#ifdef PROFILING
    osg::Timer_t p_start = osg::Timer::instance()->tick();
    unsigned p_features = workingSet.size();
#endif

    // for debugging/validation.
    std::vector<std::string> history;
    bool trackHistory = (_options.validate() == true);

    osg::ref_ptr<osg::Group> resultGroup = new osg::Group();

    // create a filter context that will track feature data through the process
    FilterContext sharedCX = context;

    if ( !sharedCX.extent().isSet() && sharedCX.profile() )
    {
        sharedCX.extent() = sharedCX.profile()->getExtent();
    }

    // ref_ptr's to hold defaults in case we need them.
    osg::ref_ptr<PointSymbol>   defaultPoint;
    osg::ref_ptr<LineSymbol>    defaultLine;
    osg::ref_ptr<PolygonSymbol> defaultPolygon;

    // go through the Style and figure out which filters to use.
    const PointSymbol*     point     = style.get<PointSymbol>();
    const LineSymbol*      line      = style.get<LineSymbol>();
    const PolygonSymbol*   polygon   = style.get<PolygonSymbol>();
    const ExtrusionSymbol* extrusion = style.get<ExtrusionSymbol>();
    const AltitudeSymbol*  altitude  = style.get<AltitudeSymbol>();
    const TextSymbol*      text      = style.get<TextSymbol>();
    const MarkerSymbol*    marker    = style.get<MarkerSymbol>();    // to be deprecated
    const IconSymbol*      icon      = style.get<IconSymbol>();
    const ModelSymbol*     model     = style.get<ModelSymbol>();

    // Perform tessellation first.
    if ( line )
    {
        if ( line->tessellation().isSet() )
        {
            TemplateFeatureFilter<TessellateOperator> filter;
            filter.setNumPartitions( *line->tessellation() );
            filter.setDefaultGeoInterp( _options.geoInterp().get() );
            sharedCX = filter.push( workingSet, sharedCX );
            if ( trackHistory ) history.push_back( "tessellation" );
        }
        else if ( line->tessellationSize().isSet() )
        {
            TemplateFeatureFilter<TessellateOperator> filter;
            filter.setMaxPartitionSize( *line->tessellationSize() );
            filter.setDefaultGeoInterp( _options.geoInterp().get() );
            sharedCX = filter.push( workingSet, sharedCX );
            if ( trackHistory ) history.push_back( "tessellationSize" );
        }
    }

    // if the style was empty, use some defaults based on the geometry type of the
    // first feature.
    if ( !point && !line && !polygon && !marker && !extrusion && !text && !model && !icon && workingSet.size() > 0 )
    {
        Feature* first = workingSet.begin()->get();
        Geometry* geom = first->getGeometry();
        if ( geom )
        {
            switch( geom->getComponentType() )
            {
            case Geometry::TYPE_LINESTRING:
            case Geometry::TYPE_RING:
                defaultLine = new LineSymbol();
                line = defaultLine.get();
                break;
            case Geometry::TYPE_POINTSET:
                defaultPoint = new PointSymbol();
                point = defaultPoint.get();
                break;
            case Geometry::TYPE_POLYGON:
                defaultPolygon = new PolygonSymbol();
                polygon = defaultPolygon.get();
                break;
            case Geometry::TYPE_MULTI:
            case Geometry::TYPE_UNKNOWN:
                break;
            }
        }
    }

    // resample the geometry if necessary:
    if (_options.resampleMode().isSet())
    {
        ResampleFilter resample;
        resample.resampleMode() = *_options.resampleMode();        
        if (_options.resampleMaxLength().isSet())
        {
            resample.maxLength() = *_options.resampleMaxLength();
        }                   
        sharedCX = resample.push( workingSet, sharedCX ); 
        if ( trackHistory ) history.push_back( "resample" );
    }    
    
    // check whether we need to do elevation clamping:
    bool altRequired =
        _options.ignoreAltitudeSymbol() != true &&
        altitude && (
            altitude->clamping() != AltitudeSymbol::CLAMP_NONE ||
            altitude->verticalOffset().isSet() ||
            altitude->verticalScale().isSet() ||
            altitude->script().isSet() );

    // marker substitution -- to be deprecated in favor of model/icon
    if ( marker )
    {
        if ( trackHistory ) history.push_back( "marker" );

        // use a separate filter context since we'll be munging the data
        FilterContext markerCX = sharedCX;

        if ( marker->placement() == MarkerSymbol::PLACEMENT_RANDOM   ||
             marker->placement() == MarkerSymbol::PLACEMENT_INTERVAL )
        {
            ScatterFilter scatter;
            scatter.setDensity( *marker->density() );
            scatter.setRandom( marker->placement() == MarkerSymbol::PLACEMENT_RANDOM );
            scatter.setRandomSeed( *marker->randomSeed() );
            markerCX = scatter.push( workingSet, markerCX );
            if ( trackHistory ) history.push_back( "scatter" );
        }
        else if ( marker->placement() == MarkerSymbol::PLACEMENT_CENTROID )
        {
            CentroidFilter centroid;
            markerCX = centroid.push( workingSet, markerCX );  
            if ( trackHistory ) history.push_back( "centroid" );
        }

        if ( altRequired )
        {
            AltitudeFilter clamp;
            clamp.setPropertiesFromStyle( style );
            markerCX = clamp.push( workingSet, markerCX );
            if ( trackHistory ) history.push_back( "altitude" );

            // don't set this; we changed the input data.
            //altRequired = false;
        }

        SubstituteModelFilter sub( style );

        sub.setClustering( *_options.clustering() );

        sub.setUseDrawInstanced( *_options.instancing() );

        if ( _options.featureName().isSet() )
            sub.setFeatureNameExpr( *_options.featureName() );

        osg::Node* node = sub.push( workingSet, markerCX );
        if ( node )
        {
            if ( trackHistory ) history.push_back( "substitute" );
            resultGroup->addChild( node );
        }
    }

    // instance substitution (replaces marker)
    else if ( model )
    {
        const InstanceSymbol* instance = model ? (const InstanceSymbol*)model : (const InstanceSymbol*)icon;

        // use a separate filter context since we'll be munging the data
        FilterContext localCX = sharedCX;
        
        if ( trackHistory ) history.push_back( "model");

        if ( instance->placement() == InstanceSymbol::PLACEMENT_RANDOM   ||
             instance->placement() == InstanceSymbol::PLACEMENT_INTERVAL )
        {
            ScatterFilter scatter;
            scatter.setDensity( *instance->density() );
            scatter.setRandom( instance->placement() == InstanceSymbol::PLACEMENT_RANDOM );
            scatter.setRandomSeed( *instance->randomSeed() );
            localCX = scatter.push( workingSet, localCX );
            if ( trackHistory ) history.push_back( "scatter" );
        }
        else if ( instance->placement() == InstanceSymbol::PLACEMENT_CENTROID )
        {
            CentroidFilter centroid;
            localCX = centroid.push( workingSet, localCX );
            if ( trackHistory ) history.push_back( "centroid" );
        }

        if ( altRequired )
        {
            AltitudeFilter clamp;
            clamp.setPropertiesFromStyle( style );
            localCX = clamp.push( workingSet, localCX );
            if ( trackHistory ) history.push_back( "altitude" );
        }

        SubstituteModelFilter sub( style );

        // activate clustering
        sub.setClustering( *_options.clustering() );

        // activate draw-instancing
        sub.setUseDrawInstanced( *_options.instancing() );

        // activate feature naming
        if ( _options.featureName().isSet() )
            sub.setFeatureNameExpr( *_options.featureName() );
        

        osg::Node* node = sub.push( workingSet, localCX );
        if ( node )
        {
            if ( trackHistory ) history.push_back( "substitute" );

            resultGroup->addChild( node );

            // enable auto scaling on the group?
            if ( model && model->autoScale() == true )
            {
                resultGroup->getOrCreateStateSet()->setRenderBinDetails(0, osgEarth::AUTO_SCALE_BIN );
            }
        }
    }

    // extruded geometry
    if ( extrusion )
    {
        if ( altRequired )
        {
            AltitudeFilter clamp;
            clamp.setPropertiesFromStyle( style );
            sharedCX = clamp.push( workingSet, sharedCX );
            if ( trackHistory ) history.push_back( "altitude" );
            altRequired = false;
        }

        ExtrudeGeometryFilter extrude;
        extrude.setStyle( style );

        // apply per-feature naming if requested.
        if ( _options.featureName().isSet() )
            extrude.setFeatureNameExpr( *_options.featureName() );

        if ( _options.mergeGeometry().isSet() )
            extrude.setMergeGeometry( *_options.mergeGeometry() );

        osg::Node* node = extrude.push( workingSet, sharedCX );
        if ( node )
        {
            if ( trackHistory ) history.push_back( "extrude" );
            resultGroup->addChild( node );
        }
        
    }

    // simple geometry
    else if ( point || line || polygon )
    {
        if ( altRequired )
        {
            AltitudeFilter clamp;
            clamp.setPropertiesFromStyle( style );
            sharedCX = clamp.push( workingSet, sharedCX );
            if ( trackHistory ) history.push_back( "altitude" );
            altRequired = false;
        }

        BuildGeometryFilter filter( style );
        filter.maxGranularity() = *_options.maxGranularity();
        filter.geoInterp()      = *_options.geoInterp();

        if ( _options.featureName().isSet() )
            filter.featureName() = *_options.featureName();

        osg::Node* node = filter.push( workingSet, sharedCX );
        if ( node )
        {
            if ( trackHistory ) history.push_back( "geometry" );
            resultGroup->addChild( node );
        }
    }

    if ( text || icon )
    {
        if ( altRequired )
        {
            AltitudeFilter clamp;
            clamp.setPropertiesFromStyle( style );
            sharedCX = clamp.push( workingSet, sharedCX );
            if ( trackHistory ) history.push_back( "altitude" );
            altRequired = false;
        }

        BuildTextFilter filter( style );
        osg::Node* node = filter.push( workingSet, sharedCX );
        if ( node )
        {
            if ( trackHistory ) history.push_back( "text" );
            resultGroup->addChild( node );
        }
    }

    if (Registry::capabilities().supportsGLSL())
    {
        if ( _options.shaderPolicy() == SHADERPOLICY_GENERATE )
        {
            // no ss cache because we will optimize later.
            Registry::shaderGenerator().run( 
                resultGroup.get(),
                "osgEarth.GeomCompiler" );
        }
        else if ( _options.shaderPolicy() == SHADERPOLICY_DISABLE )
        {
            resultGroup->getOrCreateStateSet()->setAttributeAndModes(
                new osg::Program(),
                osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE );
        
            if ( trackHistory ) history.push_back( "no shaders" );
        }
    }

    // Optimize stateset sharing.
    if ( _options.optimizeStateSharing() == true )
    {
        // Common state set cache?
        osg::ref_ptr<StateSetCache> sscache;
        if ( sharedCX.getSession() )
        {
            // with a shared cache, don't combine statesets. They may be
            // in the live graph
            sscache = sharedCX.getSession()->getStateSetCache();
            sscache->consolidateStateAttributes( resultGroup.get() );
        }
        else 
        {
            // isolated: perform full optimization
            sscache = new StateSetCache();
            sscache->optimize( resultGroup.get() );
        }
        
        if ( trackHistory ) history.push_back( "share state" );
    }

    if ( _options.optimize() == true )
    {
        OE_DEBUG << LC << "optimize begin" << std::endl;

        // Run the optimizer on the resulting graph
        int optimizations =
            osgUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS |
            osgUtil::Optimizer::REMOVE_REDUNDANT_NODES |
            osgUtil::Optimizer::COMBINE_ADJACENT_LODS |
            osgUtil::Optimizer::SHARE_DUPLICATE_STATE |
            osgUtil::Optimizer::MERGE_GEOMETRY |
            osgUtil::Optimizer::CHECK_GEOMETRY |
            osgUtil::Optimizer::MERGE_GEODES |
            osgUtil::Optimizer::STATIC_OBJECT_DETECTION;

        osgUtil::Optimizer opt;
        opt.optimize(resultGroup.get(), optimizations);
        OE_DEBUG << LC << "optimize complete" << std::endl;

        if ( trackHistory ) history.push_back( "optimize" );
    }
    

    //test: dump the tile to disk
    //osgDB::writeNodeFile( *(resultGroup.get()), "out.osg" );

#ifdef PROFILING
    static double totalTime = 0.0;
    static Threading::Mutex totalTimeMutex;
    osg::Timer_t p_end = osg::Timer::instance()->tick();
    double t = osg::Timer::instance()->delta_s(p_start, p_end);
    totalTimeMutex.lock();
    totalTime += t;
    totalTimeMutex.unlock();
    OE_INFO << LC
        << "features = " << p_features
        << ", time = " << t << " s.  cummulative = " 
        << totalTime << " s."
        << std::endl;
#endif


    if ( _options.validate() == true )
    {
        OE_NOTICE << LC << "-- Start Debugging --\n";
        std::stringstream buf;
        buf << "HISTORY ";
        for(std::vector<std::string>::iterator h = history.begin(); h != history.end(); ++h)
            buf << ".. " << *h;
        OE_NOTICE << LC << buf.str() << "\n";
        osgEarth::GeometryValidator validator;
        resultGroup->accept(validator);
        OE_NOTICE << LC << "-- End Debugging --\n";
    }

    return resultGroup.release();
}
        virtual ReadResult readNode(const std::string& uri, const Options* options) const
        {
            std::string ext = osgDB::getFileExtension(uri);
            if ( acceptsExtension(ext) )
            {
                // See if the filename starts with server: and strip it off.  This will trick OSG
                // into passing in the filename to our plugin instead of using the CURL plugin if
                // the filename contains a URL.  So, if you want to read a URL, you can use the
                // following format: osgDB::readNodeFile("server:http://myserver/myearth.earth").
                // This should only be necessary for the first level as the other files will have
                // a tilekey prepended to them.
                if ((uri.length() > 7) && (uri.substr(0, 7) == "server:"))
                    return readNode(uri.substr(7), options);

                // parse the tile key and engine ID:
                std::string tileDef = osgDB::getNameLessExtension(uri);
                unsigned int lod, x, y, engineID;
                sscanf(tileDef.c_str(), "%d/%d/%d.%d", &lod, &x, &y, &engineID);

                // find the appropriate engine:
                osg::ref_ptr<MPTerrainEngineNode> engineNode;
                MPTerrainEngineNode::getEngineByUID( (UID)engineID, engineNode );
                if ( engineNode.valid() )
                {
                    Registry::instance()->startActivity(uri);

                    OE_START_TIMER(tileLoadTime);

                    // see if we have a progress tracker
                    ProgressCallback* progress = 
                        options ? const_cast<ProgressCallback*>(
                        dynamic_cast<const ProgressCallback*>(options->getUserData())) : 0L;

                    // must have a ProgressCallback if we're profiling.
                    bool ownProgress = (progress == 0L);
                    if ( !progress && _profiling )
                        progress = new ProgressCallback();

                    // assemble the key and create the node:
                    const Profile* profile = engineNode->getMap()->getProfile();
                    TileKey key( lod, x, y, profile );

                    osg::ref_ptr<osg::Node> node;

                    if ( "osgearth_engine_mp_tile" == ext )
                    {
                        node = engineNode->createNode(key, progress);
                    }
                    else if ( "osgearth_engine_mp_standalone_tile" == ext )
                    {
                        node = engineNode->createStandaloneNode(key, progress);
                    }

                    double tileLoadTime = OE_STOP_TIMER(tileLoadTime);
                    if ( progress )
                        progress->stats()["tile_load_time"] = tileLoadTime;

                    // profiling level 1 = detailed stats about individual loads.
                    if ( _profiling == 1 )
                    {
                        progress->stats()["http_get_time_avg"] =
                            progress->stats()["http_get_time"] / progress->stats()["http_get_count"];

                        OE_NOTICE << "tile: " << tileDef << std::endl;
                        for(ProgressCallback::Stats::iterator i = progress->stats().begin();
                            i != progress->stats().end();
                            ++i)
                        {
                            std::stringstream buf;
                            if ( osgEarth::endsWith(i->first, "_time") )
                            {
                                buf 
                                    << i->first << " = " << std::setprecision(4) << (i->second*1000.0) << "ms ("
                                    << (int)((i->second/tileLoadTime)*100) << "%)";
                            }
                            else
                            {
                                buf << i->first << " = " << std::setprecision(4) << i->second;
                            }
                            OE_NOTICE << "   " << buf.str() << std::endl;
                        }

                        if ( ownProgress )
                        {
                            delete progress;
                            progress = 0L;
                        }
                    }

                    // profiling level 2 = running 60-sample averages
                    else if ( _profiling == 2 )
                    {
                        static std::deque<double> tileLoadTimes[3];
                        static int    samples[3]       = { 64, 256, 1024 };
                        static double runningTotals[3] = { 0.0, 0.0, 0.0 };
                        static Threading::Mutex averageMutex;

                        averageMutex.lock();
                        for(int i=0; i<3; ++i)
                        {
                            runningTotals[i] += tileLoadTime;
                            tileLoadTimes[i].push_back( tileLoadTime );
                            if ( tileLoadTimes[i].size() > samples[i] )
                            {
                                runningTotals[i] -= tileLoadTimes[i].front();
                                tileLoadTimes[i].pop_front();
                            }
                        }
                        OE_NOTICE << "(samples)time : "
                            << "(" << samples[0] << "): " << (tileLoadTimes[0].size() == samples[0] ? (runningTotals[0]/(double)samples[0])*1000.0 : -1.0) << "ms; "
                            << "(" << samples[1] << "): " << (tileLoadTimes[1].size() == samples[1] ? (runningTotals[1]/(double)samples[1])*1000.0 : -1.0) << "ms; "
                            << "(" << samples[2] << "): " << (tileLoadTimes[2].size() == samples[2] ? (runningTotals[2]/(double)samples[2])*1000.0 : -1.0) << "ms; "
                            << std::endl;

                        averageMutex.unlock();
                    }
                    
                    Registry::instance()->endActivity(uri);

                    // Deal with failed loads.
                    if ( !node.valid() )
                    {
                        if ( key.getLOD() == 0  )
                        {
                            // the tile will ask again next time.
                            return ReadResult::FILE_NOT_FOUND;
                        }
                        else if (progress && progress->isCanceled())
                        {
                            if ( _profiling )
                            {
                                OE_NOTICE << LC << "Tile " << key.str() << " -- canceled!" << std::endl;
                            }
                            return ReadResult::FILE_NOT_FOUND;
                        }
                        else
                        {
                            // the parent tile will never ask again as long as it remains in memory.
                            node = new InvalidTileNode( key );
                        }
                    }
                    else
                    {   
                        // notify the Terrain interface of a new tile
                        // moved this to TileNodeRegistry::add
                        //osg::Timer_t start = osg::Timer::instance()->tick();
                        //engineNode->getTerrain()->notifyTileAdded(key, node.get());
                        //osg::Timer_t end = osg::Timer::instance()->tick();
                    }
                    
                    return ReadResult( node.get(), ReadResult::FILE_LOADED );
                }
                else
                {
                    return ReadResult::FILE_NOT_FOUND;
                }
            }
            else
            {
                return ReadResult::FILE_NOT_HANDLED;
            }
        }