美文网首页
第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节 实例-写个简单的操作器

    缘由应四川的群友:挑战高起点 的要求,我给大家写一个最简单的操作器,读完本文以最大程度让读者掌握在OSG中写个操...

  • (第四季)Vue实例和内置组件

    实例入门-实例属性 概述:实例就是在构造器外部操作构造器内部的属性选项或者方法,就叫做实例?实例的作用就是给原生的...

  • #hello,JS:13-03如何搭建服务器

    (一)实例一:写一个简单的服务器 一、nodejs写服务器 1、下载github代码 2、开始操作: (1)分析项...

  • Socket for android 简单实例

    Socket for android 简单实例 最近在实现socket通信,所以写个demo来简单实现下。我用了一...

  • 实例属性(24)

    实例就是在构造器外部操作构造器内部的属性选项或者方法,就叫做实例?实例的作用就是给原生的或者其他javascrip...

  • Flutter - 如何使用Canvas绘图!

    这里先写个简单的实例,具体内容后面补上:先上图 一些处理图片的方法

  • 手工部署Docker(CentOS 7.5)华为云弹性云服务器示

    简介 本文介绍了如何在华为云上使用弹性云服务器的Linux实例部署Docker,并提供了Docker常用操作及简单...

  • Redis集群搭建的三种方式

    一、单节点实例 单节点实例还是比较简单的,平时做个测试,写个小程序如果需要用到缓存的话,启动一个 Redis 还是...

  • 实例的方法

    学习于技术胖的个人网站 实例就是在构造器外部操作构造器内部的属性选项或者方法,就叫做实例。 $mount方法 $m...

  • 刘铁猛C#(11)操作符详解(下)

    New 操作符 不仅是个关键字,也是操作符,用于创建实例,是实例构造器的操作符。 1)new 可以创建一个类型的实...

网友评论

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

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