清源游民 gameogre@gmail.com
下面直接涉及到相关类
首先看一下 ExampleFrameListener, 下面是类间关系图
ExampleFrameListener 继承了 FrameListener, 因此拥有了它的两个方法
virtual bool frameStarted(constFrameEvent& evt)
virtual bool frameEnded(constFrameEvent& evt)
在 ExampleApplication 中将它们注册到 ogre 中,在适当的时候, ogre 会调用这两个方法 . 下面是注册地点与时机
virtual void ExampleApplication::createFrameListener(void)
{
mFrameListener= newExampleFrameListener(mWindow, mCamera);
mFrameListener->showDebugOverlay(true);
mRoot->addFrameListener(mFrameListener);
}
createFrameListener 在 ExampleApplication::setup() 中被调用.
ExampleFrameListener 又继承了 WindowEventListener.
WindowEventListerner 在ogre 1.4中是新加的。按照手册:它是一个 Callback class used to send out window events to client app. 它定义以下四个接口,而显然例子程序中只用到了两个
virtual void windowMoved(RenderWindow* rw) {}
virtual void windowResized(RenderWindow* rw) {}
virtual void windowClosed(RenderWindow* rw) {}
virtual void windowFocusChange(RenderWindow* rw) {}
这种所谓的回调类如何实现?下面只说明windows操作系统的情况。
我们知道,windows操作系统监视系统中发生的一切,通过消息的方式通知相应的窗口。每个窗口类注册的时候,都指明一个回调过程,在那里处理传来的消息。应用程序又有各自的消息队列,从消息队列中取得消息,然后分开给各窗口.
不防从ogre的源码中看看上述windows基本过程如何实现,这有助理解回调类的实现过程:
首先,ogre可以为我们创建一个窗口:
mWindow = mRoot->initialise(true);//
于是我们进入到initialise()中看看吧,它倒底做了些什么事情。
RenderWindow * Root::initialise(boolautoCreateWindow, constString& windowTitle)
{
// ……
mAutoWindow = mActiveRenderer->initialise(autoCreateWindow, windowTitle);
// 这里:RenderSystem* mActiveRenderer
// ……
}
mActivaRenderer 只是接口,不用实际的事情,实际的工作由它的子类完成,现只看DirectX实现:
RenderWindow * D3D9RenderSystem::initialise( boolautoCreateWindow, constString& windowTitle )
{
// ……………………
autoWindow = this->createRenderWindow( windowTitle, width, height,
fullScreen, &miscParams );
// ………………… ..
}
好,继续下去,看看createRenderWindow做了些什么
RenderWindow * D3D9RenderSystem::createRenderWindow(constString &name,
unsigned int width, unsignedintheight, boolfullScreen,
const NameValuePairList *miscParams)
{
// …………… ..
RenderWindow * win = newD3D9RenderWindow(mhInstance, mActiveD3DDriver,
mPrimaryWindow ? mpD3DDevice : 0);
win ->create( name, width, height, fullScreen, miscParams);
// ………………
}
继续
void D3D9RenderWindow::create(constString& name, unsignedintwidth, unsignedintheight,
bool fullScreen, constNameValuePairList *miscParams)
{
// Register the window class
// NB allow 4 bytes of window data for D3D9RenderWindow pointer
WNDCLASS wc = { 0, WindowEventUtilities::_WndProc, 0, 0, hInst,
LoadIcon(0, IDI_APPLICATION), LoadCursor(NULL, IDC_ARROW),
(HBRUSH)GetStockObject(BLACK_BRUSH), 0, "OgreD3D9Wnd" };
RegisterClass(&wc);
// 定义了窗口类,并且进行注册,需要注意的是,你看看它把窗口类的回调函数的值设为了什么?先记住,一会儿讨论它吧。
// Create our main window
// Pass pointer to self
mIsExternal = false;
mHWnd = CreateWindow("OgreD3D9Wnd", title.c_str(), dwStyle,
mLeft, mTop, mWidth, mHeight, parentHWnd, 0, hInst, this);
// 调用win32API把窗口真正Create出来了
WindowEventUtilities ::_addRenderWindow(this);
// 这一步也要注意到
}
继续
void WindowEventUtilities::_addRenderWindow(RenderWindow* window)
{
_msWindows.push_back(window);
}
_msWindows 定义为:
typedef std::vector<RenderWindow*> Windows;
static Windows _msWindows;
,没什么,只是个stl容器,把生成的窗口放进去了.
到现在,容器类注册好了,回调函数也指定了,窗口也创建出来了。现在我们继续思考我们最最初的问题: WindowEventListerner 这个回调类是如何实现所谓的回调机制的,也就是说windowEventListerner不是定义了四个接口,它响应特定的windows事件,我们要讨论的就是这四个接口方法如何被调用起来,实现所谓的回调机制。 还记得注册窗口类的回调函数是什么吧,
WindowEventUtilities::_WndProc , 呵呵,只要在那里调用上述四个接口函数就好了。
哪问题又来了,_WndProc到哪里找这四个接口函数呢?
在 ExampleFrameListener 类的构造函数里我们可以看到如下代码
//Register as a Window listener
WindowEventUtilities::addWindowEventListener(mWindow, this);
啊,这就是奥秘所在!到源码中看看吧。
void WindowEventUtilities::addWindowEventListener( RenderWindow* window, WindowEventListener* listener )
{
_msListeners.inser t(std::make_pair(window, listener));
}
-msListeners 被定义成这样子:
typedef std::multimap<RenderWindow*, WindowEventListener*> WindowEventListeners;
static WindowEventListeners _msListeners;
没有什么,就是STL容器,它使得窗口(RenderWindow)与(WindowEventListener)
组成了亲密派对,窗口有了什么消息,就可以用对应的回调类响应。
只剩下一点秘密了,到底怎么调用起来的呢,某某某说过,源码之下,了无秘密,那就看源码吧:
LRESULT CALLBACK WindowEventUtilities::_WndProc(HWNDhWnd, UINTuMsg, WPARAMwParam, LPARAMlParam)
{
WindowEventListeners ::iteratorstart = _msListeners.lower_bound(win),
end = _msListeners.upper_bound(win);
// 为遍历做准备
switch( uMsg ) // 消息真的来了
{
case WM_MOVE:
//log->logMessage("WM_MOVE");
win->windowMovedOrResized();
for( ; start != end; ++start )
(start->second)->windowMoved(win);
break;
case WM_SIZE:
//log->logMessage("WM_SIZE");
win->windowMovedOrResized();
for( ; start != end; ++start )
(start->second)->windowResized(win);
break;
}
}
就这样,四个接口函数被调用起来了!
也许,也许,关于消息还有些要说明的。消息如何泵出来的?
当看到了如下代码,突然就想找个朋友,会心一笑.
void WindowEventUtilities::messagePump()
{
MSG msg;
while( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
打破铁锅问到底吧,它又如何被调用起来?
void Root::startRendering(void)
{
while( !mQueuedEnd )
{
//Pump messages in all registered RenderWindow windows
WindowEventUtilities::messagePump();
if (!renderOneFrame())
break;
}
}
就是这里了。写的乱七八糟,脑子也乱七八糟,梳理一下吧。
首先,mWindow = mRoot->initialise(true);初始化,随便通过一系统连锁反应create出来一个窗口,并把它的回调函数设定为 WindowEventUtilities::_WndProc.
ExampleFrameListener 继承了 WindowEventListener 的四个约定接口函数。并且在它的构造函数里调用 WindowEventUtilities::addWindowEventListener(mWindow, this); 进行了注册,以便特定窗口消息发生时进行响应. Root ::startRendering ()进入了渲染循环,渲染每一帧前,检测widnows消息,并进行分发。于是消息进入到窗口类的回调函数_WndProc,在函数内部根据消息的不同,分别调用已注册的侦听器的约定接口函数。
先写到这里吧,原来只是想写写CEGUI的使用,谁知已经写了这么多了,CEGUI还一字未提。脑子乱了,先休息吧。下面还得说说OIS,CEGUI又得靠后了.
posted on 2007-03-01 21:47
清源游民 阅读(2066)
评论(1) 编辑 收藏 引用 所属分类:
OGRE