日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

47. 模型加载

發(fā)布時間:2024/8/1 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 47. 模型加载 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在現(xiàn)代游戲中,人物和模型由針對每個對象的成千上萬個多邊形組成。對紋理映射而言這非常復(fù)雜,因為它需要為每個多邊形的每個頂點設(shè)置一對TU和TV紋理坐標。假定有一個包含了5000個三角形的模型。假定不涉及加索引的幾何圖形(索引),而只使用三角形,那么在整個模型中將有15000個頂點。至此,除了內(nèi)置的Direct3D以外,還得手動指定幾何圖形數(shù)據(jù)。如果一個模型包含5000個多邊形,那么試著指定這些多邊形中的每個幾何圖形數(shù)據(jù),就會是一場噩夢。同時還要為頂點手動設(shè)置紋理坐標,從時間角度而言,這也是不可以接受的,并且絕對不現(xiàn)實。即使設(shè)法對所有模型都做了這一點,那么也會存在將這些信息硬編碼到源代碼中,以及其他游戲不可使用相同資源的問題。所要做的工作就是可以比較簡單地開發(fā)資源,并能夠從游戲程序外獲取這些資源。幸運的是,本章將介紹這些內(nèi)容。

? 模型加載介紹
???????本章旨在介紹將不同類型文件中的幾何圖形數(shù)據(jù)加載到Direct3D程序中的方法。關(guān)于這方面,最好的內(nèi)容就是可以在場景中包含復(fù)雜的和施加了紋理的對象,而這只需添加幾行加載和顯示數(shù)據(jù)的代碼。然后可以在多個程序中使用這些對象,將幾何圖形信息從一個游戲中復(fù)制粘貼到另一個游戲中。

加載模型的方法有很多。現(xiàn)實中有許多不同類型的模型文件格式。文件格式只是在特定文件中保存信息的方式。某些文件格式保存頂點、法線和紋理坐標。某些文件格式除了保存前面所有的內(nèi)容,還包括三角形索引和材質(zhì)信息。使用或創(chuàng)建的文件格式將取決于對游戲的要求。本章將介紹Direct3D中三種不同的模型文件格式。這三種模型分別是Direct(X)模型、OBJ模型和最終模型格式(Ultimate Model Format,UMF)。最終所要做的全部工作就是在從文件加載模型時了解信息在文件中的存儲方式。如果文件格式先是保存了所有的頂點,然后是所有的紋理坐標,那么同樣要以這樣的順序加載它。換句話說,為了加載頂點和紋理坐標,就必須了解它們在文件中的位置。有許多可用于開發(fā)模型的軟件。這些軟件包括3D Studio Max、LightWave、Maya、TrueSpace和MilkShape3D。

模型文件
???????下面將要介紹的第一種模型文件格式是X模型文件。該文件是以X為擴展名的DirectX模型,它們既可以是文本文件,也可以是二進制文件。本書介紹的第二種模型是OBJ模型。通常可以從諸如3D Studio Max這樣的建模軟件導(dǎo)出這些常用的文本文件。這意味著可以在操作系統(tǒng)附帶的記事本程序中打開OBJ模型,并查看和編輯模型數(shù)據(jù)。本書接下來介紹的最后一類模型是在www.UltimateGameProgramming.com上開發(fā)的名為UMF的通用模型文件格式。該類文件中的每一項都和其他的完全不同。這樣可以在加載不同的可用模型格式時積累經(jīng)驗。

? ??對Direct3D中的X文件格式而言,這里將使用內(nèi)置的Direct3D函數(shù)加載、渲染和釋放X模型。目前,也許讀者已猜到,Direct3D提供了大量有用的函數(shù),可以方便地處理從模型渲染到光照到文本以及更多的圖形學(xué)方面的不同內(nèi)容。

不同的建模軟件可以加載、導(dǎo)入或?qū)С霾煌愋偷哪P臀募R恍┸浖苍S支持某些其他軟件不支持的文件類型。在尋找適合自己使用的建模軟件時一定要牢記這一點。MilkShape3D提供了大量的模型文件格式,包含了大多數(shù)主要的游戲模型,如雷神之錘3(Quake3)、彩虹6號(Rainbow6)、魔獸爭霸3(Warcraft3)、Unreal、英雄本色(Max Rayne)、半條名(Half Life)、英雄薩姆(Serious Sam)和毀滅戰(zhàn)士3(Doom3)。

