美文网首页
第08节 实例-写个简单的操作器

第08节 实例-写个简单的操作器

作者: 杨石兴 | 来源:发表于2021-06-14 21:36 被阅读0次

    缘由
    应四川的群友:挑战高起点 的要求,我给大家写一个最简单的操作器,读完本文以最大程度让读者掌握在OSG中写个操作器是咋回事儿。代码在最后一个代码块,直接新建OSG工程,拷贝进去就可以运行。特别感谢这位网友,要不然真的不知道怎么为大家好呀。

    另外如果您也想我来为您就某个功能写个例子,分享给大家,则只需要在本文评论区回复即可。或在本文出现的群里回复。

    本节所有代码在网盘中:

    注意:务必使用浏览器打开:
    链接:https://pan.baidu.com/s/13gwJLwo_LbRnN3Bl2NXXXw
    提取码:xrf5

    功能说明
    首先绘制一个场景如下,场景中间放着模型axes.osgt这个模型,它指明了xyz轴的方向,然后我们在xy方向,从-5~5,间隔0.5绘制网格,如下图所示。

    image.png

    然后我们操作器的功能是:点击a围绕z轴,眼睛看着(0,0,0)点,高度在5,逆时针旋转
    点击d则顺时针旋转。

    实现要点
    1.写个类继承自osgGA::CameraManipulator,就我们的功能而言,有两个函数就可以了,一个是virtual osg::Matrixd getInverseMatrix() const,它要返回camera的ViewMatrix,也就是我说的,Camera会在事件遍历的时候,调用这个函数来设置他的ViewMatrix。所以我们要实现它。

    实现它也很简单,自己写三个变量做成员函数:_eye(人头的位置), _center(人头在往哪看), _up(人头顶的朝向),自己想想自己的头,就能想明白这三个量一定,你在哪朝哪看就定了,接着直接构造ViewMatrix就可以了,代码如下:

        virtual osg::Matrixd getInverseMatrix() const
        {
            return osg::Matrix::lookAt(_eye, _center, _up);
        };
    

    看看简单吧。那么现在的任务就是根据事件要来设置_eye了,因为根据我们的功能设想,_center始终是0,0,0,up始终是0,0,1。

    2._Eye的更新在事件处理中,也就是另一个要实现的虚函数:virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us),我们定义了一个成员变量_theta,点A的时候就把它加一度,点D的时候就减一度。然后立即更新_eye的值,按半径是sqrt(55+55)来算:

        //事件处理,我们要点击A就围着Z轴顺时针转动,点D就逆时针转动,转的时候始终朝0 0 0 点看着
        virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us)
        {
            if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN)
            {
                //若是A键
                if ((ea.getKey() == 'A') || (ea.getKey() == 'a'))
                {
                    _theta += 1.0;
                }
                if ((ea.getKey() == 'D') || (ea.getKey() == 'd'))
                {
                    _theta -= 1.0;
                }
    
                _eye = osg::Vec3(_r * std::cos(osg::DegreesToRadians(_theta)), _r * std::sin(osg::DegreesToRadians(_theta)), 5.0);
            }
            return false;
        }
    

    3.关键代码就是上面那些。要写自己的操作器就是要看看自己按照事件怎么操作_eye,_center,_up这三个值了。多说一句,如果要让相机按着某个路径运动,可以使用AnimationPathManipulator类,它还支持将路径存在文件,然后读起来用。具体用法可以参考:applications/osgviewer/osgviewer.cpp这个程序的具体实现,主要看RecordCameraPathHandler这个类。

    4.最后下面是这个示例的所有代码,大家直接拷贝就可以使用,是完整的,没有依赖的,依赖的axes.osgt是OSG自带的模型。就算读不了来只是轴不显示,也不影响其它的功能。

    #include <osgViewer/viewer>
    #include <osgDB/ReadFile>
    #include <osg/Geode>
    #include <osg/Geometry>
    #include <osgGA/CameraManipulator>
    
    class MyCameraManipulator : public osgGA::CameraManipulator
    {
    public:
        MyCameraManipulator() 
        {
            _theta = 0.0;
            _center = osg::Vec3(0.0, 0.0, 0.0);
            _up = osg::Vec3(0.0, 0.0, 1.0);
            _r = std::sqrt(5 * 5 + 5 * 5);
            _eye = osg::Vec3(_r*std::cos(osg::DegreesToRadians(_theta)), _r * std::sin(osg::DegreesToRadians(_theta)), 5.0);
        }
    
        //这三个纯虚函数本例不会使用
        virtual void setByMatrix(const osg::Matrixd& matrix) {};
        virtual void setByInverseMatrix(const osg::Matrixd& matrix) {};
        virtual osg::Matrixd getMatrix() const { return osg::Matrix::identity(); };
    
        //最关键的是这个,这个返回的就是ViewMatrix
        virtual osg::Matrixd getInverseMatrix() const
        {
            return osg::Matrix::lookAt(_eye, _center, _up);
        };
    
        //事件处理,我们要点击A就围着Z轴顺时针转动,点D就逆时针转动,转的时候始终朝0 0 0 点看着
        virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us)
        {
            if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN)
            {
                //若是A键
                if ((ea.getKey() == 'A') || (ea.getKey() == 'a'))
                {
                    _theta += 1.0;
                }
                if ((ea.getKey() == 'D') || (ea.getKey() == 'd'))
                {
                    _theta -= 1.0;
                }
    
                _eye = osg::Vec3(_r * std::cos(osg::DegreesToRadians(_theta)), _r * std::sin(osg::DegreesToRadians(_theta)), 5.0);
            }
            return false;
        }
    
        //视点位置
        osg::Vec3d              _eye;
        //视点看向哪里
        osg::Vec3d              _center;
        //头顶的朝向
        osg::Vec3d              _up;
    
        //视点看向0 0 0的角度
        float              _theta;
        //视点离0 0 0的距离
        float              _r;
    };
    
    osg::Node* createScene()
    {
        osg::Group* root = new osg::Group();
        root->addChild(osgDB::readNodeFile("axes.osgt"));
    
        osg::Geode* gnode = new osg::Geode;
        root->addChild(gnode);
    
        osg::Geometry* geom = new osg::Geometry;
        gnode->addChild(geom);
    
        osg::Vec3Array* vertex = new osg::Vec3Array;
        geom->setVertexArray(vertex);
        //沿xy平面画线,间隔0.5米,从-5,画到5
        for (float i = -5; i <= 5; i += 0.5)
        {
            vertex->push_back(osg::Vec3(i, -5, 0));
            vertex->push_back(osg::Vec3(i, 5, 0));
            vertex->push_back(osg::Vec3(-5, i, 0));
            vertex->push_back(osg::Vec3(5, i, 0));
        }
        geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, vertex->size()));
    
        osg::Vec4Array* color = new osg::Vec4Array();
        color->push_back(osg::Vec4(0.7, 0.7, 0.7, 1.0));
        geom->setColorArray(color, osg::Array::BIND_OVERALL);
        geom->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
    
        return root;
    }
    
    
    int main()
    {
        osgViewer::Viewer viewer;
    
        viewer.setCameraManipulator(new MyCameraManipulator());
        viewer.setSceneData(createScene());
    
        return viewer.run();
    }
    
    

    相关文章

      网友评论

          本文标题:第08节 实例-写个简单的操作器

          本文链接:https://www.haomeiwen.com/subject/fonoeltx.html