osgEarth示例分析——osgearth_manip

news2025/1/16 2:53:14

前言

本示例主要演示osgEarth的事件处理的用法,内容比较多,这部分功能也很重要。

输入命令依然采用china-simple.earth的示例,加上了模型,但是模型并没有看到,可能是因为模型没有放大太小的原因。在代码中设置了不加载模型的话,会绘制两个半圆。

// 加载模型,但是看不到,估计是模型太小,需要放大
osgearth_manipd.exe earth_image/china-simple.earth --model axes.osgt

// 并没有加载模型
osgearth_manipd.exe earth_image/china-simple.earth --model modelToLoad

// 不加载模型,会默认创建一个半球,距离太远的话,半球也看不到
osgearth_manipd.exe earth_image/china-simple.earth

执行效果

按下W键,开启坐标,也就是黄色的两条线,交点始终跟随 Thing 1 节点。

按键说明

左上角操作说明
*左键按住左键拖动地球*u看不出来,没啥变化
*中键按住中键可以旋转地球*o切换操作器,地球会有大小变化
*右键按住右键前后移动,缩放地球*8视角绑定thing 1,也可以说跟踪
*

右键/左键

双击

越来越远/越来越近*9视角绑定thing 2,也可以说跟踪
*滚轮滚动视角远近控制*t地球三种转动状态的切换
*arrows

shift+上/下/左/右键,

好像没啥作用

*b解除绑定 '8' ‘9’,,或者说解除跟踪
*shift+左键看不出来,没啥变化*a看不出来,没啥变化
*q看不出来,没啥变化
*k看不出来,没啥变化
*L看不出来,没啥变化
*j定位到可以看见一组点的视点
*W绘制两条线,交点始终定位在thing 1上,屏幕坐标系

[注] 必须在英文下输入,如果没有注意,在中文状态下输入字符,地球会被卡住。此时只能退出重新运行程序。

thing1 thing 2,这两个文本是移动的。

代码分析

#include <string>

#include <osg/Notify>
#include <osg/Timer>
#include <osg/ShapeDrawable>
#include <osg/Depth>
#include <osg/PositionAttitudeTransform>
#include <osgGA/StateSetManipulator>
#include <osgGA/GUIEventHandler>
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include <osgEarth/GeoMath>
#include <osgEarth/GeoTransform>
#include <osgEarth/MapNode>
#include <osgEarth/TerrainEngineNode>
#include <osgEarth/Viewpoint>
#include <osgEarthUtil/EarthManipulator>
#include <osgEarthUtil/Controls>
#include <osgEarthUtil/ExampleResources>
#include <osgEarthUtil/LogarithmicDepthBuffer>
#include <osgEarthUtil/ViewFitter>
#include <osgEarthAnnotation/AnnotationUtils>
#include <osgEarthAnnotation/LabelNode>
#include <osgEarthSymbology/Style>
#include <osgEarth/ScreenSpaceLayout>

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

#define D2R (osg::PI/180.0)
#define R2D (180.0/osg::PI)

namespace
{
    /**
     * Tether callback test.是否绑定
     */
    struct TetherCB : public EarthManipulator::TetherCallback
    {
        void operator()(osg::Node* node)
        {
            if ( node ) {
                OE_WARN << "Tether on\n";
            }
            else {
                OE_WARN << "Tether off\n";
            }
        }
    };

    /**
     * Builds our help menu UI.创建左上角的说明文档
     */
    Container* createHelp( osgViewer::View* view )
    {
        const char* text[] =
        {
            "left mouse :",        "pan",
            "middle mouse :",      "rotate",
            "right mouse :",       "continuous zoom",
            "double-click :",      "zoom to point",
            "scroll wheel :",      "zoom in/out",
            "arrows :",            "pan",
            //"1-6 :",               "fly to preset viewpoints",
            "shift-left-mouse :",  "locked pan",
            "u :",                 "toggle azimuth lock",
            "o :",                 "toggle perspective/ortho",
            "8 :",                 "Tether to thing 1",
            "9 :",                 "Tether to thing 2",
            "t :",                 "cycle tethermode",
            "b :",                 "break tether",
            "a :",                 "toggle viewpoint arcing",
            "q :",                 "toggle throwing",
            "k :",                 "toggle collision",
            "L :",                 "toggle log depth buffer"
			//"W :",				   "坐标系",		// 源代码并没有这两个提示
			//"j :",				   "定位到指定点组的视点",
        };

        Grid* g = new Grid();// 创建网格
        unsigned i, c, r;
		std::cout << sizeof(text) << std::endl;		// 存储所有指针占位多少
		std::cout << sizeof(text[0]) << std::endl;	// 每个指针占位多少
        for( i=0; i<sizeof(text)/sizeof(text[0]); ++i )
        {
            c = i % 2;
            r = i / 2;
            g->setControl( c, r, new LabelControl(text[i]) );// 两列c,i行r,显示label控件
        }

        VBox* v = new VBox();
        v->addControl( g );// 将网格控件放入VBox控件中

        return v;
    }