GMAX是一款可以下載的免費編輯軟件,它是由3D Studio Max開發(fā)的。GMAX可用于開發(fā)模型或級別,并提供一套指導(dǎo)學(xué)習(xí)該軟件的指南。但要注意的是,GMAX除非是開發(fā)了游戲包,否則并不能導(dǎo)出模型。為了開發(fā)軟件包,就要給Discreet公司支付費用。所以雖然它可以免費下載,但最終還是要花銀子的。

使用X文件
???????DirectX模型或X模型是在Direct3D中使用的模型,可以通過幾個內(nèi)置函數(shù)直接加載和渲染到API。X模型最大的優(yōu)點就是它們十分易用,可以直接和Direct3D交互使用。動畫模擬X模型要做大量的工作,但對靜態(tài)網(wǎng)格X而言,這是個很好的選擇。同樣,有許多導(dǎo)出程序可以將許多不同的文件格式導(dǎo)出為X格式,反之亦然。X模型格式很受歡迎,并且在不久的將來會得到廣泛的使用。

X文件格式介紹
???????X文件格式基于模板格式。基于模板就可以根據(jù)定義在文件中的模板來定義X文件中的內(nèi)容。這意味著可以對所有想用的內(nèi)容使用X文件,還意味著并不止是可以存儲幾何圖形數(shù)據(jù)。由于為某些對象使用X文件而未存儲幾何圖形數(shù)據(jù)已經(jīng)超出了本書的討論范圍,因此對此介紹很少,讀者甚至沒有必要了解這方面的內(nèi)容。

???????Direct3D有一些內(nèi)置模板,可用于定義X文件中的網(wǎng)格。這些標準模板包括外觀、紋理坐標和法線。因為X文件格式基于模板格式,所以Direct3D需要知道一些存儲在文件中用于某種目的的信息。

???????X模型既可以被保存為文本文件,也可以被保存為二進制文件。這意味著可以在任意的文本編輯器中打開X文本文件,并修改其中的內(nèi)容。同樣可以創(chuàng)建一個新文件,并手動填充其中的信息。對復(fù)雜模型而言,諸如前面提到的有5000個多邊形的模型,就不想手動這樣做,可以使用如3D Studio Max這樣的建模軟件來保存該模型。下面介紹一下由兩個三角形組成的方形X模型。將為該模板施加紋理,并為網(wǎng)格添加簡單的材質(zhì)。方形X模型示例如程序清單12.1所示。

保存紋理方形網(wǎng)格的X文件示例

xof 0302txt 0032

Material UgpMat { ? // Material 0

? ?1.0;1.0;1.0;1.0;; ? ? ? // Face color

? ?0.0; ? ? ? ? ? ? ? ? ? ?// Power

? ?0.0;0.0;0.0;; ? ? ? ? ? // Specular color

? ?0.0;0.0;0.0;; ? ? ? ? ? // Emissive color

? ?TextureFilename{"ugp.bmp";} ? // Texture file name.

}

// The square mesh data.

Mesh Square {

4; ? ? ? ? ? ? ? ? ? ? ?// Number of vertices.

1.0; 1.0; 0.0;, ? ? ? ? // Vertice 1

-1.0; 1.0; 0.0;, ? ? ? ?// Vertice 2

-1.0;-1.0; 0.0;, ? ? ? ?// Vertice 3

1.0;-1.0; 0.0; ? ? ? ? ?// Vertice 4

2; ? ? ? ? ? ? ? ? ? ? ?// Number of triangles

3;0,1,2;, ? ? ? ? ? ? ? // Triangle indices 1

3;0,2,3;, ? ? ? ? ? ? ? // Triangle indices 2

MeshMaterialList {

1; ? ? ? ? ? ? ? ? ? ? ?// Number of materials

2; ? ? ? ? ? ? ? ? ? ? ?// Number of faces

0, ? ? ? ? ? ? ? ? ? ? ?// Face 0 use material 0

0, ? ? ? ? ? ? ? ? ? ? ?// Face 1 use material 0

{UgpMat} // Reference the material.

}

MeshTextureCoords {

4; ? ? ? ? ? ? ? ?// Number of vertices

0.0; 0.0;, ? ? ? ?// Vertex 1 tex coord.

0.0; 1.0;, ? ? ? ?// Vertex 2 tex coord.

1.0; 1.0;, ? ? ? ?// Vertex 3 tex coord.

1.0; 0.0;; ? ? ? ?// Vertex 4 tex coord.

}

} // End of Mesh Square

? ??

