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(); } }
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) ); }
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); }
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(); }
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; } }