osgEarth示例分析——osgearth_ephemeris

前言

osgearth_ephemeris示例,展示了深空场景下,控制仿真时间运动和停止功能。获取太阳和月亮的世界坐标,再转化为经纬度坐标,并将其投影到地球上,用PlaceNode实时代表太阳和月亮的位置。

后续可以优化,加上仿真时间反向、仿真时间速率等功能。目前木有找到支持时间反转、时间速率调整的接口方法。左下角一直保持的sky自带的控制面板。

执行命令

osgearth_ephemerisd.exe earth_image\world.earth --sky

效果

代码分析

记录一个盲点,之前一直没有在意过 app.sky = osgEarth::findTopMostNodeOfType<SkyNode>(node);   这个方法。表示:从[node]开始向下搜索场景图,并返回找到的第一个与模板参数类型匹配的节点。方法的定义如下:

    /**
     * Searchs the scene graph downward starting at [node] and returns the first node found
     * that matches the template parameter type.
     */
    template<typename T>
    T* findTopMostNodeOfType(osg::Node* node, unsigned traversalMask =~0)
    {
        if (!node) return 0;

        FindTopMostNodeOfTypeVisitor<T> fnotv;
        fnotv.setTraversalMode(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN);
        fnotv.setTraversalMask(traversalMask);
        node->accept(fnotv);

        return fnotv._foundNode;
    } 

类似的用法还有:

// osgearth_lights 示例中的运用
ShadowCaster* caster = osgEarth::findTopMostNodeOfType<ShadowCaster>(root);

// 其他
osgEarth::findTopMostNodeOfType<FeatureSourceIndexNode>(node);

findTopMostNodeOfType<osg::CoordinateSystemNode>(_terrainNode.get());

osgEarth::findTopMostNodeOfType<ControlCanvas>(view->getCamera());

 完整代码:

#include <osgViewer/Viewer>
#include <osgEarth/Notify>
#include <osgEarth/NodeUtils>
#include <osgEarthUtil/EarthManipulator>
#include <osgEarthUtil/ExampleResources>
#include <osgEarthUtil/Ephemeris>
#include <osgEarthUtil/Sky>
#include <osgEarthUtil/LatLongFormatter>
#include <osgEarthUtil/Controls>
#include <osgEarthAnnotation/PlaceNode>

#define LC "[osgearth_ephemeris] "

using namespace osgEarth;
using namespace osgEarth::Util;
using namespace osgEarth::Annotation;

// 创建ui命名空间
namespace ui = osgEarth::Util::Controls;

int
usage(const char* name)
{
    OE_NOTICE 
        << "\nUsage: " << name << " <file.earth> --sky" << std::endl
        << MapNodeHelper().usage() << std::endl;

    return 0;
}

struct App
{
    App() {
        _playing = false;// 初始状态,时间停止
        readout = new ui::LabelControl();// 显示
        readout->setVertAlign(ui::Control::ALIGN_CENTER);
    }
	
	// 在地球上显示 月球和太阳,投影到地球上的经纬度。
    osg::ref_ptr<PlaceNode> sunPos;
    osg::ref_ptr<PlaceNode> moonPos;
    SkyNode* sky;
    ui::LabelControl* readout;

    void play() { _playing = true; }
    void stop() { _playing = false; }

    void tick() {
        if (_playing) {
            TimeStamp t = sky->getDateTime().asTimeStamp() + 1;// 获取UTC时间
            sky->setDateTime(DateTime(t));
        }
        readout->setText(sky->getDateTime().asRFC1123());// 显示时间
    }
    
    bool _playing;
};

struct Play : public ui::ControlEventHandler {
    Play(App& app) : _app(app) { }
    void onClick(ui::Control*) { _app.play(); }
    App& _app;
};

struct Stop : public ui::ControlEventHandler {
    Stop(App& app) : _app(app) { }
    void onClick(ui::Control*) { _app.stop(); }
    App& _app;
};

// 创建左上角ui控制面板
ui::Container* createUI(App& app)
{
	// 水平布局,开始、停止,两个按钮,还有时间显示标签
    ui::HBox* vcr = new ui::HBox();
    vcr->addControl(new ui::ButtonControl("Play", new Play(app)));
    vcr->addControl(new ui::ButtonControl("Stop", new Stop(app)));
    vcr->addControl(app.readout);
    return vcr;
}

