本文以第三课MFC+OSG框架为基础进行讨论。参考CWorld《基于MFC(QT)的OSG的自定义事件》
首先找到资源视图,添加菜单,如果没有资源视图,选择“视图→其他窗口→资源视图”,添加菜单项替换场景\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//替换场景
};
给CArchieEventAdapter添加变量,并设置构造函数设置为NONE添加函数SetEventType。
MFC_EVENT_TYPE m_eventType;//事件类型
CArchieEventAdpater::CArchieEventAdpater(void) //这个在CArchieEventAdapter.cpp文件
{
m_eventType = NONE;
}
void SetEventType(MFC_EVENT_TYPE eventType){ m_eventType = eventType;}//内联函数
添加事件处理框架类CArchieHandler
#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);
}