void Map::clear() { ImageLayerVector imageLayersRemoved; ElevationLayerVector elevLayersRemoved; ModelLayerVector modelLayersRemoved; MaskLayerVector maskLayersRemoved; Revision newRevision; { Threading::ScopedWriteLock lock( _mapDataMutex ); imageLayersRemoved.swap( _imageLayers ); elevLayersRemoved.swap ( _elevationLayers ); modelLayersRemoved.swap( _modelLayers ); // calculate a new revision. newRevision = ++_dataModelRevision; } // a separate block b/c we don't need the mutex for( MapCallbackList::iterator i = _mapCallbacks.begin(); i != _mapCallbacks.end(); i++ ) { for( ImageLayerVector::iterator k = imageLayersRemoved.begin(); k != imageLayersRemoved.end(); ++k ) i->get()->onMapModelChanged( MapModelChange(MapModelChange::REMOVE_IMAGE_LAYER, newRevision, k->get()) ); for( ElevationLayerVector::iterator k = elevLayersRemoved.begin(); k != elevLayersRemoved.end(); ++k ) i->get()->onMapModelChanged( MapModelChange(MapModelChange::REMOVE_ELEVATION_LAYER, newRevision, k->get()) ); for( ModelLayerVector::iterator k = modelLayersRemoved.begin(); k != modelLayersRemoved.end(); ++k ) i->get()->onMapModelChanged( MapModelChange(MapModelChange::REMOVE_MODEL_LAYER, newRevision, k->get()) ); } }
void TerrainEngineNode::setMap(const Map* map, const TerrainOptions& options) { if (!map) return; _map = map; // Create a terrain utility interface. This interface can be used // to query the in-memory terrain graph, subscribe to tile events, etc. _terrainInterface = new Terrain( this, map->getProfile(), map->isGeocentric(), options ); // Set up the CSN values. We support this because some manipulators look for it, // but osgEarth itself doesn't use it. _map->getProfile()->getSRS()->populateCoordinateSystemNode( this ); // OSG's CSN likes a NULL ellipsoid to represent projected mode. if ( !_map->isGeocentric() ) this->setEllipsoidModel( NULL ); // Install an object to manage texture image unit usage: _textureResourceTracker = new TextureCompositor(); std::set<int> offLimits = osgEarth::Registry::instance()->getOffLimitsTextureImageUnits(); for(std::set<int>::const_iterator i = offLimits.begin(); i != offLimits.end(); ++i) _textureResourceTracker->setTextureImageUnitOffLimits( *i ); // Register a callback so we can process further map model changes _map->addMapCallback( new TerrainEngineNodeCallbackProxy(this) ); // Force a render bin if specified in the options if ( options.binNumber().isSet() ) { osg::StateSet* set = getOrCreateStateSet(); set->setRenderBinDetails( options.binNumber().get(), "RenderBin" ); } // This is the object that creates the data model for each terrain tile. _tileModelFactory = new TerrainTileModelFactory(options); // Manually trigger the map callbacks the first time: if (_map->getProfile()) onMapInfoEstablished(MapInfo(_map)); // Create a layer controller. This object affects the uniforms // that control layer appearance properties _imageLayerController = new ImageLayerController(_map, this); // register the layer Controller it with all pre-existing image layers: MapFrame mapf(_map); ImageLayerVector imageLayers; mapf.getLayers(imageLayers); for (ImageLayerVector::const_iterator i = imageLayers.begin(); i != imageLayers.end(); ++i) { i->get()->addCallback(_imageLayerController.get()); } _initStage = INIT_POSTINIT_COMPLETE; }
Revision Map::getImageLayers( ImageLayerVector& out_list ) const { out_list.reserve( _imageLayers.size() ); Threading::ScopedReadLock lock( const_cast<Map*>(this)->_mapDataMutex ); for( ImageLayerVector::const_iterator i = _imageLayers.begin(); i != _imageLayers.end(); ++i ) out_list.push_back( i->get() ); return _dataModelRevision; }
TerrainEngineNode::~TerrainEngineNode() { OE_DEBUG << LC << "~TerrainEngineNode\n"; //Remove any callbacks added to the image layers if (_map) { MapFrame mapf( _map ); ImageLayerVector imageLayers; mapf.getLayers(imageLayers); for( ImageLayerVector::const_iterator i = imageLayers.begin(); i != imageLayers.end(); ++i ) { i->get()->removeCallback( _imageLayerController.get() ); } } }
bool OSGTileFactory::hasMoreLevels( Map* map, const TileKey& key ) { //Threading::ScopedReadLock lock( map->getMapDataMutex() ); bool more_levels = false; ImageLayerVector imageLayers; map->getImageLayers( imageLayers ); for ( ImageLayerVector::const_iterator i = imageLayers.begin(); i != imageLayers.end(); i++ ) { const ImageLayerOptions& opt = i->get()->getImageLayerOptions(); if ( !opt.maxLevel().isSet() || key.getLevelOfDetail() < (unsigned int)*opt.maxLevel() ) { more_levels = true; break; } } if ( !more_levels ) { ElevationLayerVector elevLayers; map->getElevationLayers( elevLayers ); for( ElevationLayerVector::const_iterator j = elevLayers.begin(); j != elevLayers.end(); j++ ) { const ElevationLayerOptions& opt = j->get()->getElevationLayerOptions(); if ( !opt.maxLevel().isSet() || key.getLevelOfDetail() < (unsigned int)*opt.maxLevel() ) //if ( !j->get()->maxLevel().isSet() || key.getLevelOfDetail() < j->get()->maxLevel().get() ) { more_levels = true; break; } } } return more_levels; }
void Map::setLayersFromMap( const Map* map ) { this->clear(); if ( map ) { ImageLayerVector newImages; map->getImageLayers( newImages ); for( ImageLayerVector::iterator i = newImages.begin(); i != newImages.end(); ++i ) addImageLayer( i->get() ); ElevationLayerVector newElev; map->getElevationLayers( newElev ); for( ElevationLayerVector::iterator i = newElev.begin(); i != newElev.end(); ++i ) addElevationLayer( i->get() ); ModelLayerVector newModels; map->getModelLayers( newModels ); for( ModelLayerVector::iterator i = newModels.begin(); i != newModels.end(); ++i ) addModelLayer( i->get() ); } }
void updateControlPanel() { // erase all child controls and just rebuild them b/c we're lazy. //Rebuild all the image layers s_imageBox->clearControls(); int row = 0; LabelControl* activeLabel = new LabelControl( "Image Layers", 20, osg::Vec4f(1,1,0,1) ); s_imageBox->setControl( 1, row++, activeLabel ); // the active map layers: MapFrame mapf( s_activeMap.get() ); ImageLayerVector imageLayers; mapf.getLayers(imageLayers); int layerNum = imageLayers.size()-1; for( ImageLayerVector::const_reverse_iterator i = imageLayers.rbegin(); i != imageLayers.rend(); ++i ) createLayerItem( s_imageBox, row++, layerNum--, imageLayers.size(), i->get(), true ); MapFrame mapf2( s_inactiveMap.get() ); imageLayers.clear(); mapf2.getLayers(imageLayers); if ( imageLayers.size() > 0 ) { LabelControl* inactiveLabel = new LabelControl( "Removed:", 18, osg::Vec4f(1,1,0,1) ); s_imageBox->setControl( 1, row++, inactiveLabel ); for( unsigned int i=0; i<imageLayers.size(); ++i ) { createLayerItem( s_imageBox, row++, -1, -1, imageLayers[i].get(), false ); } } //Rebuild the elevation layers s_elevationBox->clearControls(); row = 0; activeLabel = new LabelControl( "Elevation Layers", 20, osg::Vec4f(1,1,0,1) ); s_elevationBox->setControl( 1, row++, activeLabel ); // the active map layers: ElevationLayerVector elevationLayers; mapf.getLayers(elevationLayers); layerNum = elevationLayers.size()-1; for( ElevationLayerVector::const_reverse_iterator i = elevationLayers.rbegin(); i != elevationLayers.rend(); ++i ) createLayerItem( s_elevationBox, row++, layerNum--, elevationLayers.size(), i->get(), true ); if ( mapf2.elevationLayers().size() > 0 ) { LabelControl* inactiveLabel = new LabelControl( "Removed:", 18, osg::Vec4f(1,1,0,1) ); s_elevationBox->setControl( 1, row++, inactiveLabel ); for( unsigned int i=0; i<mapf2.elevationLayers().size(); ++i ) { createLayerItem( s_elevationBox, row++, -1, -1, mapf2.elevationLayers().at(i), false ); } } //Rebuild the model layers s_modelBox->clearControls(); row = 0; activeLabel = new LabelControl( "Model Layers", 20, osg::Vec4f(1,1,0,1) ); s_modelBox->setControl( 1, row++, activeLabel ); // the active map layers: ModelLayerVector modelLayers; mapf.getLayers(modelLayers); for( ModelLayerVector::const_reverse_iterator i = modelLayers.rbegin(); i != modelLayers.rend(); ++i ) createModelLayerItem( s_modelBox, row++, i->get(), true ); }
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; ImageLayerVector imageLayers; frame.getLayers(imageLayers); for( ImageLayerVector::const_iterator i = imageLayers.begin(); i != 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, 0u ); 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(); }
void MapNodeHelper::parse(MapNode* mapNode, osg::ArgumentParser& args, osgViewer::View* view, osg::Group* root, Control* userControl ) const { if ( !root ) root = mapNode; // options to use for the load osg::ref_ptr<osgDB::Options> dbOptions = Registry::instance()->cloneOrCreateOptions(); // parse out custom example arguments first: bool useSky = args.read("--sky"); bool useOcean = args.read("--ocean"); bool useMGRS = args.read("--mgrs"); bool useDMS = args.read("--dms"); bool useDD = args.read("--dd"); bool useCoords = args.read("--coords") || useMGRS || useDMS || useDD; bool useOrtho = args.read("--ortho"); bool useAutoClip = args.read("--autoclip"); bool useShadows = args.read("--shadows"); bool animateSky = args.read("--animate-sky"); bool showActivity = args.read("--activity"); bool useLogDepth = args.read("--logdepth"); bool useLogDepth2 = args.read("--logdepth2"); bool kmlUI = args.read("--kmlui"); if (args.read("--verbose")) osgEarth::setNotifyLevel(osg::INFO); if (args.read("--quiet")) osgEarth::setNotifyLevel(osg::FATAL); float ambientBrightness = 0.2f; args.read("--ambientBrightness", ambientBrightness); std::string kmlFile; args.read( "--kml", kmlFile ); std::string imageFolder; args.read( "--images", imageFolder ); std::string imageExtensions; args.read("--image-extensions", imageExtensions); // animation path: std::string animpath; if ( args.read("--path", animpath) ) { view->setCameraManipulator( new osgGA::AnimationPathManipulator(animpath) ); } // Install a new Canvas for our UI controls, or use one that already exists. ControlCanvas* canvas = ControlCanvas::getOrCreate( view ); Container* mainContainer = canvas->addControl( new VBox() ); mainContainer->setAbsorbEvents( true ); mainContainer->setBackColor( Color(Color::Black, 0.8) ); mainContainer->setHorizAlign( Control::ALIGN_LEFT ); mainContainer->setVertAlign( Control::ALIGN_BOTTOM ); // install the user control: if ( userControl ) mainContainer->addControl( userControl ); // look for external data in the map node: const Config& externals = mapNode->externalConfig(); const Config& skyConf = externals.child("sky"); const Config& oceanConf = externals.child("ocean"); const Config& annoConf = externals.child("annotations"); const Config& declutterConf = externals.child("decluttering"); // some terrain effects. // TODO: Most of these are likely to move into extensions. const Config& lodBlendingConf = externals.child("lod_blending"); const Config& vertScaleConf = externals.child("vertical_scale"); const Config& contourMapConf = externals.child("contour_map"); // Adding a sky model: if ( useSky || !skyConf.empty() ) { SkyOptions options(skyConf); if ( options.getDriver().empty() ) { if ( mapNode->getMapSRS()->isGeographic() ) options.setDriver("simple"); else options.setDriver("gl"); } SkyNode* sky = SkyNode::create(options, mapNode); if ( sky ) { sky->attach( view, 0 ); if ( mapNode->getNumParents() > 0 ) { osgEarth::insertGroup(sky, mapNode->getParent(0)); } else { sky->addChild( mapNode ); root = sky; } Control* c = SkyControlFactory().create(sky, view); if ( c ) mainContainer->addControl( c ); if (animateSky) { sky->setUpdateCallback( new AnimateSkyUpdateCallback() ); } } } // Adding an ocean model: if ( useOcean || !oceanConf.empty() ) { OceanNode* ocean = OceanNode::create(OceanOptions(oceanConf), mapNode); if ( ocean ) { // if there's a sky, we want to ocean under it osg::Group* parent = osgEarth::findTopMostNodeOfType<SkyNode>(root); if ( !parent ) parent = root; parent->addChild( ocean ); Control* c = OceanControlFactory().create(ocean); if ( c ) mainContainer->addControl(c); } } // Shadowing. if ( useShadows ) { ShadowCaster* caster = new ShadowCaster(); caster->setLight( view->getLight() ); caster->getShadowCastingGroup()->addChild( mapNode->getModelLayerGroup() ); if ( mapNode->getNumParents() > 0 ) { insertGroup(caster, mapNode->getParent(0)); } else { caster->addChild(mapNode); root = caster; } } // Loading KML from the command line: if ( !kmlFile.empty() ) { KMLOptions kml_options; kml_options.declutter() = true; // set up a default icon for point placemarks: IconSymbol* defaultIcon = new IconSymbol(); defaultIcon->url()->setLiteral(KML_PUSHPIN_URL); kml_options.defaultIconSymbol() = defaultIcon; osg::Node* kml = KML::load( URI(kmlFile), mapNode, kml_options ); if ( kml ) { if (kmlUI) { Control* c = AnnotationGraphControlFactory().create(kml, view); if ( c ) { c->setVertAlign( Control::ALIGN_TOP ); canvas->addControl( c ); } } root->addChild( kml ); } else { OE_NOTICE << "Failed to load " << kmlFile << std::endl; } } // Annotations in the map node externals: if ( !annoConf.empty() ) { osg::Group* annotations = 0L; AnnotationRegistry::instance()->create( mapNode, annoConf, dbOptions.get(), annotations ); if ( annotations ) { root->addChild( annotations ); } } // Configure the de-cluttering engine for labels and annotations: if ( !declutterConf.empty() ) { Decluttering::setOptions( DeclutteringOptions(declutterConf) ); } // Configure the mouse coordinate readout: if ( useCoords ) { LabelControl* readout = new LabelControl(); readout->setBackColor( Color(Color::Black, 0.8) ); readout->setHorizAlign( Control::ALIGN_RIGHT ); readout->setVertAlign( Control::ALIGN_BOTTOM ); Formatter* formatter = useMGRS ? (Formatter*)new MGRSFormatter(MGRSFormatter::PRECISION_1M, 0L, MGRSFormatter::USE_SPACES) : useDMS ? (Formatter*)new LatLongFormatter(LatLongFormatter::FORMAT_DEGREES_MINUTES_SECONDS) : useDD ? (Formatter*)new LatLongFormatter(LatLongFormatter::FORMAT_DECIMAL_DEGREES) : 0L; MouseCoordsTool* mcTool = new MouseCoordsTool( mapNode ); mcTool->addCallback( new MouseCoordsLabelCallback(readout, formatter) ); view->addEventHandler( mcTool ); canvas->addControl( readout ); } // Configure for an ortho camera: if ( useOrtho ) { EarthManipulator* manip = dynamic_cast<EarthManipulator*>(view->getCameraManipulator()); if ( manip ) { manip->getSettings()->setCameraProjection( EarthManipulator::PROJ_ORTHOGRAPHIC ); } } // activity monitor (debugging) if ( showActivity ) { VBox* vbox = new VBox(); vbox->setBackColor( Color(Color::Black, 0.8) ); vbox->setHorizAlign( Control::ALIGN_RIGHT ); vbox->setVertAlign( Control::ALIGN_BOTTOM ); view->addEventHandler( new ActivityMonitorTool(vbox) ); canvas->addControl( vbox ); } // Install an auto clip plane clamper if ( useAutoClip ) { mapNode->addCullCallback( new AutoClipPlaneCullCallback(mapNode) ); } // Install logarithmic depth buffer on main camera if ( useLogDepth ) { OE_INFO << LC << "Activating logarithmic depth buffer on main camera" << std::endl; osgEarth::Util::LogarithmicDepthBuffer logDepth; logDepth.setUseFragDepth( true ); logDepth.install( view->getCamera() ); } else if ( useLogDepth2 ) { OE_INFO << LC << "Activating logarithmic depth buffer (vertex-only) on main camera" << std::endl; osgEarth::Util::LogarithmicDepthBuffer logDepth; logDepth.setUseFragDepth( false ); logDepth.install( view->getCamera() ); } // Scan for images if necessary. if ( !imageFolder.empty() ) { std::vector<std::string> extensions; if ( !imageExtensions.empty() ) StringTokenizer( imageExtensions, extensions, ",;", "", false, true ); if ( extensions.empty() ) extensions.push_back( "tif" ); OE_INFO << LC << "Loading images from " << imageFolder << "..." << std::endl; ImageLayerVector imageLayers; DataScanner scanner; scanner.findImageLayers( imageFolder, extensions, imageLayers ); if ( imageLayers.size() > 0 ) { mapNode->getMap()->beginUpdate(); for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i ) { mapNode->getMap()->addImageLayer( i->get() ); } mapNode->getMap()->endUpdate(); } OE_INFO << LC << "...found " << imageLayers.size() << " image layers." << std::endl; } // Install elevation morphing if ( !lodBlendingConf.empty() ) { mapNode->getTerrainEngine()->addEffect( new LODBlending(lodBlendingConf) ); } // Install vertical scaler if ( !vertScaleConf.empty() ) { mapNode->getTerrainEngine()->addEffect( new VerticalScale(vertScaleConf) ); } // Install a contour map effect. if ( !contourMapConf.empty() ) { mapNode->getTerrainEngine()->addEffect( new ContourMap(contourMapConf) ); } // Generic named value uniform with min/max. VBox* uniformBox = 0L; while( args.find( "--uniform" ) >= 0 ) { std::string name; float minval, maxval; if ( args.read( "--uniform", name, minval, maxval ) ) { if ( uniformBox == 0L ) { uniformBox = new VBox(); uniformBox->setBackColor(0,0,0,0.5); uniformBox->setAbsorbEvents( true ); canvas->addControl( uniformBox ); } osg::Uniform* uniform = new osg::Uniform(osg::Uniform::FLOAT, name); uniform->set( minval ); root->getOrCreateStateSet()->addUniform( uniform, osg::StateAttribute::OVERRIDE ); HBox* box = new HBox(); box->addControl( new LabelControl(name) ); HSliderControl* hs = box->addControl( new HSliderControl(minval, maxval, minval, new ApplyValueUniform(uniform))); hs->setHorizFill(true, 200); box->addControl( new LabelControl(hs) ); uniformBox->addControl( box ); OE_INFO << LC << "Installed uniform controller for " << name << std::endl; } } // Process extensions. for(std::vector<osg::ref_ptr<Extension> >::const_iterator eiter = mapNode->getExtensions().begin(); eiter != mapNode->getExtensions().end(); ++eiter) { Extension* e = eiter->get(); // Check for a View interface: ExtensionInterface<osg::View>* viewIF = ExtensionInterface<osg::View>::get( e ); if ( viewIF ) viewIF->connect( view ); // Check for a Control interface: ExtensionInterface<Control>* controlIF = ExtensionInterface<Control>::get( e ); if ( controlIF ) controlIF->connect( mainContainer ); } root->addChild( canvas ); }
void MapNodeHelper::parse(MapNode* mapNode, osg::ArgumentParser& args, osgViewer::View* view, osg::Group* root, Control* userControl ) const { // this is a dubious move. if ( !root ) root = mapNode; // options to use for the load osg::ref_ptr<osgDB::Options> dbOptions = Registry::instance()->cloneOrCreateOptions(); // parse out custom example arguments first: bool useSky = args.read("--sky"); bool useOcean = args.read("--ocean"); bool useMGRS = args.read("--mgrs"); bool useDMS = args.read("--dms"); bool useDD = args.read("--dd"); bool useCoords = args.read("--coords") || useMGRS || useDMS || useDD; bool useOrtho = args.read("--ortho"); bool useAutoClip = args.read("--autoclip"); float ambientBrightness = 0.2f; args.read("--ambientBrightness", ambientBrightness); std::string kmlFile; args.read( "--kml", kmlFile ); std::string imageFolder; args.read( "--images", imageFolder ); std::string imageExtensions; args.read("--image-extensions", imageExtensions); // install a canvas for any UI controls we plan to create: ControlCanvas* canvas = ControlCanvas::get(view, false); Container* mainContainer = canvas->addControl( new VBox() ); mainContainer->setAbsorbEvents( true ); mainContainer->setBackColor( Color(Color::Black, 0.8) ); mainContainer->setHorizAlign( Control::ALIGN_LEFT ); mainContainer->setVertAlign( Control::ALIGN_BOTTOM ); // install the user control: if ( userControl ) mainContainer->addControl( userControl ); // look for external data in the map node: const Config& externals = mapNode->externalConfig(); const Config& skyConf = externals.child("sky"); const Config& oceanConf = externals.child("ocean"); const Config& annoConf = externals.child("annotations"); const Config& declutterConf = externals.child("decluttering"); Config viewpointsConf = externals.child("viewpoints"); // some terrain effects. const Config& normalMapConf = externals.child("normal_map"); const Config& detailTexConf = externals.child("detail_texture"); const Config& lodBlendingConf = externals.child("lod_blending"); const Config& vertScaleConf = externals.child("vertical_scale"); const Config& contourMapConf = externals.child("contour_map"); // backwards-compatibility: read viewpoints at the top level: const ConfigSet& old_viewpoints = externals.children("viewpoint"); for( ConfigSet::const_iterator i = old_viewpoints.begin(); i != old_viewpoints.end(); ++i ) viewpointsConf.add( *i ); // Loading a viewpoint list from the earth file: if ( !viewpointsConf.empty() ) { std::vector<Viewpoint> viewpoints; const ConfigSet& children = viewpointsConf.children(); if ( children.size() > 0 ) { for( ConfigSet::const_iterator i = children.begin(); i != children.end(); ++i ) { viewpoints.push_back( Viewpoint(*i) ); } } if ( viewpoints.size() > 0 ) { Control* c = ViewpointControlFactory().create(viewpoints, view); if ( c ) mainContainer->addControl( c ); } } // Adding a sky model: if ( useSky || !skyConf.empty() ) { double hours = skyConf.value( "hours", 12.0 ); SkyNode* sky = new SkyNode( mapNode->getMap() ); sky->setAmbientBrightness( ambientBrightness ); sky->setDateTime( DateTime(2011, 3, 6, hours) ); sky->attach( view ); root->addChild( sky ); Control* c = SkyControlFactory().create(sky, view); if ( c ) mainContainer->addControl( c ); } // Adding an ocean model: if ( useOcean || !oceanConf.empty() ) { OceanSurfaceNode* ocean = new OceanSurfaceNode( mapNode, oceanConf ); if ( ocean ) { root->addChild( ocean ); Control* c = OceanControlFactory().create(ocean, view); if ( c ) mainContainer->addControl(c); } } // Loading KML from the command line: if ( !kmlFile.empty() ) { KMLOptions kml_options; kml_options.declutter() = true; // set up a default icon for point placemarks: IconSymbol* defaultIcon = new IconSymbol(); defaultIcon->url()->setLiteral(KML_PUSHPIN_URL); kml_options.defaultIconSymbol() = defaultIcon; osg::Node* kml = KML::load( URI(kmlFile), mapNode, kml_options ); if ( kml ) { Control* c = AnnotationGraphControlFactory().create(kml, view); if ( c ) { c->setVertAlign( Control::ALIGN_TOP ); canvas->addControl( c ); } root->addChild( kml ); } } // Annotations in the map node externals: if ( !annoConf.empty() ) { osg::Group* annotations = 0L; AnnotationRegistry::instance()->create( mapNode, annoConf, dbOptions.get(), annotations ); if ( annotations ) { root->addChild( annotations ); } } // Configure the de-cluttering engine for labels and annotations: if ( !declutterConf.empty() ) { Decluttering::setOptions( DeclutteringOptions(declutterConf) ); } // Configure the mouse coordinate readout: if ( useCoords ) { LabelControl* readout = new LabelControl(); readout->setBackColor( Color(Color::Black, 0.8) ); readout->setHorizAlign( Control::ALIGN_RIGHT ); readout->setVertAlign( Control::ALIGN_BOTTOM ); Formatter* formatter = useMGRS ? (Formatter*)new MGRSFormatter(MGRSFormatter::PRECISION_1M, 0L, MGRSFormatter::USE_SPACES) : useDMS ? (Formatter*)new LatLongFormatter(LatLongFormatter::FORMAT_DEGREES_MINUTES_SECONDS) : useDD ? (Formatter*)new LatLongFormatter(LatLongFormatter::FORMAT_DECIMAL_DEGREES) : 0L; MouseCoordsTool* mcTool = new MouseCoordsTool( mapNode ); mcTool->addCallback( new MouseCoordsLabelCallback(readout, formatter) ); view->addEventHandler( mcTool ); canvas->addControl( readout ); } // Configure for an ortho camera: if ( useOrtho ) { EarthManipulator* manip = dynamic_cast<EarthManipulator*>(view->getCameraManipulator()); if ( manip ) { manip->getSettings()->setCameraProjection( EarthManipulator::PROJ_ORTHOGRAPHIC ); } } // Install an auto clip plane clamper if ( useAutoClip ) { mapNode->addCullCallback( new AutoClipPlaneCullCallback(mapNode) ); } // Scan for images if necessary. if ( !imageFolder.empty() ) { std::vector<std::string> extensions; if ( !imageExtensions.empty() ) StringTokenizer( imageExtensions, extensions, ",;", "", false, true ); if ( extensions.empty() ) extensions.push_back( "tif" ); OE_INFO << LC << "Loading images from " << imageFolder << "..." << std::endl; ImageLayerVector imageLayers; DataScanner scanner; scanner.findImageLayers( imageFolder, extensions, imageLayers ); if ( imageLayers.size() > 0 ) { mapNode->getMap()->beginUpdate(); for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i ) { mapNode->getMap()->addImageLayer( i->get() ); } mapNode->getMap()->endUpdate(); } OE_INFO << LC << "...found " << imageLayers.size() << " image layers." << std::endl; } // Install a normal map layer. if ( !normalMapConf.empty() ) { osg::ref_ptr<NormalMap> effect = new NormalMap(normalMapConf, mapNode->getMap()); if ( effect->getNormalMapLayer() ) { mapNode->getTerrainEngine()->addEffect( effect.get() ); } } // Install a detail texturer if ( !detailTexConf.empty() ) { osg::ref_ptr<DetailTexture> effect = new DetailTexture(detailTexConf); if ( effect->getImage() ) { mapNode->getTerrainEngine()->addEffect( effect.get() ); } } // Install elevation morphing if ( !lodBlendingConf.empty() ) { mapNode->getTerrainEngine()->addEffect( new LODBlending(lodBlendingConf) ); } // Install vertical scaler if ( !vertScaleConf.empty() ) { mapNode->getTerrainEngine()->addEffect( new VerticalScale(vertScaleConf) ); } // Install a contour map effect. if ( !contourMapConf.empty() ) { mapNode->getTerrainEngine()->addEffect( new ContourMap(contourMapConf) ); } // Generic named value uniform with min/max. VBox* uniformBox = 0L; while( args.find( "--uniform" ) >= 0 ) { std::string name; float minval, maxval; if ( args.read( "--uniform", name, minval, maxval ) ) { if ( uniformBox == 0L ) { uniformBox = new VBox(); uniformBox->setBackColor(0,0,0,0.5); uniformBox->setAbsorbEvents( true ); canvas->addControl( uniformBox ); } osg::Uniform* uniform = new osg::Uniform(osg::Uniform::FLOAT, name); uniform->set( minval ); root->getOrCreateStateSet()->addUniform( uniform, osg::StateAttribute::OVERRIDE ); HBox* box = new HBox(); box->addControl( new LabelControl(name) ); HSliderControl* hs = box->addControl( new HSliderControl(minval, maxval, minval, new ApplyValueUniform(uniform))); hs->setHorizFill(true, 200); box->addControl( new LabelControl(hs) ); uniformBox->addControl( box ); OE_INFO << LC << "Installed uniform controller for " << name << std::endl; } } root->addChild( canvas ); }
void MapNodeHelper::parse(MapNode* mapNode, osg::ArgumentParser& args, osgViewer::View* view, osg::Group* root, Container* userContainer ) const { if ( !root ) root = mapNode; // options to use for the load osg::ref_ptr<osgDB::Options> dbOptions = Registry::instance()->cloneOrCreateOptions(); // parse out custom example arguments first: bool useMGRS = args.read("--mgrs"); bool useDMS = args.read("--dms"); bool useDD = args.read("--dd"); bool useCoords = args.read("--coords") || useMGRS || useDMS || useDD; bool useAutoClip = args.read("--autoclip"); bool animateSky = args.read("--animate-sky"); bool showActivity = args.read("--activity"); bool useLogDepth = args.read("--logdepth"); bool useLogDepth2 = args.read("--logdepth2"); bool kmlUI = args.read("--kmlui"); if (args.read("--verbose")) osgEarth::setNotifyLevel(osg::INFO); if (args.read("--quiet")) osgEarth::setNotifyLevel(osg::FATAL); float ambientBrightness = 0.2f; args.read("--ambientBrightness", ambientBrightness); std::string kmlFile; args.read( "--kml", kmlFile ); std::string imageFolder; args.read( "--images", imageFolder ); std::string imageExtensions; args.read("--image-extensions", imageExtensions); // animation path: std::string animpath; if ( args.read("--path", animpath) ) { view->setCameraManipulator( new osgGA::AnimationPathManipulator(animpath) ); } // Install a new Canvas for our UI controls, or use one that already exists. ControlCanvas* canvas = ControlCanvas::getOrCreate( view ); Container* mainContainer; if ( userContainer ) { mainContainer = userContainer; } else { mainContainer = new VBox(); mainContainer->setAbsorbEvents( true ); mainContainer->setBackColor( Color(Color::Black, 0.8) ); mainContainer->setHorizAlign( Control::ALIGN_LEFT ); mainContainer->setVertAlign( Control::ALIGN_BOTTOM ); } canvas->addControl( mainContainer ); // Add an event handler to toggle the canvas with a key press; view->addEventHandler(new ToggleCanvasEventHandler(canvas) ); // look for external data in the map node: const Config& externals = mapNode->externalConfig(); //const Config& screenSpaceLayoutConf = // externals.hasChild("screen_space_layout") ? externals.child("screen_space_layout") : // externals.child("decluttering"); // backwards-compatibility // some terrain effects. // TODO: Most of these are likely to move into extensions. const Config& lodBlendingConf = externals.child("lod_blending"); const Config& vertScaleConf = externals.child("vertical_scale"); // Shadowing. if (args.read("--shadows")) { int unit; if ( mapNode->getTerrainEngine()->getResources()->reserveTextureImageUnit(unit, "ShadowCaster") ) { ShadowCaster* caster = new ShadowCaster(); caster->setTextureImageUnit( unit ); caster->setLight( view->getLight() ); caster->getShadowCastingGroup()->addChild( mapNode ); if ( mapNode->getNumParents() > 0 ) { insertGroup(caster, mapNode->getParent(0)); } else { caster->addChild(mapNode); root = caster; } } } // Loading KML from the command line: if ( !kmlFile.empty() ) { KMLOptions kml_options; kml_options.declutter() = true; // set up a default icon for point placemarks: IconSymbol* defaultIcon = new IconSymbol(); defaultIcon->url()->setLiteral(KML_PUSHPIN_URL); kml_options.defaultIconSymbol() = defaultIcon; TextSymbol* defaultText = new TextSymbol(); defaultText->halo() = Stroke(0.3,0.3,0.3,1.0); kml_options.defaultTextSymbol() = defaultText; osg::Node* kml = KML::load( URI(kmlFile), mapNode, kml_options ); if ( kml ) { if (kmlUI) { Control* c = AnnotationGraphControlFactory().create(kml, view); if ( c ) { c->setVertAlign( Control::ALIGN_TOP ); canvas->addControl( c ); } } root->addChild( kml ); } else { OE_NOTICE << "Failed to load " << kmlFile << std::endl; } } //// Configure the de-cluttering engine for labels and annotations: //if ( !screenSpaceLayoutConf.empty() ) //{ // ScreenSpaceLayout::setOptions( ScreenSpaceLayoutOptions(screenSpaceLayoutConf) ); //} // Configure the mouse coordinate readout: if ( useCoords ) { LabelControl* readout = new LabelControl(); readout->setBackColor( Color(Color::Black, 0.8) ); readout->setHorizAlign( Control::ALIGN_RIGHT ); readout->setVertAlign( Control::ALIGN_BOTTOM ); Formatter* formatter = useMGRS ? (Formatter*)new MGRSFormatter(MGRSFormatter::PRECISION_1M, 0L, MGRSFormatter::USE_SPACES) : useDMS ? (Formatter*)new LatLongFormatter(LatLongFormatter::FORMAT_DEGREES_MINUTES_SECONDS) : useDD ? (Formatter*)new LatLongFormatter(LatLongFormatter::FORMAT_DECIMAL_DEGREES) : 0L; MouseCoordsTool* mcTool = new MouseCoordsTool( mapNode ); mcTool->addCallback( new MouseCoordsLabelCallback(readout, formatter) ); view->addEventHandler( mcTool ); canvas->addControl( readout ); } // Configure for an ortho camera: if ( args.read("--ortho") ) { view->getCamera()->setProjectionMatrixAsOrtho(-1, 1, -1, 1, 0, 1); } // activity monitor (debugging) if ( showActivity ) { VBox* vbox = new VBox(); vbox->setBackColor( Color(Color::Black, 0.8) ); vbox->setHorizAlign( Control::ALIGN_RIGHT ); vbox->setVertAlign( Control::ALIGN_BOTTOM ); view->addEventHandler( new ActivityMonitorTool(vbox) ); canvas->addControl( vbox ); } // Install an auto clip plane clamper if ( useAutoClip ) { mapNode->addCullCallback( new AutoClipPlaneCullCallback(mapNode) ); } // Install logarithmic depth buffer on main camera if ( useLogDepth ) { OE_INFO << LC << "Activating logarithmic depth buffer (vertex-only) on main camera" << std::endl; osgEarth::Util::LogarithmicDepthBuffer logDepth; logDepth.setUseFragDepth( false ); logDepth.install( view->getCamera() ); } else if ( useLogDepth2 ) { OE_INFO << LC << "Activating logarithmic depth buffer (precise) on main camera" << std::endl; osgEarth::Util::LogarithmicDepthBuffer logDepth; logDepth.setUseFragDepth( true ); logDepth.install( view->getCamera() ); } // Scan for images if necessary. if ( !imageFolder.empty() ) { std::vector<std::string> extensions; if ( !imageExtensions.empty() ) StringTokenizer( imageExtensions, extensions, ",;", "", false, true ); if ( extensions.empty() ) extensions.push_back( "tif" ); OE_INFO << LC << "Loading images from " << imageFolder << "..." << std::endl; ImageLayerVector imageLayers; DataScanner scanner; scanner.findImageLayers( imageFolder, extensions, imageLayers ); if ( imageLayers.size() > 0 ) { mapNode->getMap()->beginUpdate(); for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i ) { mapNode->getMap()->addImageLayer( i->get() ); } mapNode->getMap()->endUpdate(); } OE_INFO << LC << "...found " << imageLayers.size() << " image layers." << std::endl; } // Install elevation morphing if ( !lodBlendingConf.empty() ) { mapNode->getTerrainEngine()->addEffect( new LODBlending(lodBlendingConf) ); } // Install vertical scaler if ( !vertScaleConf.empty() ) { mapNode->getTerrainEngine()->addEffect( new VerticalScale(vertScaleConf) ); } // Install a contour map effect. if (args.read("--contourmap")) { mapNode->addExtension(Extension::create("contourmap", ConfigOptions())); // with the cmdline switch, hids all the image layer so we can see the contour map. for (unsigned i = 0; i < mapNode->getMap()->getNumImageLayers(); ++i) { mapNode->getMap()->getImageLayerAt(i)->setVisible(false); } } // Generic named value uniform with min/max. VBox* uniformBox = 0L; while( args.find( "--uniform" ) >= 0 ) { std::string name; float minval, maxval; if ( args.read( "--uniform", name, minval, maxval ) ) { if ( uniformBox == 0L ) { uniformBox = new VBox(); uniformBox->setBackColor(0,0,0,0.5); uniformBox->setAbsorbEvents( true ); canvas->addControl( uniformBox ); } osg::Uniform* uniform = new osg::Uniform(osg::Uniform::FLOAT, name); uniform->set( minval ); root->getOrCreateStateSet()->addUniform( uniform, osg::StateAttribute::OVERRIDE ); HBox* box = new HBox(); box->addControl( new LabelControl(name) ); HSliderControl* hs = box->addControl( new HSliderControl(minval, maxval, minval, new ApplyValueUniform(uniform))); hs->setHorizFill(true, 200); box->addControl( new LabelControl(hs) ); uniformBox->addControl( box ); OE_INFO << LC << "Installed uniform controller for " << name << std::endl; } } // Map inspector: if (args.read("--inspect")) { mapNode->addExtension( Extension::create("mapinspector", ConfigOptions()) ); } // Memory monitor: if (args.read("--monitor")) { mapNode->addExtension(Extension::create("monitor", ConfigOptions()) ); } // Simple sky model: if (args.read("--sky")) { mapNode->addExtension(Extension::create("sky_simple", ConfigOptions()) ); } // Simple ocean model: if (args.read("--ocean")) { mapNode->addExtension(Extension::create("ocean_simple", ConfigOptions())); } // Hook up the extensions! for(std::vector<osg::ref_ptr<Extension> >::const_iterator eiter = mapNode->getExtensions().begin(); eiter != mapNode->getExtensions().end(); ++eiter) { Extension* e = eiter->get(); // Check for a View interface: ExtensionInterface<osg::View>* viewIF = ExtensionInterface<osg::View>::get( e ); if ( viewIF ) viewIF->connect( view ); // Check for a Control interface: ExtensionInterface<Control>* controlIF = ExtensionInterface<Control>::get( e ); if ( controlIF ) controlIF->connect( mainContainer ); } root->addChild( canvas ); }
/** Packages an image layer as a TMS folder. */ int makeTMS( osg::ArgumentParser& args ) { // see if the user wants to override the type extension (imagery only) std::string extension = "png"; args.read( "--ext", extension ); // verbosity? bool verbose = !args.read( "--quiet" ); // find a .earth file on the command line std::string earthFile = findArgumentWithExtension(args, ".earth"); if ( earthFile.empty() ) return usage( "Missing required .earth file" ); // folder to which to write the TMS archive. std::string rootFolder; if ( !args.read( "--out", rootFolder ) ) rootFolder = Stringify() << earthFile << ".tms_repo"; // max level to which to generate unsigned maxLevel = ~0; args.read( "--max-level", maxLevel ); // load up the map osg::ref_ptr<MapNode> mapNode = MapNode::load( args ); if ( !mapNode.valid() ) return usage( "Failed to load a valid .earth file" ); // create a folder for the output osgDB::makeDirectory(rootFolder); if ( !osgDB::fileExists(rootFolder) ) return usage("Failed to create root output folder" ); Map* map = mapNode->getMap(); // fire up a packager: TMSPackager packager( map->getProfile() ); packager.setVerbose( verbose ); if ( maxLevel != ~0 ) packager.setMaxLevel( maxLevel ); // package any image layers that are enabled: ImageLayerVector imageLayers; map->getImageLayers( imageLayers ); unsigned counter = 0; for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i, ++counter ) { ImageLayer* layer = i->get(); if ( layer->getImageLayerOptions().enabled() == true ) { std::string layerFolder = toLegalFileName( layer->getName() ); if ( layerFolder.empty() ) layerFolder = Stringify() << "image_layer_" << counter; if ( verbose ) { OE_NOTICE << LC << "Packaging image layer \"" << layerFolder << "\"" << std::endl; } std::string layerRoot = osgDB::concatPaths( rootFolder, layerFolder ); TMSPackager::Result r = packager.package( layer, layerRoot, extension ); if ( !r.ok ) { OE_WARN << LC << r.message << std::endl; } } else if ( verbose ) { OE_NOTICE << LC << "Skipping disabled layer \"" << layer->getName() << "\"" << std::endl; } } // package any elevation layers that are enabled: counter = 0; ElevationLayerVector elevationLayers; map->getElevationLayers( elevationLayers ); for( ElevationLayerVector::iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i, ++counter ) { ElevationLayer* layer = i->get(); if ( layer->getElevationLayerOptions().enabled() == true ) { std::string layerFolder = toLegalFileName( layer->getName() ); if ( layerFolder.empty() ) layerFolder = Stringify() << "elevation_layer_" << counter; if ( verbose ) { OE_NOTICE << LC << "Packaging elevation layer \"" << layerFolder << "\"" << std::endl; } std::string layerRoot = osgDB::concatPaths( rootFolder, layerFolder ); packager.package( layer, layerRoot ); } else if ( verbose ) { OE_NOTICE << LC << "Skipping disabled layer \"" << layer->getName() << "\"" << std::endl; } } return 0; }
void MPTerrainEngineNode::postInitialize( const Map* map, const TerrainOptions& options ) { TerrainEngineNode::postInitialize( map, options ); // Initialize the map frames. We need one for the update thread and one for the // cull thread. Someday we can detect whether these are actually the same thread // (depends on the viewer's threading mode). _update_mapf = new MapFrame( map, Map::MASKED_TERRAIN_LAYERS, "mp-update" ); // merge in the custom options: _terrainOptions.merge( options ); // a shared registry for tile nodes in the scene graph. _liveTiles = new TileNodeRegistry("live"); // set up a registry for quick release: if ( _terrainOptions.quickReleaseGLObjects() == true ) { _deadTiles = new TileNodeRegistry("dead"); } // initialize the model factory: _tileModelFactory = new TileModelFactory(getMap(), _liveTiles.get(), _terrainOptions ); // handle an already-established map profile: if ( _update_mapf->getProfile() ) { // NOTE: this will initialize the map with the startup layers onMapInfoEstablished( MapInfo(map) ); } // populate the terrain with whatever data is in the map to begin with: if ( _terrain ) { // reserve a GPU image unit and two attribute indexes. this->getTextureCompositor()->reserveTextureImageUnit( _primaryUnit ); this->getTextureCompositor()->reserveTextureImageUnit( _secondaryUnit ); //this->getTextureCompositor()->reserveAttribIndex( _attribIndex1 ); //this->getTextureCompositor()->reserveAttribIndex( _attribIndex2 ); } // install a layer callback for processing further map actions: map->addMapCallback( new MPTerrainEngineNodeMapCallbackProxy(this) ); // Prime with existing layers: _batchUpdateInProgress = true; ElevationLayerVector elevationLayers; map->getElevationLayers( elevationLayers ); for( ElevationLayerVector::const_iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i ) addElevationLayer( i->get() ); ImageLayerVector imageLayers; map->getImageLayers( imageLayers ); for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i ) addImageLayer( i->get() ); _batchUpdateInProgress = false; //{ // i->get()->addCallback( _elevationCallback.get() ); //} // install some terrain-wide uniforms this->getOrCreateStateSet()->getOrCreateUniform( "oe_min_tile_range_factor", osg::Uniform::FLOAT)->set( *_terrainOptions.minTileRangeFactor() ); // set up the initial shaders updateShaders(); // register this instance to the osgDB plugin can find it. registerEngine( this ); // now that we have a map, set up to recompute the bounds dirtyBound(); }
void RexTerrainEngineNode::addTileLayer(Layer* tileLayer) { if ( tileLayer && tileLayer->getEnabled() ) { ImageLayer* imageLayer = dynamic_cast<ImageLayer*>(tileLayer); if (imageLayer) { // for a shared layer, allocate a shared image unit if necessary. if ( imageLayer->isShared() ) { if (!imageLayer->shareImageUnit().isSet()) { int temp; if ( getResources()->reserveTextureImageUnit(temp, imageLayer->getName().c_str()) ) { imageLayer->shareImageUnit() = temp; //OE_INFO << LC << "Image unit " << temp << " assigned to shared layer " << imageLayer->getName() << std::endl; } else { OE_WARN << LC << "Insufficient GPU image units to share layer " << imageLayer->getName() << std::endl; } } // Build a sampler binding for the shared layer. if ( imageLayer->shareImageUnit().isSet() ) { // Find the next empty SHARED slot: unsigned newIndex = SamplerBinding::SHARED; while (_renderBindings[newIndex].isActive()) ++newIndex; // Put the new binding there: SamplerBinding& newBinding = _renderBindings[newIndex]; newBinding.usage() = SamplerBinding::SHARED; newBinding.sourceUID() = imageLayer->getUID(); newBinding.unit() = imageLayer->shareImageUnit().get(); newBinding.samplerName() = imageLayer->shareTexUniformName().get(); newBinding.matrixName() = imageLayer->shareTexMatUniformName().get(); OE_INFO << LC << "Shared Layer \"" << imageLayer->getName() << "\" : sampler=\"" << newBinding.samplerName() << "\", " << "matrix=\"" << newBinding.matrixName() << "\", " << "unit=" << newBinding.unit() << "\n"; } } } else { // non-image tile layer. Keep track of these.. } if (_terrain) { // Update the existing render models, and trigger a data reload. // Later we can limit the reload to an update of only the new data. UpdateRenderModels updateModels(_mapFrame); #if 0 // This uses the loaddata filter approach which will only request // data for one layer. It mostly works but not 100%; see hires-insets // as an example. Removing the world layer and re-adding it while // zoomed in doesn't result in all tiles reloading. Possibly a // synchronization issue. ImageLayerVector imageLayers; _mapFrame.getLayers(imageLayers); if (imageLayers.size() == 1) updateModels.setReloadData(true); else updateModels.layersToLoad().insert(tileLayer->getUID()); #else updateModels.setReloadData(true); #endif _terrain->accept(updateModels); } } }
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; }
void MPTerrainEngineNode::postInitialize( const Map* map, const TerrainOptions& options ) { TerrainEngineNode::postInitialize( map, options ); // Initialize the map frames. We need one for the update thread and one for the // cull thread. Someday we can detect whether these are actually the same thread // (depends on the viewer's threading mode). _update_mapf = new MapFrame( map, Map::MASKED_TERRAIN_LAYERS, "mp-update" ); // merge in the custom options: _terrainOptions.merge( options ); // A shared registry for tile nodes in the scene graph. Enable revision tracking // if requested in the options. Revision tracking lets the registry notify all // live tiles of the current map revision so they can inrementally update // themselves if necessary. _liveTiles = new TileNodeRegistry("live"); _liveTiles->setRevisioningEnabled( _terrainOptions.incrementalUpdate() == true ); _liveTiles->setMapRevision( _update_mapf->getRevision() ); // set up a registry for quick release: if ( _terrainOptions.quickReleaseGLObjects() == true ) { _deadTiles = new TileNodeRegistry("dead"); } // initialize the model factory: _tileModelFactory = new TileModelFactory(_liveTiles.get(), _terrainOptions ); // handle an already-established map profile: if ( _update_mapf->getProfile() ) { // NOTE: this will initialize the map with the startup layers onMapInfoEstablished( MapInfo(map) ); } // install a layer callback for processing further map actions: map->addMapCallback( new MPTerrainEngineNodeMapCallbackProxy(this) ); // Prime with existing layers: _batchUpdateInProgress = true; ElevationLayerVector elevationLayers; map->getElevationLayers( elevationLayers ); for( ElevationLayerVector::const_iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i ) addElevationLayer( i->get() ); ImageLayerVector imageLayers; map->getImageLayers( imageLayers ); for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i ) addImageLayer( i->get() ); _batchUpdateInProgress = false; // install some terrain-wide uniforms this->getOrCreateStateSet()->getOrCreateUniform( "oe_min_tile_range_factor", osg::Uniform::FLOAT)->set( *_terrainOptions.minTileRangeFactor() ); // set up the initial shaders updateState(); // register this instance to the osgDB plugin can find it. registerEngine( this ); // now that we have a map, set up to recompute the bounds dirtyBound(); OE_INFO << LC << "Edge normalization is " << (_terrainOptions.normalizeEdges() == true? "ON" : "OFF") << std::endl; }
void RexTerrainEngineNode::postInitialize( const Map* map, const TerrainOptions& options ) { // Force the mercator fast path off, since REX does not support it yet. TerrainOptions myOptions = options; myOptions.enableMercatorFastPath() = false; TerrainEngineNode::postInitialize( map, myOptions ); // Initialize the map frames. We need one for the update thread and one for the // cull thread. Someday we can detect whether these are actually the same thread // (depends on the viewer's threading mode). _update_mapf = new MapFrame( map, Map::ENTIRE_MODEL ); // A callback for overriding bounding boxes for tiles _modifyBBoxCallback = new ModifyBoundingBoxCallback(*_update_mapf); // merge in the custom options: _terrainOptions.merge( myOptions ); // morphing imagery LODs requires we bind parent textures to their own unit. if ( _terrainOptions.morphImagery() == true ) { _requireParentTextures = true; } // Terrain morphing doesn't work in projected maps: if (map->getSRS()->isProjected()) { _terrainOptions.morphTerrain() = false; } // if the envvar for tile expiration is set, overide the options setting const char* val = ::getenv("OSGEARTH_EXPIRATION_THRESHOLD"); if ( val ) { _terrainOptions.expirationThreshold() = as<unsigned>(val, _terrainOptions.expirationThreshold().get()); OE_INFO << LC << "Expiration threshold set by env var = " << _terrainOptions.expirationThreshold().get() << "\n"; } // if the envvar for hires prioritization is set, override the options setting const char* hiresFirst = ::getenv("OSGEARTH_HIGH_RES_FIRST"); if ( hiresFirst ) { _terrainOptions.highResolutionFirst() = true; } // check for normal map generation (required for lighting). if ( _terrainOptions.normalMaps() == true ) { this->_requireNormalTextures = true; } // A shared registry for tile nodes in the scene graph. Enable revision tracking // if requested in the options. Revision tracking lets the registry notify all // live tiles of the current map revision so they can inrementally update // themselves if necessary. _liveTiles = new TileNodeRegistry("live"); _liveTiles->setMapRevision( _update_mapf->getRevision() ); // A resource releaser that will call releaseGLObjects() on expired objects. _releaser = new ResourceReleaser(); this->addChild(_releaser.get()); // A shared geometry pool. _geometryPool = new GeometryPool( _terrainOptions ); _geometryPool->setReleaser( _releaser.get()); this->addChild( _geometryPool.get() ); // Make a tile loader PagerLoader* loader = new PagerLoader( this ); loader->setNumLODs(_terrainOptions.maxLOD().getOrUse(DEFAULT_MAX_LOD)); loader->setMergesPerFrame( _terrainOptions.mergesPerFrame().get() ); for (std::vector<RexTerrainEngineOptions::LODOptions>::const_iterator i = _terrainOptions.lods().begin(); i != _terrainOptions.lods().end(); ++i) { if (i->_lod.isSet()) { loader->setLODPriorityScale(i->_lod.get(), i->_priorityScale.getOrUse(1.0f)); loader->setLODPriorityOffset(i->_lod.get(), i->_priorityOffset.getOrUse(0.0f)); } } _loader = loader; this->addChild( _loader.get() ); // Make a tile unloader _unloader = new UnloaderGroup( _liveTiles.get() ); _unloader->setThreshold( _terrainOptions.expirationThreshold().get() ); _unloader->setReleaser(_releaser.get()); this->addChild( _unloader.get() ); // handle an already-established map profile: MapInfo mapInfo( map ); if ( _update_mapf->getProfile() ) { // NOTE: this will initialize the map with the startup layers onMapInfoEstablished( mapInfo ); } // install a layer callback for processing further map actions: map->addMapCallback( new RexTerrainEngineNodeMapCallbackProxy(this) ); // Prime with existing layers: _batchUpdateInProgress = true; ElevationLayerVector elevationLayers; map->getLayers( elevationLayers ); for( ElevationLayerVector::const_iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i ) addElevationLayer( i->get() ); ImageLayerVector imageLayers; map->getLayers( imageLayers ); for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i ) addTileLayer( i->get() ); _batchUpdateInProgress = false; // set up the initial shaders updateState(); // register this instance to the osgDB plugin can find it. registerEngine( this ); // now that we have a map, set up to recompute the bounds dirtyBound(); }
void RexTerrainEngineNode::postInitialize( const Map* map, const TerrainOptions& options ) { TerrainEngineNode::postInitialize( map, options ); // Initialize the map frames. We need one for the update thread and one for the // cull thread. Someday we can detect whether these are actually the same thread // (depends on the viewer's threading mode). _update_mapf = new MapFrame( map, Map::ENTIRE_MODEL ); // merge in the custom options: _terrainOptions.merge( options ); // morphing imagery LODs requires we bind parent textures to their own unit. if ( _terrainOptions.morphImagery() == true ) { _requireParentTextures = true; } // if the envvar for tile expiration is set, overide the options setting const char* val = ::getenv("OSGEARTH_EXPIRATION_THRESHOLD"); if ( val ) { _terrainOptions.expirationThreshold() = as<unsigned>(val, _terrainOptions.expirationThreshold().get()); OE_INFO << LC << "Expiration threshold set by env var = " << _terrainOptions.expirationThreshold().get() << "\n"; } // if the envvar for hires prioritization is set, override the options setting const char* hiresFirst = ::getenv("OSGEARTH_HIGH_RES_FIRST"); if ( hiresFirst ) { _terrainOptions.highResolutionFirst() = true; } // check for normal map generation (required for lighting). if ( _terrainOptions.normalMaps() == true ) { this->_requireNormalTextures = true; } // A shared registry for tile nodes in the scene graph. Enable revision tracking // if requested in the options. Revision tracking lets the registry notify all // live tiles of the current map revision so they can inrementally update // themselves if necessary. _liveTiles = new TileNodeRegistry("live"); _liveTiles->setMapRevision( _update_mapf->getRevision() ); if ( _terrainOptions.quickReleaseGLObjects() == true ) { _deadTiles = new TileNodeRegistry("dead"); _quickReleaseInstalled = false; ADJUST_UPDATE_TRAV_COUNT( this, +1 ); } // A shared geometry pool. if ( ::getenv("OSGEARTH_REX_NO_POOL") == 0L ) { _geometryPool = new GeometryPool( _terrainOptions ); } // Make a tile loader PagerLoader* loader = new PagerLoader( this ); loader->setMergesPerFrame( _terrainOptions.mergesPerFrame().get() ); _loader = loader; //_loader = new SimpleLoader(); this->addChild( _loader.get() ); // handle an already-established map profile: MapInfo mapInfo( map ); if ( _update_mapf->getProfile() ) { // NOTE: this will initialize the map with the startup layers onMapInfoEstablished( mapInfo ); } // install a layer callback for processing further map actions: map->addMapCallback( new RexTerrainEngineNodeMapCallbackProxy(this) ); // Prime with existing layers: _batchUpdateInProgress = true; ElevationLayerVector elevationLayers; map->getElevationLayers( elevationLayers ); for( ElevationLayerVector::const_iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i ) addElevationLayer( i->get() ); ImageLayerVector imageLayers; map->getImageLayers( imageLayers ); for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i ) addImageLayer( i->get() ); _batchUpdateInProgress = false; // set up the initial shaders updateState(); // register this instance to the osgDB plugin can find it. registerEngine( this ); // now that we have a map, set up to recompute the bounds dirtyBound(); }
// Generates the main shader code for rendering the terrain. void RexTerrainEngineNode::updateState() { if ( _batchUpdateInProgress ) { _stateUpdateRequired = true; } else { osg::StateSet* terrainStateSet = _terrain->getOrCreateStateSet(); // everything osg::StateSet* surfaceStateSet = getSurfaceStateSet(); // just the surface //terrainStateSet->setRenderBinDetails(0, "SORT_FRONT_TO_BACK"); // required for multipass tile rendering to work surfaceStateSet->setAttributeAndModes( new osg::Depth(osg::Depth::LEQUAL, 0, 1, true) ); surfaceStateSet->setAttributeAndModes( new osg::CullFace(), osg::StateAttribute::ON); // activate standard mix blending. terrainStateSet->setAttributeAndModes( new osg::BlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA), osg::StateAttribute::ON ); // install patch param if we are tessellation on the GPU. if ( _terrainOptions.gpuTessellation() == true ) { #ifdef HAVE_PATCH_PARAMETER terrainStateSet->setAttributeAndModes( new osg::PatchParameter(3) ); #endif } // install shaders, if we're using them. if ( Registry::capabilities().supportsGLSL() ) { Shaders package; VirtualProgram* terrainVP = VirtualProgram::getOrCreate(terrainStateSet); terrainVP->setName( "Rex Terrain" ); package.load(terrainVP, package.ENGINE_VERT_MODEL); surfaceStateSet->addUniform(new osg::Uniform("oe_terrain_color", _terrainOptions.color().get())); if (_terrainOptions.enableBlending() == true) { surfaceStateSet->setDefine("OE_TERRAIN_BLEND_IMAGERY"); } // Funtions that affect only the terrain surface: VirtualProgram* surfaceVP = VirtualProgram::getOrCreate(surfaceStateSet); surfaceVP->setName("Rex Surface"); // Functions that affect the terrain surface only: package.load(surfaceVP, package.ENGINE_VERT_VIEW); package.load(surfaceVP, package.ENGINE_FRAG); // Elevation? if (this->elevationTexturesRequired()) { surfaceStateSet->setDefine("OE_TERRAIN_RENDER_ELEVATION"); } // Normal mapping shaders: if ( this->normalTexturesRequired() ) { package.load(surfaceVP, package.NORMAL_MAP_VERT); package.load(surfaceVP, package.NORMAL_MAP_FRAG); surfaceStateSet->setDefine("OE_TERRAIN_RENDER_NORMAL_MAP"); } // Morphing? if (_terrainOptions.morphTerrain() == true || _terrainOptions.morphImagery() == true) { package.load(surfaceVP, package.MORPHING_VERT); if (_terrainOptions.morphImagery() == true) { surfaceStateSet->setDefine("OE_TERRAIN_MORPH_IMAGERY"); } if (_terrainOptions.morphTerrain() == true) { surfaceStateSet->setDefine("OE_TERRAIN_MORPH_GEOMETRY"); } } // assemble color filter code snippets. bool haveColorFilters = false; { // Color filter frag function: std::string fs_colorfilters = "#version " GLSL_VERSION_STR "\n" GLSL_DEFAULT_PRECISION_FLOAT "\n" "uniform int oe_layer_uid; \n" "$COLOR_FILTER_HEAD" "void oe_rexEngine_applyFilters(inout vec4 color) \n" "{ \n" "$COLOR_FILTER_BODY" "} \n"; std::stringstream cf_head; std::stringstream cf_body; const char* I = " "; // second, install the per-layer color filter functions AND shared layer bindings. bool ifStarted = false; ImageLayerVector imageLayers; _update_mapf->getLayers(imageLayers); for( int i=0; i<imageLayers.size(); ++i ) { ImageLayer* layer = imageLayers.at(i); if ( layer->getEnabled() ) { // install Color Filter function calls: const ColorFilterChain& chain = layer->getColorFilters(); if ( chain.size() > 0 ) { haveColorFilters = true; if ( ifStarted ) cf_body << I << "else if "; else cf_body << I << "if "; cf_body << "(oe_layer_uid == " << layer->getUID() << ") {\n"; for( ColorFilterChain::const_iterator j = chain.begin(); j != chain.end(); ++j ) { const ColorFilter* filter = j->get(); cf_head << "void " << filter->getEntryPointFunctionName() << "(inout vec4 color);\n"; cf_body << I << I << filter->getEntryPointFunctionName() << "(color);\n"; filter->install( surfaceStateSet ); } cf_body << I << "}\n"; ifStarted = true; } } } if ( haveColorFilters ) { std::string cf_head_str, cf_body_str; cf_head_str = cf_head.str(); cf_body_str = cf_body.str(); replaceIn( fs_colorfilters, "$COLOR_FILTER_HEAD", cf_head_str ); replaceIn( fs_colorfilters, "$COLOR_FILTER_BODY", cf_body_str ); surfaceVP->setFunction( "oe_rexEngine_applyFilters", fs_colorfilters, ShaderComp::LOCATION_FRAGMENT_COLORING, 0.6 ); } } // Apply uniforms for sampler bindings: OE_DEBUG << LC << "Render Bindings:\n"; osg::ref_ptr<osg::Texture> tex = new osg::Texture2D(ImageUtils::createEmptyImage(1,1)); for (unsigned i = 0; i < _renderBindings.size(); ++i) { SamplerBinding& b = _renderBindings[i]; if (b.isActive()) { osg::Uniform* u = new osg::Uniform(b.samplerName().c_str(), b.unit()); terrainStateSet->addUniform( u ); OE_DEBUG << LC << " > Bound \"" << b.samplerName() << "\" to unit " << b.unit() << "\n"; terrainStateSet->setTextureAttribute(b.unit(), tex.get()); } } // uniform that controls per-layer opacity terrainStateSet->addUniform( new osg::Uniform("oe_layer_opacity", 1.0f) ); // uniform that conveys the layer UID to the shaders; necessary // for per-layer branching (like color filters) // UID -1 => no image layer (no texture) terrainStateSet->addUniform( new osg::Uniform("oe_layer_uid", (int)-1 ) ); // uniform that conveys the render order, since the shaders // need to know which is the first layer in order to blend properly terrainStateSet->addUniform( new osg::Uniform("oe_layer_order", (int)0) ); // default min/max range uniforms. (max < min means ranges are disabled) terrainStateSet->addUniform( new osg::Uniform("oe_layer_minRange", 0.0f) ); terrainStateSet->addUniform( new osg::Uniform("oe_layer_maxRange", -1.0f) ); terrainStateSet->addUniform( new osg::Uniform("oe_layer_attenuationRange", _terrainOptions.attentuationDistance().get()) ); terrainStateSet->getOrCreateUniform( "oe_min_tile_range_factor", osg::Uniform::FLOAT)->set( *_terrainOptions.minTileRangeFactor() ); terrainStateSet->addUniform(new osg::Uniform("oe_tile_size", (float)_terrainOptions.tileSize().get())); // special object ID that denotes the terrain surface. surfaceStateSet->addUniform( new osg::Uniform( Registry::objectIndex()->getObjectIDUniformName().c_str(), OSGEARTH_OBJECTID_TERRAIN) ); } _stateUpdateRequired = false; } }
void MPTerrainEngineNode::postInitialize( const Map* map, const TerrainOptions& options ) { TerrainEngineNode::postInitialize( map, options ); // Initialize the map frames. We need one for the update thread and one for the // cull thread. Someday we can detect whether these are actually the same thread // (depends on the viewer's threading mode). _update_mapf = new MapFrame( map, Map::ENTIRE_MODEL, "mp-update" ); // merge in the custom options: _terrainOptions.merge( options ); // A shared registry for tile nodes in the scene graph. Enable revision tracking // if requested in the options. Revision tracking lets the registry notify all // live tiles of the current map revision so they can inrementally update // themselves if necessary. _liveTiles = new TileNodeRegistry("live"); _liveTiles->setRevisioningEnabled( _terrainOptions.incrementalUpdate() == true ); _liveTiles->setMapRevision( _update_mapf->getRevision() ); // set up a registry for quick release: if ( _terrainOptions.quickReleaseGLObjects() == true ) { _deadTiles = new TileNodeRegistry("dead"); } // reserve GPU resources. Must do this before initializing the model factory. if ( _primaryUnit < 0 ) { getResources()->reserveTextureImageUnit( _primaryUnit, "MP Engine Primary" ); } // "Secondary" unit serves double duty; it's used for parent textures BUT it's also // used at the "slot" for the tile coordinates. if ( _secondaryUnit < 0 ) { getResources()->reserveTextureImageUnit( _secondaryUnit, "MP Engine Secondary" ); } // initialize the model factory: _tileModelFactory = new TileModelFactory(_liveTiles.get(), _terrainOptions, this); // handle an already-established map profile: if ( _update_mapf->getProfile() ) { // NOTE: this will initialize the map with the startup layers onMapInfoEstablished( MapInfo(map) ); } // install a layer callback for processing further map actions: map->addMapCallback( new MPTerrainEngineNodeMapCallbackProxy(this) ); // Prime with existing layers: _batchUpdateInProgress = true; ElevationLayerVector elevationLayers; map->getElevationLayers( elevationLayers ); for( ElevationLayerVector::const_iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i ) addElevationLayer( i->get() ); ImageLayerVector imageLayers; map->getImageLayers( imageLayers ); for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i ) addImageLayer( i->get() ); _batchUpdateInProgress = false; // register this instance to the osgDB plugin can find it. registerEngine( this ); // set up the initial shaders and reserve the texture image units. updateState(); // now that we have a map, set up to recompute the bounds dirtyBound(); OE_INFO << LC << "Edge normalization is " << (_terrainOptions.normalizeEdges() == true? "ON" : "OFF") << std::endl; }
// Generates the main shader code for rendering the terrain. void MPTerrainEngineNode::updateState() { if ( _batchUpdateInProgress ) { _stateUpdateRequired = true; } else { if ( _elevationTextureUnit < 0 && elevationTexturesRequired() ) { getResources()->reserveTextureImageUnit( _elevationTextureUnit, "MP Engine Elevation" ); } osg::StateSet* terrainStateSet = getTerrainStateSet(); if ( !terrainStateSet ) return; // required for multipass tile rendering to work terrainStateSet->setAttributeAndModes( new osg::Depth(osg::Depth::LEQUAL, 0, 1, true) ); // activate standard mix blending. terrainStateSet->setAttributeAndModes( new osg::BlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA), osg::StateAttribute::ON ); // install shaders, if we're using them. if ( Registry::capabilities().supportsGLSL() ) { VirtualProgram* vp = new VirtualProgram(); vp->setName( "osgEarth.engine_mp.TerrainNode" ); terrainStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON ); Shaders package; package.replace( "$MP_PRIMARY_UNIT", Stringify() << _primaryUnit ); package.replace( "$MP_SECONDARY_UNIT", Stringify() << (_secondaryUnit>=0?_secondaryUnit:0) ); package.define( "MP_USE_BLENDING", (_terrainOptions.enableBlending() == true) ); package.load( vp, package.EngineVertexModel ); package.load( vp, package.EngineVertexView ); package.load( vp, package.EngineFragment ); if ( this->normalTexturesRequired() ) { package.load( vp, package.NormalMapVertex ); package.load( vp, package.NormalMapFragment ); terrainStateSet->addUniform( new osg::Uniform("oe_tile_normalTex", _normalMapUnit) ); } // terrain background color; negative means use the vertex color. Color terrainColor = _terrainOptions.color().getOrUse( Color(1,1,1,-1) ); terrainStateSet->addUniform(new osg::Uniform("oe_terrain_color", terrainColor)); if ( _update_mapf ) { // assemble color filter code snippets. bool haveColorFilters = false; { // Color filter frag function: std::string fs_colorfilters = "#version " GLSL_VERSION_STR "\n" GLSL_DEFAULT_PRECISION_FLOAT "\n" "uniform int oe_layer_uid; \n" "$COLOR_FILTER_HEAD" "void oe_mp_apply_filters(inout vec4 color) \n" "{ \n" "$COLOR_FILTER_BODY" "} \n"; std::stringstream cf_head; std::stringstream cf_body; const char* I = " "; // second, install the per-layer color filter functions AND shared layer bindings. ImageLayerVector imageLayers; _update_mapf->getLayers(imageLayers); bool ifStarted = false; int numImageLayers = imageLayers.size(); for( int i=0; i<numImageLayers; ++i ) { ImageLayer* layer = imageLayers[i].get(); if ( layer->getEnabled() ) { // install Color Filter function calls: const ColorFilterChain& chain = layer->getColorFilters(); if ( chain.size() > 0 ) { haveColorFilters = true; if ( ifStarted ) cf_body << I << "else if "; else cf_body << I << "if "; cf_body << "(oe_layer_uid == " << layer->getUID() << ") {\n"; for( ColorFilterChain::const_iterator j = chain.begin(); j != chain.end(); ++j ) { const ColorFilter* filter = j->get(); cf_head << "void " << filter->getEntryPointFunctionName() << "(inout vec4 color);\n"; cf_body << I << I << filter->getEntryPointFunctionName() << "(color);\n"; filter->install( terrainStateSet ); } cf_body << I << "}\n"; ifStarted = true; } } } if ( haveColorFilters ) { std::string cf_head_str, cf_body_str; cf_head_str = cf_head.str(); cf_body_str = cf_body.str(); replaceIn( fs_colorfilters, "$COLOR_FILTER_HEAD", cf_head_str ); replaceIn( fs_colorfilters, "$COLOR_FILTER_BODY", cf_body_str ); vp->setFunction( "oe_mp_apply_filters", fs_colorfilters, ShaderComp::LOCATION_FRAGMENT_COLORING, 0.5f ); } } } // binding for the terrain texture terrainStateSet->getOrCreateUniform( "oe_layer_tex", osg::Uniform::SAMPLER_2D )->set( _primaryUnit ); // binding for the secondary texture (for LOD blending) if ( parentTexturesRequired() ) { terrainStateSet->getOrCreateUniform( "oe_layer_tex_parent", osg::Uniform::SAMPLER_2D )->set( _secondaryUnit ); // binding for the default secondary texture matrix osg::Matrixf parent_mat; parent_mat(0,0) = 0.0f; terrainStateSet->getOrCreateUniform( "oe_layer_parent_matrix", osg::Uniform::FLOAT_MAT4 )->set( parent_mat ); } // uniform for accessing the elevation texture sampler. if ( elevationTexturesRequired() ) { terrainStateSet->getOrCreateUniform( "oe_terrain_tex", osg::Uniform::SAMPLER_2D)->set( _elevationTextureUnit ); } // uniform that controls per-layer opacity terrainStateSet->getOrCreateUniform( "oe_layer_opacity", osg::Uniform::FLOAT )->set( 1.0f ); // uniform that conveys the layer UID to the shaders; necessary // for per-layer branching (like color filters) // UID -1 => no image layer (no texture) terrainStateSet->getOrCreateUniform( "oe_layer_uid", osg::Uniform::INT )->set( -1 ); // uniform that conveys the render order, since the shaders // need to know which is the first layer in order to blend properly terrainStateSet->getOrCreateUniform( "oe_layer_order", osg::Uniform::INT )->set( 0 ); // default min/max range uniforms. (max < min means ranges are disabled) terrainStateSet->addUniform( new osg::Uniform("oe_layer_minRange", 0.0f) ); terrainStateSet->addUniform( new osg::Uniform("oe_layer_maxRange", FLT_MAX) ); terrainStateSet->addUniform( new osg::Uniform("oe_layer_attenuationRange", _terrainOptions.attentuationDistance().get()) ); terrainStateSet->getOrCreateUniform( "oe_min_tile_range_factor", osg::Uniform::FLOAT)->set( *_terrainOptions.minTileRangeFactor() ); // special object ID that denotes the terrain surface. terrainStateSet->addUniform( new osg::Uniform( Registry::objectIndex()->getObjectIDUniformName().c_str(), OSGEARTH_OBJECTID_TERRAIN) ); // assign the uniforms for each shared layer. if ( _update_mapf ) { ImageLayerVector imageLayers; _update_mapf->getLayers(imageLayers); int numImageLayers = imageLayers.size(); for( int i=0; i<numImageLayers; ++i ) { ImageLayer* layer = imageLayers[i].get(); if ( layer->getEnabled() && layer->isShared() ) { terrainStateSet->addUniform( new osg::Uniform( layer->shareTexUniformName()->c_str(), layer->shareImageUnit().get() ) ); } } } } _stateUpdateRequired = false; } }
/** Packages an image layer as a TMS folder. */ int makeTMS( osg::ArgumentParser& args ) { // see if the user wants to override the type extension (imagery only) std::string extension; args.read( "--ext", extension ); // verbosity? bool verbose = !args.read( "--quiet" ); // find a .earth file on the command line std::string earthFile = findArgumentWithExtension( args, ".earth" ); /* if ( earthFile.empty() ) return usage( "Missing required .earth file" ); */ // folder to which to write the TMS archive. std::string rootFolder; if( !args.read( "--out", rootFolder ) ) rootFolder = Stringify() << earthFile << ".tms_repo"; // whether to overwrite existing tile files bool overwrite = false; if( args.read( "--overwrite" ) ) overwrite = true; // write out an earth file std::string outEarth; args.read( "--out-earth", outEarth ); std::string dbOptions; args.read( "--db-options", dbOptions ); std::string::size_type n = 0; while( (n = dbOptions.find( '"', n )) != dbOptions.npos ) { dbOptions.erase( n, 1 ); } osg::ref_ptr<osgDB::Options> options = new osgDB::Options( dbOptions ); std::vector< Bounds > bounds; // restrict packaging to user-specified bounds. double xmin = DBL_MAX, ymin = DBL_MAX, xmax = DBL_MIN, ymax = DBL_MIN; while( args.read( "--bounds", xmin, ymin, xmax, ymax ) ) { Bounds b; b.xMin() = xmin, b.yMin() = ymin, b.xMax() = xmax, b.yMax() = ymax; bounds.push_back( b ); } // max level to which to generate unsigned maxLevel = ~0; args.read( "--max-level", maxLevel ); // whether to keep 'empty' tiles bool keepEmpties = args.read( "--keep-empties" ); bool continueSingleColor = args.read( "--continue-single-color" ); // max level to which to generate unsigned elevationPixelDepth = 32; args.read( "--elevation-pixel-depth", elevationPixelDepth ); // load up the map osg::ref_ptr<MapNode> mapNode = MapNode::load( args ); if( !mapNode.valid() ) return usage( "Failed to load a valid .earth file" ); // create a folder for the output osgDB::makeDirectory( rootFolder ); if( !osgDB::fileExists( rootFolder ) ) return usage( "Failed to create root output folder" ); Map* map = mapNode->getMap(); // fire up a packager: TMSPackager packager( map->getProfile(), options ); packager.setVerbose( verbose ); packager.setOverwrite( overwrite ); packager.setKeepEmptyImageTiles( keepEmpties ); packager.setSubdivideSingleColorImageTiles( continueSingleColor ); packager.setElevationPixelDepth( elevationPixelDepth ); if( maxLevel != ~0 ) packager.setMaxLevel( maxLevel ); if( bounds.size() > 0 ) { for( unsigned int i = 0; i < bounds.size(); ++i ) { Bounds b = bounds[i]; if( b.isValid() ) packager.addExtent( GeoExtent( map->getProfile()->getSRS(), b ) ); } } // new map for an output earth file if necessary. osg::ref_ptr<Map> outMap = 0L; if( !outEarth.empty() ) { // copy the options from the source map first outMap = new Map( map->getInitialMapOptions() ); } // establish the output path of the earth file, if applicable: std::string outEarthFile = osgDB::concatPaths( rootFolder, osgDB::getSimpleFileName( outEarth ) ); // package any image layers that are enabled: ImageLayerVector imageLayers; map->getImageLayers( imageLayers ); unsigned counter = 0; for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i, ++counter ) { ImageLayer* layer = i->get(); if( layer->getImageLayerOptions().enabled() == true ) { std::string layerFolder = toLegalFileName( layer->getName() ); if( layerFolder.empty() ) layerFolder = Stringify() << "image_layer_" << counter; if( verbose ) { OE_NOTICE << LC << "Packaging image layer \"" << layerFolder << "\"" << std::endl; } osg::ref_ptr< ConsoleProgressCallback > progress = new ConsoleProgressCallback(); std::string layerRoot = osgDB::concatPaths( rootFolder, layerFolder ); TMSPackager::Result r = packager.package( layer, layerRoot, progress, extension ); if( r.ok ) { // save to the output map if requested: if( outMap.valid() ) { // new TMS driver info: TMSOptions tms; tms.url() = URI( osgDB::concatPaths( layerFolder, "tms.xml" ), outEarthFile ); ImageLayerOptions layerOptions( layer->getName(), tms ); layerOptions.mergeConfig( layer->getInitialOptions().getConfig( true ) ); layerOptions.cachePolicy() = CachePolicy::NO_CACHE; outMap->addImageLayer( new ImageLayer( layerOptions ) ); } } else { OE_WARN << LC << r.message << std::endl; } } else if( verbose ) { OE_NOTICE << LC << "Skipping disabled layer \"" << layer->getName() << "\"" << std::endl; } } // package any elevation layers that are enabled: counter = 0; ElevationLayerVector elevationLayers; map->getElevationLayers( elevationLayers ); for( ElevationLayerVector::iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i, ++counter ) { ElevationLayer* layer = i->get(); if( layer->getElevationLayerOptions().enabled() == true ) { std::string layerFolder = toLegalFileName( layer->getName() ); if( layerFolder.empty() ) layerFolder = Stringify() << "elevation_layer_" << counter; if( verbose ) { OE_NOTICE << LC << "Packaging elevation layer \"" << layerFolder << "\"" << std::endl; } std::string layerRoot = osgDB::concatPaths( rootFolder, layerFolder ); TMSPackager::Result r = packager.package( layer, layerRoot ); if( r.ok ) { // save to the output map if requested: if( outMap.valid() ) { // new TMS driver info: TMSOptions tms; tms.url() = URI( osgDB::concatPaths( layerFolder, "tms.xml" ), outEarthFile ); ElevationLayerOptions layerOptions( layer->getName(), tms ); layerOptions.mergeConfig( layer->getInitialOptions().getConfig( true ) ); layerOptions.cachePolicy() = CachePolicy::NO_CACHE; outMap->addElevationLayer( new ElevationLayer( layerOptions ) ); } } else { OE_WARN << LC << r.message << std::endl; } } else if( verbose ) { OE_NOTICE << LC << "Skipping disabled layer \"" << layer->getName() << "\"" << std::endl; } } // Finally, write an earth file if requested: if( outMap.valid() ) { MapNodeOptions outNodeOptions = mapNode->getMapNodeOptions(); osg::ref_ptr<MapNode> outMapNode = new MapNode( outMap.get(), outNodeOptions ); if( !osgDB::writeNodeFile( *outMapNode.get(), outEarthFile ) ) { OE_WARN << LC << "Error writing earth file to \"" << outEarthFile << "\"" << std::endl; } else if( verbose ) { OE_NOTICE << LC << "Wrote earth file to \"" << outEarthFile << "\"" << std::endl; } } return 0; }
/** Packages image and elevation layers as a TMS. */ int TMSExporter::exportTMS(MapNode* mapNode, const std::string& path, std::vector< osgEarth::Bounds >& bounds, const std::string& outEarth, bool overwrite, const std::string& extension) { if ( !mapNode ) { _errorMessage = "Invalid MapNode"; if (_progress.valid()) _progress->onCompleted(); return 0; } // folder to which to write the TMS archive. std::string rootFolder = path; osg::ref_ptr<osgDB::Options> options = new osgDB::Options(_dbOptions); // create a folder for the output osgDB::makeDirectory(rootFolder); if ( !osgDB::fileExists(rootFolder) ) { _errorMessage = "Failed to create root output folder"; if (_progress.valid()) _progress->onCompleted(); return 0; } Map* map = mapNode->getMap(); // new map for an output earth file if necessary. osg::ref_ptr<Map> outMap = 0L; if ( !outEarth.empty() ) { // copy the options from the source map first outMap = new Map(map->getInitialMapOptions()); } // establish the output path of the earth file, if applicable: std::string outEarthName = osgDB::getSimpleFileName(outEarth); if (outEarthName.length() > 0 && osgEarth::toLower(osgDB::getFileExtension(outEarthName)) != "earth") outEarthName += ".earth"; std::string outEarthFile = osgDB::concatPaths(rootFolder, outEarthName); // semaphore and tasks collection for multithreading osgEarth::Threading::MultiEvent semaphore; osgEarth::TaskRequestVector tasks; int taskCount = 0; // package any image layers that are enabled and visible ImageLayerVector imageLayers; map->getImageLayers( imageLayers ); unsigned imageCount = 0; for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i, ++imageCount ) { ImageLayer* layer = i->get(); if ( layer->getEnabled() && layer->getVisible() ) { std::string layerFolder = toLegalFileName( layer->getName() ); if ( layerFolder.empty() ) layerFolder = Stringify() << "image_layer_" << imageCount; ParallelTask<PackageLayer>* task = new ParallelTask<PackageLayer>( &semaphore ); task->init(map, layer, options, rootFolder, layerFolder, true, overwrite, _keepEmpties, _maxLevel, extension, bounds); task->setProgressCallback(new PackageLayerProgressCallback(this)); tasks.push_back(task); taskCount++; } } // package any elevation layers that are enabled and visible ElevationLayerVector elevationLayers; map->getElevationLayers( elevationLayers ); int elevCount = 0; for( ElevationLayerVector::iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i, ++elevCount ) { ElevationLayer* layer = i->get(); if ( layer->getEnabled() && layer->getVisible() ) { std::string layerFolder = toLegalFileName( layer->getName() ); if ( layerFolder.empty() ) layerFolder = Stringify() << "elevation_layer_" << elevCount; ParallelTask<PackageLayer>* task = new ParallelTask<PackageLayer>( &semaphore ); task->init(map, layer, options, rootFolder, layerFolder, true, overwrite, _keepEmpties, _maxLevel, extension, bounds); task->setProgressCallback(new PackageLayerProgressCallback(this)); tasks.push_back(task); taskCount++; } } // Run all the tasks in parallel _totalTasks = taskCount; _completedTasks = 0; semaphore.reset( _totalTasks ); for( TaskRequestVector::iterator i = tasks.begin(); i != tasks.end(); ++i ) _taskService->add( i->get() ); // Wait for them to complete semaphore.wait(); // Add successfully packaged layers to the new map object and // write out the .earth file (if requested) if (outMap.valid()) { for( TaskRequestVector::iterator i = tasks.begin(); i != tasks.end(); ++i ) { PackageLayer* p = dynamic_cast<PackageLayer*>(i->get()); if (p) { if (p->_packageResult.ok) { TMSOptions tms; tms.url() = URI(osgDB::concatPaths(p->_layerFolder, "tms.xml"), outEarthFile ); if (p->_imageLayer.valid()) { ImageLayerOptions layerOptions( p->_imageLayer->getName(), tms ); layerOptions.mergeConfig( p->_imageLayer->getInitialOptions().getConfig(true) ); layerOptions.cachePolicy() = CachePolicy::NO_CACHE; outMap->addImageLayer( new ImageLayer(layerOptions) ); } else { ElevationLayerOptions layerOptions( p->_elevationLayer->getName(), tms ); layerOptions.mergeConfig( p->_elevationLayer->getInitialOptions().getConfig(true) ); layerOptions.cachePolicy() = CachePolicy::NO_CACHE; outMap->addElevationLayer( new ElevationLayer(layerOptions) ); } } else { OE_WARN << LC << p->_packageResult.message << std::endl; } } } } if ( outMap.valid() ) { MapNodeOptions outNodeOptions = mapNode->getMapNodeOptions(); osg::ref_ptr<MapNode> outMapNode = new MapNode(outMap.get(), outNodeOptions); if ( !osgDB::writeNodeFile(*outMapNode.get(), outEarthFile) ) { OE_WARN << LC << "Error writing earth file to \"" << outEarthFile << "\"" << std::endl; } else { OE_NOTICE << LC << "Wrote earth file to \"" << outEarthFile << "\"" << std::endl; } } // Mark the progress callback as completed if (_progress.valid()) _progress->onCompleted(); return elevCount + imageCount; }