Ogre 界面

Mage小组 著
Email: norman_chen@163.com 
renwind@163.com
QQ: 18725262
http://www.173d8.com
http://blog.csdn.net/pizi0475

 

 

界面
 这里的界面是指菜单、HUD及提示信息框在内的综合体。
基本概念
Overlay
将被渲染在“普通”场景内容之上的层。Overlay是那些将在主场景被渲染之后才渲染的可视组件的容器。这些可视组件将构成HUD(heads-up-display)、菜单或其它在主场景内容之上的任何东西。
 一个Overlay总是占满一个viewport的全部尺寸,尽管它包含的组件并没有那么大,那么多。Overlay并不代表任何可视组件,它只是一个可视组件的容器。
    Overlay可以通过调用SceneManager::createOverlay函数来创建,或者把它们定义在一个脚本文件里(.overlay files),引擎会自动解析这些脚本,并创建它们。你可以定义无限多个Overlay。一个overlay被创建之后是不可见的,你必须调用show函数让它们显示出来。这样你就可以预先定义很多Overlay(比如菜单),在需要它们的时候才将它们显示出来。在同一时刻可以存在和显示多个Overlay,Overlay的zorder属性决定了谁在上谁在下。
缺省情况下,Overlay会被渲染到全部的viewport。当你只有一个全屏viewport的时候这非常好用,但是当你在程序中用几个viewport构成“画中画”的时候,你可能不想在小画面中也显示Overlay。你可以通过调用viewport的Viewport::setDisplayOverlays方法将某些viewport的Overlay显示状态关闭。
Overlay支持旋转、卷动和缩放。通过Overlay::scroll, Overlay::rotate and Overlay::scale这几个函数可以达到目的。
Overlay中可以包括2D元素,也可以包括3D元素。
GuiElement
将在Overlay 中被显示的2D元素。这个类抽象了在overlay中出现的2D元素的全部信息。事实上,并非所有的GuiElement的实例都可以被加入到Overlay中,只有GuiContainer类(GuiElement类的派生类)的实例才可以。GuiContainer对象可以包含GuiElement对象。也就是说一般情况下,Overlay包含一个或多个GuiContainer,而GuiContainer包含GuiElement。
GuiElements被GuiManager管理。GuiManager负责创建和删除GuiElements,并且负责从plugins接收新类型的GuiElements。
GuiElements属性中的位置和尺寸表达方式(标尺模式)有两种:Pixel Mode模式和Relative Mode模式。当采用Pixel Mode模式时,位置和尺寸用Pixel为单位。用这种方式屏幕元素的位置和大小将与屏幕分辨率有关。例如:800*600分辨率下,某个GuiElements的左上角坐标为10,10,大小为50,50。那么它将显示在屏幕左上方。但当分辨率变为1024*768的时候,它的位置将向左上移动一点,且看起来会变小。当采用Relative Mode模式时,GuiElements中的位置和尺寸是用0.0 - 1.0的参数表达的,是一个和屏幕宽度及高度的比值。这样做的方式是为了使这些值与显示分辨率无关,从而使显示结果可以适应任何分辨率。例如:0.5x0.5的尺寸代表屏幕宽和高的一半大,此外请注意,0.5x0.5的尺寸在屏幕上显示出来并不是一个正方形,因为屏幕本身就不是正方形。
GuiElements被设计成可扩展的,它是StringInterface类的子类,所以它的参数也是通用参数。
我们可以继承GuiElements类,实现自己的功能复杂GuiElements。一般情况下通过写plugin来扩展GuiElements。
GuiContainer
可以包含其它GuiElement实例的特殊GuiElement。GuiContainer类其实是GuiElement类的派生类。它也是可以被直接attach到Overlay上的最小元素。GuiContainers也由GuiManager管理。GuiManager负责创建和删除GuiElements,并且负责从plugins接收新类型的GuiElements。
向Overlay加入一个GuiContainer的方法很简单,调用Overlay的add2D函数就可以了。
向一个GuiContainer加入子元素的方法也很简单,调用GuiContainer的addChild函数就可以了。子元素可以是GuiContainer,也可以是GuiElements。通过一级一级加入子元素可以构成一个树状结构。需要注意的是,子元素的位置是相对于父元素的。
GuiElementFactory
GuiElementFactory:创建GuiElement的工厂类。该类是一个抽象类,任何一个具体GuiElement的工厂都必须继承它,并实现其中的接口。
GuiManager
它的任务是管理GuiElement(及其子类)的实例的生命周期,并从plugin模块中注册新的GuiElement类型。
TextAreaGuiElement
 2D界面元素,GuiElement的直接派生类,定义界面中的文本区域。