X模型看上去非常像一組C結(jié)構(gòu)。如果分解整個模型文件,那么在最后可以看到,這里并沒有包含大量復(fù)雜的信息。該文件由多個模板組成,這些模板包括針對網(wǎng)格的模板、針對材質(zhì)的模板、針對紋理坐標的模板。這些模板都是Direct3D可以理解的標準模板,所以要做的全部工作就是在模型文件中定義這些模板,并設(shè)置這些模板。同樣還可以在文件中為X文件格式添加注釋。這些注釋就像C/C++中的一樣,其目的只是為了增加模板的可讀性。如程序清單12.1所示,很容易描述文件每一部分的工作。

???????每個X文件的開頭先是一個標識符。這個標識符告訴程序正在讀取X文件,而且是一個有效的X文件,這里還包含了文件版本。

X文件頭示例

xof 0302txt 0032

文件頭開始先是xof。xof意味著程序正在加載某個版本的X文件。后面是文件的主版本號和次版本號:本例中是3.2。版本后面是txt,這意味著程序正在加載的是一個X模型的文本文件,而不是一個二進制文件。文件頭最后一部分表示的是程序加載的文件使用的浮點數(shù)位數(shù),在本示例文件是32(0032)。

???????這里不需要創(chuàng)建任何模板,因為本書正使用的是Direct3D中的標準模板。程序清單12.1中的X示例文件中已經(jīng)指定了標準模板Material、Mesh、MeshMaterialList和MeshTextureCoords。這里包含大量模板,可以在DirectX SDK文檔中的X File Format Reference(X 文件格式參考)一節(jié)可以找到。唯一要用到的其他模板是MeshNormals和MeshVertexColors。

Material 模板
???????X文件中的Material標準模板用于指定可以施加在單個表面(多邊形)的材質(zhì)。針對材質(zhì)的標準模板定義了環(huán)境顏色、發(fā)射能量、鏡面顏色、反射量、與材質(zhì)相關(guān)的紋理圖像文件名。讀者可以創(chuàng)建許多不同類型的材質(zhì),可以在模板中讓不同的外觀引用它們。這樣可以使用包含完全不同材質(zhì)和/或紋理的部分模型,而不是相同模型網(wǎng)格的所有其他部分。

從X示例文件中提取的Material模板

Material UgpMat {

? // Material 0

? ?1.0;1.0;1.0;1.0;; ? ? ? // Diffuse color

? ?0.0; ? ? ? ? ? ? ? ? ? ?// SpecularPower

? ?0.0;0.0;0.0;; ? ? ? ? ? // Specular color

? ?0.0;0.0;0.0;; ? ? ? ? ? // Emissive color

? ?TextureFilename{"ugp.bmp";} ? // Texture file name.

}

?Mesh 模板

???????Mesh模板定義X文件中的完整網(wǎng)格。X模型可以包含多個網(wǎng)格。例如,可以在單個模型文件中使用不同的mesh模板將網(wǎng)格分成頭網(wǎng)格、上身網(wǎng)格、下身網(wǎng)格。X文件中的這些網(wǎng)格由多個小模板組成,它們嵌入在一個大模板中。網(wǎng)格模板結(jié)構(gòu)開始先定義頂點總數(shù),然后是頂點的x、y、z坐標。定義完頂點之后,接下來必須定義網(wǎng)格的外觀或多邊形。開始先是網(wǎng)格中的外觀數(shù),后面是每個外觀的三角形索引。每個三角形索引行開始是代表外觀使用的三角形數(shù)目,后面才是索引號。定義完頂點和三角形之后,就可以隨意定義其他屬性了,如每一外觀的材質(zhì)、紋理坐標等。程序清單12.4給出了X示例文件完整的正方形網(wǎng)格模板。

// The square mesh data.

Mesh Square {

4; ? ? ? ? ? ? ? ? ? ? ?// Number of vertices.

1.0; 1.0; 0.0;, ? ? ? ? // Vertice 1

-1.0; 1.0; 0.0;, ? ? ? ?// Vertice 2

-1.0;-1.0; 0.0;, ? ? ? ?// Vertice 3

1.0;-1.0; 0.0; ? ? ? ? ?// Vertice 4

2; ? ? ? ? ? ? ? ? ? ? ?// Number of triangles

3;0,1,2;, ? ? ? ? ? ? ? // Triangle indices 1

3;0,2,3;, ? ? ? ? ? ? ? // Triangle indices 2

MeshMaterialList {

1; ? ? ? ? ? ? ? ? ? ? ?// Number of materials

2; ? ? ? ? ? ? ? ? ? ? ?// Number of faces

0, ? ? ? ? ? ? ? ? ? ? ?// Face 0 use material 0

0, ? ? ? ? ? ? ? ? ? ? ?// Face 1 use material 0

{UgpMat} // Reference the material.

}

MeshTextureCoords {

4; ? ? ? ? ? ? ? ?// Number of vertices

0.0; 0.0;, ? ? ? ?// Vertex 1 tex coord.

0.0; 1.0;, ? ? ? ?// Vertex 2 tex coord.

1.0; 1.0;, ? ? ? ?// Vertex 3 tex coord.

1.0; 0.0;; ? ? ? ?// Vertex 4 tex coord.

}

} // End of Mesh Square

