本文是扩展CEGUI控件库的第2部分,对FalButton.CPP文件的分析记录。
目的是探究下如何去控制 Renderer,从而彻底实现一个CEGUI没有的控件类型。
但是分析未完成,希望这些半成品能对大家有用。
PS:由于公司的网络限制,所以Blog很长时间未更新。家里还没电脑,上班偷偷写的....
下次将整理下单独编译CEGUI,并给出CEGUI与Ogre结合的详细步骤。
//=====================================================================================================================
// 2008.4.18
//=====================================================================================================================
本来打算今天探究下如何去控制 Renderer,从而彻底实现一个CEGUI没有的控件类型。
首先从FalButton开始切入,但是发现问题远没有想象中那么简单。
在先前对Falagard的翻译中得知,如果要绘制一个控件,需要定义规定的StateImagery。
所以我期望在FalButton中看到这些状态的集合,否则手册里规定的状态从哪来呢?
实际上我看到的是 "Pushed" : "PushedOff"; "Disabled" "Hover"之类的 魔字符串!
现在知道这些状态是哪来的了:在CEGUIfalagardWRBase项目里相关文件里这样定义的!这可真要命,太不可接受了。
OK,既然已经知道如何定义需要实现哪些状态,再找找和显示相关的代码,在这个对象中,只有一句跟这个相关:
wlf.getStateImagery(actualStateName(state)).render(*w);
1、wlf是WidgetLookFeel,
2、getStateImagery返回StateImagery,
获取WidgetLookFeel的唯一对象,然后得到一个叫actualStateName(state)的StateImagery,并调用它的render()方法来进行绘制。
以w为参数。w是什么?
w是d_window,继续查找这个成员变量,发现是WindowRenderer类的成员变量,回头看H文件的定义,FalButton继承自WindowRenderer。
跟WindowRenderer类似的还有很多***Renderer的文件,所以推测Button,Frame等简单控件类型的绘制都继承这个WindowRenderer。
而List等复合控件类型需要另外特别的***Renderer来实现绘制。
***Renderer里实现绘制,确实是这样的吗?如果是这样的话,为什么要调用StateImagery对象的render()方法。
如果要绘制什么东西,一定需要绘制它的参数信息,包括数据和规则,既然***Renderer是作为参数传递给StateImagery的render(),
那么***Renderer要么给出了绘制方法,要么给出数据,要么给出规则。现在虽然不能确定,但是应该是提供规则的可能性大一些。
[后面将证明是错误的,它提供的不是规则而是数据。]
现在又有2条路了:
1、WindowRenderer是什么东西。
2、StateImagery的render()到底做了什么事。
先查看WindowRenderer类的介绍:
// Base-class for the assignable WindowRenderer object
这是什么,可支持的窗口渲染器对象的基类?显然不太好对付。
从类视图中可以看到它的派生类,找个复合控件,EditboxWindowRenderer类的介绍:
// Base class for the EditboxWindowRenderer class
OK,现在知道如果不看***Renderer的代码的话,是不知道这个类做什么用的了。
所以还是顺着render()看。
有2个版本,但结构差不多,所以只盯着一个看先。
先看这个函数的介绍,没什么很详细的信息....这说明如果我们了解整个设计目的的话,这个函数是理所当然的。
没什么好说的,RTFS。
void StateImagery::render(Window& srcWindow, const ColourRect* modcols, const Rect* clipper) const
{
float base_z;
// render all layers defined for this state
for(LayersList::const_iterator curr = d_layers.begin(); curr != d_layers.end(); ++curr)
{
// TODO: Magic number removal
base_z = -0.0000001f * static_cast<float>((*curr).getLayerPriority());
(*curr).render(srcWindow, base_z, modcols, clipper, d_clipToDisplay);
}
}
又冒出很多新鲜玩意出来了,就知道!
LayersList?估计就是 XML里的Layer关键字定义的那段东西,在CPP文件里的对应对象,暂时不管它的细节。
getLayerPriority?优先权?为什么要和-0.0000001f相乘?请教老大之后得知,这个Z值是给3D渲染器用的。因为
getLayerPriority返回的是整数,而3D渲染器的Z值是0到1,所以才需要这么乘一下。
再看看 *cuur.render() 接受的参数:1个窗口的引用,1个Z值,1个顶点颜色矩形,一个裁减矩形,还有一个d_clipToDisplay?
bool d_clipToDisplay; //!< true if Imagery for this state should be clipped to the display instead of winodw (effectively, not clipped).
如果为true,就是相对于显示器进行裁减,而不是窗口(实际上没裁减)?
构造函数里的初始化为d_clipToDisplay(false),搜索操作这个变量的方法,发现跟XMLHelper扯上关系,
而整个CEGUI里没有调用这个方法来设置这个变量,看来是放出去给外部进行设置的,那就不管了先,等以后在某个例子里发现的时候再回头看。
好吧,现在继续LayersList的render()。
// render all sections in this layer
for(SectionList::const_iterator curr = d_sections.begin(); curr != d_sections.end(); ++curr)
(*curr).render(srcWindow, base_z, modcols, clipper, clipToDisplay);
又是个遍历,不过这次遍历的是SectionList。
再次回忆上次LookNFeel里的定义:
StateImagery
layer
section = ImagerySection
而ImagerySection的层次是:
ImagerySection
ImageryComponent
Area
Image
VertFormat
那在这里预测,如果查找(*curr).render(),一定是遍历一个ComponentList,然后调用 (*xx).render()。
不过找到的是获取ImagerySection的代码:
const ImagerySection* sect =
&WidgetLookManager::getSingleton().getWidgetLook(d_owner).getImagerySection(d_sectionName);
然后sect->render(...);
结果与预测的不太一样,在这个地方已经开始使用try和catch,说明已经开始实际的绘制操作,马上就可以知道WindowRenderer提供
的到底是数据信息,还是规则信息了!
与预测不一样的地方有2个:
1、获取的不是Component,而是ImagerySection,为什么?
2、没有遍历,为什么?
回头查看SectionList,
typedef std::vector<SectionSpecification> SectionList;
说明Layer里可以有很多section,而section再来指出是哪个ImagerySection。所以应该先获取ImagerySection。
而section和ImagerySection是1对1的关系,所以没有遍历。
继续看ImagerySection的render(...),在里面发现了3个遍历:
FrameList: FrameComponent 的render()
ImageryList: ImageryComponent 的render()
TextList: TextComponent 的render()
FrameComponent 的render()里进行了一大堆的计算,然后分别绘制了Frame的4个角、4条边、1个背景。
虽然没看,但估计其他2个差不多。这就是实际的绘制工作了!
现在回头看看,Button有Frame吗?之前写Button的时候,只写了ImageryComponent。而我从FalButton开始切入,最后却看到了
FrameComponent的绘制,这说明了什么?
再从更大范围上整理一下:
我在XML文件里定义 Button的渲染器 FalButton,
FalButton里(通过d_window)判断当前按钮的状态(现在对于***Renderer到底是个什么还不清楚,等整理完就去看。),
再通过这个状态来(通过WidgetLookFeel对象)得到对应的StateImagery对象。
然后调用StateImagery对象的render()方法,以*w为参数。
然而现在来看,从调用StateImagery对象的render()方法开始,已经跟Button没关系了,一切都是按照规定好的流程在进行。
这说明如果我们要使用CEGUI的话,从这里开始就是不变的东西,如果要改变的话,要做的事情就比较多了。
在这之前,我们能控制的(自由度)就是(添加或减少一个)控件状态的定义。
但是在之前我写Button1的时候,没实现Hover状态下的代码,然后当我鼠标移到Button1的时候,就没图片显示出来。
如果按这个流程的话,得不到StateImagery对象,就不会调用render,所以就没图片显示出来,这就说的通了。
今天就到这里,下周顺着WindowRenderer开始查。