我们的目标是不借助已有的BaseApplication
文件,从零新建一个Ogre项目。
准备
腾空整个项目,只使用TutorialApplication
文件,其中代码为
TutorialApplication.h1
2
3
4
5
6
7
8class TutorialApplication
{
public:
TutorialApplication();
virtual ~TutorialApplication();
bool go();
};
TutorialApplication.cpp1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
TutorialApplication::TutorialApplication()
{
}
TutorialApplication::~TutorialApplication()
{
}
bool TutorialApplication::go()
{
return true;
}
// MAIN FUNCTION OMITTED
Ogre启动过程
启动过程如下:
- 创建Root对象。
- 定义Ogre将要使用的资源。
- 选择并设置渲染系统(即DirectX, OpenGL等)。
- 创建渲染窗口(Ogre所处的窗口)。
- 初始化你要使用的资源。
- 用这些资源来建立一个场景。
- 设置第三方库或插件。
- 创建一些帧监听器。
- 启动渲染循环
请注意步骤1-4必须严格按顺序进行,而5和6可以放在更后一点
创建Root对象
Root对象是Ogre引擎的核心,在用这个引擎做其它事情之前,必须先创建它。Root的构造函数需要三个参数。第一个是插件配置文件的名称和路径。第二个是Ogre配置文件的路径(它告诉Ogre关于显卡、显示设置等信息)。最后一个是日志文件的名称和路径。因为我们不需要修改任何一个属性,所以用默认的就行。
首先在TutorialApplication.h
中的私有区域中定义变量
TutorialApplication.h中1
1 | //Ogre核心对象 |
在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
27TutorialApplication::TutorialApplication()
: mRoot(0),
mResourcesCfg(Ogre::StringUtil::BLANK),
mPluginsCfg(Ogre::StringUtil::BLANK)
{
}
TutorialApplication::~TutorialApplication(void)
{
//释放Root内存
delete mRoot;
}
bool TutorialApplication::go()
{
//调试模式下的文件路径
mResourcesCfg = "resources_d.cfg";
mPluginsCfg = "plugins_d.cfg";
mResourcesCfg = "resources.cfg";
mPluginsCfg = "plugins.cfg";
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
在方法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
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//添加依赖
//添加变量
Ogre::SceneManager* mSceneMgr;
TutorialApplication.cpp中1
mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC);
创建摄像机
TutorialApplication.h中1
2
3
4//添加依赖
//添加变量
Ogre::Camera* mCamera;
TutorialApplication.cpp中1
2
3
4
5mCamera = mSceneMgr->createCamera("MainCam");
mCamera->setPosition(0, 0, 80);
mCamera->lookAt(0, 0, -300);
mCamera->setNearClipDistance(5);
添加视口
TutorialApplication.h中1
2//添加依赖
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//添加依赖
TutorialApplication.cpp中1
2
3
4
5
6
7
8
9Ogre::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//添加依赖
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.h1
2
3
4
1 | //添加变量 |
OIS的初始化
在渲染循环前加入1
2
3
4
5
6
7
8
9
10
11
12
13Ogre::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
2Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, this);
windowClosed(mWindow);
帧监听器
在我们开始渲染循环,并让程序运行之前,我们还需要添加帧监听器。
使TutorialApplication.h继承FrameListener接口,并添加接口内的方法1
2
3
4class 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
14bool 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
extern "C" {
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
int main(int argc, char *argv[])
{
// Create application object
TutorialApplication app;
try {
app.go();
} catch( Ogre::Exception& e ) {
MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
std::cerr << "An exception has occured: " <<
e.getFullDescription().c_str() << std::endl;
}
return 0;
}
}
结语
自己建的类可能会很乱,可以参考BaseApplication将类优化并作为基类或者直接把它拿过来使用。