PanelGuiElement
 2D界面元素,GuiContainer的直接派生类,定义界面中的面板区域,在面板中还能包括其它界面元素。
BorderPanelGuiElement
 2D界面元素,GuiContainer的直接派生类,定义界面中的带边面板区域,在带边面板中还能包括其它界面元素。
ButtonGuiElement
 2D界面元素,BorderPanelGuiElement的直接派生类,定义界面中的按钮。
ListGuiElement
 2D界面元素,PanelGuiElement的直接派生类,定义界面中的列表选择框。

 


GUI静态结构图


Overlay脚本
Overlay脚本为我们提供了使用脚本来定义可重用的overlays的手段。
载入脚本
Overlay脚本在系统初始化的时候被载入。缺省情况下,系统会把公共资源路径(参考Root::addResourceLocation)下所有扩展名为'.overlay'的文件都载入并解析。如果你的Overlay脚本使用了其它扩展名,你可以使用OverlayManager::getSingleton().parseAllSources函数载入它们,如果你想载入单个Overlay脚本,就使用OverlayManager::getSingleton().parseScript函数。
格式
可以在一个脚本文件中定义多个overlay。脚本采用伪C++格式,用{ }分块,注释采用‘//’(注意不支持注释嵌套),而且支持模板继承。以下是一个典型的实例:
// The name of the overlay comes first
MyOverlays/ANewOverlay
{
    zorder 200

    container Panel(MyGuiElements/TestPanel)
    {
        // Center it horzontally, put it at the top
        left 0.25
        top 0
        width 0.5
        height 0.1
        material MyMaterials/APanelMaterial

        // Another panel nested in this one
        container Panel(MyGuiElements/AnotherPanel)
        {
             left 0
             top 0
             width 0.1
             height 0.1
             material MyMaterials/NestedPanel
        }
    }

}