? ??

MeshMaterialList 模板
???????MeshMaterialList模板指明網(wǎng)格中的哪個外觀使用哪種材質(zhì)。在X模型示例文件中只定義了一種材質(zhì),所以將該材質(zhì)施加給所有的外觀。材質(zhì)鏈表結(jié)構(gòu)開始先定義材質(zhì)數(shù)目,然后是將材質(zhì)施加到外觀總數(shù)。這之后,每個外觀占一行。每行定義一個值,用該值引用要用的材質(zhì)。該結(jié)構(gòu)的最后一行是涉及到的所有材質(zhì)鏈表。第一個材質(zhì)索引號為0,第二個為1,依此類推。所以對于X示例文件中的每個外觀都指定材質(zhì)索引號為0,因為這里只有一個材質(zhì)可用。程序清單12.5給出了X示例文件中的MeshMaterialList模板。

MeshMaterialList {

1; ? ? ? ? ? ? ? ? ? ? ?// Number of materials

2; ? ? ? ? ? ? ? ? ? ? ?// Number of faces

0, ? ? ? ? ? ? ? ? ? ? ?// Face 0 use material 0

0, ? ? ? ? ? ? ? ? ? ? ?// Face 1 use material 0

{UgpMat} // Reference the material.

}

? ?

MeshTextureCoords 模板
???????要介紹的最后一個標準模板是MeshTextureCoords模板。有了該模板就可以在Direct3D中將紋理映射到網(wǎng)格上。該模板很容易理解,非常明了。模板開始先指定網(wǎng)格中的索引數(shù)。然后用逗號隔開,簡單地列出每個頂點的紋理坐標。程序清單12.6給出了X示例文件中的MeshTextureCoords模板。

MeshTextureCoords {

4; ? ? ? ? ? ? ? ?// Number of vertices

0.0; 0.0;, ? ? ? ?// Vertex 1 tex coord.

0.0; 1.0;, ? ? ? ?// Vertex 2 tex coord.

1.0; 1.0;, ? ? ? ?// Vertex 3 tex coord.

1.0; 0.0;; ? ? ? ?// Vertex 4 tex coord.

}

加載和渲染X模板
???????Direct3D不涉及動畫時,加載和渲染X模型很簡單。所有的X模型都保存在相同的名為LPD3DXMESH的結(jié)構(gòu)中。第3章中的Direct3D內(nèi)置對象使用了該結(jié)構(gòu)。為了從文件加載X模型,調(diào)用D3DXLoadMeshFromX()函數(shù)即可。該函數(shù)可將X模型加載到LPD3DXMEH對象中,并在Direct3D中使用該模型。D3DXLoadMeshFromX()函數(shù)原型如程序清單12.7所示。 HRESULT D3DXLoadMeshFromX(
LPCTSTR pFilename,
// 要加載的X文件的文件名
DWORD Options, // 創(chuàng)建網(wǎng)格時所使用的創(chuàng)建標記
LPDIRECT3DDEVICE9 pD3DDevice, // 與該網(wǎng)格對象相關(guān)的設(shè)備指針
LPD3DXBUFFER * ppAdjacency, // 返回一個ID3DXBuffer對象,該對象包含了一個
// 描述該網(wǎng)格對象的鄰接信息的DWORD類型的數(shù)組
LPD3DXBUFFER * ppMaterials, // 返回一個ID3DXBuffer對象,該對象包含了一個
// 存儲該網(wǎng)格的材質(zhì)數(shù)據(jù)的D3DXMATERIAL類型的結(jié)構(gòu)數(shù)組
LPD3DXBUFFER * ppEffectInstances, // 返回一個ID3DXBuffer對象,該對象包含了一個
// D3DXEFFECTINSTANCE結(jié)構(gòu)
DWORD * pNumMaterials, // 返回網(wǎng)格中的材質(zhì)數(shù)目(即有ppMaterials參數(shù)輸出的D3DXMATERIAL數(shù)
// 組中元素的個數(shù))
LPD3DXMESH * ppMesh // 返回所創(chuàng)建的并已填充了X文件幾何數(shù)據(jù)的ID3DXMesh對象.
);

  D3DXLoadMeshFromX()函數(shù)的參數(shù)包括X文件的文件名、加載網(wǎng)格的選項標識符、Direct3D設(shè)備對象、存儲鄰近數(shù)據(jù)(三角形索引)的LPD3DXBUFFER、存儲定義在文件中的材質(zhì)的緩存、存儲在文件中使用的效果(陰影器)實例的緩存、指向材質(zhì)總數(shù)的指針以及該函數(shù)調(diào)用創(chuàng)建的網(wǎng)格對象的地址。本書中,該函數(shù)只涉及前三個參數(shù)和最后一個參數(shù)。其他參數(shù)可選。本書并不使用涉及效果實例的參數(shù),因為效果文件,即DirectX高級編程陰影器,涉及到高級圖形學(xué)的內(nèi)容,超出了本書的討論范圍。如果該函數(shù)返回D3D_OK,則表示加載成功。否則,加載過程出現(xiàn)錯誤,模型沒有被加載到內(nèi)存中。

