现在的位置: 首页 > 综合 > 正文

CEGUI学习笔记七–如何扩展CEGUI控件库

2018年06月07日 ⁄ 综合 ⁄ 共 4524字 ⁄ 字号 评论关闭

本文是扩展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开始查。

抱歉!评论已关闭.