終于決定,還是通過wow model viewer起手,研究一下WOW的數據類型,從另一個角度,體驗一把這個唯一讓我充過值的游戲。
這將是一系列隨筆,即在讀代碼的時候,順便記錄,以理清思路和加深映象。 其中會有很多讓人費解的地方,如果有幸被某位兄弟看見,請勿見笑。
我們從讀取模型數據開始。。。
下面是這是頂點結構體 這是wow model viewer中的定義
struct ModelVertex
{
Vec3D pos; //頂點位置
uint8 weights[4];//骨骼權重
uint8 bones[4];//受影響的骨骼索引
Vec3D normal;//法線
Vec2D texcoords;//紋理坐標,只有一層
int unk1, unk2; // 總是0,0 可能沒有被使用到
};
讀完頂點數據后,我們需要對坐標系做一點修正,因為WOW用的是Z軸向上, Y軸向里(依稀記得torque也是這樣子)
很多人用得不是太習慣
若要轉換為GL中的坐標(Z向外),則 pos = vec3D(pos.x,pos.z,-pos.y);
若要轉換為D3D中的坐標(Z向里),則pos = vec3D(pos.x,pos.z,pos.y);
法線轉換方式和坐標一樣
轉換為了我們想要的坐標數據以后。我們還要強制對法線進行單位化。在這里,為了對法線進行壓縮,其實我們可以僅存儲X,Y分量就可以了。
不知WOW為什么沒有這樣子。
同時,在進行模型頂點數據讀取的時候,由于我們本來就要進行頂點數據遍歷,所以我們可以順便得出這個模型的半徑,用來做球形檢測
模型數據讀完了,緊接著是BoundingMesh(想說是包圍網格,又不太對,又或者,叫碰撞網格,這是一個簡化的網格,用于碰撞檢測和拾取之類的)數據
它由兩個部分組成BoundingVertices & BoundingTriangles (我又詞窮了,都懂的。)
BoundingVertices由一串float3組成,順序讀取即可,讀取完了后,如果上面的MESH做了坐標系統(tǒng)轉換,那這里也得做。
BoundingTriangles由一串uint16索引組成,順序讀取即可。
讀取完上面的模型數據后,接下來就是紋理數據。
在WMV中的定義如下
#define TEXTURE_MAX 32 //最大紋理數
struct ModelTextureDef
{
uint32 type; //紋理類型
uint32 flags; //紋理標記
uint32 nameLen; //名字長度
uint32 nameOfs; //名字在DBC中的偏移
};
搞笑得很啊,在結構體定義的時候,沒有對上面字段說明,在使用的地方,卻有一段描述。不過想想也是,用的時候方便查看嘛。
/*
Texture Types
Texture type is 0 for regular textures, nonzero for skinned textures (filename not referenced in the M2 file!)
For instance, in the NightElfFemale model, her eye glow is a type 0 texture and has a file name,
the other 3 textures have types of 1, 2 and 6. The texture filenames for these come from client database files:
DBFilesClient\CharSections.dbc
DBFilesClient\CreatureDisplayInfo.dbc
DBFilesClient\ItemDisplayInfo.dbc
(possibly more)
0 Texture given in filename
1 Body + clothes 身體和布料
2 Cape 肩膀
6 Hair, beard 頭發(fā),胡子
8 Tauren fur 牛頭人的皮毛
11 Skin for creatures #1
12 Skin for creatures #2
13 Skin for creatures #3
Texture Flags
Value Meaning
1 Texture wrap X X方向環(huán)繞
2 Texture wrap Y Y方向環(huán)繞
*/
下面是我對這段說明的理解
0 表示是普通紋理 并且,可以直接獲取它的紋理名字
非0表示是皮膚 值得說明的是,紋理名字不包含在M2文件中。
比如說,在暗夜男模型中,他的眼睛發(fā)光就是一個類型為0的紋理,并且,有一個文件名(這個文件名就存在本文件中)。其它3個紋理類型是1,2和6. 紋理名字是從客戶端數據庫文件中提取。nameOfs就是表示其位置
額,寫到這里的時候,突然發(fā)現,其實是有宏定義的
/*
Texture Types
Texture type is 0 for regular textures, nonzero for skinned textures (filename not referenced in the M2 file!) For instance, in the NightElfFemale model, her eye glow is a type 0 texture and has a file name, the other 3 textures have types of 1, 2 and 6. The texture filenames for these come from client database files:
DBFilesClient\CharSections.dbc
DBFilesClient\CreatureDisplayInfo.dbc
DBFilesClient\ItemDisplayInfo.dbc
(possibly more)
*/
enum TextureTypes
{
TEXTURE_FILENAME=0, // Texture given in filename
TEXTURE_BODY, // Body + clothes
TEXTURE_CAPE, // Item, Capes ("Item\ObjectComponents\Cape\*.blp")
TEXTURE_ITEM=TEXTURE_CAPE,
TEXTURE_ARMORREFLECT, //
TEXTURE_HAIR=6, // Hair, bear
TEXTURE_FUR=8, // Tauren fur
TEXTURE_INVENTORY_ART1, // Used on inventory art M2s (1): inventoryartgeometry.m2 and inventoryartgeometryold.m2
TEXTURE_QUILL, // Only used in quillboarpinata.m2. I can't even find something referencing that file. Oo Is it used?
TEXTURE_GAMEOBJECT1, // Skin for creatures or gameobjects #1
TEXTURE_GAMEOBJECT2, // Skin for creatures or gameobjects #2
TEXTURE_GAMEOBJECT3, // Skin for creatures or gameobjects #3
TEXTURE_INVENTORY_ART2, // Used on inventory art M2s (2): ui-buffon.m2 and forcedbackpackitem.m2 (LUA::Model:ReplaceIconTexture("texture"))
TEXTURE_15, // Patch 12857, Unknown
TEXTURE_16, //
TEXTURE_17, //
};
enum TextureFlags
{
TEXTURE_WRAPX=1,
TEXTURE_WRAPY
};
總之,就是如果遇上是0號類型,則直接讀文件名,否則就去DBC中取公共紋理數據。
比如頭發(fā)什么的,而上面牛頭人的毛發(fā)單獨定義,可能是因為毛發(fā)和一般人型生物不一樣吧。
另外,從TEXTURE_ARMORREFLECT中可以看出,WOW中的武器和盔甲是加上了反射紋理的,這樣才看起來有高光的感覺。
------------------------------------------------------------------------
------------------------------------------------------------------------
讀完模型,碰撞網格,紋理數據,接下來,就要讀取掛接物了,最常見的掛接物,就是WOW中的肩膀,頭盔或者武器上的一些粒子效果。
WMV中,掛接物的定義如下
/*
* This block specifies a bunch of locations on the body - hands, shoulders, head, back,
* knees etc. It is used to put items on a character. This seems very likely as this block
* also contains positions for sheathed weapons, a shield, etc.
*/
struct ModelAttachmentDef
{
uint32 id; // Just an id. Is referenced in the enum POSITION_SLOTS.
uint32 bone; // Somewhere it has to be attached.
Vec3D pos; // Relative to that bone of course.
AnimationBlock unk; // (Int32) Its an integer in the data. It has been 1 on all models I saw. Whatever.
};
無非就是定義了掛接的骨骼索引,偏移位置等。 最后一個參數,是動畫塊數據。定義如下
// sub-block in block E - animation data, size 28 bytes, WotLK 20 bytes
struct AnimationBlock
{
int16 type; // 插值類型 (0=none, 1=linear, 2=hermite)
int16 seq; // 全局隊列ID,-1表示無
//下面的就是數據個數+數據在緩沖區(qū)中的偏移
#ifndef WotLK
uint32 nRanges;
uint32 ofsRanges;
#endif
uint32 nTimes; //
uint32 ofsTimes;
uint32 nKeys;
uint32 ofsKeys;
};
上面的定義可以看中,WLK版本中,BLZ對文件進行了改動,加入了一個范圍數據。
讀取完上面的掛接頭信息以后,就可以根據這個信息,實例化一個掛接物,添加到模型身上。
下面是一個模型掛接物的信息
struct ModelAttachment
{
int id; //ID
Vec3D pos; 位置
int bone; //撞接的骨骼
Model *model; //掛接的模型
void init(MPQFile &f, ModelAttachmentDef &mad, uint32 *global);
void setup();
void setupParticle();
};
讀完上面的信息后,我發(fā)現,還有一個詭異的attLookup數據, 單看字面上意思,應該是拿來裝一個供掛接物ID查詢的數據的。
就是ModelAttachment中的ID作為下標,進行查詢。 目前還沒有搞明白。
本來想繼續(xù)寫下去,但發(fā)現寸步難行了,后面的數據都沒看明白是什么意思,只好留到下次了。
睡覺了,晚安!!!!!
其實我也在問自己,為什么整來整去,又搗鼓起這個東西了。
首先,irrlicht的商業(yè)性是很淺的,如果要想應用于商業(yè)化,不下一翻功夫是不行的。 比起現在滿天飛舞的UINTY3D,就更不用說了。就算和OGRE比,也因為IRRLICHT沒有提供太多花哨的特性,而導致這么多年來ARPU值一直沒有OGRE高,玩家流失率是巨大的。
Niko自己整的SupperCuber.就更不用說了,我自己下載來弄了一下,也沒見得有多好使,反正沒有官方介紹得那么牛B。
在重新定位自己要深入挖掘的引擎之前,也曾再一次被OGRE吸引過。 原因有很多種,
一是天龍代碼的泄漏,里面有很多OGRE模型,可以直接加載,構建場景。很快速地獲得像模像樣的成就感。
二是,OGRE本身的DEMO就提供了大量的SHADER,不用自己再辛辛苦苦地去東拼西找了。
三是,本來與一個朋友相約用OGRE整RTS的, 因為目前公司的項目是RTS,所以一時間,對RTS興趣大增, 看了0 A.D. GLEST等代碼。 也明白了RTS中,工具與AI,遠遠大過于畫面顯示。 所以,使用OGRE,有現成的OgreCrowd等可以使用。 不用再為動態(tài)尋路找麻煩。
四是,OGRE的招聘和成熟的案例遠遠大于irrlicht. 光是我知道的 天龍八部,成吉思汗,獨孤求敗,極光世界,火炬之光等,就足夠說明它的威力。
五是,OGRE官方支持WIN8,ADDROID,IOS。。
原因太多了,這是一篇講irrlicht的文章,老是夸OGRE是不道德的。
下面,我來說說我的原因,也供和我一樣糾結的朋友作一個參考。
一、我時間有限,雖然之前看過OGRE的代碼,但是對OGRE還是不敢說有掌握, 如果要用OGRE,其實還是得重頭再來。
二、IRRLICHT因為東西不多,所以以前在大學的時候,就對其很熟悉了,回過頭來上手,也更容易。
三、有一點點控制欲在里面,想看看IRRLICHT經過改造后,是不是真的比不上OGRE。
四、蜀門的成功,足夠說明一個游戲的畫面,不是全部,只要不影響大局, 有一個比較亮點的技術或者效果,就可以留住玩家。 我想,蜀門里裝備的流光效果,雖然就是經過美術精心設計后的紋理動畫, 但已經足夠體現高級裝備和低級裝備的差距。 玩家也能感受到自己高級裝備的華麗, 所以,我更喜歡蜀門的小巧。
五、犯賤,越是多的人喜歡的東西,越是不想整。
六、想慢慢過渡,先使用IRRLICHT,直到IRRLICHT不夠用,就改,改完了,就把IRR所有的東西刪除了,名字換了。 就是自己的了。
七、GAMELOFT的刺客信條用的是IRRLICHT,所以,我覺得還是可以的。 (PS:下載來玩了一下,在IPAD上,主角站立不動的時候,會來回抖動, 攝相機移動的時候,也會抖動,很費解, 難道是浮點精度問題? 但UNITY3D和COCOS2D-X等是沒有這問題的呀)。
其實,列了很多條,最后也發(fā)現,IRRLICHT除了要簡單點以外,是沒有OGRE那么強大的。 不過,我還是選擇了從簡,畢竟精力有限。 如果要把OGRE整套東西理解了,再逐步重寫,我估計會瘋掉。 畢竟大而全的東西,具體在用的時候,是要拋掉很多東西來換取效率的。
既然到這個點了,不得不說點別的。
11年的時候,公司研發(fā)的引擎在演示完DEMO后,就叫停了。 項目進行了兩年半,最后只有一堆代碼和演示程序。 對公司來說,其實是一定的損失。 后來項目組成員轉戰(zhàn)WEB。 走到這一步,其實原來的成員只剩和我另一個搭檔了。
沒想到,進入了WEB,就一去不復返了。 并且,公司的WEB項目進展也不是很順利。 都說畢業(yè)后兩年是一個分水嶺, 當時正好畢業(yè)兩年。 于是決定換一個環(huán)境挑戰(zhàn)。 就來到了目前的公司,做RTS游戲服務器。 面對未知的東西,貌似更能激發(fā)我的興趣,如今馬上又是一年了。漸漸地,開始懷念引擎,懷念圖形。 可以說IRRLICHT目前就是被我用來表達我對圖形的思念。 雖然我圖形方面的技術很陳舊,老土, 但并不影響我說我喜歡搞圖形。
文章就到此吧。也不知道再要說些什么了。
還是先上圖吧

