这里写目录标题

  • 功能
  • 原理
    • 视点
    • 投影
    • 鼠标滚轮
    • 旋转
    • 平移
      • 全部代码:

功能

本节模拟一个AutoCAD的操作器,发现有很多网友是在使用OSG做CAD相关的操作,问题了很多关于使用正交操作器的问题。本文功能如下:

  • 绘制一个线框当背景
  • 正交投影
  • 鼠标滚轮向上向下,可以放大缩小线框
  • 点击a/d键向视点向左/右移动
  • 点击q键逆时针旋转线框

本节的内容在网盘中:

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

原理

视点

首先我们要明白,视点位置与投影是两码事。视点的位置由三个要素决定,_eye(眼睛的位置),_center(你在往哪个点看),_up(你头顶指向哪里,可以想象你歪脖子就是改变了up)

而投影就不同了,投影是你怎么看这个世界,人的眼睛是近大远小的透视投影,我们就不说了。本文要使用的投影要做正交2D投影,就是眼睛位置确定之后,只取眼睛左-右,下-上,这么大的范围的数据,且没有近大远小,这个范围内的全部叠加在一起。

想象一个从视点出发无限长的方形的管道,管道里的所有物体叠于一个面上,那就是正交投影显示的结果。当然正交投影也可以设置远-近,就是不是无限长的方形管道。此处不做过多说明。不设置默认是无限长的。

本文操作器我们仍然做了如下定义:

     _center = osg::Vec3(0.0, 0.0, 0.0);_up = osg::Vec3(0.0, 0.0, 1.0);_eye = osg::Vec3(0.0, -10.0, 0.0);

也就是默认的情况下我们站在y轴的-10的位置,看向原点。在这里我们要有空间的概念,x轴向屏幕右,y轴向屏幕里,z轴向屏幕上。

可以认为当前我们站在离屏幕10的距离朝向屏幕看,且头顶朝向屏幕的上方。如下图所示,x是屏幕右,z是屏幕上,狮子所在的位置是 负y轴的方向,也就是y轴朝向屏幕里。

投影

对于正交投影来说,参数特别的简单,就是left, right, bottom, top,这个是相对于视点来说的,想象视点在管道中间(0, 0)点, left就是管道左壁,right就是右壁,bottom就是下壁,top就是上壁。

因为我们绘制的网格是在xz平面上,1米划一条线,总共划20条,从(0, 0)开始划到(19, 19),视点又在(0, 0)点,因此我们把left, right, top, bottom这么设置:

     _l = -2.0;_r = 2.0;_b = -2.0;_t = 2.0;

会只看到两个网格:

鼠标滚轮

滚轮事件的处理是最简单的,我们只需要重新定义left, right, top, bottom的范围即可。让其更大或者更小。

旋转

旋转其实就是改变up的方向,我们定义了_theta,每点一次q我们就把其加1。然后将初始up = osg::Vec3(0.0, 0.0, 1.0) 绕y轴旋转_theta即可。

平移

本例只处理了a左移与d右移,当不旋转的时候,视点左移_deltaEye=0.3这么远。当鼠标滚轮时,因为视场变大了_deltaEye也会跟着left, right一起变大变小。这个不难理解。

假如我们不旋转,我们只点a向左平移是这样的:

我们只需要将eye的位置,沿x方向减去deltaEye就可以了。这就是我注释的代码:
//_eye += osg::Vec3(-_deltaEye, 0.0, 0.0);

那么现在旋转了,情况复杂了,当我们点击q的时候,旋转_theta度,假设在0~90度内,情况变这样了:

上图粗线是要行走的_deltaEye,旋转角度_theta后,红线画的是其在x方向上与y方向上的变化,在0~90度内,sin与cos都是正的,因此新eye的位置为:

_eye += osg::Vec3(-_deltaEye*std::cos(osg::inDegrees(_theta)), 0.0, _deltaEye*std::sin(osg::inDegrees(_theta)));

eye一改,center也要改

_center = osg::Vec3(_eye.x(), 0.0, _eye.z());

也就是永远站在屏幕外,看屏幕里,你怎么移都是这样。因为y值没有变化,eye的y永远是-10,永远看向y=0

其它的情况读者自己推吧,旋转[90, 360]的时候公式还一样吗?以及w向上s向下用户都可以自己写。或者鼠标拖动等,用户都可以自己写。

全部代码:

