Ogre三维框架基础篇(四)

我们的目标是不借助已有的BaseApplication文件,从零新建一个Ogre项目。

准备

腾空整个项目,只使用TutorialApplication文件,其中代码为
TutorialApplication.h

1
2
3
4
5
6
7
8
class TutorialApplication
{
public:
TutorialApplication();
virtual ~TutorialApplication();

bool go();
};

TutorialApplication.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "TutorialApplication.h"

#include <OgreException.h>

TutorialApplication::TutorialApplication()
{
}

TutorialApplication::~TutorialApplication()
{
}

bool TutorialApplication::go()
{
return true;
}

// MAIN FUNCTION OMITTED

Ogre启动过程

启动过程如下:

  1. 创建Root对象。
  2. 定义Ogre将要使用的资源。
  3. 选择并设置渲染系统(即DirectX, OpenGL等)。
  4. 创建渲染窗口(Ogre所处的窗口)。
  5. 初始化你要使用的资源。
  6. 用这些资源来建立一个场景。
  7. 设置第三方库或插件。
  8. 创建一些帧监听器。
  9. 启动渲染循环
    请注意步骤1-4必须严格按顺序进行,而5和6可以放在更后一点

创建Root对象

Root对象是Ogre引擎的核心,在用这个引擎做其它事情之前,必须先创建它。Root的构造函数需要三个参数。第一个是插件配置文件的名称和路径。第二个是Ogre配置文件的路径(它告诉Ogre关于显卡、显示设置等信息)。最后一个是日志文件的名称和路径。因为我们不需要修改任何一个属性,所以用默认的就行。
首先在TutorialApplication.h中的私有区域中定义变量
TutorialApplication.h

1
#include <OgreRoot.h>

1
2
3
4
5
6
//Ogre核心对象
Ogre::Root* mRoot;
//资源的配置文件
Ogre::String mResourcesCfg;
//插件的配置文件
Ogre::String mPluginsCfg;

TutorialApplication.cpp中添加代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
TutorialApplication::TutorialApplication()
: mRoot(0),
mResourcesCfg(Ogre::StringUtil::BLANK),
mPluginsCfg(Ogre::StringUtil::BLANK)
{
}

TutorialApplication::~TutorialApplication(void)
{
//释放Root内存
delete mRoot;
}

bool TutorialApplication::go()
{
//调试模式下的文件路径
#ifdef _DEBUG
mResourcesCfg = "resources_d.cfg";
mPluginsCfg = "plugins_d.cfg";
#else
mResourcesCfg = "resources.cfg";
mPluginsCfg = "plugins.cfg";
#endif

mRoot = new Ogre::Root(mPluginsCfg);
return true;
}

其中Root的构造函数有三个参数,分别为

Ogre::String pluginFileName ———— “plugins.cfg”
Ogre::String configFileName ———— “ogre.cfg”
Ogre::String logFileName ———— “Ogre.log”

定义资源

资源包括纹理、模型、脚本等。Ogre中的资源,必须先定义,再初始化,然后才能使用。定义资源就是把每一个资源所在的文件夹添加到资源组管理器中ResourceGroupManager
resources.cfg配置文件让Ogre知道从哪里读取资源。
TutorialApplication.h

1
#include <OgreConfigFile.h>

在方法go()中添加代码让程序读取资源文件的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//读取资源文件的配置
Ogre::ConfigFile cf;
cf.load(mResourcesCfg);

//遍历配置文件中的所有节点使得程序记住文件路径
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);
}
}

创建渲染系统

我们需要选择一个渲染系统( 如DirectX或OpenGL),然后配置它。在Demo中使用一个Ogre配置对话框来进行。

1
2
3
4
5
//渲染窗口
Ogre::RenderWindow* mWindow;
//显示默认设置窗口
if(!(mRoot->restoreConfig() || mRoot->showConfigDialog()))
return false;

创建渲染窗口

选择渲染系统后,还需要一个渲染Ogre的窗口。实际上有许多种方式来创建渲染窗口。下面由Ogre为你创建一个渲染窗口,第一个参数表示是否让Ogre为你创建一个渲染窗口,第二个为窗口标题。也可以使用win32 API、wxWidgets或其它Windows/Linux的GUI系统,自己创建一个渲染窗口。
TutorialApplication.h

1
#include "OgreRenderWindow.h"

TutorialApplication.cpp

1
2
//添加渲染窗口,可以在窗口中更改程序的设置(Ogre.cfg)
mWindow = mRoot->initialise(true, "TutorialApplication Render Window");

初始化资源

初始化我们将要使用的资源,从mesh到脚本,到所有的东西,由于在某一时刻,只用部分资源。为了减少内存消耗,可以只加载正在使用的资源。为此,我们把资源分解成各种部分,只在运行时初始化它们。初始化资源之前,我们应该设置纹理mipmap的缺省值。

1
2
3
4
//材质管理器的缺省值
Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);
//资源管理器初始化
Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

创建场景

包括三件事:创建场景管理器,创建摄像机,添加视口(cpp中的代码都放在go()方法下)

创建场景管理器

TutorialApplication.h

1
2
3
4
//添加依赖
#include <OgreSceneManager.h>
//添加变量
Ogre::SceneManager* mSceneMgr;

TutorialApplication.cpp

1
mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC);

创建摄像机

TutorialApplication.h

1
2
3
4
//添加依赖
#include <OgreCamera.h>
//添加变量
Ogre::Camera* mCamera;

TutorialApplication.cpp

