CEGUI窗口的渲染顺序
定义和实现:CEGUIBase 里的:CEGUIWindow.h和CEGUIWindow.cpp。
cegui使用一个vector来存放所有的窗口,另外,再用一个vector存放渲染的顺序。
//! definition of type used for the list of attached child windows.
typedef std::vector<Window*> ChildList;
//! The list of child Window objects attached to this.
ChildList d_children;
//! Child window objects arranged in rendering order.
ChildList d_drawList;
再有新的窗口添加时有两个重载的函数addChildWindow:
//添加一个名为name窗口作为this的子窗口。如果窗口存在,则先分离再添加。
void Window::addChildWindow(const String& name{
addChildWindow(WindowManager::getSingleton().getWindow(name));
}
//----------------------------------------------------------------------------//
//添加wnd窗口作为this的子窗口。如果窗口存在,则先分离再添
void Window::addChildWindow(Window* window){
// don't add null window or ourself as a child
if (!window || window == this) //检查不能添加自己为自己的孩子
return;
addChild_impl(window);//执行添加
WindowEventArgs args(window); //激发事件
onChildAdded(args);
window->onZChange_impl();//通知窗口的Z值改变,窗口的Z值影响窗口的描述顺序
}
//添加指定窗口到合适的位置
void Window::addChild_impl(Window* wnd)
{
//如果先前有父窗口则断开与先前父窗口的联系。
if (wnd->getParent())
wnd->getParent()->removeChildWindow(wnd);
//添加窗口到描绘列表
addWindowToDrawList(*wnd);
//添加子窗口到列表
d_children.push_back(wnd);
// set the parent window
wnd->setParent(this);
//强制更新子窗口区域,因为父窗口已变
WindowEventArgs args(this);
wnd->onParentSized(args);
}
/*Add the given window to the drawing list at an appropriate position for
it's settings and the required direction. Basically, when \a at_back
is false, the window will appear in front of all other windows with the
same 'always on top' setting. When \a at_back is true, the window will
appear behind all other windows wih the same 'always on top' setting.*/
添加指定的窗口到绘制的列表的合适的位置。如果at_back为false,并且设置“always on top”,这个窗口会显示有所有的窗口前面。如果at_back为true,则这窗口会设置在所有"'always on top"的窗口的后面。
void Window::addWindowToDrawList(Window& wnd, bool at_back)
{
// add behind other windows in same group
if (at_back)
{
// calculate position where window should be added for drawing
ChildList::iterator pos = d_drawList.begin();
if (wnd.isAlwaysOnTop())
{
// find first topmost window
while ((pos != d_drawList.end()) && (!(*pos)->isAlwaysOnTop()))
++pos;
}
// add window to draw list
d_drawList.insert(pos, &wnd);
}
// add in front of other windows in group
else
{
// calculate position where window should be added for drawing
ChildList::reverse_iterator position = d_drawList.rbegin();
if (!wnd.isAlwaysOnTop())
{
// find last non-topmost window
while ((position != d_drawList.rend()) && ((*position)->isAlwaysOnTop()))
++position;
}
// add window to draw list
d_drawList.insert(position.base(), &wnd);
}
}
/*!
\brief
Causes the Window object to render itself and all of it's attached
children
*/
//渲染时父窗口及其的子窗口,先渲染父,再渲染其子窗口
void Window::render()
{
// don't do anything if window is not visible
if (!isVisible())
return;
// get rendering context
RenderingContext ctx;
getRenderingContext(ctx);
// clear geometry from surface if it's ours
if (ctx.owner == this)
ctx.surface->clearGeometry();
// redraw if no surface set, or if surface is invalidated
if (!d_surface || d_surface->isInvalidated())
{
// perform drawing for 'this' Window
drawSelf(ctx);
// 按顺序取得d_drawList里的所有的子窗口进行渲染
const size_t child_count = getChildCount();
for (size_t i = 0; i < child_count; ++i)
d_drawList[i]->render();
}
// do final rendering for surface if it's ours
if (ctx.owner == this)
ctx.surface->draw();
}
综上所述,cegui通过两个vector列表来控制所有的窗口渲染及其渲染顺序。渲染顺序又是通过一个添加窗口及at_back参数控制,at_back 默认是false,即默认添加的新窗口是在最前面,同时受“'always on top”影响。在实际游戏开发中Z值,应该要比这个复杂,并且有一些模态的窗口。我们可以改写addWindowToDrawList 来控制自己窗口实际显示时应在的位置,以达到项目的需求。
对于模态窗口,以前我们的项目有个“变态”显示方法,每来一个模态的都显示在所有的窗口的最上面,但是鼠标消息却又还在第一个模态的窗口那里抢占着。我觉得应该再建立一个模态的列表,同时加上权重,但同时弹出N个模态的窗口时,选择权重最大的在最上面,即新弹出窗口时,则比较要弹出的模态窗口与显示的模态窗口的权重,如果权重大,则销毁前面的窗口,显示这个。 另一种方案是:如果有模态窗口显示时,不允许再弹出模态窗口。第三种方案是:管理一个要弹出的模态的窗口的列表,当判断有模态窗口显示在最上面时,则将此窗口压到栈里,每释放一个模态窗口时,在里面调一个接口检查是否有没有显示的模态窗口,如果有则显示下一个模态窗口,并从栈去掉该窗口。