OGRE启动过程详解(OGRE HelloWorld程序原理解析)
本文介绍 OGRE 3D 1.9 程序的启动过程,即从程序启动到3D图形呈现,背后有哪些OGRE相关的代码被执行。会涉及的OGRE类包括:
- Root
- RenderSystem
- RenderWindow
- ResourceGroupManager
- LogManager
- Viewport
- SceneManager
- Camera
- SceneNode
- Entity
- Light
建议在阅读本文时参考OGRE API Reference,OGRE官方给的API Reference没有类的协作图,可以自己用Doxygen生成API文档,见:Bullet的学习资源(用Doxygen生成API文档)。
关于如何安装OGRE和如何配置一个可以运行的OGRE HelloWorld程序见:OGRE 1.9 的第一个程序(OGRE HelloWorld程序)。
本节所有代码如下,可以先迅速浏览,然后看后面详细解释,后面将用“启动代码”来指代这段代码:
- #include<OgreRoot.h>
- #include<OgreRenderSystem.h>
- #include<OgreRenderWindow.h>
- #include<OgreConfigFile.h>
- #include<OgreResourceGroupManager.h>
- #include<OgreLogManager.h>
- #include<OgreViewport.h>
- #include<OgreSceneManager.h>
- #include<OgreCamera.h>
- #include<OgreLight.h>
- #include<OgreEntity.h>
- int main(int argc, char *argv[])
- {
- Ogre::Root* mRoot;
- Ogre::RenderWindow* mWindow;
- Ogre::SceneManager* mSceneMgr;
- Ogre::Camera* mCamera;
- // 创建Root,在调用OGRE任何功能之前必须已经创建了Root
- mRoot = new Ogre::Root("plugins.cfg","ogre.cfg","Ogre.log");
- // 设定 RenderSystem
- Ogre::RenderSystem *rs =
- mRoot->getRenderSystemByName("OpenGL Rendering Subsystem");
- mRoot->setRenderSystem(rs);
- rs->setConfigOption("Full Screen", "No");
- rs->setConfigOption("Video Mode", "800x600 @ 32-bit colour");
- // 另一种方法是: if(!mRoot->showConfigDialog()) return false;
- // 初始化 RenderSystem
- mRoot->initialise(false);
- // 创建 RenderWindow
- int hWnd = ;
- Ogre::NameValuePairList misc;
- misc["externalWindowHandle"] = Ogre::StringConverter::toString((int)hWnd);
- mWindow = mRoot->createRenderWindow("Win Ogre", , , false, &misc);
- // 上2步的另一种实现是: mWindow = mRoot->initialise(true, "Win Ogre");
- // 创建SceneManager,将渲染目标绑定到RenderWindow
- mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC);
- // Create one camera
- mCamera = mSceneMgr->createCamera("PlayerCam");
- mCamera->setNearClipDistance();
- // Create one viewport, entire window
- Ogre::Viewport* vp = mWindow->addViewport(mCamera);
- vp->setBackgroundColour(Ogre::ColourValue(,,));
- // Alter the camera aspect ratio to match the viewport
- mCamera->setAspectRatio(
- Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight()));
- // 加载资源,该歩不能早于RenderSystem的初始化和RenderWindow的创建
- // 如果使用OverlaySystem,该歩也不能早于OverlaySystem的创建
- Ogre::ConfigFile cf; cf.load("resources.cfg");
- Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
- Ogre::String secName, typeName, archName;
- while( seci.hasMoreElements() ){
- secName = seci.peekNextKey();
- Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
- Ogre::ConfigFile::SettingsMultiMap::iterator i;
- for( i=settings->begin(); i!=settings->end(); ++i ){
- typeName = i->first;
- archName = i->second;
- Ogre::ResourceGroupManager::getSingleton().
- addResourceLocation(archName, typeName, secName);
- }
- }
- Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
- // 构造及设置场景
- mSceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_ADDITIVE);
- mSceneMgr->setAmbientLight(Ogre::ColourValue(0.2f, 0.2f, 0.2f));
- Ogre::Entity* entNinja = mSceneMgr->createEntity("entNinja", "ninja.mesh");
- Ogre::SceneNode* nodeNinja = mSceneMgr->createSceneNode("nodeNinja");
- mSceneMgr->getRootSceneNode()->addChild(nodeNinja);
- nodeNinja->attachObject(entNinja);
- Ogre::Entity* entSphere = mSceneMgr->createEntity("entSphere", "sphere.mesh");
- Ogre::SceneNode* nodeSphere = mSceneMgr->createSceneNode("nodeSphere");
- mSceneMgr->getRootSceneNode()->addChild(nodeSphere);
- nodeSphere->attachObject(entSphere);
- nodeNinja->setPosition(-,-,);
- nodeSphere->translate(,,);
- Ogre::Light* pointLight1 = mSceneMgr->createLight("pointLight1");
- pointLight1->setType(Ogre::Light::LT_POINT);
- pointLight1->setDiffuseColour(Ogre::ColourValue::White);
- pointLight1->setSpecularColour(Ogre::ColourValue::White);
- pointLight1->setPosition(-,,-);
- mCamera->setPosition(Ogre::Vector3(,,-));
- mCamera->lookAt(Ogre::Vector3(,,));
- // 渲染循环
- Ogre::LogManager::getSingleton().logMessage(">>Rendering");
- mRoot->startRendering();
- // 释放资源,目前只需释放Root
- delete mRoot;
- return ;
- }
运行结果截图:
1. 启动过程概览
我们概要地看OGRE的启动,OGRE WIKI Basic Tutorial 6: The Ogre Startup Sequence中摘出下面这段,注意它和上面的代码(“启动代码”)是有差别的,各步骤的顺序不同:
The basic Ogre life cycle looks like this:
- Create the Root object.
- Define the resources that Ogre will use.
- Choose and set up the RenderSystem (that is, DirectX, OpenGL, etc).
- Create the RenderWindow (the window which Ogre resides in).
- Initialise the resources that you are going to use.
- Create a scene using those resources.
- Set up any third party libraries and plugins.
- Create any number of frame listeners.
- Start the render loop.
总的来说,先是初始化,最后启动渲染循环。我将所有这些类的关系总结如下图(不是什么UML图,就大致理解吧):
看完后面的详细解释后可以回过头来看这段,那时你就会对OGRE的启动有个大致印象。
2. 创建Root
在调用OGRE任何功能之前,首先要实例化一个Root类,该Root实例将直接或间接指向所有其他类的实例。一个OGRE程序有且只有一个Root对象,因此Root类使用Singleton设计模式(单例模式,继承自Singleton<Root>)。说到Singleton,OGRE的很多类都是Singleton,后面还会讲的。
Root类的构造函数原型如下:
- Root (const String &pluginFileName="plugins"OGRE_BUILD_SUFFIX".cfg",
- const String &configFileName="ogre.cfg", const String &logFileName="Ogre.log")
其中OGRE_BUILD_SUFFIX宏在Release下定义为空,Debug下定义为"_d"。三个参数是三个文件名。
pluginFileName是插件配置文件,该文件指示OGRE要加载哪些插件,一个plugins.cfg文件的例子如下,其中#表示注释:
- # Defines plugins to load
- # Define plugin folder
- PluginFolder=.
- # Define plugins
- # Plugin=RenderSystem_Direct3D9
- Plugin=RenderSystem_GL
- Plugin=Plugin_ParticleFX
- Plugin=Plugin_BSPSceneManager
- Plugin=Plugin_CgProgramManager
- Plugin=Plugin_PCZSceneManager
- Plugin=Plugin_OctreeZone
- Plugin=Plugin_OctreeSceneManager
configFileName文件设置渲染系统(OpenGL或者Direct3D)及其参数,如抗锯齿采样数(FSAA),一个针对OpenGL驱动的配置文件ogre.cfg例子如下:
- Render System=OpenGL Rendering Subsystem
- [OpenGL Rendering Subsystem]
- Colour Depth=
- Display Frequency=N/A
- FSAA=
- Fixed Pipeline Enabled=Yes
- Full Screen=No
- RTT Preferred Mode=FBO
- VSync=No
- VSync Interval=
- Video Mode= x
- sRGB Gamma Conversion=No
logFileName文件是OGRE程序的日志文件,在OGRE程序可以插入写日志的代码,日志文件方便对OGRE程序的调试。向日志文件写入信息的代码的一个例子如下:
- Ogre::LogManager::getSingleton().logMessage(">>Rendering");
这里的LogManager是另一个使用Singleton设计模式的类,这种类使用静态方法getSingleton获取全局唯一的类实例。
“启动代码”中创建Root对象的代码在第21行:
- mRoot = new Ogre::Root("plugins.cfg","ogre.cfg","Ogre.log");
3. 设定RenderSystem,初始化
RenderSystem类对渲染系统(底层的OpenGL或Direct3D)进行抽象,它相当于是执行渲染的设备。给 Root 添加一个RenderSystem实例的最简单方式是调用Ogre::Root:: showConfigDialog方法,运行时系统将弹出如下对话框,让用户选择要使用的图形驱动,以及相应的参数:
- if(!mRoot->showConfigDialog()) return false;
我们在这个对话框所做的设置被记录在ogre.cfg文件中(见上面第2节)。也可以不用对话框,而在程序中设置,也就是说在程序中设置我们在对话框所选的项:
- Ogre::RenderSystem *rs = mRoot->getRenderSystemByName("OpenGL Rendering Subsystem");
- mRoot->setRenderSystem(rs);
- rs->setConfigOption("Full Screen", "No");
- rs->setConfigOption("Video Mode", "800x600 @ 32-bit colour");
“启动代码”使用的是后者,代码在第24-27行。
如果不想每次都弹出对话框选择渲染系统,可以用如下代码:
- if( !(mRoot->restoreConfig() || mRoot->showConfigDialog()) )
- return false;
restoreConfig方法读入ogre.cfg文件来代替对话框设置,还记得C/C++逻辑运算表达式求值的短路性质吧,如果mRoot->restoreConfig()返回true(存在ogre.cfg文件),mRoot->showConfigDialog()将不被执行。
RenderSystem对象创建后需要初始化,Ogre::Root::initialise(bool, const String, const String)方法就是初始化root的RenderSystem的,如果第一个bool参数为true,将自动创建窗口,“启动代码”没有这样做,在第31行:
- mRoot->initialise(false);
另外还要说的是,OGRE作为一个跨平台的高层3D图形库,对图形系统进行了高度抽象,这种抽象使用户不需要关心底层技术(如OpenGL或Direct3D、win32或Xwindow),但程序的执行必然会用到底层功能(如具体渲染任务必然是OpenGL或Direct3D执行)。OGRE(或者其他很多开源程序库)是这样做到这一点的:用户使用基类(如RenderSystem和RenderWindow)接口和OGRE进行交互,代码执行时程序自动根据系统配置调用相应子类的实现来执行命令(这得益于面向对象的继承性和多态性)。RenderSystem类的继承图如下:
对于我们,使用的是OpenGL图形驱动,所以到程序执行时,实际使用的是GLRenderSystem的实现。其实RenderSystem压根就是个抽象类,不能被实例化。
4. 创建 RenderWindow
RenderWindow是对窗口的抽象,该窗口用来显示渲染结果(对于离线渲染或渲染到纹理则不需要窗口)。创建窗口最简单的方法是在调用Ogre::Root::initialise方法时传入true作为第一个参数:
- mWindow = mRoot->initialise(true, "Win Ogre");
但“启动代码”为了代码的清晰,使用了手动创建RenderWindow的方法:
- int hWnd = ;
- Ogre::NameValuePairList misc;
- misc["externalWindowHandle"] = Ogre::StringConverter::toString((int)hWnd);
- mWindow = mRoot->createRenderWindow("Win Ogre", , , false, &misc);
注意上面使用的NameValuePairList类是用来构造参数的,你可能发现了,OGRE的很多参数都使用string数据类型。
5. 创建SceneManager,将渲染目标绑定到RenderWindow
SceneManager类管理OGRE的场景图形(Scene Graph),《Ogre 3D 1.7 Beginner's Guide》的Chapter 6中将SceneManager的功能总结为两个方面:
- 管理Camera, SceneNode, Entity, Light等场景中的对象,作为Factory提供create方法如createEntity(), createLight()(也负责释放它们);
- 管理场景图形,包括维护场景树的可用性,计算节点的Transform信息,隐藏面剔除(Culling)。
SceneManager不是Singleton,可以从Root创建一个(或多个)SceneManager,“启动代码”的第41行创建了一个普通类型的SceneManager:
- mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC);
有了SceneManager,就可以从这个Factory创建场景的对象了,主要是Camera, SceneNode, Entity, Light,构建场景(构建场景树)留到后面说,这里说Camera和RenderWindow的对应关系。
Camera是场景到窗口输出的媒介,负责将3D场景映射到2D窗口,这个映射涉及到另一个类Viewport,Viewport将Camera对场景的“拍摄”结果“贴”到窗口的全部可绘制区域的一个矩形部分。一个Root可以有多个SceneManager,一个SceneManager中也可以有多个Camera,但每个Camera都需要一个Viewport对应到窗口的一个矩形区域。现在你应该知道怎样把一个场景的不同视角,或者多个场景绘制到一个窗口的不同区域了吧。
“启动代码”中创建Camera的代码在第43行:
- mCamera = mSceneMgr->createCamera("PlayerCam");
- mCamera->setNearClipDistance();
其中也设置了Camera的近裁剪面。“启动代码”中创建建Viewport的代码在随后的第46行:
- // Create one viewport, entire window
- Ogre::Viewport* vp = mWindow->addViewport(mCamera);
- vp->setBackgroundColour(Ogre::ColourValue(,,));
- // Alter the camera aspect ratio to match the viewport
- mCamera->setAspectRatio(
- Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight()));
其中也设置了Viewport背景颜色和Camera长宽比例,addViewport是RenderWindow的方法,并以Camera为参数,这样就把RenderWindow和Camera联系起来了,正如我们所分析的。另外addViewport方法还有其他参数用来指定Viewport在窗口的哪一区域,上述代码使用了缺省参数,即将Viewport对应到整个窗口。
同第4节最后说的,RenderWindow是抽象类,具体的和窗口相关的功能实际是由子类实现的,在windows上,这个子类是Win32Window。
6. 加载资源
OGRE的资源文件是OGRE的一个特色,最常见的资源文件莫过于Mesh(.mesh)和Material(.material)了,注意Mesh是一个可渲染物体,而不仅仅是一个网格,Material定义可渲染物体除几何信息外的其他所有属性,可以是而不限于颜色、纹理、着色器什么的。
资源文件的一个好处就是当修改了物体的外观等信息之后,不需要重新编译程序,如果将物体的顶点数据什么的写在代码里那就当然要重新编译啦。资源文件的缺点,程序在启动时要对资源文件进行解析(分析脚本),这增加了程序启动时间,这也是HelloWorld程序需要好几秒之后才能看见图形的原因。另一个缺点,对于初学者来说,最初可能就是想画一个长方体,但在OGRE里,你就需要创建Mesh资源。当然啦,OGRE作为面向而不限于3D游戏的3D引擎,强大的资源管理能力可以大大提高开发效率,应当说,正是资源文件的庞杂换来了程序代码的简洁。
有关OGRE对资源文件处理的细节见:Resources and ResourceManagers,要使用一个程序外定义(即脚本定义)的资源,需要:
- 用ResourceGroupManager::addResourceLocation方法添加资源文件所在目录;
- 用ResourceGroupManager::declareResource方法声明(declare)资源,可选的;
- 用ResourceGroupManager::initialiseResourceGroup或ResourceGroupManager::initialiseAllResourceGroups方法初始化所添加目录中的资源文件脚本;
- 默认下,资源文件的数据直到该资源使用时才被加载,如一个纹理的图片并不是在纹理定义时加载,而是在纹理被首次使用时加载至内存,也可以手动调用ResourceGroupManager::loadResourceGroup加载。
上面的第一步在“启动代码”中对应代码如下,位于第54-67行:
- Ogre::ConfigFile cf; cf.load("resources.cfg");
- Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
- Ogre::String secName, typeName, archName;
- while( seci.hasMoreElements() ){
- secName = seci.peekNextKey();
- Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
- Ogre::ConfigFile::SettingsMultiMap::iterator i;
- for( i=settings->begin(); i!=settings->end(); ++i ){
- typeName = i->first;
- archName = i->second;
- Ogre::ResourceGroupManager::getSingleton().
- addResourceLocation(archName, typeName, secName);
- }
- }
其中"resources.cfg"是资源文件名字,文件内容如下(为了简洁,删减了一些):
- # Resources required by the sample browser and most samples.
- [Essential]
- Zip=../../media/packs/SdkTrays.zip
- Zip=../../media/packs/profiler.zip
- FileSystem=../../media/thumbnails
- # Common sample resources needed by many of the samples.
- # Rarely used resources should be separately loaded by the
- # samples which require them.
- [Popular]
- FileSystem=../../media/fonts
- FileSystem=../../media/models
- Zip=../../media/packs/cubemap.zip
- Zip=../../media/packs/cubemapsJS.zip
- [General]
- FileSystem=../../media
- # Materials for visual tests
- [Tests]
- FileSystem=../../media/../../Tests/Media
Ogre::ConfigFile是一个资源配置文件解析的辅助类,类似于XML解析,和代码对应,Essential、Popular、Popular是secName,这是OGRE为方便对资源进行管理而分的组,每个settings的格式为:typeName= archName(参数类型=参数值)。
注意下面这句代码:
- Ogre::ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
OGRE有很多xxxManager类,它们负责管理特定事物,如ResourceGroupManager提供对资源组的操作,LogManager提供写日志文件功能,SceneManager管理场景图形等等。这些Manager中的很多,比如LogManager和ResourceGroupManager使用Singleton设计模式,可以调用静态方法getSingleton获取全局唯一的实例。但SceneManager不是单例模式的,因为一个Root可以有多个场景图形(场景树)。
“启动代码”中declare资源的代码如下,在第68行:
- Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
这里采取的是简单粗暴的方式,解析资源目录的所有脚本,怪不得程序启动后要等那么久了。
注意declare资源有时不能进行的太早,例如,不能早于RenderSystem的初始化和RenderWindow的创建,如果使用OverlaySystem,也不能早于OverlaySystem的创建。原因同第3节最后分析的,因为资源的解析具体是由子类实现的,在没有确定使用的是RenderSystem和RenderWindow的哪个子类前,不能确定使用哪个解析资源的子类,例如,RenderSystem的不同子类GLRenderSystem或D3D9RenderSystem,使用的纹理解析的类不同,如下图:
截止目前你应该了解plugins.cfg、ogre.cfg、Ogre.log、resources.cfg文件的作用了吧。
7. 构造场景树
目前大多数3D图形库采用了场景图形(Scene Graph)技术,即用场景树来组织场景的所有物体,场景树的节点可分为两种:分支节点和叶节点。分支节点SceneNode(继承自Node)主要负责空间位置变化,叶节点可以为:Entity(可绘制实体),Light,Camera等。关于场景树,最需要了解的是,叶节点对象在世界坐标中的最终位置是由父节点级联决定的。一个典型的场景树如下图:
Entity3的世界坐标由Node5、Node4、Node2联合决定(世界坐标计算方式可以修改)。每个Node都有一些空间变换方法:setPosition、setOrientation、setScale、translate、rotate、scale,其中前三个是覆盖式修改,后三个是增量式修改。用Ogre::Node::addChild方法连接两个Node,用Ogre::SceneNode::attachObject方法连接Node和叶节点。上图有一个容易混淆的地方:Light1是不是只作用于Node4子树呢,答案是否定的,Light1作用于整个场景树,Camera1也是类似的。Light和Camera总是作用于整个场景树,其上级Node只起到对其世界坐标进行变换的作用。
注意,一个可用的场景树不能有循环路径,如下图的场景树,OGRE程序运行时会抛出异常:
可以调用Ogre::SceneManager::setShadowTechnique方法设置阴影,Ogre::SceneManager::setSkyBox方法设置天空,Ogre::SceneManager::setFog方法设置雾效果。
“启动代码”构建了一个简单的场景,代码在第71-91行:
- mSceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_ADDITIVE);
- mSceneMgr->setAmbientLight(Ogre::ColourValue(0.2f, 0.2f, 0.2f));
- Ogre::Entity* entNinja = mSceneMgr->createEntity("entNinja", "ninja.mesh");
- Ogre::SceneNode* nodeNinja = mSceneMgr->createSceneNode("nodeNinja");
- mSceneMgr->getRootSceneNode()->addChild(nodeNinja);
- nodeNinja->attachObject(entNinja);
- Ogre::Entity* entSphere = mSceneMgr->createEntity("entSphere", "sphere.mesh");
- Ogre::SceneNode* nodeSphere = mSceneMgr->createSceneNode("nodeSphere");
- mSceneMgr->getRootSceneNode()->addChild(nodeSphere);
- nodeSphere->attachObject(entSphere);
- nodeNinja->setPosition(-,-,);
- nodeSphere->translate(,,);
- Ogre::Light* pointLight1 = mSceneMgr->createLight("pointLight1");
- pointLight1->setType(Ogre::Light::LT_POINT);
- pointLight1->setDiffuseColour(Ogre::ColourValue::White);
- pointLight1->setSpecularColour(Ogre::ColourValue::White);
- pointLight1->setPosition(-,,-);
- mCamera->setPosition(Ogre::Vector3(,,-));
- mCamera->lookAt(Ogre::Vector3(,,));
8. 渲染循环
调用Root::startRendering方法进入渲染循环,渲染结束释放Root:
- Ogre::LogManager::getSingleton().logMessage(">>Rendering");
- mRoot->startRendering();
- // 释放资源,目前只需释放Root
- delete mRoot;
其中使用LogManager这个Singleton类的功能向日志文件中写入了信息。
startRendering函数实现如下:
- void Root::startRendering(void)
- {
- assert(mActiveRenderer != );
- mActiveRenderer->_initRenderTargets();
- // Clear event times
- clearEventTimes();
- // Infinite loop, until broken out of by frame listeners
- // or break out by calling queueEndRendering()
- mQueuedEnd = false;
- while( !mQueuedEnd )
- {
- //Pump messages in all registered RenderWindow windows
- WindowEventUtilities::messagePump();
- if (!renderOneFrame())
- break;
- }
- }
Root::renderOneFrame方法代码如下:
- bool Root::renderOneFrame(void)
- {
- if(!_fireFrameStarted())
- return false;
- if(!_updateAllRenderTargets())
- return false;
- return _fireFrameEnded();
- }
也可以自行构造渲染循环,这样就可以解决“启动代码”点击关闭窗口程序也不退出的问题了:
- while(true)
- {
- // Pump window messages for nice behaviour
- Ogre::WindowEventUtilities::messagePump();
- if(mWindow->isClosed())
- {
- return false;
- }
- // Render a frame
- if(!mRoot->renderOneFrame()) return false;
- }
9. 总结
本文要点总结如下:
- OGRE程序总是从创建Root实例开始;
- OGRE的很多xxxManager类使用了Singleton设计模式,可以调用类的静态方法getSingleton来获取全局唯一的类实例,如:ResourceGroupManager、LogManager、TextureManager、MeshManager等等,但SceneManager不是;
- OGRE对图形系统进行了高度抽象,用户使用基类接口和OGRE交互,程序执行时会自动根据系统配置调用特定子类的实现,如RenderSystem和RenderWindow;
- OGRE的场景数据用场景图形(Scene Graph)来组织,其本质是树(Tree),由SceneManager来组织和管理;
- 每个Camera通过一个Viewport映射到窗口的一个矩形部分(当然也可以渲染到纹理);
- OGRE的资源文件是其一大特色,资源需要特定程序加载到执行期间的程序;
- OGRE采用配置文件,本文涉及的有plugins.cfg、ogre.cfg、Ogre.log、resources.cfg文件,你应该清楚它们的作用了;
- 场景树的可用性要求场景树不能有循环。
好了,关于OGRE的基本启动过程你应该了解的吧,本文并没涉及WindowEventListener、FrameListener等一些事件的处理,也没有涉及鼠标键盘输入,甚至,“启动代码”运行起来后关闭窗口都不能结束程序,这些留到以后再讲吧。
参考文献:
OGRE WIKI Basic Tutorial 6: The Ogre Startup Sequence
OGRE WIKI: Resources and ResourceManagers
Ogre 3D 1.7 Beginner's Guide (Felix Kerger, 2010)
OGRE API Reference(OGRE SDK下载包中有离线版本)
OGRE 1.9 的第一个程序(OGRE HelloWorld程序)
OGRE启动过程详解(OGRE HelloWorld程序原理解析)的更多相关文章
- Linux启动过程详解(inittab、rc.sysinit、rcX.d、rc.local)
启动第一步--加载BIOS 当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的重要,以至于计算机必须在最开始就找到它.这是因为BIOS中包含了CPU的相关信息.设备启动顺序信息.硬 ...
- Linux启动过程详解
Linux启动过程详解 附上两张图,加深记忆 图1: 图2: 第一张图比较简洁明了,下面对第一张图的步骤进行详解: 加载BIOS 当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的 ...
- Android 核心分析 之八Android 启动过程详解
Android 启动过程详解 Android从Linux系统启动有4个步骤: (1) init进程启动 (2) Native服务启动 (3) System Server,Android服务启动 (4) ...
- 【STM32H7教程】第13章 STM32H7启动过程详解
完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980 第13章 STM32H7启动过程详解 本章教 ...
- (转)Linux 开机引导和启动过程详解
Linux 开机引导和启动过程详解 编译自:https://opensource.com/article/17/2/linux-boot-and-startup作者: David Both 原创:LC ...
- fabric网络环境启动过程详解
这篇文章对fabric的网络环境启动过程进行讲解,也就是我们上节讲到的启动测试fabric网络环境时运行network_setup.sh这个文件的执行流程 fabric网络环境启动过程详解 上一节我们 ...
- linux 开机启动过程详解
Linux开机执行内核后会启动init进程,该进程根据runlevel(如x)执行/etc/rcx.d/下的程序,其下的程序是符号链接,真正的程序放在/etc/init.d/下.开机启动的程序(服务等 ...
- 转-Linux启动过程详解(inittab、rc.sysinit、rcX.d、rc.local)
http://blog.chinaunix.net/space.php?uid=10167808&do=blog&id=26042 1)BIOS自检2)启动Grub/Lilo3)加 ...
- [转载] Linux启动过程详解-《别怕Linux编程》之八
本原创文章属于<Linux大棚>博客,博客地址为http://roclinux.cn.文章作者为rocrocket.为了防止某些网站的恶性转载,特在每篇文章前加入此信息,还望读者体谅. = ...
随机推荐
- Python之路,Day9, 进程、线程、协程篇
本节内容 操作系统发展史介绍 进程.与线程区别 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者 ...
- [转载]GlassFish 的安装及设置命令
移步: http://blog.csdn.net/joyous/article/details/8008870
- 破解Google Gmail的https新思路
最近,Google针对gmail被攻击事件,全面默认启用了始终以https访问Gmail的方式了.但是,对于可以动用整个国家力量的黑客来说,从网络通讯数据中(在此不讨论对用户电脑种木马破解https的 ...
- sql之多表连接
最近遇到特别多多表连接的问题,因此随笔记下,开始学java和mysql的时间太短,有见解不周的地方,希望读者可以提出探讨. 对于left join.right join和inner join(join ...
- 清除Linux OS 缓存
1.查看内存使用情况 [root@ip---- tpch_2_17_0]# free -m total used free shared buffers cached Mem: -/+ buffers ...
- Work around by " Due to heavy load, the latest workflow operation has been queued. " 分类: Sharepoint 2015-07-08 00:19 3人阅读 评论(0) 收藏
I hope most of the users and developers might have come across above note and worried about it. Ther ...
- 一个有趣的回答(摘自http://www.51testing.com/html/03/n-860703.html)
假设这有一个各种字母组成的字符串,假设这还有另外一个字符串,而且这个字符串里的字母数相对少一些.从算法上讲,什么方法能最快的查出所有小字符串里的字母在大字符串里都有? 比如,如果是下面两个字符串: S ...
- 在CentOS6.8上面安装Python3.5
以前每次装Linux,升级Python,都会一堆问题,然后Google,本来想着记录一下,结果问题太多了,也就记不住了,这次特地记了下来. 在CentOS6.8上面安装Python3.5我的系统是Ce ...
- Bootstrap 模态框在用户点击背景空白处时会自动关闭
问题: Bootstrap 模态框在用户点击背景空白处时,会自动关闭. 解决方法: 在HTML页面中编写模态框时,在div初始化时添加属性 aria-hidden=”true” data-backdr ...
- hihoCoder#1094
刚开始学习C语言,准备在做hiho的题目的过程中来学习,在此进行记录,如果代码中有错误或者不当的地方还请指正. 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 Littl ...