1
2
3
4
5
mCamera = mSceneMgr->createCamera("MainCam");

mCamera->setPosition(0, 0, 80);
mCamera->lookAt(0, 0, -300);
mCamera->setNearClipDistance(5);

添加视口

TutorialApplication.h

1
2
//添加依赖
#include <OgreViewport.h>

TutorialApplication.cpp

1
2
3
4
5
6
7
8
//添加视口
Ogre::Viewport* vp = mWindow->addViewport(mCamera);

vp->setBackgroundColour(Ogre::ColourValue(0,0,0));

mCamera->setAspectRatio(
Ogre::Real(vp->getActualWidth()) /
Ogre::Real(vp->getActualHeight()));

设置场景

TutorialApplication.h

1
2
//添加依赖
#include <OgreEntity.h>

TutorialApplication.cpp

1
2
3
4
5
6
7
8
9
Ogre::Entity* ogreEntity = mSceneMgr->createEntity("ogrehead.mesh");

Ogre::SceneNode* ogreNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
ogreNode->attachObject(ogreEntity);

mSceneMgr->setAmbientLight(Ogre::ColourValue(.5, .5, .5));

Ogre::Light* light = mSceneMgr->createLight("MainLight");
light->setPosition(20, 80, 50);

无线循环渲染进程

TutorialApplication.h

1
2
//添加依赖
#include <OgreWindowEventUtilities.h>

TutorialApplication.cpp

1
2
3
4
5
6
7
8
9
//循环渲染
while(true)
{
Ogre::WindowEventUtilities::messagePump();

if(mWindow->isClosed()) return false;

if(!mRoot->renderOneFrame()) return false;
}

设置第三方库

虽然在OGRE里,OIS不是唯一的选择,但它是最好的之一。若要使用这个库,请参考OIS自身的文档。主要有以下三步:
1.利用渲染窗口句柄创建输入系统
2.创建鼠标、键盘等输入对象
3.在帧监听其中捕获输入对象并处理输入

TutorialApplication.h

1
2
3
4
#include <OISEvents.h>
#include <OISInputManager.h>
#include <OISKeyboard.h>
#include <OISMouse.h>

1
2
3
4
//添加变量
OIS::InputManager* mInputManager;
OIS::Mouse* mMouse;
OIS::Keyboard* mKeyboard;

OIS的初始化

在渲染循环前加入

1
2
3
4
5
6
7
8
9
10
11
12
13
Ogre::LogManager::getSingletonPtr()->logMessage("*** Initializing OIS ***");
OIS::ParamList pl;
size_t windowHnd = 0;
std::ostringstream windowHndStr;

mWindow->getCustomAttribute("WINDOW", &windowHnd);
windowHndStr << windowHnd;
pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));

mInputManager = OIS::InputManager::createInputSystem( pl );

mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject( OIS::OISKeyboard, false ));
mMouse = static_cast<OIS::Mouse*>(mInputManager->createInputObject( OIS::OISMouse, false ));

关闭OIS

使TutorialApplication.h继承WindowEventListener接口

1
class TutorialApplication : public Ogre::WindowEventListener

添加接口中的方法

1
2
3
4
//Adjust mouse clipping area
virtual void windowResized(Ogre::RenderWindow* rw);
//Unattach OIS before window shutdown (very important under Linux)
virtual void windowClosed(Ogre::RenderWindow* rw);

TutorialApplication.cpp具体实现方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//窗口尺寸改变时调用
void TutorialApplication::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;
}

//窗口结束前卸掉OIS (very important under Linux)
void TutorialApplication::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;
}
}
}

在程序中注册。在go()方法中加入(循环渲染前)

1
2
3
4
//更新鼠标位置和状态
windowResized(mWindow);
//注册WindowEventListener
Ogre::WindowEventUtilities::addWindowEventListener(mWindow, this);

析构方法中加入

1
2
Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, this);
windowClosed(mWindow);

帧监听器

在我们开始渲染循环,并让程序运行之前,我们还需要添加帧监听器。
使TutorialApplication.h继承FrameListener接口,并添加接口内的方法

1
2
3
4
class TutorialApplication : public Ogre::WindowEventListener, public Ogre::FrameListener

// 帧监听器
virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);

TutorialApplication.cpp实现接口方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
bool TutorialApplication::frameRenderingQueued(const Ogre::FrameEvent& evt)
{
if(mWindow->isClosed())
return false;

//Need to capture/update each device
mKeyboard->capture();
mMouse->capture();

if(mKeyboard->isKeyDown(OIS::KC_ESCAPE))
return false;

return true;
}

注册帧监听器

go()方法中注册帧监听器,而先前的循环渲染就可以去掉

1
mRoot->addFrameListener(this);

开始循环

go()方法开启帧的监听

1
mRoot->startRendering();

WinMain

最后不要忘记了TutorialApplication.cpp中的入口函数,它负责运行go()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
int main(int argc, char *argv[])
#endif
{
// Create application object
TutorialApplication app;

try {
app.go();
} catch( Ogre::Exception& e ) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
std::cerr << "An exception has occured: " <<
e.getFullDescription().c_str() << std::endl;
#endif
}

return 0;
}

#ifdef __cplusplus
}
#endif

结语

自己建的类可能会很乱,可以参考BaseApplication将类优化并作为基类或者直接把它拿过来使用。


参考文章:Ogre基础教程6

文章作者: cpacm
文章链接: http://www.cpacm.net/2015/02/15/Ogre三维框架基础篇(四)/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 cpacm
打赏
  • 微信
  • 支付宝

评论