第五,打开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;
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);
为了访问数组或字符串,其本质就是把指针转化成匹配的类型,方便指针的定位操作。
至此,我们已经介绍了X文件的使用方法,X文件是动画模型的载体,所以有必要了解它,进而才能更好的操作它。同时也为后面的动画技术做好了准备。
0 评论
Recommended Comments
没有要显示的评论。
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new account登录
Already have an account? Sign in here.
现在登录