首先祝大家国庆快乐
本节内容
本节开始介绍鼠标选择的操作。正常情况下我们都会在场景中使用鼠标框选一些物体。本节我们绘制了框选框,程序启动后,按住键盘上的左CTRL键,然后鼠标在场景中拖动,会出来一个白色的框,符合框选的习惯。如下图所示:
本节代码在如下网盘中,根据章节编号有对应附件,请使用浏览器打开,平时遇到问题或加群也可以加我微信:13324598743:
【击此打开网盘资源链接】
实现要点
要点只有两个,其它均为正常的绘制和逻辑处理:
- 在点下左CTRL的时候,鼠标拖动时要在框选的状态,此时场景的操作器就不起作用了。不能一边选着,场景随着鼠标的拖动还转着,那就不行。此处只需要在事件中返回true,后面的事件处理器就不再处理该事件了。场景也就不转动了 这是个很隐蔽但是又很重要的知识点。具体实现参见MyEvent中的handle,注释较为详尽。
- 在屏幕表面绘制一个白色的框要用到HUD,HUD的初始化需要知道当前场景的窗口是多大,而当前的窗口是在viewer.realize之后才创建的,因此需要在viewer.realize之后获取viewport参数之后,再创建HUD中的相机,设置setProjectionMatrix。
以下为全部代码
#include <osgViewer/Viewer>
#include <osgDB/ReadFile>
#include <osg/Camera>
#include <osgGA/GUIEventHandler>
//结点的掩码,显示与隐藏
#define NODE_SHOW ~0x0
#define NODE_HIDE 0x0
//选择框做为一个全局变量,使用起来方便
osg::Geometry* g_geomSelectBox = new osg::Geometry;
//
osg::Camera* createHUD(osg::Viewport* vp)
{
osg::Camera* camera = new osg::Camera;
//设置投影矩阵为正交投影
camera->setProjectionMatrix(osg::Matrix::ortho2D(0, vp->width(), 0, vp->height()));
//设置其观察矩阵为单位矩阵,且不改变,该相机永远显示,也不用参与拣选
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
camera->setViewMatrix(osg::Matrix::identity());
//只清空深度缓存,使得其显示内容可以以主相机为背景
camera->setClearMask(GL_DEPTH_BUFFER_BIT);
//最后渲染,因为需要以主相机显示的内容为背景
camera->setRenderOrder(osg::Camera::POST_RENDER);
//不需要响应事件
camera->setAllowEventFocus(false);
//绘制选择框
osg::Geode* gnode = new osg::Geode;
camera->addChild(gnode);
gnode->addDrawable(g_geomSelectBox);
//设置透明
osg::StateSet* ss = gnode->getOrCreateStateSet();
ss->setMode(GL_BLEND, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
//设置顶点
osg::Vec3Array* vertices = new osg::Vec3Array;
float depth = - 0.1;
vertices->push_back(osg::Vec3(0, 0, depth));
vertices->push_back(osg::Vec3(100, 0, depth));
vertices->push_back(osg::Vec3(100, 100, depth));
vertices->push_back(osg::Vec3(0, 100, depth));
g_geomSelectBox->setVertexArray(vertices);
//设置颜色
osg::Vec4Array* color = new osg::Vec4Array;
color->push_back(osg::Vec4(0.8, 0.8, 0.8, 0.2));
g_geomSelectBox->setColorArray(color, osg::Array::BIND_OVERALL);
//绘制盒子
g_geomSelectBox->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4));
return camera;
}
class MyEvent : public osgGA::GUIEventHandler
{
public:
MyEvent() :osgGA::GUIEventHandler(),
_xStart(0),
_yStart(0)
{
}
virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
{
//左ctrl按着,又鼠标点击,则进入绘制状态
if (ea.getEventType() == ea.PUSH) //
{
//判断左CTRL键是否按下
if (ea.getModKeyMask() == ea.MODKEY_LEFT_CTRL)
{
_xStart = ea.getX();
_yStart = ea.getY();
//清空之前绘制的结果,这里仅隐藏即可
g_geomSelectBox->setNodeMask(NODE_HIDE);
//返回真代表后续事件处理器包括操作器不再处理该事件,就实现了拖动时场景不动
return true;
}
}
//左ctrl点下时,进入到选择状态,开始绘制选择框,且操作器不再处理鼠标拖动事件
if (ea.getEventType() == ea.DRAG) //鼠标拖动,拖动是鼠标按键按下的时候移动鼠标
{
//判断左CTRL键是否按下
if (ea.getModKeyMask() == ea.MODKEY_LEFT_CTRL)
{
//开始绘制,调整顶点参数就可以
g_geomSelectBox->setNodeMask(NODE_SHOW);
//获取顶点、更新顶点
osg::Vec3Array* vertices = (osg::Vec3Array*)g_geomSelectBox->getVertexArray();
int xEnd = ea.getX();
int yEnd = ea.getY();
float depth = -0.1;
vertices->at(0).set(_xStart, _yStart, depth);
vertices->at(1).set(xEnd, _yStart, depth);
vertices->at(2).set(xEnd, yEnd, depth);
vertices->at(3).set(_xStart, yEnd, depth);
//重绘
g_geomSelectBox->dirtyDisplayList();
//返回真代表后续事件处理器包括操作器不再处理该事件,就实现了拖动时场景不动
return true;
}
}
return false;
}
int _xStart, _yStart;
};
int main()
{
osgViewer::Viewer viewer;
osg::Group* root = new osg::Group;
root->addChild(osgDB::readNodeFile("cow.osg"));
viewer.setSceneData(root);
viewer.realize();
//realize之后,上下文已经被初始化,可以获得视口大小
//在此处获得视口大小是为了创建HUD时使其视口大小与创建的一致
osg::Viewport* vp = viewer.getCamera()->getViewport();
root->addChild(createHUD(vp));
viewer.addEventHandler(new MyEvent);
return viewer.run();
}
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
暂无评论内容