Ogre启动过程&原理

#include <Ogre.h>
#include <OIS/OIS.h>
#include <CEGUI/CEGUI.h>
#include <OgreCEGUIRenderer.h>
using namespace Ogre;
class ExitListener : public FrameListener
{
public:
    ExitListener(OIS::Keyboard *keyboard)
        : mKeyboard(keyboard)
    {
    }
    bool frameStarted(const FrameEvent& evt)
    {
        mKeyboard->capture();
        return !mKeyboard->isKeyDown(OIS::KC_ESCAPE);
    }
private:
    OIS::Keyboard *mKeyboard;
};
class Application
{
public:
    void go()
    {
        /************************************************************************
        1、创建Root对象。 
        2、定义Ogre将要使用的资源。 
        3、选择并设置渲染系统(即DirectX, OpenGL等)。 
        4、创建渲染窗口(Ogre所处的窗口)。 
        5、初始化你要使用的资源。 
        6、用这些资源来建立一个场景。 
        7、设置第三方库或插件。 
        8、创建一些帧监听器。 
        9、启动渲染循环 
        ************************************************************************/
        createRoot();
        defineResources();
        setupRenderSystem();
        createRenderWindow();
        initializeResourceGroups();
        setupScene();
        setupInputSystem();
        setupCEGUI();
        createFrameListener();
        startRenderLoop();
    }
    ~Application()
    {
        /************************************************************************
        最后一件事是,当程序终止时,我们要对所创建的所有对象进行清理。为止,我们
        将按照与创建时相反的顺序来删除或销毁这些对象。我们从OIS开始下手,它有一个
        专门的方法来销毁它的对象。添加如下代码: 
        ************************************************************************/
        mInputManager->destroyInputObject(mKeyboard);
        OIS::InputManager::destroyInputSystem(mInputManager);
        /************************************************************************
        现在我们来清理CEGUI,只要删除对象即可:
        ************************************************************************/
        delete mRenderer;
        delete mSystem;
        /************************************************************************
        最后,我们要删除Root和FrameListener对象。当删除Root对象时,我们创建的其它
        对象(SceneManager, the RenderWindow等)也会一并删除。 
        ************************************************************************/
        delete mListener;
        delete mRoot;
        /************************************************************************
        好了! 你现在可以编译并运行你的程序了,虽然你将只能看见一个黑屏,因为我们
        并没有往场景添加任何东西。如果在编译里遇到链接问题,确保CEGUIBase_d.lib
        和OgreGUIRenderer_d.lib添加到了链接器的输入里(这是debug模式的,如果是release
        模式,去掉_d)。现在你应该对Ogre的启动过程比较熟悉了,可以抛开示例框架而使
        用其它的了。
        ************************************************************************/
    }
private:
    Root *mRoot;
    OIS::Keyboard *mKeyboard;
    OIS::InputManager *mInputManager;
    CEGUI::OgreCEGUIRenderer *mRenderer;
    CEGUI::System *mSystem;
    ExitListener *mListener;
    void createRoot()
    {
        /************************************************************************
        1、创建Root对象 
        Root的构造函数需要三个参数。第一个是插件配置文件的名称和路径。
        第二个是Ogre配置文件的路径(它告诉Ogre关于显卡、显示设置等信息)。
        最后一个是日志文件的名称和路径。因为我们不需要修改任何一个属性,所以用默认的。
        ************************************************************************/
        mRoot = new Root();
    }
    void defineResources()
    {
        /************************************************************************
        2、资源
        注意: 你最好在SDK的bin/release目录里找到resources.cfg,看看它的内容是很有帮助的。 
        下面我们要定义程序将要使用的资源,包括纹理、模型、脚本等等。请记住,你必须预先定
        义好你的资源,在Ogre能使用它们之前,你还必须对它进行初始化。在这一步里,我们来定义
        所有程序可能使用的资源。为止,我们把每一个资源所在的文件夹添加到ResourceGroupManager。
        ************************************************************************/
        String secName, typeName, archName;
        ConfigFile cf;
        cf.load("resources.cfg");
        /************************************************************************
        这里使用了一个Ogre的ConfigFile类来解析"resources.cfg"里的所有的资源,
        但并不是把它们装入到Ogre(你必须手工添加)。记住,你可以自由地使用你自己的
        文件格式和解析器,只需要用你自己的解析器来替换ConfigFile的。装入资源的方
        式并不十分重要,只要你能把资源添加到ResourceGroupManager。现在我们解析好
        了cfg文件,还需要把各部分添加到ResourceGroupManager中。以下代码启动一个循
        环: 
        ************************************************************************/
        ConfigFile::SectionIterator seci = cf.getSectionIterator();
        while (seci.hasMoreElements())
        {
            /************************************************************************
            每次循环里,我们再循环一次,提取它里面所有的内容:
            ************************************************************************/
            secName = seci.peekNextKey();
            ConfigFile::SettingsMultiMap *settings = seci.getNext();
            ConfigFile::SettingsMultiMap::iterator i;
            /************************************************************************
            添加部件名称(那一组资源的),资源类型(zip,文件夹等等),以及资源本身的文件
            名,给ResourceGroupManager: 
            ************************************************************************/
            for (i = settings->begin(); i != settings->end(); ++i)
            {
                typeName = i->first;
                archName = i->second;
                ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
            }
        }
    }
    void setupRenderSystem()
    {
        /************************************************************************
        3、创建渲染系统 
        接下来,我们需要选择一个渲染系统(在Windows机器上通常是DirectX或者OpenGL),
        然后配置它。大多数Demo程序使用的是一个Ogre配置对话框,这是一个很好的一个东
        东。Ogre提供了一种保存用户设置的方法,意味着除了第一次需要设置外,以后都不
        需要了。并添加以下代码: 
        ************************************************************************/
        if (!mRoot->restoreConfig() && !mRoot->showConfigDialog())
            throw Exception(52, "User canceled the config dialog!", "Application::setupRenderSystem()");
        /************************************************************************
        在if语句里的第一部分,尝试恢复这个config文件。如果函数返回false,意味着文
        件不存在,则应该显示配置对话框,也就是if语句的第二部分。如果仍然返回false
        ,意味着用户取消了配置对话框(也就是他们想退出程序)。在这个例子里,抛出了一
        个异常,但实际上简单地返回false且关闭应用程序,这样可能更好。由于 restoreConfig
        和 showConfigDialog 也可能抛出异常,保存这些真正的异常可能更好。然而,这将
        增加了教程的不必要的复杂度,我在这里只使用了一个异常。 
        如果你在Ogre的启动过程中捕获了一个异常,最好在catch里删除这个ogre.cfg文件。
        因为他们在配置对话框里进行的设置可能导致了问题的发生,所以他们需要更改它。
        你也可以不使用它,关闭配置对话框可以节省开发时间,因为你不必每次程序运行时
        确认这些显示设置。
        如果你不打算使用Ogre的配置对话框,你需要手动地设置渲染系统。以下是一个基本
        的例子: 
        ************************************************************************/
        // Do not add this to the application
        RenderSystem *rs = mRoot->getRenderSystemByName("Direct3D9 Rendering Subsystem");
                                              // or use "OpenGL Rendering Subsystem"
        mRoot->setRenderSystem(rs);
        rs->setConfigOption("Full Screen", "No");
        rs->setConfigOption("Video Mode", "1440 x 900 @ 32-bit colour");
    }
    void createRenderWindow()
    {
        /************************************************************************
        4、创建渲染窗口
        目前选择了一个渲染系统,还需要一个渲染Ogre的窗口。实际上有许多种方式来实现
        ,但这里只介绍两种。 
        ************************************************************************/
        mRoot->initialise(true, "Tutorial Render Window");
        /************************************************************************
        第一个参数表示是否让Ogre为你创建一个渲染窗口。否则,你可以自己创建一个渲
        染窗口,通过使用win32 API、wxWidgets或其它Windows/Linux的GUI系统。关于在
        Windows下的一个简单例子是这样: 
        ************************************************************************/
         Do not add this to the application
        //mRoot->initialise(false);
        //HWND hWnd = 0;  // Get the hWnd of the application!
        //NameValuePairList misc;
        //misc["externalWindowHandle"] = StringConverter::toString((int)hWnd);
        //RenderWindow *win = mRoot->createRenderWindow("Main RenderWindow", 800, 600, false, &misc);
        /************************************************************************
        在这里你仍然使用Root::initialise,第一个参数设置成了false。然后,你必须获
        取你希望Ogre渲染的窗口的句柄。你如何取得它,完全决定于你用来创建窗口的GUI
        工具箱(在Linux下我估计这有一点区别)。你拥有了它之后,你通过NameValuePairList
        handle把这个句柄赋予"externalWindowHandle"。Root::createRenderWindow方法被
        用来从你创建的窗口来创建RenderWindow对象。想了解更多,参考这个方法的API文
        档。
        ************************************************************************/
    }
    void initializeResourceGroups()
    {
        /************************************************************************
        5、初始化资源
        现在我们创建了Root对象、渲染系统、以及渲染窗口,继续。接下来是初始化将要使
        用的资源。从mesh到脚本,所有的东西,在某一时刻,我们只用到这些资源其中的一
        小部分。为了减少内存消耗,我们可以只加载正在使用的资源。为止,我们把资源分
        解成各种部分,只在运行时初始化它们。在本课里,不将详细介绍。其它的地方有一
        个专门介绍资源的教程。初始化资源之前,我们应该设置纹理mipmap的缺省值。添加
        如下代码: 
        ************************************************************************/
        TextureManager::getSingleton().setDefaultNumMipmaps(5);
        ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
    }
    void setupScene()
    {
        /************************************************************************
        6、创建场景
        你应该了解在把各种东西添加到场景之前,你要做三件事:创建场景管理器
        (SceneManager)、创建摄像机(Camera)、创建视口(Viewport)。在setupScene
        方法里添加如下代码:
        ************************************************************************/
        SceneManager *mgr = mRoot->createSceneManager(ST_GENERIC, "Default SceneManager");
        Camera *cam = mgr->createCamera("Camera");
        Viewport *vp = mRoot->getAutoCreatedWindow()->addViewport(cam);
        /************************************************************************
        如果需要的话,可以创建多个场景管理器、多个摄像机,但当真正打算使用摄像机
        把事物渲染到屏幕上时,请确保已经为它添加了视口中。为止,要借助RenderWindow
        类,它在“创建渲染窗口”一节里被建立。由于我们没有这个对象的指针,所以我们
        通过Root::getAutoCreatedWindow方法来获取它。 
        这三件事完了以后,你可以尽情地往你的场景里添加物体了。 
        ************************************************************************/
    }
    void setupInputSystem()
    {
        /************************************************************************
        7、设置第三方库
        OIS 
        虽然在OGRE里,OIS不是唯一的选择,但它是最好的之一。我来简单介绍一下OIS如何
        在程序里启动。若真想要使用这个库,请参考这个教程,以及OIS自身的文档。 
        设置无缓冲输入 
        OIS使用一个统一的InputManager,它比较难配置,但一旦正确地创建之后,非常好使
        用。实际上,它只是需要Ogre渲染窗口的句柄。幸好,由于我们使用的是自动创建的窗
        口,Ogre使之简化了。添加如下代码: 
        ************************************************************************/
        size_t windowHnd = 0;
        std::ostringstream windowHndStr;
        OIS::ParamList pl;
        RenderWindow *win = mRoot->getAutoCreatedWindow();
        win->getCustomAttribute("WINDOW", &windowHnd);
        windowHndStr << windowHnd;
        pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
        mInputManager = OIS::InputManager::createInputSystem(pl);
        /************************************************************************
        这样InputManager就建好了,但为了从键盘、鼠标、或是手柄中获得输入,你还必
        须创建这些对象:
        ************************************************************************/
        try
        {
            mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject(OIS::OISKeyboard, false));
            //mMouse = static_cast<OIS::Mouse*>(mInputManager->createInputObject(OIS::OISMouse, false));
            //mJoy = static_cast<OIS::JoyStick*>(mInputManager->createInputObject(OIS::OISJoyStick, false));
        }
        catch (const OIS::Exception &e)
        {
            throw new Exception(42, e.eText, "Application::setupInputSystem");
        }
        /************************************************************************
        我把Mouse和Joystick对象注释掉了,因为这里我们不使用,但它们就是这样创建的。
        InputManager::createInputObject的第二个参数是指是否使用带缓冲输入(在以前的
        教程里介绍过)。把第二个参数设置成false,创建了一个无缓冲的输入对象,我们这
        里就使用这个。 
        ************************************************************************/
    }
    void setupCEGUI()
    {
        /************************************************************************
        CEGUI 
        CEGUI是直接整合到Ogre里的一个非常灵活的GUI库。在这里不使用CEGUI的任何功能
        ,但我还是来简单介绍一下它的设置。CEGUI需要RenderWindow和SceneManager以供
        渲染。 
        ************************************************************************/
        SceneManager *mgr = mRoot->getSceneManager("Default SceneManager");
        RenderWindow *win = mRoot->getAutoCreatedWindow();
        // CEGUI setup
        mRenderer = new CEGUI::OgreCEGUIRenderer(win, Ogre::RENDER_QUEUE_OVERLAY, false, 3000, mgr);
        mSystem = new CEGUI::System(mRenderer);
        /************************************************************************
        就这样,你就能使用CEGUI了。如果你程序中途你改变了SceneManager,你必须通知
        CEGUI应该渲染到一个新的SceneManager。
        为止,使用OgreCEGUIRenderer::setTargetSceneManager就行了。 
        ************************************************************************/
        // Other CEGUI setup here.
    }
    void createFrameListener()
    {
        /************************************************************************
        8、渲染循环以及最后的工作
        帧监听 
        在我们开始渲染循环,并让程序运行之前,我们还需要添加帧监听器。请注意我已经
        创建一个非常简单的帧监听器,名为ExitListener,它等待ESC键被按下,以退出程
        序。在你的程序里,我可能需要更多的帧监听器,来做更复杂的事情。看一下这个
        ExitListener,确保了解它的流程。
        ************************************************************************/
        mListener = new ExitListener(mKeyboard);
        mRoot->addFrameListener(mListener);
    }
    void startRenderLoop()
    {
        /************************************************************************
        9、渲染循环 
        最后我们要做的是启动Ogre的渲染循环。非常简单,添加如下代码: 
        ************************************************************************/
        mRoot->startRendering();
        /************************************************************************
        这样程序就开始渲染,直到FrameListener返回false。你也可以提取单个帧,并在
        每帧之间做一些事情。Root::renderOneFrame渲染一帧,如何任何一个FrameListener
        返回false,它也返回false: 
        ************************************************************************/
         Do not add this to the application
        //while (mRoot->renderOneFrame())
        //{
        //    // Do some things here, like sleep for x milliseconds or perform other actions.
        //}
        /************************************************************************
        然而,在我看来,你应该把所有while循环里的代码转移到FrameListener。我能想
        到的这种模式的唯一用处就是,中途睡眠某些毫秒,从而人为地降低帧率到某一个
        值。一般在FrameListener里不会这样做,因为它会与FrameEvent::timeSinceLastFrame
        变量搞混淆。 
        ************************************************************************/
    }
};
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)
#else
int main(int argc, char **argv)
#endif
{
    try
    {
        Application app;
        app.go();
    }
    catch(Exception& e)
    {
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
        MessageBoxA(NULL, e.getFullDescription().c_str(), "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
        fprintf(stderr, "An exception has occurred: %s/n",
            e.getFullDescription().c_str());
#endif
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值