骨骼动画(Skeletal Animation)(二)
现在我们知道为了播放骨骼动画,需要有骨骼(bone)的数据,模型(mesh)的数据,关联骨骼和模型上每个顶点的关联数据,以及关键帧的坐标变换数据。所有这些数据必须以某种形式存在于某个地方供我们获取才行。这里要介绍的MS的x文件格式以及从中获取数据的方法。强烈建议大家都来学习一下x文件格式!你会发现它即简单又强大,即使用来存放自定义数据也是相当的方便,一旦掌握之后我保证你会对它爱不释手。
典型的x文件以数据模板和实际数据两部分组成。数据模板类似c++中的结构定义,不过更为灵活和开放。实际数据就是遵守模板定义的数据段。看一个例子,
template Employee {
<3D82AB43-62DA-11cf-AB39-0020AF71E433> // 每个模板关联唯一的GUID
STRING Name; // 姓名
DWORD Sex; // 性别
[ContactEntry] // 联系方式, 另一个模板,模板可以嵌套
}
template ContactEntry {
<4C9D055B-C64D-4bfe-A7D9-981F507E45FF> // GUID
STRING PhoneNumber; // 电话号码
STRING Address; // 地址
}
Employee David{
"David";
1;
ContactEntry{
"100-100000000";
"far far away";
}
}
从上面这个简单的例子我们就可以看出x文件的大概模样了,详细的情况大家可以参考《Advanced Animation with DirectX》。下面我们看如何来读取这样一个x文件,借助下几个对象,
ID3DXFile -- x文件格式文档对象。例如Employee.x这样一个文件。
ID3DXFileEnumObject -- 用来枚举x文档的顶级模板数据。所谓顶级模板数据是指那些没有
父模板的数据,例如上面的David数据段。
ID3DXFILEDATA -- 模板数据。上面的David和他的联系方式都是ID3DXFILEDATA
对象,自包含。
下面看实际的分析函数, 下面的代码适用于DirectX 9.0 SDK Update (October 2004),原书的代码有点过时了...
//-----------------------------------------------------------------------------
// 名称 : Parse
// 描述 : 分析x文件格式文档
//-----------------------------------------------------------------------------
bool Parse( char *filename, void **pData )
{
LPD3DXFILE lpD3DXFile;
LPD3DXFILEENUMOBJECT lpD3DXFileEnumObj;
LPD3DXFILEDATA lpD3DXFileData;
// 参数检查
if( NULL == filename )
return false;
// 创建X文件对象
HRESULT hr = D3DXFileCreate( &lpD3DXFile );
if( FAILED( hr ) )
return false;
// 注册标准模板
hr = lpD3DXFile->RegisterTemplates(
( LPVOID )D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES );
if( FAILED( hr ) )
{
Release<LPD3DXFILE>( lpD3DXFile );
return false;
}
// 创建X文件枚举对象
hr = lpD3DXFile->CreateEnumObject(
filename, D3DXF_FILELOAD_FROMFILE, &lpD3DXFileEnumObj );
if( FAILED( hr ) )
{
Release<LPD3DXFILE>( lpD3DXFile );
return false;
}
// 解析开始
bool parseResult = BeginParse( pData );
if( true == parseResult )
{
// 查询顶级模板数
SIZE_T childCount = 0;
lpD3DXFileEnumObj->GetChildren( &childCount );
// 分析每个订级模板
for( DWORD i=0; i<childCount; i++ )
{
// 获取当前模板
hr = lpD3DXFileEnumObj->GetChild( i, &lpD3DXFileData );
if( FAILED( hr ) )
break;
// 分析
parseResult = ParseObject( lpD3DXFileData, NULL, 0, pData );
// 释放FileData对象
Release<LPD3DXFILEDATA>( lpD3DXFileData );
// 出现错误,中断分析
if( false == parseResult )
break;
}
// 解析结束
if( parseResult )
parseResult = EndParse( pData );
}
// 释放相关对象
Release<LPD3DXFILEENUMOBJECT>( lpD3DXFileEnumObj );
Release<LPD3DXFILE>( lpD3DXFile );
// 解析结束
return parseResult;
}
//-----------------------------------------------------------------------------
// 名称 : ParseObject
// 描述 : 递归解析顶级模板
//-----------------------------------------------------------------------------
bool ParseObject(
LPD3DXFILEDATA pDataObj,
LPD3DXFILEDATA pParentDataObj,
DWORD depth,
void **pData )
{
LPD3DXFILEDATA pSubDataObj;
bool parseResult = true;
HRESULT hr;
// 获取子模板数目
DWORD childCount;
pDataObj->GetChildren( &childCount );
// 遍历模板并分析
for( DWORD i=0; i<childCount; i++ )
{
// 取子模板对象
hr = pDataObj->GetChild( i, &pSubDataObj );
if( FAILED( hr ) )
break;
// 分析子模板
parseResult = ParseObject( pSubDataObj, pDataObj, depth+1, pData );
// 释放数据对象
Release<LPD3DXFILEDATA>( pSubDataObj );
// 出现错误,停止分析
if( false == parseResult )
break;
}
return parseResult;
}
就那么简单,相信大家都看得明白。通过重载ParseObject方法,我们以判断当前分析的模板类型,然后创建实际的模板对象,从文档中复制数据。有了上面的工具,我们就可以自己来读取和解析x格式的骨骼动画文件了。
引自:http://bbs.gameres.com/showthread.asp?threadid=27887