    /**
     * Some preset viewpoints to show off the setViewpoint function.
     */
    static Viewpoint VPs[] = {
        Viewpoint( "Africa",            0.0,   0.0, 0.0, 0.0, -90.0, 10e6 ),
        Viewpoint( "California",     -121.0,  34.0, 0.0, 0.0, -90.0, 6e6 ),
        Viewpoint( "Europe",            0.0,  45.0, 0.0, 0.0, -90.0, 4e6 ),
        Viewpoint( "Washington DC",   -77.0,  38.0, 0.0, 0.0, -90.0, 1e6 ),
        Viewpoint( "Australia",       135.0, -20.0, 0.0, 0.0, -90.0, 2e6 ),
        Viewpoint( "Boston",         -71.096936, 42.332771, 0, 0.0, -90, 1e5 )
    };


    /**
     * Handler that demonstrates the "viewpoint" functionality in 
     *  osgEarthUtil::EarthManipulator. Press a number key to fly to a viewpoint.
     */
    struct FlyToViewpointHandler : public osgGA::GUIEventHandler 
    {
        FlyToViewpointHandler( EarthManipulator* manip ) : _manip(manip) { }

        bool handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa )
        {
            if ( ea.getEventType() == ea.KEYDOWN && ea.getKey() >= '1' && ea.getKey() <= '6' )
            {
                _manip->setViewpoint( VPs[ea.getKey()-'1'], 4.0 );// 根据设置好的位置,123456定位不同的地点
                aa.requestRedraw();
            }
            return false;
        }