???????使用內(nèi)置的Direct3D對象渲染X模型,同樣的方法在第3章已經(jīng)用過。調(diào)用LPD3DXMESH對象的DrawSubset()函數(shù)可以繪制模型。DrawSubset()的函數(shù)原型如程序清單12.8所示。該函數(shù)只有一個參數(shù),即要繪制的網(wǎng)格索引。記住:在X模型文件中可以指定多個網(wǎng)格。在渲染X模型時,如果想在屏幕上渲染整個模型,就要渲染所有的網(wǎng)格。雖然第3章只涉及到一個網(wǎng)格模型,但對X模型而言,可以有多個網(wǎng)格。

HRESULT DrawSubset(

? UINT AttribId ? ? // 要繪制的網(wǎng)格索引

);

? ??

就像在第3章所做的工作一樣,必須牢記一點:一定要調(diào)用對象的Release()函數(shù)釋放所有用到的內(nèi)存。

Model Loading 演示程序


???首先,從main源文件的全局部分入手。這一部分包含了常用的對象,還添加了Direct3D光照對象、網(wǎng)格、材質(zhì)總數(shù)、LPD3DXBUFFER緩存、材質(zhì)鏈表以及模型使用的紋理鏈表等內(nèi)容。光照對象和網(wǎng)格對象很容易理解,而且在前面的章節(jié)已經(jīng)做過介紹。用于保存材質(zhì)總數(shù)的全局變量可以得到定義在模型文件中的材質(zhì)數(shù)量。LPD3DXBUFFER對象從X模型文件中獲取材質(zhì)數(shù)據(jù),這樣就可以在代碼中施加材質(zhì)。材質(zhì)鏈表指明了從該模型文件可以創(chuàng)建的真正材質(zhì),而紋理鏈表指明了施加給每個網(wǎng)格的紋理對象。X Model Loading 演示程序全局部分完整代碼如程序清單12.9所示。

???????程序清單12.9 X Model Loading 演示程序完整的全局部分

#include<d3d9.h>

#include<d3dx9.h>

#pragma comment(lib, "d3d9.lib")

#pragma comment(lib, "d3dx9.lib")

#define WINDOW_CLASS ? ?"UGPDX"

#define WINDOW_NAME ? ? "X Model Loading"

#define WINDOW_WIDTH ? ?640

#define WINDOW_HEIGHT ? 480

#define FULLSCREEN ? ? ?0

// Function Prototypes...

bool InitializeD3D();

bool InitializeObjects();

void RenderScene();

void Shutdown();

// Global window handle.

HWND g_hwnd = 0;

// Direct3D object and device.

LPDIRECT3D9 g_D3D = NULL;

LPDIRECT3DDEVICE9 g_D3DDevice = NULL;

// Matrices.

D3DXMATRIX g_projection;

D3DXMATRIX g_worldMatrix;

D3DXMATRIX g_ViewMatrix;

// Display object.

LPD3DXMESH g_model = NULL;

DWORD g_numMaterials;

LPD3DXBUFFER g_matBuffer = NULL;

D3DMATERIAL9* g_matList = NULL;

LPDIRECT3DTEXTURE9* g_textureList = NULL;

// Scene light source.

