osg操纵器解析

转载自:http://lzchenheng.blog.163.com/blog/static/8383353620106710534514/

 osg操纵器是我们实现场景漫游的主要手段,osg提供了驾驶操纵器、飞行操纵器、轨迹球操纵器等,这些操纵器的基类为osgGA::MatrixManipulator,有时为了漫游的需要,我们需要编写自己的操纵器,要想编写好自己的操纵器,必须对osg操纵器的实现方式进行剖分,知道其实现的机理,才能有的放矢,编写一个“听话”的操纵器,下面我们从源代码入手,对操纵器的实现进行剖分。

      设置操纵器的代码为:viewer->setCameraManipulator(),其中setCameraManipulator()的代码如下:

      void View::setCameraManipulator(osgGA::MatrixManipulator* manipulator)
     {
           _cameraManipulator = manipulator;
    
          if (_cameraManipulator.valid())
          {
                    _cameraManipulator->setCoordinateFrameCallback(new ViewerCoordinateFrameCallback(this));

                    //得到场景的根节点

                    if (getSceneData()) _cameraManipulator->setNode(getSceneData());
                    //得到当前事件适配器
                    osg::ref_ptr<osgGA::GUIEventAdapter> dummyEvent = _eventQueue->createEvent();

                    //执行home

                     _cameraManipulator->home(*dummyEvent, *this);
           }
      }

      在这段代码中,首先得到根节点,调用setNode()方法给操纵器设置根节点,然后调用操纵器本身的home方法,osgGA::MatrixManipulator类的home()和setNode()方法为一个虚方法,其实现在其派生类中,我们看一下TrackballManipulator类的实现方式。

      我们先来看一下setNode()方法的代码:

      void TrackballManipulator::setNode(osg::Node* node)
      {
           _node = node;
           if (_node.get())
           {
                //得到根节点包围球的范围

                const osg::BoundingSphere& boundingSphere=_node->getBound();
                 _modelScale = boundingSphere._radius;
           }
           //是否自动计算视点的位置

           if (getAutoComputeHomePosition()) computeHomePosition();
      }  
      该方法主要得到根节点的包围球范围,然后计算视点的位置。

      那么我们来看一下home方法在做什么

       void TrackballManipulator::home(double /*currentTime*/)
       {
              //自动计算

               if (getAutoComputeHomePosition()) computeHomePosition();
               //计算视点的位置

               computePosition(_homeEye, _homeCenter, _homeUp);
              _thrown = false;
       }

       在osgGA::MatrixManipulator类中,相关的参数如下:

       MatrixManipulator::MatrixManipulator()
       {
           _minimumDistance = 0.001;
    
          _intersectTraversalMask = 0xffffffff;

          //自动计算

          _autoComputeHomePosition = true;
          //视点的默认位置
          _homeEye.set(0.0,-1.0,0.0);
          _homeCenter.set(0.0,0.0,0.0);
          _homeUp.set(0.0,0.0,1.0);
     }

     由于_autoComputeHomePosition = true,所以,首先执行 computeHomePosition()方法,该方法在MatrixManipulator类中实现如下:

        virtual void computeHomePosition()
        {
            if(getNode())  //如果根节点存在
            {
               //计算根节点的包围球

               const osg::BoundingSphere& boundingSphere=getNode()->getBound();

               //根据根节点包围球的大小,设置视点位置

                setHomePosition(boundingSphere._center+osg::Vec3( 0.0,-3.5f * boundingSphere._radius,0.0f),
                                boundingSphere._center,
                                osg::Vec3(0.0f,0.0f,1.0f),
                                _autoComputeHomePosition);
            }
        }

        setHomePosition()方法的实现如下:

         virtual void setHomePosition(const osg::Vec3d& eye, const osg::Vec3d& center, const osg::Vec3d& up,

                                                          bool autoComputeHomePosition=false)
            {
                  setAutoComputeHomePosition(autoComputeHomePosition);
                  _homeEye = eye;
                  _homeCenter = center;
                  _homeUp = up;
            }
            下面我们再看一下computePosition()方法的实现:

            void TrackballManipulator::computePosition(const osg::Vec3& eye,const osg::Vec3& center,const osg::Vec3& up)
            {

                 //生成观察坐标系

                 osg::Vec3 lv(center-eye);

                  osg::Vec3 f(lv);
                  f.normalize();
                  osg::Vec3 s(f^up);
                  s.normalize();
                  osg::Vec3 u(s^f);
                  u.normalize();
                  //建立观察旋转矩阵
                  osg::Matrix rotation_matrix(  s[0],     u[0],     -f[0],     0.0f,
                                                           s[1],     u[1],     -f[1],     0.0f,
                                                           s[2],     u[2],     -f[2],     0.0f,
                                                            0.0f,     0.0f,     0.0f,      1.0f);
                   
                  _center = center;
                 _distance = lv.length();
                _rotation = rotation_matrix.getRotate().inverse();
           }
           从以上的代码可以看出,要编写一个好的操纵器,必须首先重载setNode()和home()方法,根据根节点的包围球,确定视点的初始位置,然后,根据视点的初始位置和用户的操作(移动、旋转等操作),重载getInverseMatrix()和getMatrix()方法,构建观察矩阵或物体的位置姿态矩阵,这两个矩阵互为逆矩阵。

          场景在更新遍历时,不再调用操纵器的setCameraManipulator()方法,该方法只能在初始时设置,场景在每次更新遍历时,只调用getInverseMatrix()方法获取观察矩阵,更新相机的姿态、位置,实现场景的漫游,如下代码所示:

void Viewer::updateTraversal()
{
    ...........

      if (_cameraManipulator.valid())
     {
            setFusionDistance( getCameraManipulator()->getFusionDistanceMode(),
                            getCameraManipulator()->getFusionDistanceValue() );

            //得到操纵器的观察矩阵,更新相机

            _camera->setViewMatrix(_cameraManipulator->getInverseMatrix());
      }

    ...............................

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值