本文以第三课MFC+OSG框架为基础进行讨论。参考CWorld《基于MFC(QT)的OSG的自定义事件》
source下载链接
首先找到资源视图,添加菜单,如果没有资源视图,选择“视图→其他窗口→资源视图”,添加菜单项替换场景\t(&R), 在菜单编辑器中修改ID为IDM_REPLACE_SCENE
右击菜单项,选择添加事件处理程序
添加编辑
菜单单击属于一种事件,我们应该针对事件设置回调函数,但是OSG是通用渲染引擎,不会为了MFC、Qt提供菜单、按钮这样的事件类型。所以我们只能自定义事件。
基本思路:
在MFC中,存在两个线程(MFC+UI)线程和OSG线程,简单的我们可以认为存在两个事件队列,即MFC的事件队列和OSG的事件队列,我们的思路就是在菜单响应函数里,把我们的自定义事件压入OSG的事件队列,然后在OSG的渲染线程里进行处理。具体思路如下图
下面我们以替换模型和给模型添加包围盒为例,来谈谈如何结合mfc+osg来实现自定义事件,压入事件队列,如何处理自定义事件。
添加自定义事件类,我命名为CArchieEventAdapter,继承自 public osg::Referenced
以下代码至添加事件处理类框架类之前都在CArchieEventAdapter.h文件中添加
#include <osgDB/ReadFile> #include <osgGA/GUIEventHandler> #include <osgViewer/Viewer> #include <iostream> #include<osg/Referenced>
首先定义事件枚举类型
enum MFC_EVENT_TYPE { NONE = 0, BOUNDINGBOX = 1,//包围盒 REPLACE = 2//替换场景 };
MFC_EVENT_TYPE m_eventType;//事件类型
CArchieEventAdpater::CArchieEventAdpater(void) //这个在CArchieEventAdapter.cpp文件 { m_eventType = NONE; } void SetEventType(MFC_EVENT_TYPE eventType){ m_eventType = eventType;}//内联函数
#include<osg/Referenced> #include <osg/Node> #include<osg/Geode> #include <osg/Group> #include <osg/ShapeDrawable> #include <osg/ref_ptr> #include <osgGA/GUIEventHandler> #include <osgDB/WriteFile> #include <osgDB/ReadFile>
#include "ArchieEventAdpater.h"
class CArchieHandler : public osgGA::GUIEventHandler
protected: osg::ref_ptr<osg::Group> root;
public: CArchieHandler( osg::Group* rt ): root(rt){}
virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa);
bool CArchieHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) { switch(ea.getEventType()) { case osgGA::GUIEventAdapter::KEYDOWN: break; case osgGA::GUIEventAdapter::FRAME: break; case osgGA::GUIEventAdapter::USER: { const CArchieEventAdpater* adapter = dynamic_cast<const CArchieEventAdpater*>(ea.getUserData()); switch(adapter->m_eventType) { case NONE: break; case REPLACE: root->removeChild(0,1); root->addChild(osgDB::readNodeFile("glider.osg")); break; case BOUNDINGBOX: {osg::Node *node=root->getChild(0); root->addChild(createBoudingShpere(node)); break;} default: break; } // end case adapter->m_eventType break; } // end case USER default: break; } return false; }
osg::ref_ptr<osg::Geode> createBoudingShpere(osg::Node * node) { osg::ref_ptr<osg::Geode> geode = new osg::Geode(); //计算模型的包围盒 const osg::BoundingSphere bs = node->getBound(); //得到球体半径 float radius =bs.radius(); //设置精细度 osg::ref_ptr<osg::TessellationHints> hints = new osg::TessellationHints; hints->setDetailRatio(0.5f); osg::Vec3 v = bs._center; osg::ShapeDrawable *shapeBall=new osg::ShapeDrawable(new osg::Sphere(v,radius),hints.get()); shapeBall->setColor(osg::Vec4(1.0f,1.0f,0.5f,0.3f)); geode->addDrawable(shapeBall); osg::ref_ptr<osg::StateSet> stateset = geode->getOrCreateStateSet(); //关闭光照 stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF); stateset->setMode(GL_BLEND, osg::StateAttribute::ON | osg::StateAttribute::PROTECTED); //设置透明渲染 stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); return geode.release(); }
有了事件处理函数还不够,必须添加到viewer的事件处理队列中
在CoreOSG.cpp中添加包含文件
#include "ArchieHandler.h"
添加如下代码
//添加事件 mViewer->addEventHandler(new CArchieHandler(mRoot.get()));
此时就关联在了一起
void CCoreOSG::InitCameraConfig(void) { // 局部变量存放窗口矩形 .......... osg::ref_ptr<osg::Camera> camera = new osg::Camera; .......... mViewer->setSceneData(mRoot.get()); //添加事件 mViewer->addEventHandler(new CArchieHandler(mRoot.get())); // 实现VIEWER mViewer->realize(); }
我们可以在我们的CView里把事件压入OSG的事件队列,并且我们的基本事件类型都是GUIEventAdapter::User类型的,然后在USER类型里,我们再进行我们具体的事件分类,即是包围盒事件还是替换模型事件等,具体的压入事件方法如下:
View.cpp
// CMy3DVisionView #include "ArchieEventAdpater.h"
void CMy3DVisionView::OnReplaceScene() { CArchieEventAdpater *myType = new CArchieEventAdpater(); myType->SetEventType(REPLACE); mOSG->GetViewer()->getEventQueue()->userEvent(myType); }
void CMy3DVisionView::OnAddBoundingbox() { CArchieEventAdpater *myType = new CArchieEventAdpater(); myType->SetEventType(BOUNDINGBOX); mOSG->GetViewer()->getEventQueue()->userEvent(myType); }