int
main(int argc, char** argv)
{
    osg::ArgumentParser arguments(&argc,argv);

    // help?
    if ( arguments.read("--help") )
        return usage(argv[0]);

	// 执行命令时,最后要加上 --sky
    if ( arguments.find("--sky") < 0 )
        return usage(argv[0]);

    osgViewer::Viewer viewer(arguments);

    EarthManipulator* em = new EarthManipulator(arguments);
    em->getSettings()->setMinMaxPitch(-89, 89);// 设置最大俯仰角
    viewer.setCameraManipulator( em );

    viewer.getCamera()->setSmallFeatureCullingPixelSize(-1.0f);

	// 读取一张图片,定位在地球上
    osg::ref_ptr<osg::Image> mark = osgDB::readRefImageFile("D:/FreeXGIS/osgearth_gch/data/placemark32.png");
    
	// 声明app对象
    App app;

    // load an earth file, and support all or our example command-line options
    // and earth file <external> tags 
	// 加载earth文件
    osg::Node* node = MapNodeHelper().load( arguments, &viewer );
    if ( node )
    {
		// 设置根节点
        osg::Group* root = new osg::Group();
        root->addChild( node );

		// 获取mapNode
        MapNode* mapNode = MapNode::get(node);

		// 创建太阳和月亮标签
        app.sunPos = new PlaceNode("Sun", Style(), mark.get());
        app.sunPos->setDynamic(true);// 动态改变
        mapNode->addChild( app.sunPos.get() );

        app.moonPos = new PlaceNode("Moon", Style(), mark.get());
        app.moonPos->setDynamic(true);
        mapNode->addChild( app.moonPos.get() ); 

		// osgEarth::findTopMostNodeOfType<SkyNode>:从[node]开始向下搜索场景图,并返回找到的第一个与模板参数类型匹配的节点。
		// 获取天空对象
        app.sky = osgEarth::findTopMostNodeOfType<SkyNode>(node);        
        const Ephemeris* ephemeris = 0L;
        if ( app.sky )
        {
            ephemeris = app.sky->getEphemeris();// 将获取到的星历表保存下来
        }

        LatLongFormatter llf;// 经纬度坐标格式器
        llf.setOptions( LatLongFormatter::Options(llf.FORMAT_DEGREES_MINUTES_SECONDS) );
        llf.setPrecision( 8 );// 控制精度8位

        viewer.setSceneData( root );

		// 创建左上角的ui控制面板
        ui::ControlCanvas* container = ui::ControlCanvas::getOrCreate(&viewer);
        container->addChild(createUI(app));

        while(!viewer.done())
        {
            viewer.frame();

            if ( ephemeris )
            {
				// 获取当前sky的时间
                const DateTime& dt = app.sky->getDateTime();

				// 获取当前时间下太阳天体
                CelestialBody sun = ephemeris->getSunPosition(dt);
                GeoPoint sunPos;
				// 将太阳坐标转化为世界坐标
                sunPos.fromWorld(mapNode->getMapSRS(), sun.geocentric);// sun.geocentric:地心直角坐标系的坐标XYZ
				// std::cout << "sun alt = " << sunPos.alt() << std::endl; // sun alt = 1.496e+11
                sunPos.alt() = 0.0;// 设置高度为0
                app.sunPos->setPosition( sunPos );
                app.sunPos->setText( "Sun\n" + llf.format(sunPos) );

                CelestialBody moon = ephemeris->getMoonPosition(dt);
                GeoPoint moonPos;
                moonPos.fromWorld(mapNode->getMapSRS(), moon.geocentric);
				// std::cout << "moon alt = " << moonPos.alt() << std::endl; // moon alt = 3.98562e+08
                moonPos.alt() = 0.0;
                app.moonPos->setPosition( moonPos );
                app.moonPos->setText( "Moon\n" + llf.format(moonPos) );
            }

            app.tick();// 保证时间一直运行
        }
    }
    else
    {
        return usage(argv[0]);
    }
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值