        osg::observer_ptr<EarthManipulator> _manip;
    };
    

    /**
     * Toggles the logarithmic depth buffer切换对数深度缓冲区
     */
    struct ToggleLDB : public osgGA::GUIEventHandler
    {
        ToggleLDB(char key) : _key(key), _installed(false) { }// key=L

        bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
        {
            if (ea.getEventType() == ea.KEYDOWN && ea.getKey() == _key)
            {
                if ( !_installed )
                {
                    _nfratio = aa.asView()->getCamera()->getNearFarRatio();// 获取到近/远率
                    _ldb.install(aa.asView()->getCamera());
                    aa.asView()->getCamera()->setNearFarRatio(0.00001);
                }
                else
                {
                    _ldb.uninstall(aa.asView()->getCamera());
                    aa.asView()->getCamera()->setNearFarRatio(_nfratio);
                }

                _installed = !_installed;// 每次切换
                return true;
            }
            return false;
        }

        void getUsage(osg::ApplicationUsage& usage) const
        {
            using namespace std;
            usage.addKeyboardMouseBinding(string(1, _key), string("Toggle LDB"));
        }

        char _key;
        float _nfratio;
        bool _installed;
        osgEarth::Util::LogarithmicDepthBuffer _ldb;
    };

    /**
     * Toggles screen space layout on the sismulated objects 切换模拟对象的屏幕空间布局
     */
    struct ToggleSSL : public osgGA::GUIEventHandler
    {
        ToggleSSL(osg::Group* g, char key) : _group(g), _key(key), _installed(false) { }// key=)

        bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
        {
            if (ea.getEventType() == ea.KEYDOWN && ea.getKey() == _key)
            {
                _installed = !_installed;
                ScreenSpaceLayout::setDeclutteringEnabled(_installed);// 启动或禁用清理
                return true;
            }
            return false;
        }

        void getUsage(osg::ApplicationUsage& usage) const
        {
            using namespace std;
            usage.addKeyboardMouseBinding(string(1, _key), string("Toggle SSL"));
        }

        char _key;
        osg::Group* _group;
        bool _installed;
    };

    /**
     * Handler to toggle "azimuth locking", which locks the camera's relative Azimuth
     * while panning. For example, it can maintain "north-up" as you pan around. The
     * caveat is that when azimuth is locked you cannot cross the poles.
     */
    struct LockAzimuthHandler : public osgGA::GUIEventHandler // 锁定/解锁 方位角
    {
        LockAzimuthHandler(char key, EarthManipulator* manip) // 传入key = u
            : _key(key), _manip(manip) { }

        bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
        {
            if (ea.getEventType() == ea.KEYDOWN && ea.getKey() == _key)
            {
                bool lockAzimuth = _manip->getSettings()->getLockAzimuthWhilePanning();
				std::cout << "按下 u" << std::endl;// 确实收到消息,但是在操作上并没有看到所定方位角
                _manip->getSettings()->setLockAzimuthWhilePanning(!lockAzimuth);// 设置为当前状态相反的状态
                aa.requestRedraw();
                return true;
            }
            return false;
        }

        void getUsage(osg::ApplicationUsage& usage) const
        {
            using namespace std;
            usage.addKeyboardMouseBinding(string(1, _key), string("Toggle azimuth locking"));
        }

        char _key;
        osg::ref_ptr<EarthManipulator> _manip;
    };


    /**
     * Handler to toggle "viewpoint transition arcing", which causes the camera to "arc"
     * as it travels from one viewpoint to another.
     */
    struct ToggleArcViewpointTransitionsHandler : public osgGA::GUIEventHandler
    {
        ToggleArcViewpointTransitionsHandler(char key, EarthManipulator* manip)
            : _key(key), _manip(manip) { }// key=a

        bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
        {
            if (ea.getEventType() == ea.KEYDOWN && ea.getKey() == _key)
            {
                bool arc = _manip->getSettings()->getArcViewpointTransitions();
				std::cout << "按下 a" << std::endl;// 确实收到消息,但是没看出什么变化
                _manip->getSettings()->setArcViewpointTransitions(!arc);
                aa.requestRedraw();
                return true;
            }
            return false;
        }

        void getUsage(osg::ApplicationUsage& usage) const
        {
            using namespace std;
            usage.addKeyboardMouseBinding(string(1, _key), string("Arc viewpoint transitions"));
        }

        char _key;
        osg::ref_ptr<EarthManipulator> _manip;
    };


    /**
     * Toggles the throwing feature.切换投掷功能。
     */
    struct ToggleThrowingHandler : public osgGA::GUIEventHandler
    {
        ToggleThrowingHandler(char key, EarthManipulator* manip)
            : _key(key), _manip(manip)// key=q
        {
        }

        bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
        {
            if (ea.getEventType() == ea.KEYDOWN && ea.getKey() == _key)
            {
                bool throwing = _manip->getSettings()->getThrowingEnabled();
				std::cout << "按下 q" << std::endl;// 确实收到消息,但是没看出什么变化
                _manip->getSettings()->setThrowingEnabled( !throwing );
                aa.requestRedraw();
                return true;
            }
            return false;
        }

        void getUsage(osg::ApplicationUsage& usage) const
        {
            using namespace std;
            usage.addKeyboardMouseBinding(string(1, _key), string("Toggle throwing"));
        }

        char _key;
        osg::ref_ptr<EarthManipulator> _manip;
    };

    /**
     * Toggles the collision feature.切换碰撞功能。
     */
    struct ToggleCollisionHandler : public osgGA::GUIEventHandler
    {
        ToggleCollisionHandler(char key, EarthManipulator* manip)
            : _key(key), _manip(manip)// key=k
        {
        }

        bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
        {
            if (ea.getEventType() == ea.KEYDOWN && ea.getKey() == _key)
            {
                bool value = _manip->getSettings()->getTerrainAvoidanceEnabled();
				std::cout << "按下 k" << std::endl;// 确实收到消息,但是没看出什么变化
                _manip->getSettings()->setTerrainAvoidanceEnabled( !value );
                aa.requestRedraw();
                return true;
            }
            return false;
        }

        void getUsage(osg::ApplicationUsage& usage) const
        {
            using namespace std;
            usage.addKeyboardMouseBinding(string(1, _key), string("Toggle terrain avoidance"));
        }

        char _key;
        osg::ref_ptr<EarthManipulator> _manip;
    };

    /**
     * Breaks a tether.三种状态的切换
     */
    struct CycleTetherMode : public osgGA::GUIEventHandler
    {
        CycleTetherMode(char key, EarthManipulator* manip)
            : _key(key), _manip(manip) { }// key=t

        bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
        {
            if (ea.getEventType() == ea.KEYDOWN && ea.getKey() == _key)
            {
                EarthManipulator::TetherMode mode = _manip->getSettings()->getTetherMode();
                if ( mode == _manip->TETHER_CENTER ) { 
                    _manip->getSettings()->setTetherMode( _manip->TETHER_CENTER_AND_HEADING );// 相机将跟随节点,仅跟随方向旋转
                    OE_NOTICE << "Tether mode = TETHER_CENTER_AND_HEADING\n";
                }
                else if ( mode == _manip->TETHER_CENTER_AND_HEADING ) {
                    _manip->getSettings()->setTetherMode( _manip->TETHER_CENTER_AND_ROTATION ); // 摄影机将跟随节点和节点所做的所有旋转
                    OE_NOTICE << "Tether mode = TETHER_CENTER_AND_ROTATION\n";
                }
                else {
                    _manip->getSettings()->setTetherMode( _manip->TETHER_CENTER );// 摄影机将跟随节点的中心。
                    OE_NOTICE << "Tether mode = CENTER\n";
                }

                aa.requestRedraw();
                return true;
            }
            return false;
        }

        void getUsage(osg::ApplicationUsage& usage) const
        {
            using namespace std;
            usage.addKeyboardMouseBinding(string(1, _key), string("Cycle Tether Mode"));
        }

        char _key;
        osg::ref_ptr<EarthManipulator> _manip;
    };

    /**
     * Breaks a tether.解绑
     */
    struct BreakTetherHandler : public osgGA::GUIEventHandler
    {
        BreakTetherHandler(char key, EarthManipulator* manip)
            : _key(key), _manip(manip) { }//key=b

        bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
        {
            if (ea.getEventType() == ea.KEYDOWN && ea.getKey() == _key)
            {
                _manip->clearViewpoint();// 解除所有操作器绑定,恢复自由状态。thing1和 thing2 的状态改变明显
                aa.requestRedraw();
                return true;
            }
            return false;
        }

        void getUsage(osg::ApplicationUsage& usage) const
        {
            using namespace std;
            usage.addKeyboardMouseBinding(string(1, _key), string("Break a tether"));
        }

        char _key;
        osg::ref_ptr<EarthManipulator> _manip;
    };
    

    /**
     * Adjusts the position offset.调整位置偏移。
     */
    struct SetPositionOffset : public osgGA::GUIEventHandler
    {
        SetPositionOffset(EarthManipulator* manip)
            : _manip(manip) { }

        bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
        {
            if (ea.getEventType() == ea.KEYDOWN && (ea.getModKeyMask() & ea.MODKEY_SHIFT) )
            {
                Viewpoint oldvp = _manip->getViewpoint();

                double seconds = 0.5; // 时间秒。每次切换都需要0.5秒

                if ( ea.getKey() == ea.KEY_Left )// 左键
                {
                    Viewpoint vp;
                    vp.positionOffset() = oldvp.positionOffset().get() + osg::Vec3f(-1000,0,0);
                    _manip->setViewpoint( vp, seconds );
                }
                else if ( ea.getKey() == ea.KEY_Right )// 右键
                {
                    Viewpoint vp;
                    vp.positionOffset() = oldvp.positionOffset().get() + osg::Vec3f(1000,0,0);
                    _manip->setViewpoint( vp, seconds );
                }
                else if ( ea.getKey() == ea.KEY_Up )// 上键
                {
                    Viewpoint vp;
                    vp.positionOffset() = oldvp.positionOffset().get() + osg::Vec3f(0,0,1000);
                    _manip->setViewpoint( vp, seconds );
                }
                else if ( ea.getKey() == ea.KEY_Down )// 下键
                {
                    Viewpoint vp;
                    vp.positionOffset() = oldvp.positionOffset().get() + osg::Vec3f(0,0,-1000);
                    _manip->setViewpoint( vp, seconds );
                }
                aa.requestRedraw();
                return true;
            }
            return false;
        }

        osg::ref_ptr<EarthManipulator> _manip;
    };


    /**
     * Toggles perspective/ortho projection matrix. 切换投影矩阵,透视投影矩阵和正射投影矩阵
     */
    struct ToggleProjMatrix : public osgGA::GUIEventHandler
    {
        ToggleProjMatrix(char key, EarthManipulator* manip)
            : _key(key), _manip(manip)// ley=o
        {
        }

        bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
        {
            if (ea.getEventType() == ea.KEYDOWN && ea.getKey() == _key)
            {
                osg::Matrix proj = aa.asView()->getCamera()->getProjectionMatrix();
                if ( proj(3,3) == 0 )
                {
                    OE_NOTICE << "Switching to orthographc.\n";
                    proj.getPerspective(_vfov, _ar, _zn, _zf); // 获取值
                    aa.asView()->getCamera()->setProjectionMatrixAsOrtho(-1, 1, -1, 1, _zn, _zf);// 正射投影矩阵
                }
                else
                {
                    OE_NOTICE << "Switching to perspective.\n";
                    aa.asView()->getCamera()->setProjectionMatrixAsPerspective(_vfov, _ar, _zn, _zf);// 透视矩阵
                }
                aa.requestRedraw();
                return true;
            }
            return false;
        }

        void getUsage(osg::ApplicationUsage& usage) const
        {
            using namespace std;
            usage.addKeyboardMouseBinding(string(1, _key), string("Toggle projection matrix type"));
        }

        char _key;
        osg::ref_ptr<EarthManipulator> _manip;
        double _vfov, _ar, _zn, _zf;
    };
    // 定位到某组点的视点,一组点都可以看到
    struct FitViewToPoints : public osgGA::GUIEventHandler
    {
        std::vector<GeoPoint> _points;
        const SpatialReference* _mapSRS;

        FitViewToPoints(char key, EarthManipulator* manip, const SpatialReference* mapSRS)
            : _key(key), _manip(manip), _mapSRS(mapSRS)// key=j
        {
            // Set up a list of control points
            const SpatialReference* srs = SpatialReference::get("wgs84");
            _points.push_back(GeoPoint(srs, -120, 30, 0));
            _points.push_back(GeoPoint(srs, -100, 45, 0));
        }

        bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
        {
            if (ea.getEventType() == ea.KEYDOWN && ea.getKey() == _key)
            {
				// fitter 创建适合摄影机视图平截头体的视点,以尽可能紧密地包围一组地理空间点。
                ViewFitter fitter(_mapSRS, aa.asView()->getCamera());
                fitter.setBuffer( 100000.0 );
                Viewpoint vp;
                if (fitter.createViewpoint(_points, vp))// 根据_points重新生成一个vp视点
                {
                    _manip->setViewpoint(vp);// 定位到vp视点
                    aa.requestRedraw();
                }
                return true;
            }
            return false;
        }

        void getUsage(osg::ApplicationUsage& usage) const
        {
            using namespace std;
            usage.addKeyboardMouseBinding(string(1, _key), string("FitViewToPoints"));
        }

        char _key;
        osg::ref_ptr<EarthManipulator> _manip;
    };
        

    /**
     * A simple simulator that moves an object around the Earth. We use this to
     * demonstrate/test tethering.
     */
	// 一个简单的模拟器,可以使物体绕地球运动。用来演示跟踪thing
    struct Simulator : public osgGA::GUIEventHandler
    {
        Simulator( osg::Group* root, EarthManipulator* manip, MapNode* mapnode, osg::Node* model, const char* name, char key)
            : _manip(manip), _mapnode(mapnode), _model(model), _name(name), _key(key) // key=8 或 9
        {
            if ( !model )
            { 
				// 模型不存在,则创建一个半球,半径和颜色
                _model = AnnotationUtils::createHemisphere(250.0, osg::Vec4(1,.7,.4,1));
				std::cout << "no model" << std::endl;
            }

            _geo = new GeoPositionNode();
            _geo->getPositionAttitudeTransform()->addChild(_model);

            Style style;
            TextSymbol* text = style.getOrCreate<TextSymbol>();
            text->size() = 32.0f;
            text->declutter() = false;
            text->pixelOffset()->set(50, 50);// 文本偏移像素点
            
            _label = new LabelNode(_name, style);// _name 即要显示的名称
            _label->setDynamic( true );// 因为文本移动,所以此处设置为true
            _label->setHorizonCulling(false);// 不需要裁剪

            _geo->getPositionAttitudeTransform()->addChild(_label);

            mapnode->addChild(_geo.get());
        }

        bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
        {
            if ( ea.getEventType() == ea.FRAME )// 此语段为标签添加了动画
            {
                double t0 = osg::Timer::instance()->time_s();// 儒略日时间
                double t = fmod( t0, 6000.0 ) / 6000.0;// t0%6000/6000, 将数字控制在[0,1)范围内
                GeoPoint p = _start.interpolate(_end, t);// 差值,在_end和t之间
				// GeoMath::bearing 以弧度计算从一个点到下一个点的初始方位
                double bearing = GeoMath::bearing(_start.y(), _start.x(), p.y(), p.x());

                float a = sin(t0*0.2);// a值没有被用
                float pitch = 0.0;

                _geo->setPosition(p);

                _geo->setLocalRotation(
                    osg::Quat(pitch, osg::Vec3d(1, 0, 0)) *
                    osg::Quat(bearing, osg::Vec3d(0, 0, -1)));
            }
            else if ( ea.getEventType() == ea.KEYDOWN )
            {
                if ( ea.getKey() == _key )// 8 或 9
                {                                
                    Viewpoint vp = _manip->getViewpoint();
                    //vp.setNode( _pat.get() );
                    vp.setNode(_model);
                    vp.range() = 25000.0;
                    vp.pitch() = -45.0;
                    _manip->setViewpoint(vp, 2.0);// 2秒内飞到指定位置
                }
                return true;
            }
            return false;
        }

        std::string                        _name;
        char                               _key;
        MapNode*                           _mapnode;
        EarthManipulator*                  _manip;
        GeoPoint                           _start, _end;
        LabelNode*                         _label;
        osg::Node*                         _model;
        float                              _heading;
        float                              _pitch;

        osg::ref_ptr<GeoPositionNode>      _geo;
    };

    /**
     * Place an X at the sim entity position, in screen space.
     * The point of this is to test the EarthManipulator::UpdateCameraCallback
     * which provides a frame-synched camera matrix (post-update traversal)
     */
	// 创建一个十字坐标
    struct CalculateWindowCoords : public osgGA::GUIEventHandler
                                   
    {
        CalculateWindowCoords(char key, EarthManipulator* manip, Simulator* sim)
            : _key(key), _active(false), _sim(sim), _xform(0L)
        {
            //nop
        }

        void onUpdateCamera(const osg::Camera* cam)
        {
            if (_active)
            {
                if (!_xform)
                {
                    osg::Geometry* geom = new osg::Geometry();
                    osg::Vec3Array* verts = new osg::Vec3Array();
                    verts->push_back(osg::Vec3(-10000, 0, 0));
                    verts->push_back(osg::Vec3( 10000, 0, 0));
                    verts->push_back(osg::Vec3( 0, -10000, 0));
                    verts->push_back(osg::Vec3( 0,  10000, 0));
                    verts->push_back(osg::Vec3( 0, 0, -10000));
                    verts->push_back(osg::Vec3( 0, 0,  10000));
                    geom->setVertexArray(verts);
                    osg::Vec4Array* colors = new osg::Vec4Array();
                    colors->push_back(osg::Vec4(1, 1, 0, 1));// 黄色线
                    colors->setBinding(colors->BIND_OVERALL);
                    geom->setColorArray(colors);
                    geom->addPrimitiveSet(new osg::DrawArrays(GL_LINES, 0, 6));// 6个点,绘制3条线,但屏幕上仅能看出2条线,水平和垂直
                    geom->setCullingActive(false);// 不裁剪
                    geom->getOrCreateStateSet()->setAttributeAndModes(new osg::Depth(osg::Depth::ALWAYS, 0, 1, true), 1);//深度设置

                    _xform = new osg::MatrixTransform();
                    _xform->addChild(geom);

                    osg::View* view = const_cast<osg::View*>(cam->getView());
                    ControlCanvas::getOrCreate(view)->addChild(_xform);
                }
				/// 坐标变换 V_clip=M_projection⋅M_view⋅M_model⋅V_local  裁剪坐标=透视矩阵*观察矩阵*模型矩阵*本地坐标——来自OpenGL
                GeoPoint p = _sim->_geo->getPosition();// 获取thing1的坐标点

                osg::Vec3d world;
                p.toWorld(world);// 转化为世界坐标

                osg::Matrix worldToWindow =
                    cam->getViewMatrix() *
                    cam->getProjectionMatrix() *
                    cam->getViewport()->computeWindowMatrix();// 世界坐标转到窗口坐标

                osg::Vec3d win = world * worldToWindow;// 得到窗口坐标

                _xform->setMatrix(osg::Matrix::translate(win));
            }
        }

        bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
        {
            if (ea.getEventType() == ea.KEYDOWN && ea.getKey() == _key)
            {
                _active = !_active;
                aa.requestRedraw();
                return true;
            }
            
            return false;
        }

        void getUsage(osg::ApplicationUsage& usage) const
        {
            using namespace std;
            usage.addKeyboardMouseBinding(string(1, _key), string("Show Window Coords"));
        }

        osg::MatrixTransform* _xform;
        Simulator* _sim;
        bool _active;
        char _key;
    };
	// 更新相机回调
    struct CameraUpdater : public EarthManipulator::UpdateCameraCallback
    {
        CalculateWindowCoords* _calc;

        CameraUpdater(CalculateWindowCoords* calc) : _calc(calc) { }
        
        void onUpdateCamera(const osg::Camera* cam)
        {
            _calc->onUpdateCamera(cam);// 当物体运动时,需要让十字线跟随物体运动
        }
    };
}


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

    if (arguments.read("--help") || argc==1)
    {
        OE_WARN << "Usage: " << argv[0] << " [earthFile] [--model modelToLoad]"
            << std::endl;
        return 0;
    }

    osgViewer::Viewer viewer(arguments);

    // install the programmable manipulator.安装操作器
    EarthManipulator* manip = new EarthManipulator();
    viewer.setCameraManipulator( manip );

    // UI: 创建帮助说明控件,在左上角的位置。当鼠标移动到控件上之后,是无法点击到地球上的,没有鼠标穿透功能。
    Container* help = createHelp(&viewer);
	// MapNodeHelper 显示帮助
	// load() 加载earth映射文件并处理所有内置示例命令行参数和XML外部变量(help)。
    osg::Node* earthNode = MapNodeHelper().load( arguments, &viewer, help );
    if (!earthNode)
    {
        OE_WARN << "Unable to load earth model." << std::endl;
        return -1;
    }

    osg::Group* root = new osg::Group();
    root->addChild( earthNode );
	// 获取mapNode
    osgEarth::MapNode* mapNode = osgEarth::MapNode::findMapNode( earthNode );

    // user model?
	// 加载模型,这里传入osg osgt格式的模型,并没有显示
    osg::ref_ptr<osg::Node> model;
    std::string modelFile;
    if (arguments.read("--model", modelFile))
        model = osgDB::readRefNodeFile(modelFile + ".osgearth_shadergen");

    osg::Group* sims = new osg::Group();
    root->addChild( sims );

    const SpatialReference* wgs84 = SpatialReference::get("wgs84");// 坐标系

    // Simulator for tethering: 系留模拟器
	// thing 1 和 thing 2 会随着时间移动
    Simulator* sim1 = new Simulator(sims, manip, mapNode, model.get(), "Thing 1", '8');// 8:绑住第一个物体
	sim1->_name = "Thing 1";
    sim1->_start = GeoPoint(wgs84, 45.0, 55.0, 10000);// 开始坐标点
    sim1->_end = GeoPoint(wgs84, -45, -55.0, 10000);  // 结束坐标点
    viewer.addEventHandler(sim1);	// 加入到操作器

    Simulator* sim2 = new Simulator(sims, manip, mapNode, model.get(), "Thing 2", '9');// 9:绑住第二个物体
    sim2->_name = "Thing 2";
    sim2->_start = GeoPoint(wgs84, 45.0, 54.0, 10000);
    sim2->_end = GeoPoint(wgs84, -44.0, -54.0, 10000);
    viewer.addEventHandler(sim2);

    manip->getSettings()->getBreakTetherActions().push_back( EarthManipulator::ACTION_GOTO );  // 操作器动作,直接过去  

    // Set the minimum distance to something larger than the default
	// 距离物体小于最小距离时,物体会变大
    manip->getSettings()->setMinMaxDistance(10.0, manip->getSettings()->getMaxDistance());

    // Sets the maximum focal point offsets (usually for tethering)
    manip->getSettings()->setMaxOffset(5000.0, 5000.0);
    
    // Pitch limits. 俯仰角范围
    manip->getSettings()->setMinMaxPitch(-90, 90);


    viewer.setSceneData( root );
	//为 鼠标左键按下、修改键、拖动鼠标的动作指定行为。
    manip->getSettings()->bindMouse(
        EarthManipulator::ACTION_EARTH_DRAG,
        osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON,
        osgGA::GUIEventAdapter::MODKEY_SHIFT );

    manip->getSettings()->bindMouseClick(
        EarthManipulator::ACTION_GOTO,
        osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON,
        osgGA::GUIEventAdapter::MODKEY_SHIFT);

    manip->getSettings()->setArcViewpointTransitions( true );    
	// 设置tether连接和断裂时的动作回调
    manip->setTetherCallback( new TetherCB() );
    
    //viewer.addEventHandler(new FlyToViewpointHandler( manip ));
	// 添加 自定义的各个事件处理
    viewer.addEventHandler(new LockAzimuthHandler('u', manip));
    viewer.addEventHandler(new ToggleArcViewpointTransitionsHandler('a', manip));
    viewer.addEventHandler(new ToggleThrowingHandler('q', manip));
    viewer.addEventHandler(new ToggleCollisionHandler('k', manip));
    viewer.addEventHandler(new ToggleProjMatrix('o', manip));
    viewer.addEventHandler(new BreakTetherHandler('b', manip));
    viewer.addEventHandler(new CycleTetherMode('t', manip));
    viewer.addEventHandler(new SetPositionOffset(manip));// 设置偏移
    viewer.addEventHandler(new ToggleLDB('L'));
    viewer.addEventHandler(new ToggleSSL(sims, ')'));
    viewer.addEventHandler(new FitViewToPoints('j', manip, mapNode->getMapSRS()));
    
	// 根据 thing 1 的位置,更新相机
    CalculateWindowCoords* calc = new CalculateWindowCoords('W', manip, sim1);// 根据thing 1计算窗口坐标
    viewer.addEventHandler(calc);
    manip->setUpdateCameraCallback(new CameraUpdater(calc));// 相机回调

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

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

        // simulate slow frame rate
        //OpenThreads::Thread::microSleep(1000*1000);
    }
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/44971.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

