一、Irrlicht引擎簡介
Irrlicht引擎是一個用C++編寫的高性能實時3D引擎。該引擎支持底層圖形接口Direct3D和OpenGL,並且自帶了軟件渲染的實現。Irrlicht引擎還提供了諸如動態陰影,粒子系統,角色動畫,室內和室外技術以及碰撞檢測等功能特性。
Irrlicht是一個德國神話故事中的一種動物的名字,它能夠發光和飛翔,可以在大部分的沼澤地附近發現它。單詞"Irrlicht"是兩個德國單詞("irr"意思是瘋狂的;而"Licht"意思是光)的組合。在英語中,它被譯爲"鬼火"。
二、Irrlicht引擎的組成結構
Irrlicht引擎共分爲五部分:
1)Core
該部分由一些容器類及數學庫組成,如string、vector等。
對應的名字空間爲:namespace irr::core
2)Scene
該部分主要負責三維場景的繪製及管理,包括場景節點,攝像機,粒子系統、mesh 資源,公告板,燈光,動畫器,天空體,地形等。
Irrlicht的場景中的所有的東西都是場景節點,統一由場景管理器來管理。
對應的名字空間爲:namespace irr::scene
3)Video
該部分主要負責圖片紋理的載入及管理,包括紋理,材質,燈光,圖片,頂點等渲 染屬性的控制。
對應的名字空間爲:namespace irr::video
4)GUI
該部分包括了一些二維GUI控件
對應的名字空間爲:namespace irr::gui
5)FileSystem
該部分負責文件系統的讀寫。
對應的名字空間爲:namespace irr::io
三、Irrlicht編程步驟
1、獲取設備指針
在編寫任何一個Irrlicht程序時,首選需要做的就是獲取設備指針
IrrlichtDevice *device :
video::E_DRIVER_TYPE driver_type = irr::video::EDT_OPENGL;
core::dimension2d<s32> screen_resolution = core::dimension2d<s32>(1280, 800);
u32 color_depth = 32;
bool is_full_screen = true;
IrrlichtDevice* device = irr::createDevice(driver_type, screen_resolution, color_depth, is_full_screen);
driver_type爲驅動類型,可以選擇OPENGL,DX8 或 DX9。其餘幾個參數分別爲分辨率,顏色 深度,是否全屏。
獲得 device 指針以後,就可以得到屬於該 device 的四大塊功能:
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* scene_mgr = device->getSceneManager();
gui::IGUIEnvironment* gui_env = device->getGUIEnvironment();
io::IFileSystem* file_system = device->getFileSystem();
2、創建3D場景
創建3D場景有三個步驟:
(1) 通過場景管理器scene_mgr添加3D物體:
// 爲場景添加一個立方體,邊長100
scene::ISceneNode* cube = scene_mgr->addCubeSceneNode(100.0f);
(2) 爲3D物體貼上紋理:
// 載入紋理
video::ITexture* tex = driver->getTexture("box.jpg");
// 將紋理附加到立方體上
cube->setMaterialTexture(0, tex);
// 將紋理EMF_LIGHTING屬性設爲false
// 表示該紋理現實與光源無關,即爲圖片自身顏色
cube->setMaterialFlag(video::EMF_LIGHTING, false);
// 使紋理支持半透明,半透明效果與圖片相同
cube->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL);
(3) 添加Camera,使物體可見:
// 添加一個相機,在(700,700,-700)位置,往(0,0,0)位置拍攝。
scene::ICameraSceneNode* camera = scene_mgr->addCameraSceneNode(0,
core::vector3df(700,700,-700), core::vector3df(0,0,0));
3、進入主循環
通過 driver 載入圖片和 texture,通過 scene_mgr 爲3d場景添加 irrlicht 內置支持的3D對象等工作後,即可進入主循環,主循環結束時,釋放 device,程序結束。其中 beginScene 的參數 SColor(alpha, r, g, b) 爲背景色:
1 while (device->run())
2 {
3 if (device->isWindowActive())
4 {
5 driver->beginScene(true, true, video::SColor(0, 0, 0, 0));
6 scene_mgr->drawAll();
7 gui_env->drawAll();
8 driver->endScene();
9 }
10 }
11 device->drop();
值得一提的是 drop() 函數。Irrlicht中大部分類都繼承自一個 IReferenceCounted 的接口,類似智能指針。Irrlicht 中的慣例是不使用 delete 刪除對象,而調用該接口的 drop() 函數。在添加對象的引用時,調用 grap() 函數。
四、Irrlicht引擎之場景中的對象結構
3D對象在場景中被組織成一個樹形結構,在該樹中,僅有一個根節點。當在程序的主循環中調用scene_mgr->drawAll()時,系統會從場景的樹形結構的根節點開始遞歸繪製所有的節點對象。
每一個節點對象都維護了一個“動畫效果”ISceneNodeAnimator列表:
core::list<ISceneNodeAnimator*> Animators;
在繪製每一個節點對象之前,都會將其所維護的所有動畫效果應用到該節點上。
“動畫效果”的原理是,當節點每次被繪製之前,均先根據其維護的動畫對象Animators來計算出該節點的位置、大小、紋理。這樣,當一個節點不斷被繪製時,就產生了動畫效果。
五、Irrlicht引擎之3D對象運動原理
所謂運動,實際上是計算機在不停地繪製場景,每繪製一次稱之爲一幀。 當各幀中物體的位置或外觀有所變化,那麼它就動起來了。 在irrlicht中,繪製一幀是在run循環中完成的:
1 while (device->run())
2 {
3 if (device->isWindowActive())
4 {
5 driver->beginScene(true, true, video::SColor(0, 0, 0, 0));
6 scene_mgr->drawAll(); // 繪製一幀
7 driver->endScene();
8 }
9 }
10 device->drop();
我們所要做的,就是在 drawAll() 函數中,更新物體的位置及大小等屬性,那麼場景就動起來了。
在Irrlicht中,所有與運動相關的一切,都與 scene::ISecenNodeAnimator 這個接口相關。凡是實現這個接口的類實例,都可以通過 addAnimator() 函數加入到 ISceneNode 所維護的animators列表中。
SceneManager 的drawAll() 函數在渲染(render)場景前,會調用其OnAnimate() 函數。這個函數是遞歸的,以保證加入場景中的每個 SceneNode 都會被調用。 在OnAnimate() 函數中, SceneNode 的每一個 ISecenNodeAnimator 的 animateNode() 函數都會被調用,以更新 SceneNode 的位置、大小或紋理等屬性。
其具體的調用順序如下:
1. SceneManager --> drawAll() 繪製一幀畫面
2. ISceneNode --> OnAnimate() SceneNode運動
3. ISceneNodeAnimator --> animateNode(ISceneNode) 實現運動的具體函數
4. SceneManager --> render() 渲染
可見,只要實現 ISecenNodeAnimator 接口,並加入到 SceneNode 中,就能夠讓該SceneNode 運動起來。而至於具體如何運動,完全可由自己來定義。
六、Irrlicht引擎之消息傳遞原理
Irrlicht引擎中消息的傳遞是從device->run()開始的,首先由具體平臺的操作系統接口將接收到的用戶消息打包成Irrlicht的SEvent結構(這一步打包過程是在device->run()中完成的),再由postEventFromUser()將該消息依次傳遞給UserReceiver, GUI 和 3D Scene。
Irrlicht 中所有處理消息的類都必須實現 IEventReciever 接口。UserReceiver 就是在CreateDevice函數中指定的一個 IEventReceiver。 也就是說,消息處理的優先級爲 UserReceiver > GUI > 3D Scene。
如果在程序中,沒有指定UserReceiver和GUI,消息就會直接交給SceneManager。
SceneManager 的消息,也由 ISceneManager 的 postEventFromUser() 傳遞。 這個函數的實現如下:
1 bool CSceneManager::postEventFromUser(const SEvent& event)
2 {
3 bool ret = false;
4 ICameraSceneNode* cam = getActiveCamera();
5 if (cam)
6 ret = cam->OnEvent(event);
7
8 _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
9 return ret;
10 }
也就是說,只有當前Active(有效)的 ICameraSceneNode 纔會接收到消息。ICameraSceneNode 會檢測ISceneNodeAnimator 的isEventReceiverEnabled(), 如果爲真則調用其OnEvent 函數。
整理消息傳遞的機制如下:
1. IrrlichtDevice --> run() 蒐集消息並打包
2. IrrlichtDevice --> postEventFromUser() 傳遞消息到userReceiver GUI 和 Scene
3. ISceneManager --> postEventFromUser() 傳遞消息到CameraNode
4. ICameraSceneNode --> onEvent() 調用 Animator 的onEvent
5. ISceneNodeAnimator --> onEvent() // if enabled 響應消息。
可見,實現 ISceneNodeAnimator 雖然可以 SceneNode 動起來,但只有在 ICameraSceneNode 上,才能夠接收和處理消息。