這是使用freetype進行中文顯示的效果。
irrlicht由于是使用位圖字體的方式,是很容易替換掉字體的。 同時,其本身也提供了Font接口替換的功能。
具體做法和網上大多數人是一樣的。
在做這個的時候,又引入了另一個話題, gameswf和kfont(KlayGE Font)
gameswf是一個開源的C++ FLASH PLAYER。
gameloft以及很多移動應用或者游戲都在使用它。
當然,也包括ScaleForm. 因為ScaleForm是商業(yè)的,所以比gameswf更加完善,雖然說,gameswf是ScaleForm的原型。
kfont一直是自己喜歡的一種字體解決方案,其無比拉伸的能力非常討人喜歡。加上現在又單獨成庫了,如果不用gameswf的話,我想把它整合進irrlicht中。 兩年前公司(先前的公司)的引擎就用上了這個,遺憾字體庫不是我弄進去的,一直對kfont沒有近距離接觸。
網上下載下來的代碼 下面地址可供參考,這是我覺得眾多文章中,講得比較細的一個。
http://blog.csdn.net/lee353086/article/details/5260101
至于FreeType,大家去官方下載來編譯就可以了。
先上圖,再說點別的。
BLOOM開
BLOOM關
在IRRLICHT中實現BLOOM,和其它引擎中沒有太多的不同。 SHADER還是那個SHADER。
關于BLOOM的算法,也就那樣了,沒有特別之處,況且,我這BLOOM很暴力
render scene to texture.
1/4 downsample 選擇暴光像素
h_blur 7次采樣 和權重混合
v_blur 7次采樣 和權重混合
compose 兩圖疊加
下面說說我在irrlicht中實現post processing的方案。
在irrlicht中是沒有屏幕對齊四邊形節(jié)點的,如果要特殊擴展,就只能修改代碼了。我是盡量保證自己不修改IRR一行代碼, 除非是真正使用時,要對效率進行優(yōu)化。前現實現的GPU蒙皮,水面,鏡面等,都沒有修改過一行代碼, 因為我不想因為自己的一時需求,而改動了那一堆。 當我真的需要改動irrlicht才能達到目標的時候,表示irrlicht中我使用的部分,可以退休了。
渲染場景的時候,我們通常在使用addXXXXSceneNode的時候,都默認不傳父節(jié)點。這樣就是默認的場景根節(jié)點。但是,當我們要做post process的時候,就需要對場景中的物體進行顯示的開和關, 于是,我們?yōu)榱撕芸焖俚乜刂疲?于是將普通場景節(jié)點多加了一個父節(jié)點, 而post processing作為場景的兄弟節(jié)點, 這樣在渲染的時候,就可以方便地進行相關控制了。
大概是這樣的
RootSceneNode
PostProcessingNode SceneOjbectsNode
Obj1… Obj2….Obj3…
流程:
關閉 PostProcessingNode , 渲染 SceneOjbectsNode 下所有的物體到RT上。
關閉 SceneOjbectsNode, 打開PostProcessingNode, 進行一系列的后期效果處理。
在irrlicht中是沒有提供屏幕對齊四邊形繪制的, 如果手工構建,就很麻煩。 所以,我采用的是一種很常見的手法, 即通過UV坐標來計算最最終的頂點坐標值。
VS的輸出,是規(guī)一化坐標系, 即X,Y是處于 (-1,1)之間的, 于是。 我們只需要 pos = (uv-0.5)*2; pos.y = –pos.y;就可以了。
最近一直在加班,沒時間整理出代碼。 有興趣的朋友可以加下面的群
Irrlicht Engine-China
254431855