传奇单机架设登录器配置教程

传奇单机顾名思义就是在本地电脑上架设传奇&#xff0c;限制同一个局域网才能一起玩&#xff0c;我接触到几个朋友不明白外网和单机的区别 架设单机需要准备以下程序&#xff1a; 传奇服务端&#xff08;版本Mirserver&#xff09; DBC2000 (百度可直接下载&#xff09; 配套登…

IPv6进阶:IPv6 过渡技术之 GRE 隧道

实验拓扑 R1-R3-R2之间的网络为IPv4环境PC1及PC2处于IPv6孤岛 实验需求 R1及R2为IPv6/IPv4双栈设备在R1及R2上部署GRE隧道使得PC1及PC2能够互相访问&#xff08;先采用IPv6静态路由实现互通&#xff09;R1及R2基于建立好的GRE隧道运行OSPFv3交互IPv6路由前缀 实验步骤及配置…

【数据可视化】免费开源BI工具 DataEase 之 Tab 组件前世今生

小D &#xff1a;小助理&#xff0c;小助理&#xff0c;在哪儿呢&#xff1f;&#xff08;焦急脸&#xff09; BI 小助理&#xff1a;在呢&#xff0c;啥事&#xff1f;&#xff08;不耐烦脸&#xff09; 小D &#xff1a;又有 BI 工具放大招啦&#xff01;&#xff01;&…