D3DLIGHT9 g_light;

? ??

該演示程序中出現(xiàn)改動的三個函數(shù)中的第一個是InitializeObjects()。該函數(shù)的工作方式除了加載X模型的代碼之外,和前面章節(jié)演示程序中的類似。為了在演示程序中加載X模型,首先調(diào)用D3DXLoadMeshFromX()函數(shù)。然后使用CloneMesh()函數(shù)將D3DVERTEXELEMENT9對象復(fù)制到網(wǎng)格中。這樣做是因為想通過該對象添加比其在文件中多許多的信息而更改網(wǎng)格。在文件中并未指明法線,但使用D3DXComputeNormals()函數(shù)就可以計算網(wǎng)格的法線,而不必在文件中指明那些法線。當(dāng)然,這對大量多邊形而言就很慢。但對只有絕對需要使用法線的情況而言,這樣做可以節(jié)省磁盤空間。由于原始模型不包含法線,因此必須調(diào)用包含法線信息的CloneMesh()函數(shù)更新對象。

#include<d3d9.h>
#include<d3dx9.h>

#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")


#define WINDOW_CLASS "UGPDX"
#define WINDOW_NAME "X Model Loading"
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
#define FULLSCREEN 0

// Function Prototypes...
bool InitializeD3D();
bool InitializeObjects();
void RenderScene();
void Shutdown();


// Global window handle.
HWND g_hwnd = 0;


// Direct3D object and device.
LPDIRECT3D9 g_D3D = NULL;
LPDIRECT3DDEVICE9 g_D3DDevice = NULL;


// Matrices.
D3DXMATRIX g_projection;
D3DXMATRIX g_worldMatrix;
D3DXMATRIX g_ViewMatrix;


// Display object.
// 網(wǎng)格對象
LPD3DXMESH g_model = NULL;
// 材質(zhì)總數(shù)
DWORD g_numMaterials;
// LPD3DXBUFFER緩存,用于從X模型文件中獲取材質(zhì)數(shù)據(jù),這樣就可以在代碼中施加材質(zhì).
LPD3DXBUFFER g_matBuffer = NULL;
// 材質(zhì)鏈表,指明了從該模型文件可以創(chuàng)建的真正材質(zhì).
D3DMATERIAL9* g_matList = NULL;
// 紋理鏈表,指明了施加給每個網(wǎng)格的紋理對象.
LPDIRECT3DTEXTURE9* g_textureList = NULL;

// Scene light source.
D3DLIGHT9 g_light;


LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
case WM_CLOSE:
PostQuitMessage(0);
return 0;
break;

case WM_KEYUP:
if(wParam == VK_ESCAPE) PostQuitMessage(0);
break;
}

return DefWindowProc(hWnd, msg, wParam, lParam);
}


int WINAPI WinMain(HINSTANCE hInst, HINSTANCE prevhInst, LPSTR cmdLine, int show)
{
// Register the window class
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
WINDOW_CLASS, NULL };
RegisterClassEx(&wc);

// Create the application's window
HWND hWnd = CreateWindow(WINDOW_CLASS, WINDOW_NAME, WS_OVERLAPPEDWINDOW,
100, 100, WINDOW_WIDTH, WINDOW_HEIGHT,
GetDesktopWindow(), NULL, wc.hInstance, NULL);

// Show the window
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);

// Record for global.
g_hwnd = hWnd;

// Initialize Direct3D
if(InitializeD3D())
{
// Enter the message loop
MSG msg;
ZeroMemory(&msg, sizeof(msg));

while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
RenderScene();
}
}

// Release any and all resources.
Shutdown();

// Unregister our window.
UnregisterClass(WINDOW_CLASS, wc.hInstance);
return 0;
}


bool InitializeD3D()
{
D3DDISPLAYMODE displayMode;

// Create the D3D object.
g_D3D = Direct3DCreate9(D3D_SDK_VERSION);
if(g_D3D == NULL) return false;

// Get the desktop display mode.
if(FAILED(g_D3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode)))
return false;

// Set up the structure used to create the D3DDevice
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));

if(FULLSCREEN)
{
d3dpp.Windowed = FALSE;
d3dpp.BackBufferWidth = WINDOW_WIDTH;
d3dpp.BackBufferHeight = WINDOW_HEIGHT;
}
else
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = displayMode.Format;
d3dpp.BackBufferCount = 1;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;


// Create the D3DDevice
if(FAILED(g_D3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hwnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE,
&d3dpp, &g_D3DDevice))) return false;

