void Map::setCache( Cache* cache ) { if (_cache.get() != cache) { _cache = cache; if ( _cache.valid() ) { _cache->store( _dbOptions.get() ); } // Propagate the cache to any of our layers for (ImageLayerVector::iterator i = _imageLayers.begin(); i != _imageLayers.end(); ++i) { i->get()->setDBOptions( _dbOptions.get() ); //i->get()->setCache( _cache.get() ); } for (ElevationLayerVector::iterator i = _elevationLayers.begin(); i != _elevationLayers.end(); ++i) { i->get()->setDBOptions( _dbOptions.get() ); //i->get()->setCache( _cache.get() ); } } }
void Map::removeImageLayer( ImageLayer* layer ) { osgEarth::Registry::instance()->clearBlacklist(); unsigned int index = -1; osg::ref_ptr<ImageLayer> layerToRemove = layer; Revision newRevision; if ( layerToRemove.get() ) { Threading::ScopedWriteLock lock( _mapDataMutex ); index = 0; for( ImageLayerVector::iterator i = _imageLayers.begin(); i != _imageLayers.end(); i++, index++ ) { if ( i->get() == layerToRemove.get() ) { _imageLayers.erase( i ); newRevision = ++_dataModelRevision; break; } } } // a separate block b/c we don't need the mutex if ( newRevision >= 0 ) // layerToRemove.get() ) { for( MapCallbackList::iterator i = _mapCallbacks.begin(); i != _mapCallbacks.end(); i++ ) { i->get()->onMapModelChanged( MapModelChange( MapModelChange::REMOVE_IMAGE_LAYER, newRevision, layerToRemove.get(), index) ); //i->get()->onImageLayerRemoved( layerToRemove.get(), index, newRevision ); } } }
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 Map::moveImageLayer( ImageLayer* layer, unsigned int newIndex ) { unsigned int oldIndex = 0; unsigned int actualIndex = 0; Revision newRevision; if ( layer ) { Threading::ScopedWriteLock lock( _mapDataMutex ); // preserve the layer with a ref: osg::ref_ptr<ImageLayer> layerToMove = layer; // find it: ImageLayerVector::iterator i_oldIndex = _imageLayers.end(); for( ImageLayerVector::iterator i = _imageLayers.begin(); i != _imageLayers.end(); i++, actualIndex++ ) { if ( i->get() == layer ) { i_oldIndex = i; oldIndex = actualIndex; break; } } if ( i_oldIndex == _imageLayers.end() ) return; // layer not found in list // erase the old one and insert the new one. _imageLayers.erase( i_oldIndex ); _imageLayers.insert( _imageLayers.begin() + newIndex, layerToMove.get() ); newRevision = ++_dataModelRevision; } // a separate block b/c we don't need the mutex if ( layer ) { for( MapCallbackList::iterator i = _mapCallbacks.begin(); i != _mapCallbacks.end(); i++ ) { i->get()->onMapModelChanged( MapModelChange( MapModelChange::MOVE_IMAGE_LAYER, newRevision, layer, oldIndex, newIndex) ); } } }
void Map::setCache( Cache* cache ) { if (_cache.get() != cache) { _cache = cache; _cache->setReferenceURI( _mapOptions.referenceURI().value() ); //Propagate the cache to any of our layers for (ImageLayerVector::iterator i = _imageLayers.begin(); i != _imageLayers.end(); ++i) { i->get()->setCache( _cache.get() ); } for (ElevationLayerVector::iterator i = _elevationLayers.begin(); i != _elevationLayers.end(); ++i) { i->get()->setCache( _cache.get() ); } } }
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 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; }
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 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(); }
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 ); }
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 Map::calculateProfile() { if ( !_profile.valid() ) { osg::ref_ptr<const Profile> userProfile; if ( _mapOptions.profile().isSet() ) { userProfile = Profile::create( _mapOptions.profile().value() ); } if ( _mapOptions.coordSysType() == MapOptions::CSTYPE_GEOCENTRIC ) { if ( userProfile.valid() ) { if ( userProfile->isOK() && userProfile->getSRS()->isGeographic() ) { _profile = userProfile.get(); } else { OE_WARN << LC << "Map is geocentric, but the configured profile SRS (" << userProfile->getSRS()->getName() << ") is not geographic; " << "it will be ignored." << std::endl; } } if ( !_profile.valid() ) { // by default, set a geocentric map to use global-geodetic WGS84. _profile = osgEarth::Registry::instance()->getGlobalGeodeticProfile(); } } else if ( _mapOptions.coordSysType() == MapOptions::CSTYPE_GEOCENTRIC_CUBE ) { //If the map type is a Geocentric Cube, set the profile to the cube profile. _profile = osgEarth::Registry::instance()->getCubeProfile(); } else // CSTYPE_PROJECTED { if ( userProfile.valid() ) { _profile = userProfile.get(); } } // At this point, if we don't have a profile we need to search tile sources until we find one. if ( !_profile.valid() ) { Threading::ScopedReadLock lock( _mapDataMutex ); for( ImageLayerVector::iterator i = _imageLayers.begin(); i != _imageLayers.end() && !_profile.valid(); i++ ) { ImageLayer* layer = i->get(); if ( layer->getTileSource() ) { _profile = layer->getTileSource()->getProfile(); } } for( ElevationLayerVector::iterator i = _elevationLayers.begin(); i != _elevationLayers.end() && !_profile.valid(); i++ ) { ElevationLayer* layer = i->get(); if ( layer->getTileSource() ) { _profile = layer->getTileSource()->getProfile(); } } } // convert the profile to Plate Carre if necessary. if (_profile.valid() && _profile->getSRS()->isGeographic() && getMapOptions().coordSysType() == MapOptions::CSTYPE_PROJECTED ) { OE_INFO << LC << "Projected display with geographic SRS; activating Plate Carre mode" << std::endl; _profile = _profile->overrideSRS( _profile->getSRS()->createPlateCarreGeographicSRS() ); } // finally, fire an event if the profile has been set. if ( _profile.valid() ) { OE_INFO << LC << "Map profile is: " << _profile->toString() << std::endl; for( MapCallbackList::iterator i = _mapCallbacks.begin(); i != _mapCallbacks.end(); i++ ) { i->get()->onMapInfoEstablished( MapInfo(this) ); } } else { OE_WARN << LC << "Warning, not yet able to establish a map profile!" << std::endl; } } if ( _profile.valid() ) { // tell all the loaded layers what the profile is, as a hint { Threading::ScopedWriteLock lock( _mapDataMutex ); for( ImageLayerVector::iterator i = _imageLayers.begin(); i != _imageLayers.end(); i++ ) { ImageLayer* layer = i->get(); if ( layer->getEnabled() == true ) { layer->setTargetProfileHint( _profile.get() ); } } for( ElevationLayerVector::iterator i = _elevationLayers.begin(); i != _elevationLayers.end(); i++ ) { ElevationLayer* layer = i->get(); if ( layer->getEnabled() ) { layer->setTargetProfileHint( _profile.get() ); } } } // create a "proxy" profile to use when querying elevation layers with a vertical datum if ( _profile->getSRS()->getVerticalDatum() != 0L ) { ProfileOptions po = _profile->toProfileOptions(); po.vsrsString().unset(); _profileNoVDatum = Profile::create(po); } else { _profileNoVDatum = _profile; } } }
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 Map::calculateProfile() { if ( !_profile.valid() ) { osg::ref_ptr<const Profile> userProfile; if ( _mapOptions.profile().isSet() ) { userProfile = Profile::create( _mapOptions.profile().value() ); } if ( _mapOptions.coordSysType() == MapOptions::CSTYPE_GEOCENTRIC ) { if ( userProfile.valid() ) { if ( userProfile->isOK() && userProfile->getSRS()->isGeographic() ) { _profile = userProfile.get(); } else { OE_WARN << LC << "Map is geocentric, but the configured profile does not " << "have a geographic SRS. Falling back on default.." << std::endl; } } if ( !_profile.valid() ) { // by default, set a geocentric map to use global-geodetic WGS84. _profile = osgEarth::Registry::instance()->getGlobalGeodeticProfile(); } } else if ( _mapOptions.coordSysType() == MapOptions::CSTYPE_GEOCENTRIC_CUBE ) { //If the map type is a Geocentric Cube, set the profile to the cube profile. _profile = osgEarth::Registry::instance()->getCubeProfile(); } else // CSTYPE_PROJECTED { if ( userProfile.valid() ) { _profile = userProfile.get(); } } // At this point, if we don't have a profile we need to search tile sources until we find one. if ( !_profile.valid() ) { Threading::ScopedReadLock lock( _mapDataMutex ); for( ImageLayerVector::iterator i = _imageLayers.begin(); i != _imageLayers.end() && !_profile.valid(); i++ ) { ImageLayer* layer = i->get(); if ( layer->getTileSource() ) { _profile = layer->getTileSource()->getProfile(); } } for( ElevationLayerVector::iterator i = _elevationLayers.begin(); i != _elevationLayers.end() && !_profile.valid(); i++ ) { ElevationLayer* layer = i->get(); if ( layer->getTileSource() ) { _profile = layer->getTileSource()->getProfile(); } } } // finally, fire an event if the profile has been set. if ( _profile.valid() ) { OE_INFO << LC << "Map profile is: " << _profile->toString() << std::endl; for( MapCallbackList::iterator i = _mapCallbacks.begin(); i != _mapCallbacks.end(); i++ ) { i->get()->onMapInfoEstablished( MapInfo(this) ); } } else { OE_WARN << LC << "Warning, not yet able to establish a map profile!" << std::endl; } } if ( _profile.valid() ) { // tell all the loaded layers what the profile is, as a hint { Threading::ScopedWriteLock lock( _mapDataMutex ); for( ImageLayerVector::iterator i = _imageLayers.begin(); i != _imageLayers.end(); i++ ) { ImageLayer* layer = i->get(); layer->setTargetProfileHint( _profile.get() ); } for( ElevationLayerVector::iterator i = _elevationLayers.begin(); i != _elevationLayers.end(); i++ ) { ElevationLayer* layer = i->get(); layer->setTargetProfileHint( _profile.get() ); } } } }
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(); }
/** 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(); }
/** 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; }