C语言习题练习11--指针

1.代码结果 #include <stdio.h> int main() {int arr[] {1,2,3,4,5};short *p (short*)arr;int i 0;for(i0; i<4; i){*(pi) 0;}for(i0; i<5; i){printf("%d ", arr[i]);}return 0; } 正常&#xff1a;0001--00 02--00 03--00 04--00 05 数组内部是倒…

Kotlin高仿微信-第2篇-登录

Kotlin高仿微信-项目实践58篇详细讲解了各个功能点&#xff0c;包括&#xff1a;注册、登录、主页、单聊(文本、表情、语音、图片、小视频、视频通话、语音通话、红包、转账)、群聊、个人信息、朋友圈、支付服务、扫一扫、搜索好友、添加好友、开通VIP等众多功能。 Kotlin高仿…

【安卓逆向】去除云注入(使用MT论坛dl的方法总结拓展)

1 需求 因为最近使用的虚拟机突然不能用了&#xff0c;被人云注入强制弹窗&#xff0c;如下图&#xff1a;&#xff08;这一看就是云注入了&#xff09; 2 大佬的方法 如图&#xff08;MT大佬分享的&#xff0c;感兴趣的朋友可以去大佬主页看看他其他文章&#xff09;&…

把backtrader改造成金融强化学习回测引擎

