#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;
}
Ogre启动过程&原理
最新推荐文章于 2019-01-12 17:10:36 发布