osgEarth 2.1.1
|
00001 /* -*-c++-*- */ 00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph 00003 * Copyright 2008-2010 Pelican Mapping 00004 * http://osgearth.org 00005 * 00006 * osgEarth is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU Lesser General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public License 00017 * along with this program. If not, see <http://www.gnu.org/licenses/> 00018 */ 00019 00020 #include <osg/Notify> 00021 #include <osgGA/StateSetManipulator> 00022 #include <osgGA/GUIEventHandler> 00023 #include <osgViewer/Viewer> 00024 #include <osgViewer/ViewerEventHandlers> 00025 #include <osgEarth/MapNode> 00026 #include <osgEarth/XmlUtils> 00027 #include <osgEarthUtil/EarthManipulator> 00028 #include <osgEarthUtil/AutoClipPlaneHandler> 00029 #include <osgEarthUtil/Controls> 00030 #include <osgEarthUtil/Graticule> 00031 #include <osgEarthUtil/SkyNode> 00032 #include <osgEarthUtil/Viewpoint> 00033 #include <osgEarthUtil/Formatters> 00034 #include <osgEarthUtil/Annotation> 00035 #include <osgEarthSymbology/Color> 00036 #include <osgEarthDrivers/kml/KML> 00037 00038 using namespace osgEarth::Util; 00039 using namespace osgEarth::Util::Annotation; 00040 using namespace osgEarth::Util::Controls; 00041 using namespace osgEarth::Symbology; 00042 using namespace osgEarth::Drivers; 00043 00044 int 00045 usage( const std::string& msg ) 00046 { 00047 OE_NOTICE << msg << std::endl; 00048 OE_NOTICE << std::endl; 00049 OE_NOTICE << "USAGE: osgearth_viewer [options] file.earth" << std::endl; 00050 OE_NOTICE << " --sky : activates the atmospheric model" << std::endl; 00051 OE_NOTICE << " --autoclip : activates the auto clip-plane handler" << std::endl; 00052 OE_NOTICE << " --dms : format coordinates as degrees/minutes/seconds" << std::endl; 00053 OE_NOTICE << " --mgrs : format coordinates as MGRS" << std::endl; 00054 00055 00056 return -1; 00057 } 00058 00059 static EarthManipulator* s_manip =0L; 00060 static Control* s_controlPanel =0L; 00061 static SkyNode* s_sky =0L; 00062 static bool s_dms =false; 00063 static bool s_mgrs =false; 00064 00065 struct SkySliderHandler : public ControlEventHandler 00066 { 00067 virtual void onValueChanged( class Control* control, float value ) 00068 { 00069 s_sky->setDateTime( 2011, 3, 6, value ); 00070 } 00071 }; 00072 00073 struct ToggleNodeHandler : public ControlEventHandler 00074 { 00075 ToggleNodeHandler( osg::Node* node ) : _node(node) { } 00076 00077 virtual void onValueChanged( class Control* control, bool value ) 00078 { 00079 osg::ref_ptr<osg::Node> safeNode = _node.get(); 00080 if ( safeNode.valid() ) 00081 safeNode->setNodeMask( value ? ~0 : 0 ); 00082 } 00083 00084 osg::observer_ptr<osg::Node> _node; 00085 }; 00086 00087 struct ClickViewpointHandler : public ControlEventHandler 00088 { 00089 ClickViewpointHandler( const Viewpoint& vp ) : _vp(vp) { } 00090 Viewpoint _vp; 00091 00092 virtual void onClick( class Control* control ) 00093 { 00094 s_manip->setViewpoint( _vp, 4.5 ); 00095 } 00096 }; 00097 00098 struct MouseCoordsHandler : public osgGA::GUIEventHandler 00099 { 00100 MouseCoordsHandler( LabelControl* label, osgEarth::MapNode* mapNode ) 00101 : _label( label ), 00102 _mapNode( mapNode ) 00103 { 00104 _mapNodePath.push_back( mapNode->getTerrainEngine() ); 00105 } 00106 00107 bool handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa ) 00108 { 00109 osgViewer::View* view = static_cast<osgViewer::View*>(aa.asView()); 00110 if (ea.getEventType() == ea.MOVE || ea.getEventType() == ea.DRAG) 00111 { 00112 osgUtil::LineSegmentIntersector::Intersections results; 00113 if ( view->computeIntersections( ea.getX(), ea.getY(), _mapNodePath, results ) ) 00114 { 00115 // find the first hit under the mouse: 00116 osgUtil::LineSegmentIntersector::Intersection first = *(results.begin()); 00117 osg::Vec3d point = first.getWorldIntersectPoint(); 00118 osg::Vec3d lla; 00119 00120 // transform it to map coordinates: 00121 _mapNode->getMap()->worldPointToMapPoint(point, lla); 00122 00123 std::stringstream ss; 00124 00125 if ( s_mgrs ) 00126 { 00127 MGRSFormatter f( MGRSFormatter::PRECISION_1M ); 00128 ss << "MGRS: " << f.format(lla.y(), lla.x()) << " "; 00129 } 00130 // lat/long 00131 { 00132 LatLongFormatter::AngularFormat fFormat = s_dms? 00133 LatLongFormatter::FORMAT_DEGREES_MINUTES_SECONDS : 00134 LatLongFormatter::FORMAT_DECIMAL_DEGREES; 00135 00136 LatLongFormatter f( fFormat ); 00137 00138 ss 00139 << "Lat: " << f.format( Angular(lla.y(),Units::DEGREES), 4 ) << " " 00140 << "Lon: " << f.format( Angular(lla.x(),Units::DEGREES), 5 ); 00141 } 00142 00143 _label->setText( ss.str() ); 00144 } 00145 else 00146 { 00147 //Clear the text 00148 _label->setText( "" ); 00149 } 00150 } 00151 return false; 00152 } 00153 00154 osg::ref_ptr< LabelControl > _label; 00155 MapNode* _mapNode; 00156 osg::NodePath _mapNodePath; 00157 }; 00158 00159 00160 00161 void 00162 createControlPanel( osgViewer::View* view, std::vector<Viewpoint>& vps ) 00163 { 00164 ControlCanvas* canvas = ControlCanvas::get( view ); 00165 00166 VBox* main = new VBox(); 00167 main->setBackColor(0,0,0,0.5); 00168 main->setMargin( 10 ); 00169 main->setPadding( 10 ); 00170 main->setChildSpacing( 10 ); 00171 main->setAbsorbEvents( true ); 00172 main->setVertAlign( Control::ALIGN_BOTTOM ); 00173 00174 if ( vps.size() > 0 ) 00175 { 00176 // the viewpoint container: 00177 Grid* g = new Grid(); 00178 g->setChildSpacing( 0 ); 00179 g->setChildVertAlign( Control::ALIGN_CENTER ); 00180 00181 unsigned i; 00182 for( i=0; i<vps.size(); ++i ) 00183 { 00184 const Viewpoint& vp = vps[i]; 00185 std::stringstream buf; 00186 buf << (i+1); 00187 Control* num = new LabelControl(buf.str(), 16.0f, osg::Vec4f(1,1,0,1)); 00188 num->setPadding( 4 ); 00189 g->setControl( 0, i, num ); 00190 00191 Control* vpc = new LabelControl(vp.getName().empty() ? "<no name>" : vp.getName(), 16.0f); 00192 vpc->setPadding( 4 ); 00193 vpc->setHorizFill( true ); 00194 vpc->setActiveColor( Color::Blue ); 00195 vpc->addEventHandler( new ClickViewpointHandler(vp) ); 00196 g->setControl( 1, i, vpc ); 00197 } 00198 main->addControl( g ); 00199 } 00200 00201 // sky time slider: 00202 if ( s_sky ) 00203 { 00204 HBox* skyBox = new HBox(); 00205 skyBox->setChildVertAlign( Control::ALIGN_CENTER ); 00206 skyBox->setChildSpacing( 10 ); 00207 skyBox->setHorizFill( true ); 00208 00209 skyBox->addControl( new LabelControl("Time: ", 16) ); 00210 00211 HSliderControl* skySlider = new HSliderControl( 0.0f, 24.0f, 18.0f ); 00212 skySlider->setBackColor( Color::Gray ); 00213 skySlider->setHeight( 12 ); 00214 skySlider->setHorizFill( true, 200 ); 00215 skySlider->addEventHandler( new SkySliderHandler ); 00216 skyBox->addControl( skySlider ); 00217 00218 main->addControl( skyBox ); 00219 } 00220 00221 canvas->addControl( main ); 00222 00223 s_controlPanel = main; 00224 } 00225 00229 struct KMLUIBuilder : public osg::NodeVisitor 00230 { 00231 KMLUIBuilder( ControlCanvas* canvas ) : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _canvas(canvas) 00232 { 00233 _grid = new Grid(); 00234 _grid->setAbsorbEvents( true ); 00235 _grid->setPadding( 5 ); 00236 _grid->setVertAlign( Control::ALIGN_TOP ); 00237 _grid->setHorizAlign( Control::ALIGN_LEFT ); 00238 _grid->setBackColor( Color(Color::Black,0.5) ); 00239 _canvas->addControl( _grid ); 00240 } 00241 00242 void apply( osg::Node& node ) 00243 { 00244 AnnotationData* data = dynamic_cast<AnnotationData*>( node.getUserData() ); 00245 if ( data ) 00246 { 00247 ControlVector row; 00248 CheckBoxControl* cb = new CheckBoxControl( node.getNodeMask() != 0, new ToggleNodeHandler( &node ) ); 00249 cb->setSize( 12, 12 ); 00250 row.push_back( cb ); 00251 std::string name = data->getName().empty() ? "<unnamed>" : data->getName(); 00252 LabelControl* label = new LabelControl( name, 14.0f ); 00253 label->setMargin(Gutter(0,0,0,(this->getNodePath().size()-3)*20)); 00254 if ( data->getViewpoint() ) 00255 { 00256 label->addEventHandler( new ClickViewpointHandler(*data->getViewpoint()) ); 00257 label->setActiveColor( Color::Blue ); 00258 } 00259 row.push_back( label ); 00260 _grid->addControls( row ); 00261 } 00262 traverse(node); 00263 } 00264 00265 ControlCanvas* _canvas; 00266 Grid* _grid; 00267 }; 00268 00269 void addMouseCoords(osgViewer::Viewer* viewer, osgEarth::MapNode* mapNode) 00270 { 00271 ControlCanvas* canvas = ControlCanvas::get( viewer ); 00272 LabelControl* mouseCoords = new LabelControl(); 00273 mouseCoords->setHorizAlign(Control::ALIGN_CENTER ); 00274 mouseCoords->setVertAlign(Control::ALIGN_BOTTOM ); 00275 mouseCoords->setBackColor(0,0,0,0.5); 00276 mouseCoords->setSize(400,50); 00277 mouseCoords->setMargin( 10 ); 00278 canvas->addControl( mouseCoords ); 00279 00280 viewer->addEventHandler( new MouseCoordsHandler(mouseCoords, mapNode ) ); 00281 } 00282 00283 struct ViewpointHandler : public osgGA::GUIEventHandler 00284 { 00285 ViewpointHandler( const std::vector<Viewpoint>& viewpoints ) 00286 : _viewpoints( viewpoints ) { } 00287 00288 bool handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa ) 00289 { 00290 if ( ea.getEventType() == ea.KEYDOWN ) 00291 { 00292 int index = (int)ea.getKey() - (int)'1'; 00293 if ( index >= 0 && index < (int)_viewpoints.size() ) 00294 { 00295 s_manip->setViewpoint( _viewpoints[index], 4.5 ); 00296 } 00297 else if ( ea.getKey() == 'v' ) 00298 { 00299 Viewpoint vp = s_manip->getViewpoint(); 00300 XmlDocument xml( vp.getConfig() ); 00301 xml.store( std::cout ); 00302 std::cout << std::endl; 00303 } 00304 else if ( ea.getKey() == '?' ) 00305 { 00306 s_controlPanel->setVisible( !s_controlPanel->visible() ); 00307 } 00308 } 00309 return false; 00310 } 00311 00312 std::vector<Viewpoint> _viewpoints; 00313 }; 00314 00315 int 00316 main(int argc, char** argv) 00317 { 00318 osg::ArgumentParser arguments(&argc,argv); 00319 osg::DisplaySettings::instance()->setMinimumNumStencilBits( 8 ); 00320 osgViewer::Viewer viewer(arguments); 00321 00322 bool useAutoClip = arguments.read( "--autoclip" ); 00323 bool useSky = arguments.read( "--sky" ); 00324 s_dms = arguments.read( "--dms" ); 00325 s_mgrs = arguments.read( "--mgrs" ); 00326 00327 std::string kmlFile; 00328 arguments.read( "--kml", kmlFile ); 00329 00330 // load the .earth file from the command line. 00331 osg::Node* earthNode = osgDB::readNodeFiles( arguments ); 00332 if (!earthNode) 00333 return usage( "Unable to load earth model." ); 00334 00335 s_manip = new EarthManipulator(); 00336 s_manip->getSettings()->setArcViewpointTransitions( true ); 00337 viewer.setCameraManipulator( s_manip ); 00338 00339 osg::Group* root = new osg::Group(); 00340 root->addChild( earthNode ); 00341 00342 // create a graticule and clip plane handler. 00343 Graticule* graticule = 0L; 00344 osgEarth::MapNode* mapNode = osgEarth::MapNode::findMapNode( earthNode ); 00345 if ( mapNode ) 00346 { 00347 const Config& externals = mapNode->externalConfig(); 00348 00349 if ( mapNode->getMap()->isGeocentric() ) 00350 { 00351 // Sky model. 00352 Config skyConf = externals.child( "sky" ); 00353 if ( !skyConf.empty() ) 00354 useSky = true; 00355 00356 if ( useSky ) 00357 { 00358 double hours = skyConf.value( "hours", 12.0 ); 00359 s_sky = new SkyNode( mapNode->getMap() ); 00360 s_sky->setDateTime( 2011, 3, 6, hours ); 00361 s_sky->attach( &viewer ); 00362 root->addChild( s_sky ); 00363 } 00364 00365 if ( externals.hasChild("autoclip") ) 00366 useAutoClip = externals.child("autoclip").boolValue( useAutoClip ); 00367 00368 // the AutoClipPlaneHandler will automatically adjust the near/far clipping 00369 // planes based on your view of the horizon. This prevents near clipping issues 00370 // when you are very close to the ground. If your app never brings a user very 00371 // close to the ground, you may not need this. 00372 if ( useSky || useAutoClip ) 00373 { 00374 viewer.getCamera()->addEventCallback( new AutoClipPlaneCallback() ); 00375 } 00376 } 00377 00378 // read in viewpoints, if any 00379 std::vector<Viewpoint> viewpoints; 00380 const ConfigSet children = externals.children("viewpoint"); 00381 if ( children.size() > 0 ) 00382 { 00383 for( ConfigSet::const_iterator i = children.begin(); i != children.end(); ++i ) 00384 viewpoints.push_back( Viewpoint(*i) ); 00385 00386 viewer.addEventHandler( new ViewpointHandler(viewpoints) ); 00387 } 00388 00389 // Add a control panel to the scene 00390 root->addChild( ControlCanvas::get( &viewer ) ); 00391 if ( viewpoints.size() > 0 || s_sky ) 00392 createControlPanel(&viewer, viewpoints); 00393 00394 addMouseCoords( &viewer, mapNode ); 00395 00396 // Load a KML file if specified 00397 if ( !kmlFile.empty() ) 00398 { 00399 KMLOptions kmlo; 00400 kmlo.defaultIconImage() = URI("http://www.osgearth.org/chrome/site/pushpin_yellow.png").readImage(); 00401 00402 osg::Node* kml = KML::load( URI(kmlFile), mapNode, kmlo ); 00403 if ( kml ) 00404 { 00405 root->addChild( kml ); 00406 00407 KMLUIBuilder uibuilder( ControlCanvas::get(&viewer) ); 00408 root->accept( uibuilder ); 00409 } 00410 } 00411 } 00412 00413 // osgEarth benefits from pre-compilation of GL objects in the pager. In newer versions of 00414 // OSG, this activates OSG's IncrementalCompileOpeartion in order to avoid frame breaks. 00415 viewer.getDatabasePager()->setDoPreCompile( true ); 00416 00417 viewer.setSceneData( root ); 00418 00419 // add some stock OSG handlers: 00420 viewer.addEventHandler(new osgViewer::StatsHandler()); 00421 viewer.addEventHandler(new osgViewer::WindowSizeHandler()); 00422 viewer.addEventHandler(new osgViewer::ThreadingHandler()); 00423 viewer.addEventHandler(new osgViewer::LODScaleHandler()); 00424 viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet())); 00425 00426 return viewer.run(); 00427 }