原创文章第119篇&#xff0c;专注“个人成长与财富自由、世界运作的逻辑&#xff0c; AI量化投资”。 继续强化学习应用于金融投资。 我们的AI量化平台&#xff0c;针对传统规则量化策略&#xff0c;进行了“积木式”的拆分&#xff0c;这种拆分的好处&#xff0c;就是最大化…

【三维目标检测】Part-A2(二)

PartA2数据和源码配置调试过程请参考上一篇博文&#xff1a;【三维目标检测】Part-A2&#xff08;一&#xff09;_Coding的叶子的博客-CSDN博客。本文主要详细介绍PartA2网络结构及其运行中间状态。 1 PointRCNN模型总体过程 Part-A2的整体结构如下图所示&#xff0c;主要包括…

据2019年中国社交电商行业研究报告称,电商正处于更新换代的时期

引言&#xff1a; 据艾瑞咨询《2019年中国社交电商行业研究报告》示,传统主流电商平台用户与交易规模增速均呈现逐渐放缓的趋势,平台亟需找到更高效、低价、高粘性的流量来源,来跳出竞争日益激烈获客成本持续攀升的困境。移动互联网时代,微信、QQ、微博、快手、抖音等社交类AP…

Wordpress模板主题中functions.php常用功能代码与常用插件(持续收集整理)

