1. 前言
話說有一段時間沒有更新了,這段時間好好玩了一下,也抽空寫了一點程序,把八叉樹場景管理寫了,也優化了一點資源加載的一些內容。之前對DirectX的好些地方還是沒有弄清楚,現在對這些地方至少有了更多的了解。發一下今天完成的八叉樹場景管理,裡面的模型用的是DirectX示例的老虎,這兒看不太清楚--;
今天主要說說我的引擎中的模型,特效,貼圖的實現,至少把大概的意思說清楚。如果還有不懂的地方可以跟我聯系。
2. 模型、特效、貼圖的資源復用
大家也可以看到了,上面那張圖上的老虎不止一個,當同一個模型的數目多到一定程度(比如說10000個),就要考慮一定的優化了,如果每次都傻傻的從文件中把模型讀取出來,保存為一個ID3DXMesh,那不僅加載的時間慢的令人無法忍受,而且耗費的內存同樣巨大。在WuguiEngine中,我使用了一個比較簡單的方法來解決這個問題。
設計模式的中心思想就是對修改關閉,對擴展開放,中心思想就是“變化”,但是很不好意思的說,我對D3D一知半解,在很多時候甚至不清楚“變化”將出現在什麼地方,所以我使用的方法,很簡單,現在用起來很清晰,但是如果對之後的擴展支持不好或者有更好的架構方法還望大家指點。本系列文章的宗旨就是拋磚引玉。
我使用了一個簡單的Map來完成這些內容下面給出資源管理器(ResourceManager)的定義
1: class GraphicsDevice;
2:
3: //資源的類型
4: enum ResourceType
5: {
6: RTTexture = 0,
7: RTMesh = 1,
8: RTEffect = 2,
9: };
10:
11: //一個資源的描述
12: //由ResourceType,Void*類型的指針pResource,引用次數useCount組成
13: typedef struct StructRes
14: {
15: public:
16: StructRes(void* pRes, ResourceType type)
17: {
18: pResource = pRes;
19: useCount = 1;
20: resourceType = type;
21: }
22: ResourceType resourceType;
23: void* pResource;
24: int useCount;
25: } StructRes;
26:
27: //資源管理器
28: class ResourceManager
29: {
30: public:
31: static ResourceManager* GetInstance();
32:
33: //根據文件名獲取資源文件
34: void* GetD3DResource(string filename,
35: GraphicsDevice* pDevice,
36: ResourceType type);
37:
38: //根據文件名卸載資源文件
39: void DisposeD3DResource(string filename);
40: protected:
41: ResourceManager();
42:
43: map<string, StructRes*> resourceTable;
44: static ResourceManager* pInstance;
45: };
下面對代碼進行一點解釋:
ResourceType故名思意就是資源的類型,在目前階段,由貼圖,特效,模型組成。
StructRes是每一個資源的結構體,裡面的void*指針指向所包含數據的內存。所包含數據的中心思想就是“保存公有的內容”。例如對於一個模型來說:頂點數據是公有的內容,但是貼圖就不一樣了,一個立方體可以貼成一個骰子,也可以貼成一個箱子,還有模型的位置也不屬於公有的內容。之後在講到模型的時候我將會更詳細的解釋這個地方
ResourceManager就是我們程序中資源管理器了。在這兒設計成為單件模式,如果看了我之前幾篇文章的朋友們可能會發現我特別喜歡用單件模式,可能是我不喜歡把一個指針老當成參數傳來傳去吧,呵呵。
在ResourceManager中最重要的第一是數據結構,在目前我使用的STL中的map。
1: map<string, StructRes*> resourceTable;
第一個參數string在目前是資源的文件名,目前引擎還不具備文件系統,讀取文件的時候就直接使用的文件名,如果具有一個完整的文件系統,不僅可以從硬盤中,還可以從內存中,壓縮文件中,而且還可以在多個位置按先後順序查找。StructRes*就是之前提過的結構體了。
ResourceManager最重要的方法是GetD3DResource
1: void* ResourceManager::GetD3DResource(string filename, GraphicsDevice* pDevice,
2: ResourceType type)
3: {
4: ResTable::iterator pResult = resourceTable.find(filename);
5:
6: //如果找到了對應的資源(已加載)
7: if (pResult != resourceTable.end())
8: {
9: (*pResult).second->useCount ++;
10: return (*pResult).second->pResource;
11: }
12: ..........
ResTable就是resourceTable的類型,stl的類型寫起來太長了,就用typedef處理了一下。
從第4行開始就是查找資源, 首先看看這個文件名所對應的資源是否存在,如果存在,就返回,代碼都是調用的基本stl的函數,如果看不太明白可以翻翻stl的幫助文件。
1: //如果沒有找到對應的資源
2: else
3: {
4: //如果是需要載入貼圖
5: if (type == ResourceType::RTTexture)
6: {
7: IDirect3DTexture9* pTexture;
8:
9: //從filename中創建Texture
10: LPSTR str = UtilityTools::ConvertStringLPSTR(&filename);
11: if (D3DXCreateTextureFromFileExA(pDevice->GetLPDIRECT3DDEVICE9(),
12: str,
13: D3DX_DEFAULT, //width
14: D3DX_DEFAULT, //height
15: D3DX_DEFAULT, //levels
16: 0, //usage
17: D3DFMT_UNKNOWN, //format
18: D3DPOOL_MANAGED, //pool
19: D3DX_DEFAULT,
20: D3DX_DEFAULT,
21: 0,
22: NULL,
23: NULL,
24: &pTexture) != D3D_OK)
25:
26: {
27: throw "Exception To Create Texture";
28: }
29: delete str;
30:
31: //在Table中加入這個Resource
32: StructRes* pRes = new StructRes(pTexture, type);
33: resourceTable.insert(make_pair(filename, pRes));
34:
35: return pTexture;
36: }
37: else if (type == ResourceType::RTMesh)
38: {
39: .......
40: }
41: else if (type == ResourceType::RTEffect)
42: {
43: .......
44: }
接著上面的代碼,如果對應文件名的資源沒有存在,則根據資源的類型進行加載,調用API函數。
這段代碼應該還是比較好理解的,這裡就不說了。
貼一段用的時候的代碼,以Texture為例,比如需要獲取一份IDirect3DTexture9*(pTexture)的資源。
1: ResourceManager* pResourceMgr = ResourceManager::GetInstance();
2:
3: pTexture = (IDirect3DTexture9*)(pResourceMgr->
4: GetD3DResource(_filename, device,ResourceType::RTTexture));
另外還有一個地方就是資源的釋放,在StructRes中有一個useCount, 當被引用一次+1,被釋放一次-1,最後一個出門的人就關燈鎖門了。
3. 模型
之前將了一下資源復用,以貼圖為例。貼圖的內容相對比較簡單,只需要保存一個引用IDirect3DTexture9*類型的指針就好了,但是模型就不一樣,模型涉及到很多操作,比如說獲取mesh的VertexBuffer,IndexBuffer等等,這些內容也同樣需要在ResourceManager中加載,因為他們也是“公有”的內容。
為了更方便的使用模型,我加入了ModelResource類,保存模型中的公有數據
下面就貼上ModelResource的定義
1: class ModelResource : IDisposed
2: {
3: public:
4: ModelResource(string _filename, GraphicsDevice* device);
5: virtual void Dispose();
6:
7: ID3DXMesh* GetD3DMesh();
8: IDirect3DVertexBuffer9* GetVertexBuffer();
9: IDirect3DIndexBuffer9* GetIndexBuffer();
10: BoundingSphere* GetOriginalSphere();
11: protected:
12: void Initialize();
13: void CalculateBoundingSphere();
14:
15: string filename;
16: GraphicsDevice* pDevice;
17: ID3DXMesh* pMesh;
18: IDirect3DVertexBuffer9* pVertexBuffer;
19: IDirect3DIndexBuffer9* pIndexBuffer;
20: BoundingSphere* pOriginalSphere;
21: DWORD FVF;
22: };
我在ModelResource中保存有VertexBuffer,IndexBuffer。OriginalSphere是模型加載時候的包圍球,也是屬於公有的,方便做變換的時候使用。
代碼具體參加ModelResource.cpp,這裡就不詳細的說了。
模型類中還包含有Material類(主要包含貼圖),Transformation類(包含變換),這些代碼都是比較好懂的,就不一一解釋了。
4. 示例
本節的示例代碼在Mainlogic中的Example2中可以找到
初始化一個模型,並加入貼圖與Effect的代碼看起來像這個樣子:
1: //初始化模型
2: pModel = new Model("Content\\Tiger.x", pDevice);
3: pModel->SetDiffuseMap("Content\\Tiger.bmp");
4: pModel->SetPosition(D3DXVECTOR3(0,0,-20));
5:
6: //初始化模型的屬性
7: pModel->SetEffect(new SimpleEffect(pDevice, pModel, "SimpleTexture.fx"));
8: pModel->GetEffect()->Initialize();
很簡單吧。