查看: 2509|回复: 21

初学OpenSceneGraph,先摸索摸索OSG中的回调机制,希望各位前辈们多加指教哇!

[复制链接]

该用户从未签到

发表于 2013-5-16 17:00:28 | 显示全部楼层 |阅读模式
本帖最后由 hunter_wwq 于 2013-7-18 10:15 编辑


在盖二楼之前还有点东西原来没有补充上来,现在将这部分不上来。


猜想:所有的消息的起源都来自osgViewer::View,所以,先分析下osgViewer::View是如何将事件处理器或回调机制器加载进来的。
此类中的方法:addEventHandler(osgGA::GUIEventHandler * eventHandler);
类View中有一个属性_eventHandlers,其定义如下所示:
{{{
typedef std::list<osg::ref_ptr<osgGA::GUIEventHandler>> EventHandlers;
EventHandlers _eventHandlers;
}}}
#Q: 既然类View可以添加多个事件处理器,那么在事件发生时是如何选择事件处理器的?
类关系图如下所示:
osgViewerViewer继承关系图.png
osgGAGUIEventHandler.png
osgGAGUIEventAdapter.png
视图相关类的功能.png
> OsgGA::GUIEventHandler主要提供了窗口系统的GUI事件接口。它使用osgGA::GUIEventAdapter来接受更新,使用osgGA::GUIActionAdapter来向系统提出请求。
适配器osgGA::GUIActionAdapter已为基类,此类的作用是向系统提出请求,#Q: 具体是如何提出请求的呢?
#A:
osgGUIActionAdapter派生关系图.png

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继承关系图

osgGA::EventQueue继承关系图


而调用osgGA::EventQueue中具体某类消息的源头是在:osgViewer::GraphicsWindowWin32中的方法:
{{{
virtual LRESULT handleNativeWindowingEvent(HWND hwnd, UINT uMsg, WPARAM wParam , LPARAM lParam);
}}}
而最终handleNativeWindowingEvent方法调用的源头在文件GraphicsWindowWin32.cpp中的窗口过程函数static LRESULT CALLBACK WindowProc中:

  1. static LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  2. {
  3.     osgViewer::GraphicsWindowWin32* window = Win32WindowingSystem::getInterface()->getGraphicsWindowFor(hwnd);
  4.     return window ? window->handleNativeWindowingEvent(hwnd, uMsg, wParam, lParam) :
  5.                     ::DefWindowProc(hwnd, uMsg, wParam, lParam);
  6. }
复制代码
#Q: 有关窗口类、窗口、及窗口过程的注册如何进行的?
#A: 在源文件GraphicsWindowWin32.cpp文件中声明并定义的类:

  1. class Win32WindowSystem : public osg::GraphicsContext::WindowSystemInterface
复制代码
该类提供了注册窗口、注册窗口类的方法。通过将HWND和osgViewer::GraphicsWindowWin32关联起来。
窗口的创建是在osgViewer::GraphicsWindowWin32::createWindow中进行的。
而这些工作全都在osgViewer::GraphicsWindowWin32::init方法中进行,方法init在该类的构造函数中进行调用。
最终的方法还是在Win32WindowSystem::createGraphicsContext中进行的。
可参照项目Review中对GraphicsWindow的初始化代码:在RenderModule.cpp文件中方法initViewer中:

  1.         HWND hWnd = context.uc().mainframe()->m_wndView->m_hWnd;

  2.         RECT rect;
  3.         ::GetClientRect(hWnd, &rect);

  4.         osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
  5.         osg::ref_ptr<osgViewer::GraphicsWindowWin32::WindowData> windata = new osgViewer::GraphicsWindowWin32::WindowData(hWnd);

  6.         traits->x = 0;
  7.         traits->y = 0;
  8.         traits->width = rect.right - rect.left;
  9.         traits->height = rect.bottom - rect.top;
  10.         traits->windowDecoration = false;
  11.         traits->doubleBuffer = true;
  12.         traits->sharedContext = 0;
  13.         traits->setInheritedWindowPixelFormat = true;
  14.         traits->inheritedWindowData = windata;
  15.         if (context.ac().antialiasing)
  16.         {
  17.                 traits->sampleBuffers = true;
  18.                 traits->samples = 4;
  19.         }

  20.         osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
复制代码

该用户从未签到

 楼主| 发表于 2013-5-17 11:43:04 | 显示全部楼层
本帖最后由 hunter_wwq 于 2013-7-18 10:16 编辑

找到osg消息来源的源头:来自osgViewer::GraphicsWindowWin32,原理和一般地在windows编程中一样:先注册一个窗口类,给此窗口类绑定一个窗口过程,在GraphicsWindow.cpp中定义的是窗口方法是static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);然后通过重新组装定义windows发送过来的窗口消息来构建属于osg的消息模型,具体的消息管理由osgViewer::EventQueue进行。

该用户从未签到

 楼主| 发表于 2013-5-17 11:45:33 | 显示全部楼层
