转载请注明出处http://blog.sina.com.cn/u/1580340211
因为最近重拾Ogre,决定重新研究一遍以前不是弄得很懂的例子程序的框架,这个框架不同于ExampleApplication里的框架,主线比较明确,所以理解起来也是比较快的,从go函数入手很快就可以理解Ogre从资源初始化->资源加载->初始化->使用资源创建场景(并增加监听)的一整个过程:
1) 首先可以从应用程序的入口点着手:
#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
TestCEGUI076 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
可以看到,在WIN32平台下,Ogre通过try应用程序实例app,进入其入口函数go()中,开始一整个的渲染流程;
2) 于是我们从go()函数开始研究其过程(我们的类是继承于已有的BaseApplication,所以基本的功能在其中已经定义,当然也包括go函数)
void BaseApplication::go(void)
{
#ifdef _DEBUG
mResourcesCfg = "resources_d.cfg";
mPluginsCfg = "plugins_d.cfg";
#else
mResourcesCfg = "resources.cfg";
mPluginsCfg = "plugins.cfg";
#endif
if (!setup())
return;
mRoot->startRendering();
// clean up
destroyScene();
}
3) 如上可以看到Ogre中各个功能模块所调用的函数(首先由cfg文件资源路径,然后使用setup函数加载资源,调用根节点的startRedering函数开始渲染,最后destroyscene函数销毁场景),下面分开看这些函数做了一些什么事:
Setup:
bool BaseApplication::setup(void)
{
mRoot = new Ogre::Root(mPluginsCfg); --------------加载插件初始化Root(注意此处的是plugin的cfg)
setupResources(); --------------------真正执行资源装载的函数,等会再说
bool carryOn = configure();
if (!carryOn) return false; --------------------资源成功加载才继续运行程序
chooseSceneManager(); -------------------选择渲染器
createCamera(); --------------------创建相机
createViewports(); ---------------------创建视口
// Set default mipmap level (NB some APIs ignore this)
Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);----- ---设置默认的mipmap等级
// Create any resource listeners (for loading screens)
createResourceListener(); ------------------创建资源监听器(例子中为空,可以自己实现资源的监听)
// Load resources
loadResources(); ----------------装载资源
// Create the scene
createScene(); ------------------创建场景
createFrameListener(); ------------------创建帧监听
return true;
};
于是我们就要问那么setupResources做了什么呢?
Setupresources:
void BaseApplication::setupResources(void)
{
// Load resource paths from config file
Ogre::ConfigFile cf;
cf.load(mResourcesCfg);资源是通过Resources 的cfg加载的(这就明确了为什么你要在本地文件夹中包含plugins_d.cfg和resources_d.cfg了吧,当然你可以在go函数中就指定路径加载这两个文件,但是这种方法是难以继承的)
// Go through all sections & settings in the file
Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator(); -----创建迭代器逐个加载资源
Ogre::String secName, typeName, archName;
while (seci.hasMoreElements()) --------------------循环判断资源类型并加载(从读文件的方式你就可以看出为什么cfg中的资源路径要使用那样的命名方式了吧)
{
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);-- -加载资源的三个变量分别是【资源类型】【类型名】【组名称】
}
}
}
通过使用setupresources加载了资源后,在setup中我们就可以调用相关的函数来构建基本的环境了(首先要创建场景管理器,因为其他的包括节点和实体都要通过调用其单例来完成):
ChooseSceneManager
void BaseApplication::chooseSceneManager(void)
{
// Get the SceneManager, in this case a generic one
mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC); ------------此处调用的是一般的渲染器,如果场景需要可以调用其余的三个渲染器加速渲染
}
CreateCamera:
void BaseApplication::createCamera(void)
{
// Create the camera
mCamera = mSceneMgr->createCamera("PlayerCam");
// Position it at 500 in Z direction
mCamera->setPosition(Ogre::Vector3(0,0,80));
// Look back along -Z
mCamera->lookAt(Ogre::Vector3(0,0,-300));
mCamera->setNearClipDistance(5);
mCameraMan = new OgreBites::SdkCameraMan(mCamera); // create a default camera controller------创建一个camera的管理器,通过此种方法简化了相机的控制,不过特殊的移动相机要求需要自己在OIS回调函数中实现
}
createViewport:
void BaseApplication::createViewports(void)
{
// Create one viewport, entire window
Ogre::Viewport* vp = mWindow->addViewport(mCamera);
vp->setBackgroundColour(Ogre::ColourValue(0,0,0));
// Alter the camera aspect ratio to match the viewport
mCamera->setAspectRatio( ----------注意不加此步你就杯具了,变化窗口大小会出现图像变形或者空白部分
Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight()));
}
在setup中创建了相机和视口并且设置mipmap之后就可以完成资源的加载了,主要是通过loadResources()函数完成:
LoadResources;
void BaseApplication::loadResources(void)
{
Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}
就一个函数初始化所有资源组,没啥好说的额。
然后呢,在setup中还有两个十分重要的函数就是createScene()和createFrameListener了。
creteScene:
virtual void createScene(void) = 0; // Override me!
顾名思义,就是需要你自己去实现的场景构造函数了,它里面已经指明是一个虚函数,如果没有将其覆盖你是确定一定以及肯定的是二了。
CreateFrameListener:
void BaseApplication::createFrameListener(void)
{
Ogre::LogManager::getSingletonPtr()->logMessage("*** Initializing OIS ***");
OIS::ParamList pl; -----------------初始化OIS和Log系统,同时也完成键盘鼠标的回调初始化
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 );-----------创建基于windows的输入系统这里是很重要的,有了这个部分才能够正确的处理鼠标和键盘事件
mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject( OIS::OISKeyboard, true ));
mMouse = static_cast<OIS::Mouse*>(mInputManager->createInputObject( OIS::OISMouse, true ));
mMouse->setEventCallback(this);
mKeyboard->setEventCallback(this);
//Set initial mouse clipping size
windowResized(mWindow);
//Register as a Window listener
Ogre::WindowEventUtilities::addWindowEventListener(mWindow, this);
mTrayMgr = new OgreBites::SdkTrayManager("InterfaceName", mWindow, mMouse, this);---因为是Ogre自己的例子wizard所以它选择用自己的一套UI,主要是利用包含在OgreBites中的Trays完成,其实我不是很懂,但是实际用起来应该是要简单一点的,毕竟它下面只用了一个栈就建立了UI
mTrayMgr->showFrameStats(OgreBites::TL_BOTTOMLEFT);
mTrayMgr->showLogo(OgreBites::TL_BOTTOMRIGHT);
mTrayMgr->hideCursor();
// create a params panel for displaying sample details
Ogre::StringVector items;
items.push_back("cam.pX");
items.push_back("cam.pY");
items.push_back("cam.pZ");
items.push_back("");
items.push_back("cam.oW");
items.push_back("cam.oX");
items.push_back("cam.oY");
items.push_back("cam.oZ");
items.push_back("");
items.push_back("Filtering");
items.push_back("Poly Mode");
mDetailsPanel = mTrayMgr->createParamsPanel(OgreBites::TL_NONE, "DetailsPanel", 200, items);
mDetailsPanel->setParamValue(9, "Bilinear");
mDetailsPanel->setParamValue(10, "Solid");
mDetailsPanel->hide();
mRoot->addFrameListener(this); ------------调用函数为本类注册监听
}
4) 好了,setup完成以后就可以开始从根节点调用startRendering()进行渲染了,至于为什么可以循环渲染就是Ogre渲染循环的问题了,留待以后再详细的说明。
5) DestroyScene也是一个需要注意的问题,虽然例子中没有具体的内容,但是在自己实现的时候需要在此处释放你之前自己load的各种资源(创建的粒子系统,CEGUI的destroy等等)
好了,通过Ogre的标准例子程序,现在已经对于Ogre渲染流程有了初步了解,可以自己慢慢在其中添加各种功能了,这个例子程序充分的体现出了Ogre仅仅是一个实现框架,最大的功能是提供了一个有效组织各种资源和渲染系统的平台,利用其灵活并且日渐完善的平台体系,越来越多的库被添加到其中,形成一个十分强大的3D渲染引擎,因其开源特性,Ogre 的更新是非常快的,所以给学习图形或者是3D渲染和游戏编程的童鞋提供了非常好的学习资料(只是不知为何最近Ogre3D开放资源地带不能上了,是域名到期疑惑天朝一片河蟹不得而知…..)