用Wordpress建站的初学者一定会需要用到的Wordpress模板主题中functions.php常用功能代码与常用插件。慢慢持续收集整理....... 目录 一、Wordpress模板主题中functions文件常用的代码 二、Wordpress自定义字段的设定与调用代码&#xff08;系统常规自定义字段&#xff09; …

【面试宝典】Java八股文之Dubbo 面试题

Dubbo 面试题1、为什么要用 Dubbo?2、Dubbo 的整体架构设计有哪些分层?3、默认使用的是什么通信框架&#xff0c;还有别的选择吗?4、服务调用是阻塞的吗?5、一般使用什么注册中心?还有别的选择吗?6、默认使用什么序列化框架&#xff0c;你知道的还有哪些?7、服务提供者能…

下沉市场投资热度提升 7天酒店打造酒店投资“极致性价比”

近日&#xff0c;7天酒店 “总裁面对面”酒店投资云沙龙活动举办&#xff0c;通过微信、抖音双平台联合直播&#xff0c;多维度探讨酒店行业的“新蓝海”机遇以及下沉市场的投资模式&#xff0c;助力更多投资人把握新的市场红利。 经济型酒店拥抱“新蓝海” 下沉市场投资热度提…

【学习笔记60】JavaScript原型链的理解

一、万物皆对象 JS中, 万物都可以都可以称为对象 1、对象概念 含义1: 一种数据格式 {key: value, key2: value2}含义2: 某一类事务的实例(某一类内容中的真实个体) 2、说明 arr1就是Array这一类内容中的某一个真实个体数组也可以算作一个对象(Array 这一类事务中的一个个体) …