既然已经知道有osgViewer::EventQueue来生成消息并管理消息的队列,那么osg是如何对消息进行分发的呢?

该用户从未签到

发表于 2013-5-17 12:32:46 | 显示全部楼层
osgViewer::Viewer  eventTravel 中处理消息

该用户从未签到

 楼主| 发表于 2013-5-17 14:30:36 | 显示全部楼层
本帖最后由 hunter_wwq 于 2013-7-18 10:40 编辑

#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也一样。

该用户从未签到

 楼主| 发表于 2013-5-17 14:36:57 | 显示全部楼层
liuzhiyu123 发表于 2013-5-17 12:32
osgViewer::Viewer  eventTravel 中处理消息

谢谢!

该用户从未签到

 楼主| 发表于 2013-5-17 14:38:49 | 显示全部楼层
而最终osg消息处理的循环体是在osgViewer::ViewBase::run方法中进行的。

该用户从未签到

发表于 2013-5-17 14:41:58 | 显示全部楼层
是在自问自答么?请继续。。。

该用户从未签到

 楼主| 发表于 2013-5-17 14:47:23 | 显示全部楼层
不是的,只是在厘清概念的时候习惯了抛出问题再找答案的学习方式

该用户从未签到

 楼主| 发表于 2013-5-17 14:55:00 | 显示全部楼层
写出来,可能哪里理解的不对或者是有什么不足,是希望大牛们能够指点指点,如果是新手再看的话也就不用再走弯路了

该用户从未签到

发表于 2013-5-17 15:54:13 | 显示全部楼层
支持下!

该用户从未签到

发表于 2013-5-17 16:46:29 | 显示全部楼层
总结吧,完事之后 我移到教程区里面

该用户从未签到

 楼主| 发表于 2013-5-17 17:22:09 | 显示全部楼层
OK,回调部分还没分析总结,得把这两块讨论分析完。。

该用户从未签到

 楼主| 发表于 2013-5-23 11:19:31 | 显示全部楼层
osgGA::GUIEventHandler类中handle方法是在osgViewer::Viewer::eventTraverser方法中执行的,那么它的重载方法()是在哪里执行的,能否贴出具体的代码实例??我找了半天找不到。。

该用户从未签到

发表于 2013-5-23 12:16:43 | 显示全部楼层
handleWithCheckAgainstIgnoreHandledEventsMask 中调用handle 方法

该用户从未签到

 楼主| 发表于 2013-5-23 12:36:00 | 显示全部楼层
调用handle方法我知道是在这里调用的,那个()重载方法我不清楚是在什么地方调用的。。

该用户从未签到

发表于 2013-5-23 13:05:18 | 显示全部楼层
本帖最后由 liuzhiyu123 于 2013-5-23 13:05 编辑

operator 是在 以NodeCallback的方式 运行时由visitor进行调用
class OSGGA_EXPORT GUIEventHandler : public osg::NodeCallback, public osg::Drawable::EventCallback

所以在处理GUIEventHandler时 是不会调用operator的 只有在setNodeCallback 或者 addXXX 时才会调用,而且也转换为了handle函数的处理

该用户从未签到

 楼主| 发表于 2013-5-23 14:53:27 | 显示全部楼层
liuzhiyu123 发表于 2013-5-23 13:05
operator 是在 以NodeCallback的方式 运行时由visitor进行调用
class OSGGA_EXPORT GUIEventHandler : pub ...

多谢指点,我再把它细化一下

该用户从未签到

 楼主| 发表于 2013-5-23 15:17:38 | 显示全部楼层
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处理更新回调。

该用户从未签到

发表于 2013-5-23 15:26:12 | 显示全部楼层
因为osgGA中相关的类 都是跟Event事件处理相关的   就像UpdateVisitor 放在osgUtil中一样

该用户从未签到

 楼主| 发表于 2013-5-23 15:50:48 | 显示全部楼层
本帖最后由 hunter_wwq 于 2013-5-23 15:51 编辑

osg:: Drawable::EventCallback,osg:: Drawable::CullCallback,osg:: Drawable::UpdateCallback,这三个回调类是专门针对osg::Geode的回调,在osgGA::EventVisitor,osgUtil::UpdateVisitor和osgUtil::CullVisitor中的handle_geode_callback方法中增加了对osg:: Drawable。。的回调处理。

该用户从未签到

 楼主| 发表于 2013-5-23 16:06:16 | 显示全部楼层
OSG的消息机制和回调机制的实现就分析到这里了,具体如何去运用它的消息机制和回调机制下次可以再开新帖进行分析。多谢@liuzhiyu123 和 @木子匕的支持!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

OSG中国官方论坛-有您OSG在中国才更好

网站简介:osgChina是国内首个三维相关技术开源社区,旨在为国内更多的技术开发人员提供最前沿的技术资讯,为更多的三维从业者提供一个学习、交流的技术平台。

联系我们

  • 工作时间:09:00--18:00
  • 反馈邮箱:1315785073@qq.com
快速回复 返回顶部 返回列表