osg中消息机制和调度机制的实现:
1. 猜想:所有的消息的起源都来自osgViewer::View,所以,先分析下osgViewer::View是如何将事件处理器或回调机制器加载进来的。
此类中的方法:addEventHandler(osgGA::GUIEventHandler * eventHandler);
类View中有一个属性_eventHandlers,其定义如下所示:
typedefstd::list<osg::ref_ptr<osgGA::GUIEventHandler>> EventHandlers;
EventHandlers _eventHandlers;
#Q: 既然类View可以添加多个事件处理器,那么在事件发生时是如何选择事件处理器的?
类关系图如下所示:
>
> OsgGA::GUIEventHandler主要提供了窗口系统的GUI事件接口。它使用osgGA::GUIEventAdapter来接受更新,使用osgGA::GUIActionAdapter来向系统提出请求。
适配器osgGA::GUIActionAdapter已为基类,此类的作用是向系统提出请求,#Q:具体是如何提出请求的呢?
#A:
osgGA::GUIAdctionAdapter完全是一个抽象基类,只声明了几个抽象方法。主要有以下几个请求:requestRedraw,requestContinuosUpdate,requestWrapPointer。
不论是osgViewer::GraphicsWindow还是osgViewer::View在实现这三类方法时都是直接或间接的去设置osgViewer::ViewerBase的标志位:_requestRedraw,_requestContinusUpdate;而requestWrapPointer都是间接地去调用osgViewer::GraphicsWindow中的继承方法。
#Q: 相机在这中间期到了一个什么作用?相机既能获取osgViewer::View又能获取osgViewer::GraphicsWindow。osg::View用来管理所有的相机视图。。
#Q: 适配器osgGA::GUIEventAdapter如何获取更新?
#A:osgGA::GUIEventAdapter只定义了各类消息和事件的枚举类型,以及设置和获取消息和事件值(set/get)。具体设置消息和事件值得地方在osgGA::EventQueue中。所以消息的更进一个源头是在osgGA::EventQueue中产生。
#Q: osgGA::EventQueue是如何接收消息的?
#A:
而调用osgGA::EventQueue中具体某类消息的源头是在:osgViewer::GraphicsWindowWin32中的方法:
virtual LRESULT handleNativeWindowingEvent(HWND hwnd, UINTuMsg, WPARAM wParam , LPARAM lParam);
而最终handleNativeWindowingEvent方法调用的源头在文件GraphicsWindowWin32.cpp中的窗口过程函数staticLRESULT CALLBACK WindowProc中:
static LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg,WPARAM wParam, LPARAM lParam )
{
osgViewer::GraphicsWindowWin32* window =Win32WindowingSystem::getInterface()->getGraphicsWindowFor(hwnd);
returnwindow ? window->handleNativeWindowingEvent(hwnd,uMsg, wParam, lParam) :
::DefWindowProc(hwnd, uMsg,wParam, lParam);
}
#Q: 有关窗口类、窗口、及窗口过程的注册如何进行的?
#A: 在源文件GraphicsWindowWin32.cpp文件中声明并定义的类:
classWin32WindowSystem : public osg::GraphicsContext::WindowSystemInterface
该类提供了注册窗口、注册窗口类的方法。通过将HWND和osgViewer::GraphicsWindowWin32关联起来。
窗口的创建是在osgViewer::GraphicsWindowWin32::createWindow中进行的。
而这些工作全都在osgViewer::GraphicsWindowWin32::init方法中进行,方法init在该类的构造函数中进行调用。
最终的方法还是在Win32WindowSystem::createGraphicsContext中进行的。
可参照项目Review中对GraphicsWindow的初始化代码:在RenderModule.cpp文件中方法initViewer中:
HWND hWnd = context.uc().mainframe()->m_wndView->m_hWnd;
RECT rect;
::GetClientRect(hWnd, &rect);
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
osg::ref_ptr<osgViewer::GraphicsWindowWin32::WindowData>windata = newosgViewer::GraphicsWindowWin32::WindowData(hWnd);
traits->x = 0;
traits->y = 0;
traits->width = rect.right - rect.left;
traits->height = rect.bottom - rect.top;
traits->windowDecoration = false;
traits->doubleBuffer = true;
traits->sharedContext = 0;
traits->setInheritedWindowPixelFormat = true;
traits->inheritedWindowData = windata;
if (context.ac().antialiasing)
{
traits->sampleBuffers = true;
traits->samples = 4;
}
osg::ref_ptr<osg::GraphicsContext> gc =osg::GraphicsContext::createGraphicsContext(traits.get());
找到osg消息来源的源头:来自osgViewer::GraphicsWindowWin32,原理和一般地在windows编程中一样:先注册一个窗口类,给此窗口类绑定一个窗口过程,在GraphicsWindow.cpp中定义的是窗口方法是static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAMwParam, LPARAM lParam);然后通过重新组装定义windows发送过来的窗口消息来构建属于osg的消息模型,具体的消息管理由osgViewer::EventQueue进行。
2. #Q: 既然已经知道有osgViewer::EventQueue来生成消息并管理消息的队列,那么osg是如何对消息进行分发的呢?
#A: 在osgViewer::GraphicsWindowWin32的父类osgViewer::GraphicsWindow中定义的属性:
osg::ref_ptr<osgGA::EventQueue>_eventQueue;
在该类的方法handleNativeWindowingEvent中就是用到的_eventQueue;那么既然osgViewer::GraphicsWindowWin32是osg消息的源头,就从此类的_eventQueue开始分析。
在osgViewer::CompositeViewer和osgViewer::Viewer的eventTraversal方法中都有用到osgViewer::GraphicsWindowWin32的_eventQueue属性进行事件处理。而此二类中也有自己的_eventQueue,#Q: 那么osgViewer::Viewer中的_eventQueue是如何获取消息的呢?
#A: 首先osgViewer::Viewer中的_eventQueue在此类及父类中中都没有定义过,那就只能通过其父类osgViewer::View::setEventQueue来对其进行设置,其次对_eventQueue的赋值是在osgViewer::eventTraversal中进行的,其中间接用到了osgViewer::GraphicsWindowWin32中的_eventQueue,所以sgViewer::View::_eventQueue的最终来源还是来自福osgViewer::GraphicsWindowWin32中的_eventQueue,此类的_eventQueue在其构造函数中就对其进行了赋值操作。
分析可得所有的事件均是在osgViewer::Viewer::eventTraversal中进行的,它将每个事件都发送到_eventHandlers中的每个事件处理器中,而最终的过滤操作还是由osgGA::GUIEventHandler进行处理,通过调用虚拟方法handle。
#Q: 那么,还剩下最后一个问题,那就是osgViewer::Viewer::eventTraversal方法是以什么样的方式进行调用?
#A: 通过osgViewer::ViewBase::frame方法和osgViewer::Viewer::checkNeedToFrame方法进行。osgViewer::CompositeViewer也一样。
而最终osg消息处理的循环体是在osgViewer::ViewBase::run方法中进行的。
osg定义的事件有:
3. 回调的设计与分析
http://www.osgchina.org/projects/osgcn/wikicn/Supportcn/Tutorials/Callbacks.php
#TODO: 对回调进行分析。
#Q: 重载方法()是什么时候执行的?
http://www.cnblogs.com/indif/archive/2011/04/22/2024805.html
>10、OSG处理事件的时机和顺序是怎样的?
答:OSG在Viewer::eventTraversal函数中处理输入事件,eventTraversal在帧循环的更新遍历和渲染遍历之前被调用。
eventTraversal中事件处理的顺序为:
1) 为本帧每一个事件遍历场景树,调用Node、Drawable、StateSet对象的事件处理回调处理事件。
2) 为本帧每一个事件遍历Viewer的事件处理器(GUIEventHandler)队列,调用事件处理器处理事件。
3) 为本帧每一个事件,调用Viewer的相机操作器(CameraManipulator)处理事件。
#A: 都是在UpdateVisitor::apply方法中进行的,此类方法为虚拟重载方法,类UpdateVistor的父类为osg::NodeVisitor。
osg::NodeCallback首先回调是应用在节点上的,即osg::Node,该类分别提供对更新(update)、事件(event)、拣选(cull)的回调。在osgViewer::Viewer中只处理update回调和event回调。具体调用回调使用的虚拟方法osg::Node::accept,此方法调用的是osg::NodeVisitor::apply方法。分别有三个osg::NodeVisitor的派生类:osgGA::EventVisitor,osgUtil::UpdateVisitor和osgUtil::CullVisitor,其中有一点不解:为什么EventVisitor放在了osgGA库中??
方法osgViewer::Viewer::eventTraversal处理事件回调,方法updateTraversal处理更新回调。
osg::Drawable::EventCallback,osg::Drawable::CullCallback,osg::Drawable::UpdateCallback,这三个回调类是专门针对osg::Geode的回调,在osgGA::EventVisitor,osgUtil::UpdateVisitor和osgUtil::CullVisitor中的handle_geode_callback方法中增加了对osg::Drawable。。的回调处理。
结贴。