以上的例子定义了一个名为“MyOverlays/ANewOverlay”的overlay,其中包括2个嵌套的panels。它使用缺省的相对标尺模式(用0-1的数来表示长度和位置,0-1的数代表与屏幕宽度和高度的比值)。
脚本中的每一个overlay都必须有名字,且必须在“{”之前一行指定。名字不能重复。名字中可以包含“路径”格式(用/分割),用以区分层次和避免重名,但OGRE引擎并不把这些名字解析成一个层次结构,它仅仅是一个字符串。
在大括号中间是overlay的属性和其他嵌套的元素。本例中,overlay只有一个'zorder'属性,'zorder'用于为重叠的overlay区分覆盖关系,zorder值大的将渲染在上面。
在overlay中加入元素
在overlay里可以包括2D和3D元素。这些元素必须以如下关键字开头:
 'element' 不能再嵌套其它元素的2D元素。
'container' 可嵌套2D元素和container的容器。
'entity'  3D元素,只能放在overlay里,不能嵌套在其它容器中。
通过'container'可以实现2D元素的层层嵌套。
'container' 和 'element' 块
以如下格式开头:
[container | element] <type_name> ( <instance_name>) [: <template_name>]
{ ...
type_name::GuiElement的类型名,必须在GuiManager中注册。OGRE引擎的Plugin_GuiElements.dll中已提供的类型有:Panel、 BorderPanel 、TextArea、Button和List。你可以自己写Plugin扩充其它类型。
instance_name:标识本元素的唯一的名称。可以通过GuiManager::getSingleton().getGuiElement(name)获取到指定名称的元素指针。
template_name::可选参数,指定模板名称。
块中的属性取决于每个元素类型,以下属性是每个元素都有的:
metrics_mode  标尺模式
horz_align  横向对齐   
vert_align   纵向对齐
left     左边位置
top     上边位置
width    宽度
height    高度
material    材质
caption   标题

TextArea特有的属性:
 font_name    字体名
 char_height    字符高度
 colour_top    上部颜色
 colour_bottom   下部颜色

BorderPanel特有的属性:
 border_size
    border_material
    border_topleft_uv    
 border_top_uv        
 border_topright_uv   
 border_left_uv       
 border_right_uv      
 border_bottomleft_uv 
    border_bottom_uv     
 border_bottomright_uv

Button特有的属性:
 border_up_material   按钮弹起状态材质
 border_down_material  按钮按下状态材质

List特有的属性:
item_template    选项文字模板
v_spacing     选项间的纵向距离
h_spacing
item_material_selected  选项被选中后的材质

'entity' 块
必须以以下格式开头:
entity <mesh_name> ( <entity_name>)
{ ...
mesh_name: .mesh文件名
entity_name: 唯一的entity名
你还可以定义如下属性
position
rotation
注意:材质名可以从.mesh文件中读出,当然你可以指定为.material中定义的其它材质。
Templates
你可以使用模板来定义大量具有相同属性的元素。模板是不能加入到overlay中的抽象元素。各种元素可以继承它并获得它定义的缺省属性。在元素(container, element, or entity)的定义前加'template'关键字就可以定义模板。模板元素一般定义在脚本文件的开头,而且不能定义在Overlay里面。建议把模板元素定义在独立的脚本文件中,这样可以很容易实现重用和个性化调整。
元素可以象C++那样用“:”符号继承自模板。“:”放在元素名称的右括号后。模板名称又放在“:”符号后。
A template can contain template children which are created when the template is subclassed and instantiated. Using the template keyword for the children of a template is optional but recommended for clarity, as the children of a template are always going to be templates themselves.

template container BorderPanel(MyTemplates/BasicBorderPanel)
{
    left 0
    top 0
    width 1
    height 1
// setup the texture UVs for a borderpanel
// do this in a template so it doesn't need to be redone everywhere
    material Core/StatsBlockCenter
    border_size 0.05 0.05 0.06665 0.06665
    border_material Core/StatsBlockBorder
    border_topleft_uv 0.0000 1.0000 0.1914 0.7969
    border_top_uv 0.1914 1.0000 0.8086 0.7969
    border_topright_uv 0.8086 1.0000 1.0000 0.7969
    border_left_uv 0.0000 0.7969 0.1914 0.2148
    border_right_uv 0.8086 0.7969 1.0000 0.2148
    border_bottomleft_uv 0.0000 0.2148 0.1914 0.0000
    border_bottom_uv 0.1914 0.2148 0.8086 0.0000
    border_bottomright_uv 0.8086 0.2148 1.0000 0.0000
}
template container Button(MyTemplates/BasicButton) : MyTemplates/BasicBorderPanel
{
    left 0.82
    top 0.45
    width 0.16
    height 0.13
    material Core/StatsBlockCenter
    border_up_material Core/StatsBlockBorder/Up
    border_down_material Core/StatsBlockBorder/Down
}
template element TextArea(MyTemplates/BasicText)
{
    font_name Ogre
    char_height 0.08
    colour_top 1 1 0
    colour_bottom 1 0.2 0.2
    left 0.03
    top 0.02
    width 0.12
    height 0.09
}
MyOverlays/AnotherOverlay
{
    zorder 490
    container BorderPanel(MyElements/BackPanel) : MyTemplates/BasicBorderPanel
    {
        left 0
        top 0
        width 1
        height 1

        container Button(MyElements/HostButton) : MyTemplates/BasicButton
        {
            left 0.82
            top 0.45
            caption MyTemplates/BasicText HOST
        }

        container Button(MyElements/JoinButton) : MyTemplates/BasicButton
        {
            left 0.82
            top 0.60
            caption MyTemplates/BasicText JOIN
        }
    }
}

以上的例子使用模板来创建一个按钮。注意到button模板继承自borderPanel模板,这减少了创建按钮时需要设置的属性。
还注意到Button的实例用模板名来设置caption属性。模板也可以被elements用于动态创建子elements(按钮创建了一个TextAreaElement来作为自己的caption)。

Overlay实例一
 查看OGRE运行环境中的Ogre.overlay文件,在OGRE自带的DEMO中都使用了这个文件中定义的overlay。
 FPS的数据是如何显示到overlay中的????
Overlay实例二
 在Ogre.overlay的基础上,加两个新的overlay。其中包括一个OGRE的LOGO。另一个overlay包括一个3D元素。程序运行时包括LOGO的overlay来回运动。
思路
 overlay中即可以包含2D元素也可以包含3D元素。通过overlay的setScroll函数可以其运动。
部分代码
 Ogre.overlay文件中加入如下脚本,注意备份原文件。
 // A silly example of how you would do a 3D cockpit
Examples/KnotCockpit
{
 zorder 100
 entity knot.mesh(hudKnot)
 {
  position 0 0 -50
  rotation 0 0 0 0
 }

}
// another logo overlay
Examples/MyLogo
{
 zorder 100
 container Panel(Examples/LogoPanel)
 { 
  metrics_mode pixels
  horz_align right
  vert_align top
  top 20
  left -165
  width 160
  height 85
  material Core/OgreText
 }

}
 这部分脚本定义了两个Overlay。一个带3D元素一个带2D元素。
 以下是myapp.h文件
#include "ExampleApplication.h"
class myFrameListener : public ExampleFrameListener
{
protected:
 Overlay * pMyOverlayLogo;
public:
    myFrameListener(RenderWindow* win, Camera* cam)
        : ExampleFrameListener(win, cam)
    {
        pMyOverlayLogo = (Overlay*) OverlayManager::getSingleton().getByName("Examples/MyLogo");
        pMyOverlayLogo->show();
 }
    // 重新实现frameStarted函数,在这里控制Overlay的运动
 bool frameStarted(const FrameEvent& evt)
    {
  static float angle = 0.0;
  if( angle >= Math::TWO_PI)
   angle = 0.0;
  angle += 0.1;
  // 在X轴按正弦函数方式运动
pMyOverlayLogo->setScroll(Math::Sin(angle),0);
        //  不要忘了,调用基类的frameStarted函数,以实现用户输入控制(摄象机漫游控制)。
        return ExampleFrameListener::frameStarted(evt);
    }
};

class EnvMapApplication : public ExampleApplication
{
public:
    EnvMapApplication() {}
protected:

    // Just override the mandatory create scene method
    void createScene(void)
    {
        // Set ambient light
        mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));
        // Create a point light
        Light* l = mSceneMgr->createLight("MainLight");
        // Accept default settings: point light, white diffuse, just set position
        // NB I could attach the light to a SceneNode if I wanted it to move automatically with
        //  other objects, but I don't
        l->setPosition(20,80,50);
        Entity *ent = mSceneMgr->createEntity("head", "ogrehead.mesh");
        // Set material loaded from Example.material
        ent->setMaterialName("Examples/EnvMappedRustySteel");
        // Add entity to the root scene node
        mSceneMgr->getRootSceneNode()->createChild()->attachObject(ent);
  // 获取指定名称的Overlay指针
  Overlay * pMyOverlayLogo = (Overlay*) OverlayManager::getSingleton().getByName("Examples/MyLogo");
        // 缺省情况下Overlay被载入后是不显示的,让其显示。
pMyOverlayLogo->show();
  // 获取指定名称的Overlay指针
  Overlay * pMyOverlayKnot = (Overlay*) OverlayManager::getSingleton().getByName("Examples/KnotCockpit");
        // 缺省情况下Overlay被载入后是不显示的,让其显示。
pMyOverlayKnot->show();

    }

 void createFrameListener(void)
    {
        mFrameListener= new myFrameListener(mWindow, mCamera);
  mRoot->addFrameListener(mFrameListener);
 }

};

OGRE会在程序运行时自动载入.overlay文件,缺省情况下是不显示的。在OGRE应用框架的ExampleApplication类中把DebugOverlay的显示开关打开了,其它的overlay必须由程序员手工打开。以上程序在createScene函数中通过OverlayManager::getSingleton().getByName()函数获取到overlay的指针,再通过show函数打开显示开关。
Overlay提供滚动、旋转和缩放函数,在FrameListener控制其滚动很好地达到了动画效果。
实现界面事件处理
 OGRE引擎已经将一些界面元素的基本事件处理完成,例如:按钮被点击时的动作效果、List列表中选项被选择后的特殊显示等。但是按钮被按下后还要完成什么事情需要程序员自己完成,例如Exit按钮被按下后程序要退出、Option按钮被按下后要进入系统设置菜单等。
 OGRE的事件处理目前还未全部完成,请暂时参考OGRE的Demo_Gui。

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值