OSG人机交互
标签(空格分隔): OSG 键盘 鼠标 事件
Windows与OSG事件传递流程
OSG在Windows平台上的消息传递仍然遵守Windows的规则,底层调用的也仍然是Windows的API。OSG中真正处理事件的函数是handelNativeWindowingEvent
,打开handelNativeWindowingEvent
的定义可以发现待处理事件都是压入事件序列osgGA::EventQueue
中。然后依次取出进行响应。
创建一个osg窗口
首先要解决的是如何使用OSG语句创建窗口以及在什么位置创建窗口。OSG定义了osg::GraphicsContext::Traits
类来决定窗口大小、高宽等参数。通过将该类的一个对象传入到Windows注册窗口的函数中,定义OSG窗口的特征。
代码
#include <osgViewer/api/Win32/graphicswindowwin32>
int main()
{
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
traits->x = 250;
traits->y = 250;
traits->width = 800;
traits->height = 600;
traits->windowDecoration = true;
traits->doubleBuffer = true;
traits->sharedContext = 0;
//执行注册窗口的操作,内部调用了registerdWindowClasses
osgViewer::GraphicsWindowWin32* gw = (osgViewer::GraphicsWindowWin32*)
osg::GraphicsContext::createGraphicsContext(traits);
gw->realizeImplementation();
int a;
std::cin >> a;
return 0;
}
结果为一片白色的windows窗口
键盘和鼠标事件
事件被加入到osgGA::EventQueue
中后,最终由osgGA::GUIEventHandler
类处理,因此程序中我们只要继承osgGA::GUIEventHandler
类,重载handle
函数就可以实现对事件的处理。
osgGA::GUIEventHandler
实际是一个回调函数,在每一帧渲染当中,OSG会遍历所有的回调函数。
osgGA::GUIEventHandler
中的handle
函数为
bool handle(const GUIEventAdapter&,GUIActionAdapter&)
第一个参数为GUIEventAdapter
对象,它是一个事件适配器,里面定义了所有与事件相关的变量,包括键盘码,鼠标状态等。hangle
函数通过这个对象来判断当前要处理事件的类别并获得相关参数。
第二个参数GUIActionAdapter
为一个独立的类,里面包含asView
方法,且视口类View
是它的子类,因此可以在viewer当中进行事件处理。
总的来说就是通过继承osgGA::GUIEventHandler
类,在重载handle
函数的第一个回调参数中判断当前的待处理事件类别,在第二个回调参数中获得当前事件发生的视口,然后进行事件处理。
添加事件处理流程是这样的:从osgGA::GUIEventHandler
公有派生类A并实现handle
函数,然后使用viewer
类的addEventHandler
将其加入到事件处理序列中。
代码
实现的功能是按下键盘上左键时物体向左旋转,按下键盘上右键时物体向右旋转。
#include <osgViewer/viewer>
#include <osgViewer/api/Win32/graphicswindowwin32>
#include <osgViewer/viewerEventHandlers>
#include <osgGA/StateSetManipulator>
#include <osgGA/GUIEventHandler>
#include <osgDB/readfile>
#include <osg/matrixtransform>
class RotateGlider : public osgGA::GUIEventHandler
{
public:
bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
{
osgViewer::Viewer* vw = dynamic_cast<osgViewer::Viewer*>(&aa);
if (vw)
{
osg::MatrixTransform* mt = dynamic_cast<osg::MatrixTransform*>(vw->getSceneData());
if (mt)
{
static float i = 0.1f;
switch (ea.getEventType())
{
case osgGA::GUIEventAdapter::KEYDOWN:
{
if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Left)
{
mt->setMatrix(osg::Matrix::rotate(i, osg::Vec3(0.f, 0.f, 1.f)));
i += 0.0f;
}
else if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Right)
{
mt->setMatrix(osg::Matrix::rotate(i, osg::Vec3(0.f, 0.f, 1.f)));
i -= 0.1f;
}
}
break;
default:
break;
}
}
}
return false;
}
};
int main()
{
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
viewer->setUpViewOnSingleScreen();
osg::ref_ptr<osg::MatrixTransform> mt = new osg::MatrixTransform;
osg::ref_ptr<osg::Node> glider = osgDB::readNodeFile("glider.osg");
mt->addChild(glider);
viewer->setSceneData(mt);
viewer->addEventHandler(new RotateGlider);
return viewer->run();
}
效果
鼠标Pick点选物体
在OSG中,Pick并不是OSG的库函数,需要自己实现。在pick的时候有2个重要参数,即单击时屏幕坐标xy,及这个点发出的垂直于屏幕的射线与场景中物体的交点。判断射线与viewer中物体相交是通过构造鼠标单击的法线并计算法线和场景中物体的交点,通过osgView::View::computeIntersections解决交点计算问题。下面看具体代码
#pragma comment(lib,"osgFXd.lib")
#pragma comment(lib,"osgUtild.lib")
#include <osgFX\scribe>
#include <osgUtil\linesegmentintersector>
class PickHandle : public osgGA::GUIEventHandler
{
public:
PickHandle(osgViewer::Viewer* viewer) :mviewer(viewer) {}
virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
{
switch (ea.getEventType())
{
case(osgGA::GUIEventAdapter::PUSH):
if (ea.getButton() == 1)
{
Pick(ea.getX(), ea.getY());
}
return true;
default:
break;
}
return false;
}
protected:
void Pick(float x, float y)
{
std::multiset< osgUtil::LineSegmentIntersector::Intersection> inters;
if (mviewer->computeIntersections(x,y, inters))
{
for (auto iter = inters.begin(); iter != inters.end(); ++iter)
{
if (!iter->nodePath.empty() && !iter->nodePath.back()->getName().empty())
{
const osg::NodePath& np = iter->nodePath;
for (int i = np.size()-1; i >0; --i)
{
osgFX::Scribe* sc = dynamic_cast<osgFX::Scribe*>(np[i]);
if (sc != nullptr)
{
if (sc->getNodeMask() != 0)
{
sc->setNodeMask(0);
}
}
}
}
}
}
}
private:
osgViewer::Viewer* mviewer;
};
int main()
{
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
viewer->setUpViewOnSingleScreen();
osg::ref_ptr<osg::Group> root = new osg::Group;
osg::ref_ptr<osg::Node> cow = osgDB::readNodeFile("cow.osg");
osg::ref_ptr<osgFX::Scribe> sc = new osgFX::Scribe();
sc->addChild(cow);
root->addChild(cow);
root->addChild(osgDB::readNodeFile("cessna.osg"));
root->addChild(sc);
viewer->setSceneData(root);
viewer->addEventHandler(new PickHandle(viewer));
return viewer->run();
}
效果