Direct3D學習(六):動畫基礎(2)X檔案的使用

來源:互聯網
上載者:User
X檔案的使用(完整) [轉]

這兩天精神不太好,沒寫,先轉一篇同樣內容的來湊數吧

[Copy to clipboard]

CODE:

    有很多朋友也許想更加瞭解X檔案,正好,本文將全面的介紹X檔案的使用。我想這是一篇純技術性的文檔,我就不加以詩篇歌頌潤色了。相信讀我的文章,就像啃牙籤;)好了,我們板起面孔。首先給你一個完整的印象----傳說中的X檔案:
xof 0302txt 0032

template Header {
<3D82AB43-62DA-11cf-AB39-0020AF71E433>
DWORD major;
DWORD minor;
DWORD flags;
}
template Frame {
<3D82AB46-62DA-11cf-AB39-0020AF71E433>
[FrameTransformMatrix]
[Mesh]
}
Header {
1;
0;
1;
}
Frame Scene_Root {
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.000000, 0.000000, 0.000000, 1.000000;;
                     }
Frame Pyramid_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.000000, 0.000000, 0.000000, 1.000000;;
}
Mesh PyramidMesh {
5;
0.00000;10.00000;0.00000;,
-10.00000;0.00000;10.00000;,
10.00000;0.00000;10.00000;,
-10.00000;0.00000;-10.00000;,
10.00000;0.00000;-10.00000;;
6;
3;0,1,2;,
3;0,2,3;,
3;0,3,4;,
3;0,4,1;,
3;2,1,4;,
3;2,4,3;;
MeshMaterialList {
1;
6;
0,0,0,0,0,0;;
Material Material0 {
1.000000; 1.000000; 1.000000; 1.000000;;
0.000000;  
0.050000; 0.050000; 0.050000;;
0.000000; 0.000000; 0.000000;;
                   }
                 }
                 }
                    }
}
xof 0302txt 0032
xof表示這是一個真正的X檔案。0302txt表示通知程式使用Directx的X檔案,版本為3.2的模版,其中txt表示此檔案為文字檔,可讀,並非是一個2進位檔案。0032表示一個浮點數的位元為32,如果想要用64位的浮點數,可以寫成0064。
    下面將分別介紹各個主題。
