英语水平有限,欢迎大家批评指正
本文并没有将原文全部翻译,只是将其中的一些知识点翻译总结了一下,想要查看详细讲解的话,可以到原文处看一下,附上英文原文地址:http://www.ogre3d.org/tikiwiki/tiki-index.php?page=Basic+Tutorial+6&structure=Tutorials
The Startup Process in a Nutshell
首先看一下OGRE初始化过程,其生命周期如下:
1.创建Root对象。
2.定义OGRE要使用的资源。
3.选择并设置渲染系统(如DirectX, OpenGL等)。
4.创建渲染窗口(OGRE使用的窗口)。
5.初始化你所要使用的资源。
6.使用那些资源创建一个场景。
7.设置第三方库文件和插件。
8.创建侦听器。
9.启动渲染循环。
接下来我们将仔细研究这每一步,注意1到4步都需要按顺序执行,第5、6步(初始化资源和创建场景)可以按你的喜好再靠后些执行。你可以在第5、6步之前初始化第三方插件和创建侦听器,但必须在完成前四步之后。当然,侦听器或第三方库都只有在资源被初始化和场景被创建后才能接触到游戏相关的资源(如camera、实体等)。简而言之,我们并不建议,但如果你知道你在做什么,你可以按你认为的对你的程序有利的顺序执行这些步骤。
Starting up Ogre
Creating the Root Object
Root对象时Ogre库的核心,而且必须在你想利用引擎做任何事情之前创建。
添加如下类成员到本教程的BasicTutorial6类中:
private:
Ogre::Root* mRoot;
Ogre::String mPluginsCfg;
就他们看如下头文件:
#include<OgreRoot.h>
然后,修改构造函数和析构函数:
BasicTutorial6::BasicTutorial6(void)
: mRoot(0),
mPluginsCfg(Ogre::StringUtil::BLANK)
{
}
//-------------------------------------------------------------------------------------
BasicTutorial6::~BasicTutorial6(void)
{
delete mRoot;
}
然后在BasicTutorial6::go函数中创建Ogre::Root:
boolBasicTutorial6::go(void)
{
#ifdef _DEBUG
mPluginsCfg = "plugins_d.cfg";
#else
mPluginsCfg = "plugins.cfg";
#endif
// construct Ogre::Root
mRoot = new Ogre::Root(mPluginsCfg);
return true;
}
Ogre::Root的构造函数有3个参数:
pluginFileName "plugins.cfg" 插件配置文件的名字和位置
configFileName "ogre.cfg" ogre配置文件的名字和位置(告诉OGRE显卡、虚拟设置等信息)
logFileName "Ogre.log" 由Ogre写的日志文件的名字和位置
Resources
注意:在继续这部分你需要打开"resources.cfg"看一下,在你的OGRE SDK文件夹的bin/release中可以找到。"resources_d.cfg"文件可以再bin/debug中找到。
下面定义程序要使用的资源,包括纹理、模型、脚本等。我们不会过多的关注这些资源,现在你要关心的是首先要定义程序中你可能要用到的缩影资源,然后初始化你需要的资源,在OGRE可以使用他们之前。这一步我们定义所有我们的程序可能用到的资源。
为此我们需要把资源所在的文件夹都添加到资源组管理器中(ResourceGroupManager)。添加如下类成员到BasicTutorial6类中:
Ogre::String mResourcesCfg;
然后把它放进构造函数的初始化列表中:
BasicTutorial6::BasicTutorial6(void)
: mRoot(0),
mResourcesCfg(Ogre::StringUtil::BLANK),
mPluginsCfg(Ogre::StringUtil::BLANK)
我们需要保护Ogre::ConfigFile来利用Ogre配置分析功能类。添加如下代码到"BasicTutorial6.cpp"的最上面:
#include<OgreConfigFile.h>
下面我们需要把mResourceCfg添加到go函数中:
boolBasicTutorial6::go(void)
{
#ifdef _DEBUG
mResourcesCfg = "resources_d.cfg";
mPluginsCfg = "plugins_d.cfg";
#else
mResourcesCfg = "resources.cfg";
mPluginsCfg = "plugins.cfg";
#endif
现在我们准备解析一下OGRE资源。添加如下代码到go函数中:
// setupresources
// Load resourcepaths from config file
Ogre::ConfigFile cf;
cf.load(mResourcesCfg);
这里使用OGRE的ConfigFile类解析了"resources.cfg"中的所有资源,但并没有把他们加载到OGRE中(你得手动加载)。记住在你的程序中,你可以任意的使用你自己的解析器和配置文件格式,来代替Ogre的ConfigFile解析器。只有你把资源添加到了资源组管理器(ResourceGroupManager)中,加载资源的方法其实并不是很重要的。
现在解析了配置文件,我们要把这些部分添加到资源组管理器(ResourceGroupManager)中。下面的代码依次循环解析好的配置文件并通过addResourceLocation函数把每一条添加到资源组管理器(ResourceGroupManager)中:
// Go through allsections & settings in the file
Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
Ogre::StringsecName, 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他们在哪里。在你要使用他们时,你必须初始化你想要使用的资源组或初始化所有资源组。
Creating the RenderSystem
下面我们需要选择渲染系统(通常在Windows电脑上不是DirectX就是OpenGL)然后安装他。大多数实例程序都是有OGRE配置对话框来设置程序,OGRE还提供一种方法来使用一个用户已经设置好的配置,这意味着你第一次设置好之后你就不用再设置他了。
首先添加一个新的类成员到BasicTutorail6类中,用来持有我们的Ogre::RenderWindow:
Ogre::RenderWindow* mWindow;
添加如下代码到go函数:
// configure
// Show theconfiguration dialog and initialise the system
if(!(mRoot->restoreConfig()|| mRoot->showConfigDialog()))
{
return false;
}
If语句的第一部分里,我们尝试恢复(restore)配置文件。如果restoreConfig函数返回false,说明配置文件不存在,我们得使用if语句第二部分的showConfigDialog函数显示配置对话框,如果这也返回fasle,说明用户要从配置对话框中退出。
本例中只有返回false,我们就退出程序。我们也可以抛出异常,如果在OGRE启动中捕捉到异常,在catch代码块中删除ogre.cfg文件,这可能是因为在配置对话框中用户选择的设置引起了问题需要修改。
当你调试时,如果你不需要在程序运行时确认图形设置的话,关闭配置对话框可以减少一点调试完成的时间。如果你不使用OGRE的配置对话框,你的程序可能需要手动设置渲染系统,如下:
// Do not addthis to the application
RenderSystem *rs= mRoot->getRenderSystemByName("Direct3D9 Rendering Subsystem");
// or use"OpenGL Rendering Subsystem"
mRoot->setRenderSystem(rs);
rs->setConfigOption("FullScreen", "No");
rs->setConfigOption("VideoMode", "800 x 600 @ 32-bit colour");
你可以使用Root::getAvailableRenderers函数找到可以使用的渲染系统供你的程序使用。获取了渲染系统后,你可以使用RenderSystem::getConfigOptions函数来查看用户可以使用哪些选项。通过调用这两个函数,你就可以为你的程序创建你自己的配置对话框了。
Creating a RenderWindow
现在我们选好了渲染系统,就需要一个渲染OGRE的窗口。
如果你要OGRE来创建一个渲染窗口,那么很简单,添加如下代码到go函数中:
mWindow =mRoot->initialise(true, "BasicTutorial6 Render Window");
该函数的调用初始化了我们前面设置的渲染系统,第一个参数表示OGRE是否为你创建一个渲染窗口。
你也可以使用win32 API,wxWidgets,或者Windows或Linux系统中众多GUI系统之一来创建一个渲染窗口。一个用Windows来创建渲染窗口的例子如下:
// Do not addthis to the application
mRoot->initialise(false);
HWND hWnd =0; // Get the hWnd of the application!
NameValuePairListmisc;
misc["externalWindowHandle"]= StringConverter::toString((int)hWnd);
RenderWindow *win= mRoot->createRenderWindow("Main RenderWindow", 800, 600, false,&misc);
注意你仍然需要调用Root::initialise函数,但第一个参数需设为false。然后你必须获取你想要渲染OGRE的窗口的HWND(句柄),如何获取取决于你用来创建窗口的GUI工具集。获取句柄后,使用NameValuePairList将它赋给"externalWindowHandle"。Root::createRenderWindow函数可以用来创建渲染窗口。
Initializing Resources
现在Root,RenderSystem和RenderWindow对象已经创建好可以使用,我们基本上准备好创建我们的场景了。剩下要做的就是初始化我们要用的资源。
在一个大型游戏或程序中,我们可能会有成百上千的资源给我们的游戏使用——来自网格、纹理、脚本的所有东西。但在任意给定的时间内我们可能只会使用使用其中的一小部分。
为了控制内存,我们可以只加载我们的程序正在使用的资源,我们通过把资源分成多个部分并按我们的需要初始化来实现。
在初始化资源之前,我们还需要设置一下纹理所使用的贴图的数量。添加如下代码到go函数:
// Set defaultmipmap level (NB some APIs ignore this)
Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);
// initialise allresource groups
Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
程序现在初始化了所有资源且可以使用了。
Creating a Scene
下面创建场景,在往场景中添加东西前需要知道三件事情:
1.创建场景管理器(SceneManager)
2.创建摄像机(camera)
3.创建视口(Viewport)
继续之前在BasicTutorial6.cpp文件顶部添加如下头文件:
#include<OgreCamera.h>
#include<OgreViewport.h>
#include<OgreSceneManager.h>
#include<OgreRenderWindow.h>
Creating a SceneManager
首先添加一个新的私有数据成员到BasicTutorial6类中:
Ogre::SceneManager*mSceneMgr;
添加如下代码到go函数:
// Create the SceneManager,in this case a generic one
mSceneMgr =mRoot->createSceneManager("DefaultSceneManager");
Creating the Camera
首先添加一个新的私有数据成员到BasicTutorial6类中:
Ogre::Camera* mCamera;
添加如下代码到go函数:
// Create the camera
mCamera =mSceneMgr->createCamera("PlayerCam");
// Position it at 80 in Zdirection
mCamera->setPosition(Ogre::Vector3(0,0,80));
// Look back along -Z
mCamera->lookAt(Ogre::Vector3(0,0,-300));
mCamera->setNearClipDistance(5);
Adding a Viewport
// Create one viewport,entire window
Ogre::Viewport* vp =mWindow->addViewport(mCamera);
vp->setBackgroundColour(Ogre::ColourValue(0,0,0));
// Alter the camera aspectratio to match the viewport
mCamera->setAspectRatio(
Ogre::Real(vp->getActualWidth()) /Ogre::Real(vp->getActualHeight()));
Creating scenes
你可以创建任意多的场景管理器(SceneManager)和摄像机(Camera),但如果你真想在屏幕上渲染一些东西,那么使用一个摄像机,你还要为他添加一个视口(Viewport)。在go函数中添加如下代码:
Ogre::Entity* ogreHead =mSceneMgr->createEntity("Head", "ogrehead.mesh");
Ogre::SceneNode* headNode =mSceneMgr->getRootSceneNode()->createChildSceneNode();
headNode->attachObject(ogreHead);
// Set ambient light
mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5,0.5, 0.5));
// Create a light
Ogre::Light* l =mSceneMgr->createLight("MainLight");
l->setPosition(20,80,50);
在BasicTutorial6.cpp文件顶部添加如下头文件:
#include<OgreEntity.h>
Render Loop - take one
渲染场景,Ogre::Root有几个方法可以启动一个渲染循环,最简单的就是Ogre::Root::renderOneFrame()。他一直循环,直到一个函数返回值为false。代码如下:
void Root::startRendering(void)
{
assert(mActiveRenderer != 0);
mActiveRenderer->_initRenderTargets();
// Clear event times
clearEventTimes();
// Infinite loop, until broken out of byframe listeners
// or break out by callingqueueEndRendering()
mQueuedEnd = false;
while( !mQueuedEnd )
{
//Pump messages in all registeredRenderWindow windows
WindowEventUtilities::messagePump();
if (!renderOneFrame())
break;
}
}
让我们再来看一下renderOneFrame函数:
bool Root::renderOneFrame(void)
{
if(!_fireFrameStarted())
return false;
if (!_updateAllRenderTargets())
return false;
return _fireFrameEnded();
}
除了更新所有的渲染目标,他调用了帧事件函数,只有其中任意一个返回值为false,都将是渲染循环退出。
由于我们还没有使用任何FrameListener,所有我们需要在这两种循环中选择一个。
在BasicTutorial6.cpp文件顶部添加如下头文件:
#include<OgreWindowEventUtilities.h>
添加如下代码到go函数:
while(true)
{
// Pump window messages for nice behaviour
Ogre::WindowEventUtilities::messagePump();
if(mWindow->isClosed())
{
return false;
}
// Render a frame
if(!mRoot->renderOneFrame()) returnfalse;
}
他一直循环,直到退出窗口,或renderOneFrame函数返回false。
编译运行你的程序,就能看的一个窗口,里面有一个食人魔头像。
OIS
Ogre虽然不止一种输入选择,但OIS却是其中最好的之一。我们将简单的介绍如何在你的程序中启动OIS。在BasicTutorial6.h中包含以下头文件:
#include<OISEvents.h>
#include<OISInputManager.h>
#include<OISKeyboard.h>
#include <OISMouse.h>
添加以下数据成员到BasicTutorial6类中:
// OIS Inputdevices
OIS::InputManager* mInputManager;
OIS::Mouse* mMouse;
OIS::Keyboard* mKeyboard;
然后把OIS添加到我们的项目设置中:
IncludeDirectory $(OGRE_HOME)/include/OIS
InputLibrary OIS_d.lib/OIS.lib
库文件目录应该已经设置好,确保添加OIS后项目可以编译运行。
Firing up OIS
OIS使用一个全局的(general)有点难以设置但恰当的创建一次后就很容易使用的输入管理器(InputManager)。OIS没有集成进OGRE中,他是一个独立库,这意味着开始你需要给他提供一些信息来让他正确的工作。实际上他只需要OGRE正在渲染的窗口句柄。幸运的是,由于我们是自动创建的窗口,OGRE使得这对我们来说很容易。
在渲染循环之前,把如下代码添加到BasicTutorial6::go函数中:
Ogre::LogManager::getSingletonPtr()->logMessage("***Initializing OIS ***");
OIS::ParamListpl;
size_t windowHnd= 0;
std::ostringstreamwindowHndStr;
mWindow->getCustomAttribute("WINDOW",&windowHnd);
windowHndStr<< windowHnd;
pl.insert(std::make_pair(std::string("WINDOW"),windowHndStr.str()));
mInputManager =OIS::InputManager::createInputSystem( pl );
这里设置输入管理器(InputManager)来用,但实际上是使用OIS来获取键盘鼠标或Joystick的输入,你需要创建这些对象:
mKeyboard=static_cast<OIS::Keyboard*>(mInputManager->createInputObject(OIS::OISKeyboard, false ));
mMouse=static_cast<OIS::Mouse*>(mInputManager->createInputObject(OIS::OISMouse, false ));
给createInputObject函数传false是因为我们想让鼠标键盘为非缓存的。设为true,则为缓存的。
Shutting down OIS
首先在BasicTutorial6.h中包含头文件:
#include<OgreWindowEventUtilities.h>
然后修改类声明来继承WindowEventListener:
classBasicTutorial6 : public Ogre::WindowEventListener
我们想重载WindowEventListener::windowResized和WindowEventListener::windowClosed函数,所有把如下代码添加到BasicTutorial6类声明的protected部分:
//Ogre::WindowEventListener
virtual voidwindowResized(Ogre::RenderWindow* rw);
virtual voidwindowClosed(Ogre::RenderWindow* rw);
现在在BasicTutorial6.cpp中添加如下代码:
//Adjust mouseclipping area
voidBasicTutorial6::windowResized(Ogre::RenderWindow* rw)
{
unsigned int width, height, depth;
int left, top;
rw->getMetrics(width, height, depth,left, top);
const OIS::MouseState &ms =mMouse->getMouseState();
ms.width = width;
ms.height = height;
}
//Unattach OISbefore window shutdown (very important under Linux)
voidBasicTutorial6::windowClosed(Ogre::RenderWindow* rw)
{
//Only close for window that created OIS(the main window in these demos)
if( rw == mWindow )
{
if( mInputManager )
{
mInputManager->destroyInputObject( mMouse );
mInputManager->destroyInputObject( mKeyboard );
OIS::InputManager::destroyInputSystem(mInputManager);
mInputManager = 0;
}
}
}
当窗口大小改变时windowResized就会被调用,确保OIS鼠标状态和窗口的实际大小事同步的。窗口关闭时windowClosed销毁OIS。
要让我们的程序作为一个窗口事件侦听器(WindowEventListener),我们需要对他进行注册。所有在go函数中添加如下代码:
//Set initialmouse clipping size
windowResized(mWindow);
//Register as aWindow listener
Ogre::WindowEventUtilities::addWindowEventListener(mWindow,this);
还有一件事要做,找到BasicTutorial6的构造函数并添加代码:
//Remove ourselfas a Window listener
Ogre::WindowEventUtilities::removeWindowEventListener(mWindow,this);
windowClosed(mWindow);
delete mRoot;
Setting Up the Framelistener
无论你使用的是缓存的或非缓存的输入,每帧你都必须对你使用的Keyboard, Mouse,和Joystick对象调用capture方法。对应非缓存的输入,这是你所有需要做的。每帧你都可以调用不同的Keyboard和Mouse函数来查询这些对象的状态。所有我们得让我们的BasicTutorial6类变成一个侦听器。把类声明改为:
class BasicTutorial6:public Ogre::WindowEventListener, publicOgre::FrameListener
添加下面的函数声明到BasicTutorial6类声明的protected部分:
// Ogre::FrameListener
virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);
然后再BasicTutorial6.cpp中添加该函数的定义:
boolBasicTutorial6::frameRenderingQueued(const Ogre::FrameEvent& evt)
{
if(mWindow->isClosed())
return false;
//Need to capture/update eachdevice
mKeyboard->capture();
mMouse->capture();
if(mKeyboard->isKeyDown(OIS::KC_ESCAPE))
return false;
return true;
}
Final Touches
编译运行程序前,我们需要把我们的程序注册为一个侦听器。删除BasicTutorial6::go函数中的循环并添加如下代码:
mRoot->addFrameListener(this);
mRoot->startRendering();
第一行把BasicTutorial6实例作为一个侦听器添加给mRoot,意味着他将可以接收帧事件。如果我们不用Root对象把我们的类注册为侦听器,frameRenderingQueued函数将永远都不会被调用。第二行启动了渲染循环。
编译运行程序!
你会在OGRE渲染窗口上看到一个熟悉的食人魔头像,按ESC键可以退出程序。