// Initialize any objects we will be displaying.
if(!InitializeObjects()) return false;

return true;
}


bool InitializeObjects()
{
// Set default rendering states.
g_D3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
g_D3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
g_D3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE);


// Setup the g_light source and material.
g_light.Type = D3DLIGHT_DIRECTIONAL;
g_light.Direction = D3DXVECTOR3(0.0f, 0.0f, 1.0f);

D3DCOLORVALUE white;
white.a = white.r = white.g = white.b = 1;

g_light.Diffuse = white;
g_light.Specular = white;

g_D3DDevice->SetLight(0, &g_light);
g_D3DDevice->LightEnable(0, TRUE);

/*
使用CloneMesh()函數(shù)將D3DVERTEXELEMENT9對象復(fù)制到網(wǎng)格中。
這樣做是因為想通過該對象添加比其在文件中多許多的信息而更改網(wǎng)格。
在文件中并未指明法線,但使用D3DXComputeNormals()函數(shù)就可以計算網(wǎng)格
的法線,而不必在文件中指明那些法線。當(dāng)然,這對大量多邊形而言就很慢。
但對只有絕對需要使用法線的情況而言,這樣做可以節(jié)省磁盤空間。由于原
始模型不包含法線,因此必須調(diào)用包含法線信息的CloneMesh()函數(shù)更新對象。

加載完模型后,就可以得到材質(zhì)信息并加載紋理。通過將LPD3DXBUFFER對象的
材質(zhì)復(fù)制到材質(zhì)鏈表中即可完成該工作。因為LPD3DXBUFFER對象還包含了紋理名稱,
所以也可以加載紋理,這些紋理就在緩存中每種材質(zhì)的后面。有了這些內(nèi)容,
InitializeObjects()函數(shù)的其他部分跟往常一樣繼續(xù)下去。程序清單12.10給出了完整
的InitializeObjects()函數(shù)。

在創(chuàng)建頂點元素(D3DVERTEXELEMENT9)時,偏移的字節(jié)就是頂點結(jié)構(gòu)中標識數(shù)據(jù)開始位置
的字節(jié)數(shù)。所以如果第一個元素是頂點位置,那么該偏移量就是0,因為該元素前面沒有
任何元素。如果第二個元素是法線,那么偏移量為12,因為第一個元素占用了12個字節(jié)。
如果第三個元素是一組紋理坐標,那么偏移量為24,這是因為每個浮點數(shù)占4個字節(jié)。
由于在處理位置時有三個浮點值,因此總共需要12個字節(jié)。例如,如果在紋理坐標后面是顏色,
那么偏移量為32,因為第一個元素占12個字節(jié),法線占12個字節(jié),紋理坐標占8個字節(jié)。

D3DVERTEXELEMENT9
Defines the vertex data layout. Each vertex can contain one or more data types, and each data
type is described by a vertex element.

typedef struct D3DVERTEXELEMENT9
{
WORD Stream;
WORD Offset;
BYTE Type;
BYTE Method;
BYTE Usage;
BYTE UsageIndex;
} D3DVERTEXELEMENT9, *LPD3DVERTEXELEMENT9;

Members
Stream
Stream number.
Offset
Offset from the beginning of the vertex data to the data associated with the particular data type.
Type
The data type, specified as a D3DDECLTYPE. One of several predefined types that define the data size.
Some methods have an implied type.
Method
The method specifies the tessellator processing, which determines how the tessellator interprets
(or operates on) the vertex data. For more information, see D3DDECLMETHOD.
Usage
Defines what the data will be used for; that is, the interoperability between vertex data layouts
and vertex shaders. Each usage acts to bind a vertex declaration to a vertex shader. In some cases,
they have a special interpretation. For example, an element that specifies D3DDECLUSAGE_NORMAL or
D3DDECLUSAGE_POSITION is used by the N-patch tessellator to set up tessellation. See D3DDECLUSAGE
for a list of the available semantics. D3DDECLUSAGE_TEXCOORD can be used for user-defined fields
(which don't have an existing usage defined).
UsageIndex
Modifies the usage data to allow the user to specify multiple usage types.

*/
// 新mesh的頂點聲明
D3DVERTEXELEMENT9 elements[] =
{
{ 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, // 位置
{ 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 }, // 法線
{ 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, // 紋理坐標
D3DDECL_END()
};

// Load mesh into temp object then copy over (to set elements).
LPD3DXMESH temp = NULL;
if(FAILED(D3DXLoadMeshFromX("Model.x", D3DXMESH_SYSTEMMEM,
g_D3DDevice, NULL, &g_matBuffer, NULL,
&g_numMaterials, &temp))) return false;
//elements:頂點聲明
//g_model:Address of a pointer to an ID3DXMesh interface, representing the cloned mesh.
temp->CloneMesh(D3DXMESH_SYSTEMMEM, elements, g_D3DDevice, &g_model);
if(temp) temp->Release();

// Calculate normals for lighting.
D3DXComputeNormals(g_model, NULL);

// Allocate the lists for materials and textures.
g_matList = new D3DMATERIAL9[g_numMaterials];
g_textureList = new LPDIRECT3DTEXTURE9[g_numMaterials];

// Get a pointer to the buffer
D3DXMATERIAL* mat = (D3DXMATERIAL*)g_matBuffer->GetBufferPointer();

// Loop and load each textture and get each material.
for(DWORD i = 0; i < g_numMaterials; i++)
{
// Copy the materials from the buffer into our list.
g_matList[i] = mat[i].MatD3D;

// Load the textures into the list.
if(FAILED(D3DXCreateTextureFromFile(g_D3DDevice,
mat[i].pTextureFilename,
&g_textureList[i])))
g_textureList[i] = NULL;
}


// Set the projection matrix.
D3DXMatrixPerspectiveFovLH(&g_projection, D3DX_PI / 4,
WINDOW_WIDTH/WINDOW_HEIGHT, 0.1f, 1000.0f);

g_D3DDevice->SetTransform(D3DTS_PROJECTION, &g_projection);


// Define camera information.
D3DXVECTOR3 cameraPos(0.0f, 0.0f, -10.0f);
D3DXVECTOR3 lookAtPos(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 upDir(0.0f, 1.0f, 0.0f);

// Build view matrix.
D3DXMatrixLookAtLH(&g_ViewMatrix, &cameraPos,
&lookAtPos, &upDir);

return true;
}


void RenderScene()
{
// Clear the backbuffer.
g_D3DDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB(0,0,0), 1.0f, 0);

// Begin the scene. Start rendering.
g_D3DDevice->BeginScene();

// Apply the view (camera).
g_D3DDevice->SetTransform(D3DTS_VIEW, &g_ViewMatrix);

// g_numMaterials個網(wǎng)格 每個網(wǎng)格對應(yīng)一個材質(zhì),、
// 對每個mesh應(yīng)用材質(zhì)和紋理,然后渲染
// Draw the model.
for(DWORD i = 0; i < g_numMaterials; i++)
{
g_D3DDevice->SetMaterial(&g_matList[i]);
g_D3DDevice->SetTexture(0, g_textureList[i]);

g_model->DrawSubset(i);
}

// End the scene. Stop rendering.
g_D3DDevice->EndScene();

// Display the scene.
g_D3DDevice->Present(NULL, NULL, NULL, NULL);
}


void Shutdown()
{
if(g_D3DDevice != NULL) g_D3DDevice->Release();
g_D3DDevice = NULL;

if(g_D3D != NULL) g_D3D->Release();
g_D3D = NULL;

if(g_model != NULL) g_model->Release();
g_model = NULL;

for(DWORD i = 0; i < g_numMaterials; i++)
{
if(g_textureList[i] != NULL)
{
g_textureList[i]->Release();
g_textureList[i] = NULL;
}
}

if(g_matList != NULL)
{
delete[] g_matList;
g_matList = NULL;
}

if(g_textureList != NULL)
{
delete[] g_textureList;
g_textureList = NULL;
}

if(g_matBuffer != NULL)
{
g_matBuffer->Release();
g_matBuffer = NULL;
}
}

/*
一個X文件不包含頂點法線數(shù)據(jù),這是很有可能的。假如是這種情況,
那么手動計算頂點法線以便我們能夠使用燈光這是很有必要的。現(xiàn)在
知道了ID3DXMesh接口和它的父接口ID3DXBaseMesh,我們能夠使用下面
的函數(shù)來產(chǎn)生任何mesh的頂點法線:

Computes unit normals for each vertex in a mesh. Provided to support legacy applications.
Use D3DXComputeTangentFrameEx for better results.

HRESULT D3DXComputeNormals(
LPD3DXBASEMESH pMesh,
CONST DWORD * pAdjacency
);
*/



轉(zhuǎn)載于:https://www.cnblogs.com/kex1n/archive/2011/09/17/2179520.html

總結(jié)

以上是生活随笔為你收集整理的47. 模型加载的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。