天宇优配|酒企没借壳!标准股份股价上演A杀,两跌停

11月28日晚间&#xff0c;接连三日大跌的规范股份&#xff08;600302.SZ&#xff09;发布股价异动公告&#xff0c;再次否定了借壳和重组风闻。当日龙虎榜该股获净卖出774.89万元&#xff0c;闻名游资“赵老哥”常用席位中国银河绍兴现身卖一席位。另外&#xff0c;也有多家本地…

五笔会消亡吗

今天第一次看到“五笔会消亡”的说法。一看好像也没有什么消不消亡的说法&#xff0c;但是深入想一想好像的确是有一个现象90 后 00后使用五笔的应该会少很多&#xff0c;可能用的非常少。 从五笔与拼音在百度的搜索比例也可以看出&#xff0c;的确在2015~2016年间有了转折&am…

【Hack The Box】linux练习-- Paper

HTB 学习笔记 【Hack The Box】linux练习-- Paper &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月27日&#x1f334; &#x1f36…

第一期 微信云开发小程序介绍-生活智打卡

目录 1.项目介绍 1.1 开发背景 1.2 项目简介 1.2.1 雏形 1.2.2 现状 1.2.3 展望 1.3 市场分析 1.3.1 目标用户 1.3.2 市场需求分析 1.4 系统需求 1.5 竞品分析 2.产品设计 2.1产品功能 2.1.1 智打卡 2.1.2 发现 2.1.2 我的 2.2交互设计 2.2.1 智打卡流程 2.2…

BP神经网络的梯度公式推导(三层结构)

本站原创文章&#xff0c;转载请说明来自《老饼讲解-BP神经网络》bp.bbbdata.com目录 一. 推导目标 1.1 梯度公式目标 1.2 本文梯度公式目标 二. 网络表达式梳理 2.1 梳理三层BP神经网络的网络表达式 三. 三层BP神经网络梯度推导过程 3.1 简化推导目标 3.2 输出层权重…

模拟电路(详细版)--放大电路的频率效应(RC电路)

一、高通电路 1.1传输特性 AuA_uAu​ RR1jωC\frac{R}{R \frac {1} { j \omega C}}RjωC1​R​   &#xff08;补充知识&#xff1a;j是复数域中的一个旋转因子&#xff09; 详细求解思路&#xff1a;   求解AuA_uAu​就是要求输入与输出的关系。 所以AuA_uAu​ U˙oU˙…

门店数字化转型| 美容院管理系统

随着互联网信息的高速发展&#xff0c;行业数字化进程加快&#xff0c;传统美容院面临几个难题。 1、竞争激烈、拓客困难。 美业巨头迅速扩张积压中小型门店生存空间。大多数中小美容院仍旧停留在发传单、口口相传的传统渠道&#xff0c;辐射范围非常有限。 2、投资周期长、资…