聲明一個模版://////////////假設聲明 template ContactEntry
首先需要用guidgen.exe產生一個GUID。產生的GUID如下:
// {4C9D055B-C64D-4bfe-A7D9-981F507E45FF}
DEFINE_GUID(<<name>>,
0x4c9d055b, 0xc64d, 0x4bfe, 0xa7, 0xd9, 0x98, 0x1f, 0x50, 0x7e, 0x45, 0xff);
之後需要在程式碼中加入:
#include "initguid.h"
// At beginning of source code file - add DEFINE_GUIDs
DEFINE_GUID(ContactEntry, 0x4c9d055b, 0xc64d, 0x4bfe, 0xa7, 0xd9, 0x98, 0x1f, 0x50, 0x7e, 0x45, 0xff);
還要在X檔案中加入:
template ContactEntry {
<4C9D055B-C64D-4bfe-A7D9-981F507E45FF>
聲明模版用到的資料類型:
關鍵字       描述
WORD         16-bit value (short)
DWORD        32-bit value (32-bit int or long)
FLOAT        IEEE float value (float)
DOUBLE       64-bit floating-point value (double)
CHAR         8-bit signed value (signed char)
UCHAR        8-bit unsigned value (unsigned char)
BYTE         8-bit unsigned value (unsigned char)
STRING       A NULL-terminated string (char[]))
array        Signifies an array of following data type to follow ([])
舉例:
DWORD value;   
array STRING Text[20];//定義一個名為Text的數組,類型為STRING,大小為20。
DWORD ArraySize; array STRING Names[ArraySize]; //可以將大小設定為變數。
聲明一個ContactEntry模版:
template ContactEntry {
<4C9D055B-C64D-4bfe-A7D9-981F507E45FF>
STRING Name; // The contact's name
STRING PhoneNumber; // The contact's phone number
DWORD Age; // The contact's age
}
執行個體化一個模版對象:
ContactEntry JimsEntry {
"Jim Adams";
"(800) 555-1212";
30;
}
{JimsEntry} 可以用這樣的形式引用一個資料對象。例如,在一個animation sequence template中引用
一個Frame data object做為其內嵌資料對象。也可以利用參考資料表示一個資料對象的副本,沒有必要重複
書寫這個資料對象。
內嵌資料對象和模版約束://////////////template ClosedTemplate {
<4C9D055B-C64D-4bfe-A7D9-981F507E45FF>
DWORD ClosedData;
}
template OpenTemplate {
<4C9D055B-C64D-4bff-A7D9-981F507E45FF>
DWORD OpenData;
[...]
}
template RestrictedTemplate {
<4C9D055B-C64D-4c00-A7D9-981F507E45FF>
DWORD RestrictedData;
[ClosedTemplate]
[OpenTemplate]
}
ClosedTemplate是標準的模版聲明。
OpenTemplate中包含一個[...],表示這是一個開放模版。開放模版允許在[]中內嵌任何資料對象。例如
,你可以執行個體化OpenTemplate,在裡面定義一個OpenData變數和內嵌一個ClosedTemplate的執行個體。
RestrictedTemplate為約束模版。約束模版執行個體化時只允許包含它列出的資料對象,如,不能在
RestrictedTemplate包含[ClosedTemplate],[OpenTemplate]以外的資料對象。
用DirectX .X Standard Templates工作://///////////////X檔案廣泛用於包含一個mesh資訊。一個Standard Templates包含了過多的資訊。
Table 3.2: DirectX .X Standard Templates
Template Name               Description
Animation:                  Defines animation data for a single frame.
AnimationKey:               Defines a single key frame for the parent animation template.
AnimationOptions:           Contains animation playback information.
AnimationSet:               Contains a collection of animation templates.
Boolean:                    Holds a Boolean value.
Boolean2d:                  Holds two Boolean values.
ColorRGB:                   Contains red, green, and blue color values.
ColorRGBA:                  Contains red, green, blue, and alpha color values.
Coords2d:                   Defines two coordinate values.
FloatKeys:                  Contains an array of floating-point values.
FrameTransformMatrix:       Holds the transformation matrix for a parent Frame template.
Frame:                      A frame-of-reference template that defines a hierarchy.
Header:                     The .X file header that contains version numbers.
IndexedColor:               Contains an indexed color value.
Material:                   Contains material color values.
Matrix4x4:                  Holds a 4x4 homogenous matrix container.
Mesh:                       Contains a single mesh's data.
MeshFace:                   Holds a mesh's face data.
MeshFaceWraps:              Contains the texture wrapping for mesh faces.
MeshMaterialList:           Contains the material for face-mapping values.
MeshNormals:                Holds normals used for mesh data.
MeshTextureCoords:          Holds texture coordinates used for mesh data.
MeshVertexColors:           Holds vertex color information used for mesh vertices.
Patch:                      Defines a control patch.
PatchMesh:                  Contains a patch mesh (much like the Mesh template).
Quaternion:                 Holds a quaternion value.
SkinWeights:                Contains an array of weight values mapped to a mesh's vertices.
Used in skinned meshes.
TextureFilename:            Contains the texture file name to use for a material.
TimedFloatKeys:             Contains an array of FloatKeys templates.
Vector:                     Holds a 3D coordinate value.
VertexDuplicationIndices:   Informs you which vertices are duplicates of other vertices.
XSkinMeshHeader:            Used by skinned meshes to define the number of bones contained
in a mesh.
在rmxfguid.h中定義了各個模版的宏,例如:
/* {3D82AB44-62DA-11cf-AB39-0020AF71E433} */
DEFINE_GUID(TID_D3DRMMesh,
0x3d82ab44, 0x62da, 0x11cf, 0xab, 0x39, 0x0, 0x20, 0xaf, 0x71, 0xe4, 0x33);
每個模版名加上首碼TID_D3DRM就是宏定義名。
訪問.X檔案://///////////////訪問任何X檔案首先要調用DirectXFileCreate函數建立一個IDirectXFile介面。
IDirectXFile *pDXFile = NULL;
HRESULT Result = DirectXFileCreate(&pDXFile);//用&pDXFile返回指向介面的指標。用SUCCEEDED或者
FAILED宏判斷傳回值是否有效。
註冊一個定製ro標準模版://///////////你可以把X檔案中的模版移除,直接在代碼裡定義那些模版。IDirectXFile介面支援這樣的特性。需要調
用IDirectXfile::RegisterTemplates函數。
HRESULT IDirectXfile::RegisterTemplates(
LPVOID pvData, // 一個定義模版資料的緩衝,應該精確無誤。
DWORD cbSize); // pvData緩衝的位元組數。
可以如下定義一個模版資料的緩衝:
char *Templates = "
"xof 0303txt 0032 /  //標準X檔案頭。
template CustomTemplate { <4c944580-9e9a-11cf-ab43-0120af71e433> DWORD Length; array DWORD values[Length]; }";
之後在用RegisterTemplates將其註冊:
pFile->RegisterTemplates(Templates, strlen(Templates));
註冊標準模版:
首先需要在代碼中包含rmxfguid.h和rmxftmpl.h。rmxfguid.h定義了各個標準模版的GUDI,rmxftmpl.h以
2進位資料形式定義了標準模版資料的緩衝和其位元組數。然後調用RegisterTemplates將其註冊:
pFile->RegisterTemplates(D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
開啟X檔案:///////////////建立完IDirectXFile介面,註冊模版之後需要開啟X檔案,枚舉其資料對象。調用
IDirectXfile::CreateEnumObject函數。
HRESULT IDirectXfile::CreateEnumObject(LPVOID pvSource, // .X filename
DXFILELOADOPTIONS dwLoadOptions, // Load options
LPDIRECTXFILEENUMOBJECT* ppEnumObj); // Enum interface
當調用CreateEnumObject函數,用pvSource指定一個檔案的名字,用ppEnumObj返回一個枚舉對象介面指
針。用dwLoadOptions指定load操作方式。當指定DXFILELOAD_FROMFILE值,告訴DirectX從磁碟載入一個
檔案。還有DXFILELOAD_FROMRESOURCE,DXFILELOAD_FROMMEMORY和DXFILELOAD_FROMURL分別表示從一個資
源,記憶體緩衝和Internet上載入X檔案。當從Internet負載檔案時,需要為其指定完整的網址。
下面代碼從磁碟載入X檔案:
// Filename = filename to load ("test.x" for example)
IDirectXFileEnumObject *pEnum;
pFile->CreateEnumObject((LPVOID)Filename, DXFILELOAD_FROMFILE, &pEnum);
Filename指向一個有效檔案名稱,pEnum返回一個枚舉對象介面指標。
枚舉資料對象:///////////////註冊完模版,開啟X檔案並且得到一個枚舉對象介面,下面需要從X檔案讀出資料。枚舉對象介面指標指向
檔案的第一個資料對象,因為每一個資料對象可能包含內嵌資料對象或者引用的資料對象,所以與第一個
資料對象同在一層級的其它資料對象為同層級資料對象。至於包含的子資料對象的類型,需要對其分別的
行進詢問。
可以用 HRESULT IDirectXFileEnumObject::GetNextDataObject(LPDIRECTXFILEDATA*ppDataObj)得到一
個IDirectXFileData介面。它只有一個參數,如下:
IDirectXFileData *pData;
HRESULT hr = pEnum->GetNextDataObject(&pData);
利用此函數,可以不斷地訪問同一層級的資料對象介面,具體代碼如下:
while(SUCCEEDED(pEnum->GetNextDataObject(&pData))) {
// 這裡可以對pData資料對象進行操作。
pData->Release();//釋放介面。
}
當傳回值為FAILED,表示已經訪問完所有的介面。當訪問值為SUCCEEDED,你需要繼續判斷這個資料對象
是否包含子物件。利用介面IDirectXFileObject,和HRESULT IDirectXFileData::GetNextObject(
LPDIRECTXFILEOBJECT* ppChildObj)函數,代碼如下:
IDirectXFileObject *pObject;
while(SUCCEEDED(pData->GetNextObject(&pObject)))
{
// 如果一個子物件存在,需要繼續詢問它,判斷出它的類型為內嵌資料對象或者引用的資料對象。
pObject->Release();// 釋放介面。
}
接下來詢問介面,看其是否為內嵌資料對象:
IDirectXFileData *pSubData;
if(SUCCEEDED(pObject->QueryInterface( IID_IDirectXFileData, (void**)&pSubData))) {
// 如果詢問內嵌資料對象成功,可以對pSubData資料對象進行操作
pSubData->Release();//釋放介面。
}
看其是否為引用資料對象:
IDirectXFileDataReference *pRef;
IDirectXFileData *pSubData;
if(SUCCEEDED(pSubObj->QueryInterface( IID_IDirectXFileDataReference, (void**)&pRef))) {
// 如果詢問引用的資料對象成功,解析出引用的原型。
pRef->Resolve(&pSubData);
//這裡可以對pData資料對象進行操作。
pRef->Release();
pSubData->Release();//釋放介面。
}
現在整理下思路:大體的思路其實很簡單,首先枚舉最頂層的資料對象,然後判斷其是否有子物件,這個
子物件可能是內嵌對象或者引用對象二者之一,分別詢問其介面,就可以判斷出具體的類型。
下面是完整的Parse模版的函數:
BOOL Parse(char *Filename)
{
IDirectXFile *pFile = NULL;
IDirectXFileEnumObject *pEnum = NULL;
IDirectXFileData *pData = NULL;
if(FAILED(DirectXFileCreate(&pFile)))
return FALSE;
//註冊標準模版。
if(FAILED(pFile->RegisterTemplates( (LPVOID)D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES)))
return FALSE;
//建立一個枚舉對象介面。
if(FAILED(pDXFile->CreateEnumObject((LPVOID)Filename, DXFILELOAD_FROMFILE, &pEnum))) {
pFile->Release();
return FALSE;
}
// 遍曆所有的頂層資料對象。
while(SUCCEEDED(pEnum->GetNextDataObject(&pData))) {
// 用ParseObject解析其子資料對象。
ParseObject(pData);
pData->Release();
}
pEnum->Release();
pFile->Release();
return TRUE;
}
這個函數的主要部分在ParseObject(pData),它負責解析子資料對象:
void ParseObject(IDirectXFileData *pData)
{
  IDirectXFileObject *pObject = NULL;
  IDirectXFileData *pSubData = NULL;
  IDirectXFileDataReference *pRef = NULL;
  while(SUCCEEDED(pData->GetNextObject(&pObject))) {
  if(SUCCEEDED(pObject->QueryInterface( IID_IDirectXFileDataReference, (void**)&pRef))) {
  pRef->Resolve(&pSubData);
  ParseObject(pSubData);
  pSubData->Release();
  pRef->Release();
                                                                                         }
  if(SUCCEEDED(pObject->QueryInterface( IID_IDirectXFileData, (void**)&pSubData))) {
    ParseObject(pSubData);
    pSubData->Release();
                                                                                    }
  pObject->Release();
                                                    }
}
這是個第歸函數,調用函數自身。判斷子物件的類別,對其繼續解析,直到傳回值為FAILED,表示已沒有
子物件。從上面可以看出,這個函數除了枚舉所有的對象,並沒有做任何事情,下面就要從這些資料對象
檢索資料。
從資料對象檢索資料://///////////////
當你用IDirectXFileData介面指標指向一個有效資料對象,可以調用IDirectXFileData::GetName函數得到該資料對象的名字。函數原型為:
HRESULT IDirectXFileData::GetName(
LPSTR pstrNameBuf, // 名字緩衝
LPDWORD pdwBufLen); // 名字緩衝的大小
可以這樣使用這個函數:
DWORD Size;
pData->GetName(NULL, &Size);
char *Name = new char[Size];
pData->GetName(Name, &Size);
首先聲明一個DWORD Size變數,調用GetName函數時將第一個參數設為NULL,在Size返回名字緩衝的大小。之後利用這個Size值建立存放名字的緩衝,再調用GetName在Name中返回資料對象的名字。
得到了資料對象的名字,你需要得到這個資料對象的模版GUID,去判斷這個資料對象是否為你想使用的那個模版的資料對象。利用IDirectXFileData::GetType函數,其原型為:
HRESULT IDirectXFileData::GetType(const GUID ** ppguid);
可以這樣使用這個函數:
const GUID *TemplateGUID = NULL;
pData->GetType(&TemplateGUID);//在TemplateGUID中返回該資料對象對應模版的GUID。
現在去匹配這個GUID,看它是否為你想使用的模版的資料對象。
if(*TemplateGUID == TID_D3DRMMeshNormals) {
// 如果匹配成功,這裡可以繼續處理這個模版的資料對象。
}
最後將介紹GetData函數,用它真正的得到了資料對象的資料。其原型為:
HRESULT IDirectXFileData::GetData(
LPCSTR szMember, // 設定為NULL
DWORD *pcbSize, // 資料的大小
void **ppvData); // 資料指標
下面介紹用GetData得到資料對象結構的大小和資料對象的資料。
假設有這樣一個顏色的模版:
template ColorRGBA {
<35FF44E0-6C7C-11cf-8F52-0040333594A3>
FLOAT red;
FLOAT green;
FLOAT blue;
FLOAT alpha;
}
你想訪問基於此模版的資料對象的資料你可以這樣做:
DWORD DataSize;
float *DataPtr;
pData->GetData(NULL, &DataSize, (void**)&DataPtr);
float red = *DataPtr++;
float green = *DataPtr++;
float blue = *DataPtr++;
float alpha = *DataPtr++;
得到指向資料對象的資料的指標後,就像訪問一般的結構一樣簡單。當然,你可以做得更直接:
typedef struct {
float red, green, blue, alpha;
} sColorRGBA;//定一個結構方便訪問資料對象的資料。
sColorRGBA *Color;
DWORD DataSize;
pData->GetData(NULL, &DataSize,(void**)&Color);
這樣訪問資料時更直接:
float red = Color->red;
float blue = Color->blue;
float green = Color->green;
float alpha = Color->alpha;
訪問單個變數是很簡單的,下面繼續介紹訪問數組或字串。
訪問數組:
DWORD DataSize;
DWORD *DataPtr;
pData->GetData(NULL, &DataSize, (void**)&DataPtr);
DWORD NumKeys = *DataPtr++;
for(DWORD i=0;i<NumKeys;i++) {
float fvalue = *(FLOAT*)DataPtr++;
訪問字串:
DWORD DataSize;
DWORD *DataPtr;
pData->GetData(NULL, &DataSize, (void**)&DataPtr);
char *StringPtr = (char*)DataPtr;
MessageBox(NULL, StringPtr, "Texture Filename", MB_OK);
為了訪問數組或字串,其本質就是把指標轉化成匹配的類型,方便指標的定位操作。
    我以後還會寫關於3D模型檔案格式和骨骼動畫,地形渲染方面的文章,請關注 ;)

                                                                         作者:迷糊小亞
                                                                         QQ:183237048

積分: 12826
文章: 1632
註冊: 2004-11-11" href="http://bbs.skonline.net/viewpro.php?uid=8990">逍遙劍客
2007-5-27 02:50

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.