#include <osgViewer/Viewer>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/PrimitiveSet>
#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);_eye = osg::Vec3(0.0, -10.0, 0.0);_deltaEye = 0.3;_l = -2.0;_r = 2.0;_b = -2.0;_t = 2.0;}//这三个纯虚函数本例不会使用virtual void setByMatrix(const osg::Matrixd& matrix) {};virtual void setByInverseMatrix(const osg::Matrixd& matrix) {};virtual osg::Matrixd getMatrix() const { return osg::Matrix::identity(); };//最关键的是这个,这个返回的就是ViewMatrixvirtual 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::FRAME){us.asView()->getCamera()->setProjectionMatrixAsOrtho2D(_l, _r, _b, _t);}if (ea.getEventType() == osgGA::GUIEventAdapter::SCROLL){//判断滚动的方向osgGA::GUIEventAdapter::ScrollingMotion sm = ea.getScrollingMotion();if (sm == osgGA::GUIEventAdapter::SCROLL_DOWN){_deltaEye *= 1.1;_l *= 1.1;_t *= 1.1;_b *= 1.1;_r *= 1.1;}else{//范围变小0.1,正好与变大相反,左右同时往里缩_deltaEye *= 0.9;_l *= 0.9;_t *= 0.9;_b *= 0.9;_r *= 0.9;}}if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN){//旋转视角if ((ea.getKey() == 'Q') || (ea.getKey() == 'q')){//点q变1度_theta += 1;_up = osg::Vec3(0.0, 0.0, 1.0)*osg::Matrix::rotate(osg::inDegrees(_theta), osg::Y_AXIS);}//若是A键if ((ea.getKey() == 'A') || (ea.getKey() == 'a')){//_eye += osg::Vec3(-_deltaEye, 0.0, 0.0);_eye += osg::Vec3(-_deltaEye*std::cos(osg::inDegrees(_theta)), 0.0, _deltaEye*std::sin(osg::inDegrees(_theta)));_center = osg::Vec3(_eye.x(), 0.0, _eye.z());}if ((ea.getKey() == 'D') || (ea.getKey() == 'd')){//_eye += osg::Vec3(_deltaEye, 0.0, 0.0);_eye -= osg::Vec3(-_deltaEye*std::cos(osg::inDegrees(_theta)), 0.0, _deltaEye*std::sin(osg::inDegrees(_theta)));_center = osg::Vec3(_eye.x(), 0.0, _eye.z());}}return false;}//视点位置osg::Vec3d              _eye;//点击鼠标a键向左移的量度,随着鼠标滚轮的放大缩小,这个量度也在变化double _deltaEye; //视点看向哪里osg::Vec3d              _center;//头顶的朝向osg::Vec3d              _up;//视点看向0 0 0的角度float              _theta;//二维投影参数, left, right, bottom, topdouble _l, _r, _b, _t;
};osg::Geode* createNet()
{osg::Geode* gnode = new osg::Geode;osg::Geometry* geom = new osg::Geometry;gnode->addDrawable(geom);//设置线的颜色为白色osg::Vec4Array* color = new osg::Vec4Array;color->push_back(osg::Vec4(1.0, 1.0, 1.0, 1.0));geom->setColorArray(color, osg::Array::BIND_OVERALL);osg::Vec3Array* vertex = new osg::Vec3Array;geom->setVertexArray(vertex);//间隔1米,横20条,竖20条for (int i = 0; i < 20; i++){//x方向vertex->push_back(osg::Vec3(0, 0, i));vertex->push_back(osg::Vec3(17, 0, i)); //设置为17,不画满//z方向vertex->push_back(osg::Vec3(i, 0, 0));vertex->push_back(osg::Vec3(i, 0, 17));}geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, vertex->size()));geom->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);return gnode;
}int main()
{osgViewer::Viewer viewer;viewer.setSceneData(createNet());viewer.setCameraManipulator(new MyCameraManipulator());return viewer.run();
}

