下面内容第一部分可见sina博客:
一、不创建类,直接在main函数中实现的过程
首先添加头文件:#include "Ogre\Ogre.h" (使用Ogre命名空间)
第一步:建立一个空窗体要的对象
总结成:R-WMAV(WMV)共5个对象
R-root对象(外观模式),W-窗口window对象,M-场景管理器manager,A-camera对象,V-视口view对象
具体:
Ogre::Root* root = new Ogre::Root("plugins_d.cfg");
//此函数另外两个参数默认值是ogre.cfg和ogre.log。之所以用第一个参数是因为它默认是plugins.cfg,我们需要调试版本的。
if(!root->showConfigDialog())
{
return -1;
}
Ogre::RenderWindow* window = root->initialise(true,"Ogre3D Beginners Guide");
Ogre::SceneManager* sceneManager = root->createSceneManager(Ogre::ST_GENERIC);
Ogre::Camera* camera = sceneManager->createCamera("Camera");
camera->setPosition(Ogre::Vector3(0,0,50));
camera->lookAt(Ogre::Vector3(0,0,0));
camera->setNearClipDistance(5);
camera->setAspectRatio(Ogre::Real(viewport->getActualWidth())/ Ogre::Real(viewport->getActualHeight()));
//上面一句就好像把摄像机变成和视口一样的,只不过可能是视口的缩小版。这样可以保证摄像机照出来是什么样,在视口显示成什么样。
完毕!
第二步:添加资源
//添加资源位置,Eample中是用循环的方法从resource.cfg中把字符串用下面一句加载的
Ogre::ResourceGroupManager::getSingleton().addResourceLocation("../../Media/packs/Sinbad.zip","Zip");
//加载资源到程序【自动解析上面位置中的所有文件,并把如材质资源加载解析进来并以材质名称标记】
Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
如果资源比较多,不建议用上面方法一句一句的添加资源位置,建议像下面一样用resource.cfg文件:
//---------------------使用resource.cfg加载资源过程--开始--------------------------------------
Ogre::ConfigFile cf; //Ogre 3D的一个助手类,用于解析resource.cfg文件(键值对组成)
cf.load(“resources_d.cfg”);
Ogre::ConfigFile::SectionIterator sectionIter = cf.getSectionIterator(); //得到区块的迭代器
Ogre::String sectionName, typeName, dataname;
while (sectionIter.hasMoreElements()) //如果当前区块里面有东西
{
sectionName = sectionIter.peekNextKey();
//上一句获取区块名称,不用管Next,没用,区块指针并没有移动【区块中的一个指向key的指针移动了】
Ogre::ConfigFile::SettingsMultiMap *settings = sectionIter.getNext();
//上一句先get内容,再next,由于区块.getNext了,所以区块指针移动指向下一个区块【getNext是惯用方法】
Ogre::ConfigFile::SettingsMultiMap::iterator i;
//下面是在区块中遍历每个设置
for (i = settings->begin(); i != settings->end(); ++i)
{
typeName = i->first;
dataname = i->second;
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(dataname,typeName, sectionName);
}
}
关于以上代码指针太多,可以结合下图理解:

上图中MuliMap这个集合体就是区块的内容,另外区块还包含Key。实际上resource.cfg中以[General]作为一区段的开始,以另一个[sectionname]的出现作为结束
//---------------------使用resource.cfg加载资源过程--结束--------------------------------------
第三步:添加实体等场景内容,然后开始渲染
Ogre::Entity*ent=_sceneManager->createEntity("Sinbad.mesh");
_sceneManager->getRootSceneNode()->attachObject(ent); //绑定实体到场景节点
root->startRendering(); //开始渲染吧。
return 0; //最后别忘了养成好习惯返回,当然这里写在main中,也可以无返回值
二、组装成类的过程
组装成类(2+3):因为上面的所有内容直接写在main函数中,不好看,最后组装成一个类MyApplication
(1)类MyApplication至少包含2个成员变量:_root和_sceneManager ,其它对象要在不同的函数成员中通过这两个对象创建。
(2)类中至少包含3个函数成员:
void loadResources() 加载资源进入程序
void createScene() 创建各种实体,并绑定到场景节点
int startup()
启动函数,此函数里面先建立上面提到的5个对象(R-WMAV),然后调用loadResources()和createScene(),最后启动渲染循环和返回0。即:_root->startRendering(); return 0;
在main中只需要调用app.startup();即可。
注意:析构函数中只需要一句:delete _root;即可,ogre坚持谁创建,谁负责销毁原则,所以只用显示销毁root对象,其它对象因为是从root创建来的,会自动销毁。(还要注意,如果用第三方插件不一定用这个原则)
三、添加监听器
(1)创建一个监听器类,必须继承于Ogre::FrameListener(访问者模式的一个公共接口)
class MyFrameListener : public Ogre::FrameListener
{
public:
bool frameStarted(const Ogre::FrameEvent& evt)
{
return false;
}
bool frameEnded(const Ogre::FrameEvent& evt)
{
return false;
}
bool frameRenderingQueued(const Ogre::FrameEvent& evt)
{
return false;
}
}
解释:我们直接从FrameListener接口继承(访问者模式中具体访问者的父类接口)。这个接口包含了三个虚函数,这三个虚函数需要我们在自己的FrameListener中覆写。
另外,调用完frameRenderingQueued,缓存得到交换。且xxStarted()和xxEnded()是一般绘制的开始和介绍。
这三个函数调用如下图:

(2)建立一个具体帧监听器,并添加如被监听者Root的列表中(用root->add之类的函数),如下:
_listener = new MyFrameListener(); //在MyApplication已经添加了成员MyFrameListener* _listener;
_root->addFrameListener(_listener); //把访问者加入Root被监听者对象的列表。
上面的语句中把_listener(指针)看成立MyApplication的一个成员了,为了以后用着方便,但别忘了在MyApplication的析构函数中添加delete _listener。
现在讨论下,作为观察者的帧监听器是怎么起作用的呢?即他是怎么知道什么时候我该其作用呢?
在:_root->startRendering()中调用了_root->renderOneFrame()。【我估计,在_root->renderOneFrame()事件中先调用被观察者的NotifyObserver()之类的函数通知观测者该更新状态了,即调用监听器的Update之类的函数。而Update之类的函数里面又依次调用frameStarted、frameRenderingQueued、frameEnded三个函数从而更新了变化矩阵等数据,然后再根据这些变化的数据如“世界观察投影矩阵”开始真正画一帧图。】
最后,提一下一个细节,上面所有的代码中的对象都使用Ogre命名空间,所以只需要ogre.h头文件即可,不用添加任何其它头文件。
四、添加输入
首先包含头文件:#include "OIS\OIS.h" 使用OIS命名空间
大致过程如下(只关心键盘用到的键盘鼠标接口,不关心它们是怎么来的):在frameStarted等函数中通过键盘接口Keyboard或鼠标接口获取值,根据这些值跟新一些数据或执行一些任务。如下代码所示
bool frameStarted(const Ogre::FrameEvent& evt)
{
_Keyboard->capture(); //_Keyboard是监听器类的一个成员,OIS::Keyboard* _Keyboard
if(_Keyboard->isKeyDown(OIS::KC_ESCAPE))
{
return false;
}
return true;
}
但是上面的_Keyboard必须和我门的窗口window关联以便知道操作的是那个窗口传来的键盘数据,这些都是输入管理器OIS::InputManager做的事情。这个管理其负责管理键盘和鼠标接口。
以上就是粗略理解的过程。
具体实现如下:
在监听器类中至少添加四个成员变量:
OIS::InputManager* _InputManager;
OIS::Mouse* _mouse;
OIS::Keyboard* _Keyboard;
Ogre::RenderWindow* win; //这个win在new监听器对象时提供。
注:如果需要操作摄像机,还有添加摄像机成员,移动速度控制成员、场景节点、实体、甚至是视口等,所有成员通过构造函数获取值如下面的win,这里为了简化理解,就不加了。
下面是构造函数
MyFrameListener(Ogre::RenderWindow* win)
{
OIS::ParamList parameters; //OIS使用参数列表来初始化(创建系统)
unsigned int windowHandle = 0;
std::ostringstream windowHandleString;
//获得RenderWindow的句柄并转换它为一个字符串
win->getCustomAttribute("WINDOW", &windowHandle);
windowHandleString << windowHandle;
//使用键值”WINDOW,windowHandleString”来添加字符串类型的句柄到参数表
parameters.insert(std::make_pair("WINDOW", windowHandleString.str()));
//使用参数列表来创建输入管理器InputManager
_InputManager = OIS::InputManager::createInputSystem(parameters);
//用管理器来创建键盘接口keyboard,鼠标接口Mouse
_Keyboard = static_cast<OIS::Keyboard*>(_InputManager->createInputObject( OIS::OISKeyboard, false ));
_mouse = static_cast<OIS::Mouse*>(_InputManager->createInputObject( OIS::OISMouse, false ));
}
别忘了析构函数:
~MyFrameListener()
{
//输入系统不是ogre的内容,不满足谁创建谁负责销毁原则,需要显示的调用输入管理器的销毁函数
_InputManager->destroyInputObject(_Keyboard);
_InputManager->destroyInputObject(_mouse);
//销毁管理器
OIS::InputManager::destroyInputSystem(_InputManager);
}
五、自己写渲染循环(不使用_root->startRendering())
首先添加一个bool型的公有成员到MyApplication类的实例app中
bool _keepRunning;
然后在MyApplication类中添加一个函数成员:
void renderOneFrame() //MyApplication中的renderOneFrame,就两句话
{
Ogre::WindowEventUtilities::messagePump();
//处理来自操作系统的窗口事件信息【从应用程序消息泵中去信息,这些信息会发送给注册过的输入管理器OIS::InputManager】
_keepRunning = _root->renderOneFrame();
//【估计在_root->renderOneFrame()中先调用被观察者的NotifyObserver()之类的函数通知观测者更新状态了,即调用监听器的Update之类的函数。而Update之类的函数里面又依次调用frameStarted、frameRenderingQueued、frameEnded三个函数从而更新了变化矩阵等数据,然后再根据这些变化的数据如“世界观察投影矩阵”开始真正画一帧图。】
}
最后在main函数中添加一个自己的循环即可
while(app._keepRunning)
{
app.renderOneFrame();
}
编译并运行即可。
第二部分:转自http://blog.sina.com.cn/s/blog_6e521a600100nnmp.html
基础的学习可以从下面的basic tutorial开始:
-------------------------分割线-------------------------------
ORGE从一个root的构造初始化整个流程:
mRoot = new Root(pluginsPath, mResourcePath + "ogre.cfg", mResourcePath + "Ogre.log");
ogre.cfg内容大致如下:
Render System=OpenGL Rendering Subsystem
[OpenGL Rendering Subsystem]
FSAA=0
Full Screen=No
RTT Preferred Mode=PBuffer
Video Mode=800 x 600
可以看出,在Root构造函数中传入的ogre.cfg文件,可以决定用窗口模式还是全屏模式。
第一个参数是plugin.cfg的文件,一个典型的plugin内容大致如下:
# Defines plugins to load
# Define plugin folder
PluginFolder=/lib/OGRE/
# Define D3D rendering implementation plugin
Plugin=RenderSystem_GL.so
Plugin=Plugin_ParticleFX.so
Plugin=Plugin_BSPSceneManager.so
Plugin=Plugin_OctreeSceneManager.so
Plugin=Plugin_CgProgramManager.so
可以看出,主要是OGRE的架构的特性,render等都是一个plugin,这个config文件主要是指示加载的plugin的清单和路径。
一个最简单的流程:
代码可以通过Root->showConfigDialog()来让用户初始化render系统;
然后用Root->initialise(true)来初始化Root;
此处会返回跟平台相关的mWindow。
然后用Root->createSceneManager(ST_GENERIC, "ExampleSMInstance")来创建SceneManager;
然后开始相机的初始化,
mCamera = mSceneMgr->createCamera("PlayerCam");
// Position it at 500 in Z direction
mCamera->setPosition(Vector3(0,0,500));
// Look back along -Z
mCamera->lookAt(Vector3(0,0,-300));
mCamera->setNearClipDistance(5);
然后是viewPort,
ViewPort* vp = mWindow->addViewPort(mCamera);这里的mWindow, mCamera可以在上面看到来源;
vp->setBackgroundColour(ColourValue(0,0,0));
//alter the camera aspect ratio to match the viewport
mCamera->setAspectRatio(Real(vp->getActuralWidth()) / Real(vp->getActuralHeight()));
然后可以设置mipmap等级:
TextureManager::getSingleton().setDefaultNumMipmaps(5);
最后给Root装上frameListener(): 这一步也是OGRE的设计之一,一旦跑起来,OGRE会通过这个listener来给代码输送信息,如按键事件,frameStart, frameEnd等等:
mFrameListener = new ExampleFrameListener (mWindow, mCamera);
mFrameListener->showDebugOverlay(true);
mRoot->addFrameListener(mFrameListener);
----------------------官方Sample的流程----------------------
WinMain()在sampleBrowser.cpp中,很简单
OgreBites::SampleBrowser sb;
sb.go();
其中,sb.go()因为sampleBrowser虽然继承了sampleContext,而且go()在sampleContext中是个virtual函数,但是sampleBrowser没有自己的实现,所以调用的其实是sampleContext的go()。
go()中创建了root,传入了plugin.cfg和orge.cfg(此文件在MyDocument\OGRE下)。
在选中相应的sample后,如果用户点击了start,那么程序转移到
runSample(Sample* s) [SampleBrowser.h],此函数最后调用到
SampleContext::runSample(s) [SampleContext.h],然后调用到
_setup(mWindow, mKeyboard, mMouse, mFSLayer) [SdkSample.h],这个函数的实现代码里面,基本上就会调用到各个实际的sample的接口了。
因为各个sample的具体实现都是继承了SdkSample.h,而后者又继承自Sample.h,因此,Sample.h中的虚拟接口是可以被实例化的,而且,Sample.h中的virtual接口没有纯虚的,所以,可以根据需要选择实现或不实现。
go()中的初始化好了后,必须调用到root->startRendering(),该函数中实现一个while循环,来实现每一帧的开始和结束 [sampleContext.h],这个实现如下代码:
void Root::startRendering(void)
{
assert(mActiveRenderer != 0);
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;
}
可以看出,循环的退出只有2个可能:queueEndRendering()这个接口函数被用户调用到(这个函数是唯一一个会把mQueuedEnd设置成true的地方);或者,renderOneFrame()返回为false;
-----------------典型的ogre流程-----------------------
int main()
{
// load resources from resource.cfg bying using Root() construct
// Ogre::ResourceGroupManager::getSingleton().addResourceLocation();
setupResources();
//config ogre rendering system
root.showConfigDialog();
root.initialise();
//choose scenemanager
mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC);
//create camera
mCamera = mSceneMgr->createCamera("PlayerCam"); mCamera->setPosition(Ogre::Vector3(0,0,80));
mCamera->lookAt(Ogre::Vector3(0,0,-300)); mCamera->setNearClipDistance(5);
//create viewport
Ogre::Viewport* vp = mWindow->addViewport(mCamera); vp->setBackgroundColour(Ogre::ColourValue(0,0,0)); mCamera->setAspectRatio(vp->getActualWidth()) / vp->getActualHeight());
//setup mipmap level
TextureManager::getSingleton().setDefaultNumMipmaps(5);
//NOW initialise the resource
Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
//add frame listener
mRoot->addFrameListener(this);
//THEN this END-LESS LOOP INTERNAL function
Root->startRendering();
//if comes here, means quit
}
-----------------ogre
FrameListener.frameRenderingQueued-----------------------
这个功能是某个版本后加的,主要是为了能让GPU在渲染的时候,同时CPU还能做些事情,实现效率化。
这里是原文解释:
The usefulness of this event comes from the fact that rendering
commands are queued for the GPU to process. These can take a little
while to finish, and so while that is happening the CPU can be doing
useful things. Once the request to 'flip buffers' happens, the thread
requesting it will block until the GPU is ready, which can waste CPU
cycles. Therefore, it is often a good idea to use this callback to
perform per-frame processing. Of course because the frame's rendering
commands have already been issued, any changes you make will only
take effect from the next frame, but in most cases that's not noticeable.
bool Root::renderOneFrame(void)
{
//下面的代码用户会因玩家操作更新所有的需要更新的逻辑、画面等
if(!_fireFrameStarted())
return false;
//下面的功能详细在后面的function中
if (!_updateAllRenderTargets())
return false;
return _fireFrameEnded();
}
bool Root::_updateAllRenderTargets(void)
{
//下面的代码可以通知到GPU开始为这一阵工作
mActiveRenderer->_updateAllRenderTargets(false);
//算定GPU应该在工作中,CPU把能干的事情也干些吧,不要闲着。
//如果没有这句,可能的事情是GPU工作中,CPU直接执行下一句_swapxxx,但是GPU会block住CPU,
//直到它做完渲染和swap
bool ret = _fireFrameRenderingQueued();
mActiveRenderer->_swapAllRenderTargetBuffers(mActiveRenderer->getWaitForVerticalBlank());
return ret;
}
下面补充对一些语句的理解和注释,转自:http://www.cppblog.com/flyindark/archive/2011/07/01/149880.html?opt=admin
创建ROOT