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

缘由

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

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

本节所有代码在网盘中:

【击此打开网盘资源链接】

功能说明
首先绘制一个场景如下,场景中间放着模型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();
}

© 版权声明
THE END
喜欢就支持一下吧
点赞1 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片