第31节 AutoCAD操作器-正交操作器相关推荐

  1. Qt实用技巧:使用OpenCV库的视频播放器(支持播放器操作,如暂停、恢复、停止、时间、进度条拽托等...

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 需求 使用OpenCV库的视频播放器(支持播放器操作,如暂停.恢复 ...

  2. 第一百二十六节,JavaScript,XPath操作xml节点

    第一百二十六节,JavaScript,XPath操作xml节点 学习要点: 1.IE中的XPath 2.W3C中的XPath 3.XPath跨浏览器兼容 XPath是一种节点查找手段,对比之前使用标准 ...

  3. jQuery对象,jQuery查找标签,层级选择器,属性选择器,表单筛选器,操作节点标签事件...

    目录 jQuery jQuery介绍 jQuery的优势 jQuery版本 jQuery内容: jQuery对象 jQuery基础语法 查找标签 基本选择器 层级选择器: 基本筛选器: 属性选择器 表 ...

  4. 【Android RTMP】RTMPDump 推流过程 ( 独立线程推流 | 创建推流器 | 初始化操作 | 设置推流地址 | 启用写出 | 连接 RTMP 服务器 | 发送 RTMP 数据包 )

    文章目录 安卓直播推流专栏博客总结 一. Java 层传入的 RTMP 推流地址处理 二. RTMPDump 推流线程 三. 创建 RTMP 对象 四. 初始化 RTMP 对象 五. 设置 RTMP ...

  5. eja变送器故障代码al01_EJA110A差压变送器的操作使用和故障处理

    摘要:为了方便掌握差压变送器的操作使用及在实际应用中遇到的故障,本文介绍了EJA110A差压变送器的结构原理和操作方法,并分析了EJA110A差压变送器在使用中出现的各种故障都与被测流体.运行环境等外 ...

  6. 视采网站采集器用户操作手册

    文章来源:视采网站采集器 1 产品简介 DM视采网站采集器是一款可视化的数据挖掘软件,它可用于网站采集.论坛采集.文章采集.博客采集.dedecms采集.动易采集.新云采集.论坛发帖.论坛顶贴等. 2 ...

  7. mysql 数据拦截器_拦截器中操作数据库

    做了个小项目,当初设计的是只有一个模块的用户行为被记录,其他不用记录,昨天突然说,要用户在整个系统的行为都要被记录. 很懵逼,如果把用户行为的记录放在各个模块,可以很精确的记录,但是各个模块都要有更改 ...

  8. 《CCNP ROUTE 300-101学习指南》——1.4节路由和TCP/IP操作

    本节书摘来自异步社区<CCNP ROUTE 300-101学习指南>一书中的第1章,第1.4节路由和TCP/IP操作,作者 [美]戴安娜 蒂尔(Diane Teare) , 鲍勃 瓦尚(B ...

  9. 一篇文章汇总Python装饰器全知识图谱(使用场景,基本用法,参数传递,闭包操作,类装饰器和AOP)

    装饰器,是将Python代码变得低耦合,简洁优美的必经之路,同时也是实现闭包操作,AOP编程的基础.这一篇博客从装饰器的产生原因,基本使用,延伸到参数传递,闭包操作,最后到类装饰器和AOP,希望能用我 ...

最新文章

  1. C++中类的大小问题
  2. mysql 打开文件数_MySQL打开的文件描述符限制
  3. 自学python推荐书籍2019-2019年Python入门书籍推荐
  4. 性能测试关注点整理总结
  5. mysql 链式查询_MySQL的链接查询
  6. Android camera (12)---camera ap在特殊的应用场景下额外使用一套独立的camera tuning参数
  7. git 查看、创建、切换、删除、重命名和推送分支
  8. python没基础能自学吗-没有基础先要自学python,有什么比较好的书推荐?
  9. 关于UTF-8的处理方法心得
  10. NIM(Network Installation Manager)使用一例(mksysb)
  11. git下载出错GnuTLS recv error (-54): Error in the pull function
  12. 期望之后的失望-小评侯捷的《Word排版艺术》
  13. vue引入高德地图获取经纬度地址
  14. nmos导通流向_讨论一下:用NMOS还是PMOS关断好? - 模拟与混合信号 - 电子工程世界-论坛 - 手机版...
  15. 插入法、选择法、冒泡法(C++实现)
  16. Python检测文章抄袭,谈谈去重算法原理
  17. 对话bot语音输入交互竞品调研
  18. 十八、从Django入手
  19. RoboMongo简单安装和操作
  20. 【从零开始玩量化14】如何获取申万行业数据

热门文章

  1. ENSP 静态路由加VLANIF实例
  2. 0708-Pen Brush
  3. TCP 传输控制协议(Transmission Control protocol)
  4. python中模块文件的扩展名一定是py_Python 模块(Module)
  5. odoo postman测试odoo接口
  6. PLC梯形图编程基础知识详解
  7. MathType上下两式子“=“号对齐图文教程
  8. 阿里云ECS发送邮件到腾讯企业邮箱
  9. winform串口通过SCPI协议与数控电源M8811通信
  10. 【通信】【1】幅度调制,频率调制,双边带与单边带,IQ与PSK与QAM——采样一定要满足奈奎斯特定理吗