天天看點

使用模型(X檔案)

(本文原網址:http://hi.baidu.com/liu98/blog/item/80b6887ebe3ad73c0dd7da00.html)

使用模型 2008-03-05 22:04

在本章的前半部分,介紹了如何使用網格模型來建立地形或簡單的三維圖形。接下來,看看如何把三維模組化軟體生成的複雜三維模型導入程式中。流行的3D檔案格式現在有很多種,比如.3ds、.max,.cof等,它們包含了許多資訊,并且都不為像遊戲引擎這樣的實時環境所優化。遊戲需要更快、更簡單的檔案格式。根據遊戲類型的不同,每個遊戲使用的檔案格式也各不相同,比如,Quake引擎所使用的檔案格式就是.md3的模型格式。

首先,看看最簡單的3D檔案格式.X,這是Direct3D所使用的格式,下面就研究一下.X格式的模型檔案。

5.3.1 X檔案格式

.X檔案格式包含了兩個最基本的部分:使用者自定義的資料類型和層級關系。X檔案是由模闆(template)驅動的,模闆定義了如何存儲一個資料對象,這樣使用者便可以自己來定義具體的格式。預定義的模闆位于rmxftmpl.h和rmxftmpl.x中,模闆的辨別符都在rmxfguid.h中,通用檔案d3dfile.cpp包含了這兩個頭檔案。模闆所允許的資料類型被稱為“可選成員(optional member)”,這些可選對象作為資料對象的子對象來儲存。子對象可以是另一個資料對象,可以是對前一個資料對象的引用,也可以是一個二進制的對象。

X檔案格式包含了下面幾個主要部分。

1.首部(Header)

首部是X檔案起始的部分,起始的4位元組是魔數(magic number)“xof”,主版本号為03,次版本号也為03。“txt”代表文本檔案格式,“bin”代表二進制檔案格式。下面是一個典型的X檔案的檔案頭:

xof 0303txt 0032

2.幀(Frame)

這個模闆用來存儲一個幀,幀是層次場景的構造元素。幀有它們自己的轉換矩陣,還可以有自己的子對象(child objects)。幀也可以有子幀。在skin meshes中,骨骼(bone)指的就是一個幀。

3.變換矩陣(FrameTransformMatrix)

正所謂人如其名,這個變換矩陣是幀的變換。它在Frame模闆裡有執行個體。下面的檔案片斷描述一個幀chr_h_047,該幀帶有一個子幀,有一個mesh格式的子對象:

Frame chr_h_047 {

   FrameTransformMatrix {

1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,

0.000000,0.000000,1.000000,0.000000,0.000000,-0.323429,-35.371239,

1.476196,1.000000;;

}

Frame {

   FrameTransformMatrix {

1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,

0.000000,0.000000,0.000000,1.000000,0.000000,0.016750,0.671672,

-31.375168,1.000000;;

   }

   Mesh {

    }

4.網格模型(Mesh)

這個模闆存儲一個表态的mesh和mesh的材質。在skin meshes裡,整個角色将隻是一個mesh,由皮膚資訊(skinning information)确定mesh中的每一個部分是如何受到骨骼的影響的。mesh在内部會分成幾個子集,每一個子集将受到一個特定的骨骼集合的影響。

5.XskinMeshHeader

這個模闆存儲随mesh一起導出的(exported)關于皮膚資訊的屬性(nature)。這個模闆包含在mesh模闆裡面。

template XSkinMeshHeader {

<3cf169ce-ff7c-44ab-93c0-f78f62d172e2>

WORD nMaxSkinWeightsPerVertex;

WORD nMaxSkinWeightsPerFace;

WORD nBones;

}

6.SkinWeights

真正的皮膚資訊就存儲在這裡,這個模闆定義了一個特定的骨骼能夠去影響一個mesh。這個模闆在每一個影響到mesh的骨骼裡面都有執行個體。例如,有12個骨骼影響到mesh,mesh模闆裡将有12個SkinWeights模闆的執行個體。

template SkinWeights {

<6f0d123b-bad2-4167-a0d0-80224f25fabb>

STRING transformNodeName;

DWORD nWeights;

array DWORD vertexIndices[nWeights];

array FLOAT weights[nWeights];

Matrix4x4 matrixOffset;

}

如果建立的是帶有骨骼動畫的模型,它和靜态模型最大的差別就在于骨骼動畫模型必須有XskinMeshHeader和SkinWeights模闆。如果把這兩個模闆從任何一個skin mesh裡面移走的話,就會得到一個靜态的mesh(static mesh)。

7.VertexDuplicationIndices

模型的索引緩沖區格式:

template VertexDuplicationIndices {

<b8d65549-d7c9-4995-89cf-53a9a8b031e3>

DWORD nIndices;

DWORD nOriginalVertices;

array DWORD indices[nIndices];

}

8.材質和紋理

模型的材質和紋理資訊都包含在Mesh中,可以直接從X檔案裡讀取材質和紋理資訊。紋理是以檔案的形式存儲的:

Material {

1.000000;1.000000;1.000000;1.000000;;

0.000000;

1.000000;1.000000;1.000000;;

0.000000;0.000000;0.000000;;

TextureFilename {

   ".//chr_h_047.bmp";

}

}

9.動畫

在X檔案裡包含了一個動畫集(AnimationSet),每個動畫集裡的動畫都屬于對某個架構的引用。例如:下面的X檔案動畫集就分别是對幀Scene_Root和chr_h_047的引用:

AnimationSet {

Animation {

   AnimationKey {

   4;

   2;

…0;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000, 0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000;;,

     }

{ Scene_Root }

}

Animation {

    AnimationKey {

   4;

   2;

…   16160;16;1.000000,0.000000,0.000000,0.000000,0.000000,0.000000, -1.000000,0.000000,0.000000,1.000000,0.000000,0.000000,-0.323429,-35.371239,1.476196,1.000000;;;

}

{ chr_h_047 }

}

}

5.3.2 輸出.X檔案格式

可以有多種方法來得到X檔案。首先需要使用三維模組化軟體來建立需要的三維模型,如3DS Max、MAYA。接着,可以使用三維模組化軟體的插件來導出X檔案。

可以使用的導出插件包括XSkinExp、Panda等,從實用角度看,Panda更勝一籌。無論是導出靜态模型,還是導出帶有骨骼動畫的動态模型,一般都不會出問題。這個插件可從下面這個網址下載下傳:http://www.andytather.co.uk /Panda/directxmax_downloads.aspx。

Panda針對不同的3DSM ax版本,有不同版本的插件與之對應,在使用時隻要把PandaDXExport*.dle(*對應的是版本)這個檔案放在3DS Mas安裝目錄下的plugins目錄中就可以了,3DS Mas在啟動後會自動識别這個插件。

在3DS Max中打開一個模型,并單擊“輸出”按鈕後,彈出如圖5-10所示的儲存對話框,選擇Panda對應的儲存格式,并單擊“打開”按扭。在彈出的儲存選項對話框中按圖5-11~圖5-14的訓示進行選擇即可。

使用模型(X檔案)
圖5-10 Panda插件的儲存類型
使用模型(X檔案)
圖5-11 對象參數
使用模型(X檔案)
圖5-12 動畫參數
使用模型(X檔案)
圖5-13 紋理的設定
使用模型(X檔案)

圖5-14 X檔案格式的設定

經過一系列正确設定後,就會導出一個相應的X檔案。同時這個X檔案中用到的位圖檔案也會一并導出。輸出檔案後,應該檢查一下檔案内容對不對,這個時候可以用DirectX中自帶的觀看模型工具Mesh Viewer來看,如圖5-15所示。

使用模型(X檔案)
圖5-15 用Mesh Viewer工具檢視導出的模型

5.3.3 在Direct3D程式中載入X檔案

在Direct3D中,3D模型都是以網格模型的形式使用的,它将資料從X檔案中讀出,通過函數D3DCreateMesh()來建立3D圖形。不過通常情況下不用顯式地調用函數D3DcreateMesh(),它已經被封裝在其他Direct3D功能擴充庫函數中。

1.從檔案中讀取網格模型

函數D3DXLoadMeshFromX()用于從X檔案中提取多邊形網格資訊(包括頂點坐标、顔色、法線向量、紋理資訊等),生成多邊形網格。函數聲明如下:

HRESULT D3DXLoadMeshFromX(        

    LPCTSTR pFilename,                   //X檔案路徑和檔案名

    DWORD Options,                       //指定生成多邊形網格屬性

    LPDIRECT3DDEVICE9 pDevice,           //Direct3D裝置指針

    LPD3DXBUFFER* ppAdjacency,           //存儲臨近多邊形資訊的記憶體位址

    LPD3DXBUFFER* ppMaterials,           //存儲材質的記憶體位址

    LPD3DXBUFFER* ppEffectInstances, //存儲模型特殊效果的記憶體位址

    DWORD* pNumMaterials,                //存儲材質數目的記憶體位址

    LPD3DXMESH* ppMesh                   //存儲生成的多邊形網格的記憶體位址

);

其中,LPD3DXBUFFER可以存儲如頂點緩沖區、材質、紋理等多種類型的Direct3D資料,而不必對每一種資料聲明一個函數接口類型。

下面的函數片斷從名為“game”的X檔案裡讀取3D模型:

if( FAILED( D3DXLoadMeshFromX( "game.x", D3DXMESH_SYSTEMMEM,

        g_pd3dDevice, NULL, &pD3DXMtrlBuffer,NULL,

        &g_dwNumMaterials, &g_pMesh ) ) )

{

    return E_FAIL;

}

2.載入紋理和材質

函數D3DXLoadMeshFromX()調用成功後,參數pNumMaterials就會獲得 X檔案中三維模型的材質和紋理資訊,而pNumMaterials會存儲材質的數目。

下面的程式片斷用于擷取3D模型的材質和紋理資訊。

【例5-5】得到模型中的材質和紋理資訊:

// 從材質集合中把材質和紋理資訊解壓讀取出來

D3DXMATERIAL* d3dxMaterials =

                (D3DXMATERIAL*)pD3DXMtrlBuffer->GetBufferPointer();

g_pMeshMaterials = new D3DMATERIAL9[g_dwNumMaterials];

g_pMeshTextures = new LPDIRECT3DTEXTURE9[g_dwNumMaterials];

for( DWORD i=0; i<g_dwNumMaterials; i++ )

{

    // 拷貝材質

    g_pMeshMaterials[i] = d3dxMaterials[i].MatD3D;

    // 設定材質漫反射的顔色

    g_pMeshMaterials[i].Ambient = g_pMeshMaterials[i].Diffuse;

    // 建立紋理

    if( FAILED( D3DXCreateTextureFromFile( g_pd3dDevice,

       d3dxMaterials[i].pTextureFilename, &g_pMeshTextures[i] ) ) )

    {

        g_pMeshTextures[i] = NULL;

    }

}

// 釋放材質緩沖區的内容

pD3DXMtrlBuffer->Release();

3.繪制網格模型

最後,調用函數DrawSubset()繪制多邊形網格的3D模型,如圖5-12所示。

// 用循環繪制每一個網格面。

for( DWORD i=0; i<g_dwNumMaterials; i++ )

{

    // 設定材質和貼圖

    g_pd3dDevice->SetMaterial( &g_pMeshMaterials[i] );

    g_pd3dDevice->SetTexture( 0, g_pMeshTextures[i] );

   // 繪制網格

    g_pMesh->DrawSubset( i );

}

使用模型(X檔案)
圖5-16 載入模型後的效果

5.3.4 三維模型動畫

有了模型之後,要為三維模型添加相應的動作,實作遊戲效果。Direct3D支援的三維模型動畫很豐富,最主要的有三種:

(1)關節動畫;

(2)單一網格模型動畫;

(3)骨骼動畫。

1.關節動畫

關節動畫中的角色由若幹獨立的部分組成。每一部分對應着一個獨立的網格模型,不同的部分按照角色的特點組織成一個層次結構。比如說,一個人體模型可以由頭,上身,左上臂,左前臂,左手,右上臂,右前臂,右手,左大腿,左小腿,左腳,右大腿,右小腿,右腳等各部分組成。而某個部分可能是另一個部分的子節點,同時又是另一個部分的父節點。比如前面的人體模型中,右前臂就是右上臂的子節點,同時也是右手的父節點。而右上臂是上身的子節點,上身則是軀體的子節點。通過改變不同部分之間的相對位置,比如夾角、位移等,就可以實作所需要的各種動畫效果。

這類動畫的優點很多。首先,在動畫序列關鍵幀中隻需要存儲節點間的相對變化,是以動畫檔案占用的空間很小。其次,可以實作很多複雜的動畫效果,如果應用程式支援反向動力學,還可以動态實作預先存儲的動畫序列之外的新的動畫效果。

當然這類動畫也有不少缺點。其中之一是由于角色模型是一個層次模型,要獲得某一個部分相對于世界坐标的位置,必須從根結點開始,周遊該節點所有的父節點來累計計算模型世界變換。但最關鍵的問題是在不同部分的結合處往往會有很明顯的接縫,這會嚴重地影響模型的真實感。

使用關節動畫的遊戲中,最著名的就是Quake2,該引擎使用.md3格式的三維模型。一個md3模型通常由身體的三個部分和一個武器模型組成。是以,需要打開3~4個模型并把它們放到一起。每個md3模型一般會有三個标志,這些标志對應它們所在的位置,例如,在頭底部,通過tag_head标記将頭部連接配接到身體上部。每個标記實際上是一個單面的幾何體(一個直角三角形)。三角形中較長的垂直邊必須面向前方,三角形的底部是一個支點(pivot point),這個支點決定各個模型将繞何處作旋轉。标記是沒有材質的,是以是不可見的。md3将角色動畫存儲在關鍵幀裡,通過幀的重新整理來實作動畫效果,每個動畫都是由各個部分的動畫連接配接而成的。

2.單一網格模型動畫(關鍵幀動畫)

單一網格模型動畫中的角色由一個完整的網格模型構成。在動畫序列的關鍵幀中,記錄着組成網格的各個頂點的新位置是相對于原位置的改變量。通過在相鄰關鍵幀之間插值來直接改變該網格模型中各個頂點的位置,就可以實作動畫效果,如圖5-17所示。

與關節動畫相比,單一網格模型動畫的角色看上去更真實,也不會有關節動畫所面臨的接縫問題。由于沒有使用層次模型,獲得模型網格頂點在世界坐标中位置的計算量也很小。但是,這類動畫的适應性很弱,角色很難通過實時計算來與環境進行良好的互動,以獲得預先存儲的動畫序列之外的動畫效果。另一方面,由于關鍵幀要存儲網格模型所有的頂點資訊,動畫檔案占用的空間特别大。

使用模型(X檔案)
圖5-17 關鍵幀動畫

3.骨骼動畫

骨骼動畫是目前最為流行的三維模型動畫,它可以看做關節動畫和單一網格模型動畫的結合。在骨骼動畫中,一個角色由作為皮膚的單一網格模型和按照一定層次組織起來的骨骼組成。骨骼層次描述了角色的結構,就像關節動畫中的不同部分一樣,骨骼蒙皮動畫中的骨骼按照角色的特點組成一個層次結構。相鄰的骨骼通過關節相連,可以作相對的運動。通過改變相鄰骨骼間的夾角、位移,組成角色的骨骼就可以做出不同的動作,實作不同的動畫效果。

皮膚則作為一個網格蒙在骨骼之上,規定角色的外觀。這裡的皮膚不是固定不變的剛性網格,而是可以在骨骼影響下變化的可變形網格。組成皮膚的每一個頂點都會受到一個或多個骨骼的影響。在頂點受到多個骨骼影響的情況下,不同的骨骼按照與頂點的幾何、實體關系确定對該頂點的影響權重,這一權重可以通過模組化軟體計算,也可以手工設定。通過計算影響該頂點的不同骨骼對它影響的權重和,就可以得到該頂點在世界坐标系中的正确位置。

動畫檔案中的關鍵幀一般儲存着骨骼的位置、朝向等資訊。通過在動畫序列中相鄰的兩個關鍵幀間插值,可以确定某一時刻各個骨骼的新位置和新朝向。按照皮膚網格各個頂點中儲存的影響它的骨骼索引和相應權重資訊,可以計算出該頂點的新位置。這樣就實作了在骨骼驅動下的單一皮膚網格變形動畫,或者簡單地說骨骼蒙皮動畫。

骨骼蒙皮動畫的效果比關節動畫和單一網格動畫更逼真,更生動。而且,随着3D硬體性能的提高,越來越多的相關計算可以通過硬體來完成,骨骼蒙皮動畫已經成為各類實時動畫(尤其是網絡遊戲圖形顯示技術)應用中使用最廣泛的動畫技術。

5.3.5 骨骼動畫詳解

現在研究一下如何在Direct3D程式中實作一個骨骼動畫。首先需要載入建好的帶有骨骼動畫的三維模型;其次,需要将skin mesh連到骨骼上。在X檔案裡已經看到,X檔案有幀的層次和一個或多個的Mesh包含皮膚資訊的模闆,需要一個個地加載這些模闆,然後手動把mesh同它的骨骼(即幀)連接配接起來。

為了加載X檔案的資料,需要用Direct3D自帶的X檔案的代碼庫。IDirectXFile是這個庫的主接口,它有一個建立IDirectXFileEnumObject的方法。用IDirectXFileEnum Object接口的方法從一個特定的X檔案裡面擷取資料。IDirectXFileEnumObject:: GetNextDataObject将會周遊X檔案中的所有高層模闆(top-level template),然後傳回一個IDirectXFileData接口。IDirectXFileData用來在X檔案中擷取單個模型中的資料,使用的函數類似于IDirectXFileEnumObject::GetNextDataObject()。IDirectXFileData:: GetNextObject()周遊所有的子模闆,然後傳回一個IDirectXFileData接口。方法IDirectXFileData::GetData()用于擷取來自模闆的資料,但在可以擷取資料之前,需要知道模闆的類型。方法IDirectXFileData::GetID傳回模闆的GUID(global unique identifier)。例如,如果模闆是Frame模闆,那麼GetID将會傳回TID_D3DRMFrame,這在DX的頭檔案裡有定義。如果需要模闆執行個體的名字,GetName可以做到這一點。

在X檔案中,将會發現一個GUID為TID_D3DRMMesh的模闆,這意味着模闆裡面存有一個mesh。函數D3DXLoadSkinMeshFromXof将會加載skin mesh和所有其他的補充性資料,隻需要向它傳遞一個IDirectXFileData的指針,用來存儲得到的資料。

D3DXLoadSkinMeshFromXof傳回給一個指向ID3DXSkinMesh對象的指針,這個對象含有skin mesh。在内部這個對象把mesh資料分組存儲,每一組都由不同的骨骼進行轉換(transform)。這個函數也傳回一個被skin mesh使用的材質資料。需要把skin mesh和骨骼連接配接起來。D3DXLoadSkinMeshFromXof傳回一個緩沖區,這個緩沖區包含所有影響到這個mesh的骨骼的名字,這個方法還傳回另一個包含它們的變換的緩沖區。用名字來為特定的骨骼周遊所有的幀層次,骨骼轉換會令人混淆,轉換應該被包含在幀裡面而不是這裡。實際上,這個轉換是骨骼的偏移量。什麼是骨骼偏移量呢?skin mesh裡所有的頂點是相對一個原點存儲的,這個原點是mesh的原點而不是骨骼的自身坐标系原點,這就意味着要想得到骨骼對mesh的影響,應該用骨骼的目前變換和骨骼的原始變換之差使mesh變形。換句話說,應該把頂點轉換到骨骼的局部坐标空間,然後再用新的轉換把它們轉換回mesh坐标空間。例如,假定有一個骨骼在(0, 50, 0),有一個頂點在(0, 51, 0),又假定這個頂點隻受這個骨骼的影響,如果把骨骼從它的原始位置移到新的位置(0, 51, 0),頂點就應該移到(0, 52, 0),但是如果簡單地把頂點乘以骨骼的變換,則頂點将會得到新的位置(0, 102, 0),這是個錯誤的位置。是以,用骨骼偏移量矩陣把頂點從它的原點轉換到一個相對于骨骼的新位置。新位置是(0, 1, 0),這将由骨骼的目前矩陣轉換到新的位置,即(0, 52, 0)。實際上,當使用一個骨骼時,用偏移量矩陣乘以它的目前的轉換矩陣得到的結果作為世界變換矩陣。

回到ID3DXSkinMesh對象,這個對象持有skin mesh的原始形式資料。這個對象沒有渲染skin mesh的任何功能,是以首先需要将這個mesh轉換到ID3DXMesh對象。ConvertToBlendedMesh可以完成這個轉換工作。從ConvertToBlendedMesh函數轉換得來的ID3DXMesh對象和前者有不同點,它的頂點包含blending weights,是以需要打開vertex blending,然後在調用DrawSubset之後設定骨骼矩陣。正如前面所講到的,mesh将會被分成幾組子集(subset),每一個子集應該用特定的材質和特定的骨骼集合去渲染。結構D3DXBONECOMBINATION為每一個mesh子集指定材質,這個結構的一個隊列(數組)也由ConvertToBlendedMesh函數中得到,需要做的是周遊這個數組,設定材質和骨骼,然後把材質的索引傳給ID3DXMesh的DrawSubset進行渲染。

現在可以設計代碼來實作骨骼動畫了,其類結構如圖5-18所示。

使用模型(X檔案)

圖5-18 骨骼動畫中用到的類結構

CObject的目的在于提供一個樹的機制:任何一個派生自CObject的對象都有鍊成一棵樹的能力。CFrameNode是場景層次的構造元素而CMeshNode本身則擁有mesh。CMeshNode被包含在CFrameNode裡面,而CFrameNode又被包含在CSkinMesh裡面。整個場景由CSkinMesh開始,因為它擁有根幀(root frame)。所有與skin mesh相關的操作都将在CSkinMesh裡面初始化。相應地,CSkinMesh将按需要把控制交給場景層次。是以,主程式将隻是與CSkinMesh打交道,CFrameNode和CMeshNode将由CSkinMesh間接操作。

下面的僞碼算法說明了場景是怎樣從X檔案基礎上建立的。

【例5-6】通過X檔案載入具有動畫資訊的模型(僞碼):

CSKinMesh::CReate()

Begin

Initialize X file API

Register D3DRM templates

Open the X file

For every top level template in the X file

Begin

    Retrieve the X file data object

    Pass the data object to RootFrame.Load

End

Link the bones to the skin mesh(es)

End

CFrameNode::Load()

Begin

    Check the type of the data object

    If the type is Mesh

    Begin

        Create new CMeshNode object

        Attach the new object to the frame

    Pass the data object to CMeshNode::Create of the new mesh

    End

    Else if type is FrameTransformMatrix

        Load the transform matrix

    Else if type is Frame

    Begin

        Create the new CFrameNode object

        Attach the new object to this frame

        Set the name of the child frame to the name of the template

        For every child template of the current

        Begin

            Retrieve the X file data object

            Pass it to newframe.load

        End

    End

End

CMeshNode::Create()

Begin

    Set the name of the object to the name of the template

    Load the skin mesh

    Generate blended mesh from this skin mesh object

    Load materials

End

建立skin mesh之後,就可以開始渲染了。渲染操作由兩個階段構成。在第一個階段裡,計算骨骼的世界矩陣(通過矩陣相乘),并把它存儲在CMeshNode對象裡。在第二個階段裡面,skin mesh将被渲染。下面這個僞碼算法顯示了這個渲染過程。

【例5-7】渲染一個具有骨骼動畫的模型(僞碼):

CSkinMesh::Render()

Begin

    Calculate the world matrix of all the frames

    Call CMeshNode::Render of all mesh nodes in the hierarchy

End

CMeshNode::Render

Begin

    Enable vertex blending

    For every subset in the skin mesh

    Begin

        Set the bones’ transformation matrices to the device

        Set the material

        Render

    End

    Set vertex blending back disabled

End

X檔案裡的動畫不是skin mesh獨有的,它們對X檔案裡的任何的幀都是适用的。X檔案存儲了關鍵幀而程式則應用線性插值的方法産生中間幀。有4種動畫關鍵幀:轉動、縮放、位置、矩陣。函數D3DXQuaternionSlerp可以實作這種插值。在X檔案裡有專門的子產品存儲動畫資訊。

為了實作skin mesh,需要增加一個新的類。把這個類命名為CAnimationNode,這個類擁有幀碼和指向目标幀的指針,這個類同樣包含一個SetTime函數。這個函數會用從AnimationKey得到的新的時間值更新目标幀的變換矩陣。CAnimationNode的每 一個執行個體都将擁有Animation模闆的一個執行個體。圖5-19顯示出代碼的新的設計。

下面的僞碼設計了相應的新算法。

【例5-8】對載入模型算法的改進(僞碼):

CSkinMesh::Create()

Begin

    Initialize X file API

    Register D3DRM templates

    Open the X file

    For every top level template in the X file

    Begin

        Retrieve the X file data object

        Pass the data object to RootFrame.Load

    End

    Link the bones to the skin mesh(es)

    Link the bones to the animations

End

CFrameNode::Load()

Begin

    Check the type of the data object

    If the type is Mesh

    Begin

        Create new CMeshNode object

        Attach the new object to the frame

        Pass the data object to CMeshNode::Create of the new mesh

    End

    Else if type is FrameTransformationMatrix

        Load the transformation matrix

    Else if type is Frame

    Else if type is Animation

        Instruct CSkinMesh to load the new animation

    Begin

        Create new CFrameNode object

        Attach the new object to this frame

        Set the name of the child frame to the name of the template

        For every child template of the current

        Begin

            Retrieve the X file data object

            Pass it to newframe.Load

        End

    End

End

CSkinMesh::LoadAnimation()

Begin

    Create new CAnimationNode object

    Attach the new object to the link list

    For every child template

        Call CAnimationNode::Load for the new animation object

End

CAnimationNode::Load()

Begin

    Check the type of the data object

    If the type is a reference

    Begin

        Get the referenced template, which is a frame template

        Get the name of it

        Store the name

    End

    Else if type is data

    Begin

        Check the type of the animation key

        Load the key accordingly

    End

End

下面的SetTime函數就是展現動畫能力的地方,CSkinMesh::SetTime 簡單地調用動畫對象的SetTime函數。

【例5-9】對渲染模型算法的封裝(僞碼):

CAnimationNode::SetTime()

Begin

    If a matrix key is available

    Begin

        Get the nearest matrix to the given time

        Set it to the target frame

    End

    Else

    Begin

        Prepare an identity matrix called TransMat

        If a scale key is available

        Begin

            Calculate the accurate scale value

            Prepare a scale matrix for this scale value

            Append the matrix to TransMat

        End

        If a rotation key is available

        Begin

            Calculate the accurate rotation quaternion

            Prepare a rotation matrix from this value

            Append the matrix to TransMat

        End

        If a position key is available

        Begin

            Calculate the accurate position value

            Prepare a matrix for it

            Append the matrix to TransMat

        End

        Set TransMat to the target frame

    End

End