OGRE 3D程序设计(3)

代码4-5:建立并挂载一个帧监听对象到Ogre的Root中
    class MyFrameListener : public FrameListener{
    public:
    bool frameStarted (const FrameEvent &evt);
    bool frameEnded (const FrameEvent &evt );
    };
    bool MyFrameListener::frameStarted (const FrameEvent &evt){
    //在每一帧画面渲染前,调用这里你写好的代码
    return true;
    }
    bool myFrameListener::frameEnded (const FrameEvent &evt ){
    //在每一帧画面渲染后,调用这里你写好的代码
    return true;
    }
    Root * root = new Root();
    MyFrameListener myListener;
    //在这里你需要在调用startRendering()方法前,注册你的帧监听对象!!!
    root->addFrameListener(myListener);
    root->startRendering();
    当程序执行的时候,会在每一次进入Ogre渲染管线之前调用你定义的frameStarted()方法。相对而言,frameEnded方法并不常用,因为当Ogre完成一次渲染工作之后才会调用它,所以一般而言只有在你需要在每一帧结束时后程序做清理工作时候有用。
    在通常的情况来说,在每次渲染的空隙你都要去处理HID(用户输入接口,比如键盘、鼠标或者操纵杆)。通过分析接收到的事件,你就可以让模型移动或者旋转,也可以让摄像机移动,更或者让NPC的魔法师吟唱一段能让玩家角色睡着的咒语,或者任何需要的事情。但不论是什么么事情,都发生在系统调用帧监听的方法的时候——可能只是frameStarted(),因为我觉得大多数人更喜欢在产生画面前改变游戏状态,而不是马后炮。
    在这本书的支持网站上,你可以下载到这本书相关的代码;其中你可以在CH04解决方案中找到QuakeStart工程;我把上面这些代码放到这个可以工作的应用程序中,你可以从中学到更多的细节。不过其中只有最基本的功能,我简单的让帧监听在启动后15秒钟就结束程序。的确是无聊的程序,不过总算还能帮助你了解Ogre环境最基本的配置。
    Ogre初始化:手动档
    现在到了另一个阶段,我将要告诉你所有设置Ogre应用程序的步骤,而不是简单得调用自动处理的方法,你可以在这里了解“幕后”所发生的一切。同时你也将要学会如何使用自己的主循环来处调用Ogre的帧渲染。
    Root
    在前面的章节中,你了解了构造Root对象实例的所有方法,按照你的喜欢填入配置文件、插件管理文件和日志文件。你也了解了如何通过简单的方法构建Ogre的渲染窗口。所有的这些功能都是靠调用Root中的高级方法来实现的,在接下来我们手动配置的过程中,你会了解到所有具体的细节。
    “手动”似乎意味着你将要控制Ogre初始化过程中所有的细节。但事实上并没有必须这么做的理由;你可以手动的设置渲染系统和插件(你可以在游戏中提供一组GUI来配置),但仍然使用前面提到的自动创建的窗口和自动循环来做渲染。不过在这个章节中,你仍会看到如何全部手工配置,以及了解其中不同设置的功效。
    Plugins.cfg
    载入插件应该是Ogre初始化中最简单的步骤,所以我们将要从这里开始。Root提供了两种方法来处理手动载入插件。
    void loadPlugin(const String& pluginName);
    void unloadPlugin(const String& pluginName);
    上面第一个方法是用来通过名称载入相应插件的。第二个方法是被用来卸载插件。这两种方法所用到的共同参数,就是插件文件的名称,我们可以使用类似“Plugin_ParticleFX”这种字符串来载入相应的插件。你可能注意到了我们有意忽略了插件文件的扩展名,这是Ogre为了“忽略”各种平台之间文件的差异(Windows下的。dll、Mac OS X和Linux下的。so)。当然这并不意味着你会使用一个没有扩展名的插件。在不同平台上,插件仍然有不同的扩展名(Ogre并不是去掉插件的扩展名,而是在你所提供的文件名字上加上执行平台上所需的扩展名)。简单一句话:让自己轻松,让Ogre去做琐碎的“家务”。
    然后开始通过相应平台的规则在目录中寻找插件,Ogre是通过分别调用不同平台的API来实现动态库的载入的(在Windows下面使用的是LoadLibrary(),在Mac OS X和Linux下面使用dlopen())。因此,按照系统平台的差别,程序会首先在包含执行程序的目录下面寻找插件,然后如果是Windows平台就会寻找PATH系统环境变量所指的目录下面,在Linux下面就相应的搜索LD_LIBRARY_PATH环境变量。
    一般来说,Ogre同一个插件的Debug版本和Release版本使用了相同的名字;这可能很容易让你混淆它们。如果你真的搞错了的话,臭名昭着的“ms_singleton”断言就会跳出来让你的程序死掉。
    提示:“ms_singleton”断言经常会出现在你的应用程序试图载入一个相同的动态连接库的Debug和Release版本的时候。这是Ogre采用单件模式所必然导致的结果。Ogre在Root对象在其实例中构建所有遵守单件模式的“唯一”类型的实例,并且允许通过相应的静态方法来操作这些“单件”。一般情况下这些都能工作的很好,但是如果你的程序运行时载入了一个“不匹配”的动态连接库时就会出错。大多是因为插件不匹配的原因(Debug插件插入了Release的程序,或者反之)。当插件进入了不匹配的程序中的时候,它需要Ogre库的一些支持,但事实上它并不知道自己进入了“不匹配”的程序,仍然会索取对于程序“不匹配”的对象,当程序企图提供不匹配的对象的时候,因为之前已经构建了一个实例,这时候单件模式就会发现违反了“唯一性”。最后“当”!……弹出了断言窗口。
    解决Debug与Release插件之间的冲突问题,最简单的办法就是使用Ogre的命名规范,把插件重新命名,就如同OgreMain_d.dll一样,把调试的Debug插件名称后面增加“_d”后缀。然后在你的程序中使用#if defined标记来区分它们,正如下面这样:
    #if defined(_DEGUG)
    root->loadPlugin(“Plugin_ParticleFX_d”);
    #else
    root->loadPlugin(“Plugin_ParticleFX”);
    #endif
    很值得用这一点点精力去避免那些让人烦到脱发的插件“ms_singleton”断言问题。当然了,如果你决定永远不使用Debug模式来构建你的程序,你同样可以避免这个问题。不过,这也意味着你永远不会调试你的程序,节省这小小的精力的代价是——为了解决程序问题,要花费大量时间去阅读你的代码(这时候你可能迫切需要使用《星机迷航》中瓦肯人的心灵融合[1]能力来解决问题了)。所以奉劝你,还是回来使用Debug模式吧。
    基本上你是用不到卸载插件的方法的,因为Root在清理自己的时候,总会把插件合理的释放掉。当然,有时候你会希望在程序结束前尽早释放你不需要的插件,不过相信我,删除Root时候程序自动做的插件卸载工作,已经足够好用了。
    Ogre程序已经给你带来了以下的插件:
    ·Plugin_OctreeSceneManager:以八叉树空间管理为基础的OctreeSceneManager(八叉树场景管理器——OSM)。同时包含了从其中派生出来的TerrainSceneManager(地形场景管理器),用来处理从高度图(heightMapped)派生出来的地形场景。
    ·Plugin_BSPSceneManager:提供一个对BSP场景的管理系统,用来读取和处理来自雷神之锤III中的地图文件。不过在今天看来这已经是一个古老的地图格式,并且已经没有人再维护和支持它了(提供这个插件的唯一原因是为了某个演示程序的执行)。
    ·Plugin_CgProgramManager:这个插件负责载入、分析、编译并且管理Cg语言所写的GPU渲染程序。在今天看来,似乎Cg逐渐被当今技术所抛离(它只能支持3.0版本以前的profiles),因此其价值也越来越小;幸好Ogre在其内部同时支持HLSL和GLSL程序的GPU开发。
    ·Plugin_ParticleFX:粒子系统管理插件;提供了很多粒子的效果器(Affector)和发射器(Emitter),用来实现一些基本的粒子特效。
    ·RenderSystem_Direct3D9:Windows上面对Direct3D 9的抽象层实现。
    ·RenderSystem_GL:针对所有平台上OpenGL的抽象层实现。
    我们将要在后面的场景管理章节中学习到关于OctreeSceneManager得更多内容,也会在更后面的章节中介绍如何控制ParticleFX的高级特性。就像上面我们提到的,这两个插件相对而言更有实际意义(虽然其他的插件也可以良好的工作),因此我们不会在这本书中花精力研究其他插件。
    在插件列表中,你也可以发现一些渲染系统(Render Systems)的插件。当然,他们也是被“插入”到系统中的。在Ogre的设计中,这些图形API被抽象成“可以插入的”,Ogre通过和处理其他插件同样的方法来处理这些系统。
    渲染系统(Render Systems)
    Ogre系统需要一个渲染系统来进行我们希望的工作。你可以使用插件管理的loadPlugin()方法来载入所需的API:
    //建立一个没有配置文件的Root实例
    Root *root = new Root(“”, “”);
    root->loadPlugin(“RenderSystem_Direct3D9”);
    root->loadPlugin(“RenderSystem_GL”);
    上面的代码让两种渲染系统都可以被Ogre使用(你要确定之前已经安装和配置了硬件的驱动)。Root类也提供了用来确认那些API可以被使用的getAvailableRenderers()方法。
    RenderSystemList* getAvailableRenderers();
    Root同时也提供了一些用于设置和得到已经载入渲染系统的方法。
    void addRenderSystem(RenderSystem* naeRend);
    RenderSystem* getRenderSystemByName(const String& name);
    void setRenderSystem(RenderSystem* system);
    RenderSystem* getRenderSystem(void);
    基本上你可能只会用到getAvailableRenderers()和setRenderSystem ()两个方法来检查设置Ogre所使用的渲染系统,更高级的做法是通过用户的选择来决定使用哪个(参考代码4-6)。
    代码4-6:设置Ogre应用程序使用的渲染系统
    //RenderSystemList是std::vector类型
    RenderSystemList *rList = root->getAvailableRenderers();
    RenderSystemList::iterator it = rList->begin();
    while(it != rList->end()){
    //Ogre的字符串类型String是std::string的扩展
    RenderSystem *rSys = *(it++);
    if(rSys->getName()。find(“OpenGL”)){
    //把OpenGL渲染系统设置为我们使用的渲染系统
    root->setRenderSystem(rSys);
    break;
    }
    }
    //注意,如果系统没有支持OpenGL的话,我们就没有设置任何渲染系统!这将会
    //引起一个Ogre设置期间的异常产生。
    上面的代码展示了如何从可用渲染系统列表中找出OpenGL渲染系统,并将其设置导入系统中去。就如同代码中最后两行的注释所说的,如果我们找不到OpenGL渲染系统,就无法设置渲染系统到程序中,这导致调用initialise()方法的时候程序会抛出异常。因此我们必须找到解决这个问题的方法:比如,你可以把可用的系统列表在配置程序的GUI界面提供下拉菜单中给用户选择,然后再根据选择的结果来具体配置使用的渲染系统。这就既能保证渲染系统是从getAvailableRenderers()方法是从中得到的,也可以确定在setRenderSystem()设置了用户选择的渲染系统。
    addRenderSystem()是提供给插件初始化时调用的方法,除非你要通过非插件的方式来设置渲染系统(比如在程序中定制自己的载入方法),否则基本不会用到这方法。getRenderSystemByName()经常被用来通过名称来直接索引得到渲染系统,如果没有所需要的渲染系统就会返回空指针(在这里要注意插件名称的拼写和大小写)。在我们现在所展示的程序中,你可以通过“Direct3D9 Rendering Subsystem”和“OpenGL Rendering Subsystem”来分别得到我们所使用的两个渲染系统。最后,getRenderSystem()方法可以返回当前所使用的渲染系统,如果当前没有使用任何系统就会返回一个空指针。
    渲染窗口(Render Windows)
    如果你希望自己管理渲染窗口的构造参数和指针的时候,你就应该手动的创建应用程序中使用的渲染窗口。这样做的目的可能是:当你希望把渲染窗口融入到一个其他的工具或者系统中(比如Qt或者wxWidges等跨平台库,甚至MFC中),你就需要自己来设置相应的参数(如果你要制作一个关卡或者地图编辑器,所做的这些工作就是必要的)。或者你也可能想要在用户已经设置窗口的属性之后,改变或者增加使用的参数。
    当使用手动方式创建渲染窗口的时候,需要注意到是通过RenderSystem类而不是Root类的方法来创建。因此,当你得到RenderSystem有效的实例之后,你就可以创建一个或者更多的渲染窗口来使用。就好像在前面“Ogre初始化:自动档”中所提及的一样,构建视口(viewport)需要通过RenderWindow的方法一样,所以当你已经自己建立了一个渲染窗口,就可以在Root初始化前构建视口。Root在初始化过程中会进行插件的初始化工作,所以如果你希望初始化插件和初始化渲染窗口有一定次序,你就可以在这里调整(不过并不建议你这么做,毕竟可能有一些对象初始化顺序是互相依赖的)。

注:转载源地址

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值