骨骼动画Word格式.docx
- 文档编号:16943173
- 上传时间:2022-11-27
- 格式:DOCX
- 页数:10
- 大小:22.04KB
骨骼动画Word格式.docx
《骨骼动画Word格式.docx》由会员分享,可在线阅读,更多相关《骨骼动画Word格式.docx(10页珍藏版)》请在冰豆网上搜索。
<
3D82AB43-62DA-11cf-AB39-0020AF71E433>
//每个模板关联唯一的GUID
STRINGName;
//姓名
DWORDSex;
//性别
[ContactEntry]//联系方式,另一个模板,模板可以嵌套
}
templateContactEntry{
4C9D055B-C64D-4bfe-A7D9-981F507E45FF>
//GUID
STRINGPhoneNumber;
//电话号码
STRINGAddress;
//地址
EmployeeDavid{
"
David"
;
1;
ContactEntry{
100-100000000"
farfaraway"
从上面这个简单的例子我们就可以看出x文件的大概模样了,详细的情况大家可以参考《AdvancedAnimationwithDirectX》。
下面我们看如何来读取这样一个x文件,借助下几个对象,
ID3DXFile--x文件格式文档对象。
例如Employee.x这样一个文件。
ID3DXFileEnumObject--用来枚举x文档的顶级模板数据。
所谓顶级模板数据是指那些没有
父模板的数据,例如上面的David数据段。
ID3DXFILEDATA--模板数据。
上面的David和他的联系方式都是ID3DXFILEDATA
对象,自包含。
下面看实际的分析函数,下面的代码适用于DirectX9.0SDKUpdate(October2004),原书的代码有点过时了。
//-----------------------------------------------------------------------------
//名称:
Parse
//描述:
分析x文件格式文档
boolParse(char*filename,void**pData)
{
LPD3DXFILElpD3DXFile;
LPD3DXFILEENUMOBJECTlpD3DXFileEnumObj;
LPD3DXFILEDATAlpD3DXFileData;
//参数检查
if(NULL==filename)
returnfalse;
//创建X文件对象
HRESULThr=D3DXFileCreate(&
lpD3DXFile);
if(FAILED(hr))
//注册标准模板
hr=lpD3DXFile->
RegisterTemplates(
(LPVOID)D3DRM_XTEMPLATES,D3DRM_XTEMPLATE_BYTES);
Release<
LPD3DXFILE>
(lpD3DXFile);
//创建X文件枚举对象
CreateEnumObject(
filename,D3DXF_FILELOAD_FROMFILE,&
lpD3DXFileEnumObj);
//解析开始
boolparseResult=BeginParse(pData);
if(true==parseResult)
//查询顶级模板数
SIZE_TchildCount=0;
lpD3DXFileEnumObj->
GetChildren(&
childCount);
//分析每个订级模板
for(DWORDi=0;
i<
childCount;
i++)
//获取当前模板
hr=lpD3DXFileEnumObj->
GetChild(i,&
lpD3DXFileData);
break;
//分析
parseResult=ParseObject(lpD3DXFileData,NULL,0,pData);
//释放FileData对象
LPD3DXFILEDATA>
(lpD3DXFileData);
//出现错误,中断分析
if(false==parseResult)
//解析结束
if(parseResult)
parseResult=EndParse(pData);
//释放相关对象
LPD3DXFILEENUMOBJECT>
(lpD3DXFileEnumObj);
returnparseResult;
ParseObject
递归解析顶级模板
boolParseObject(
LPD3DXFILEDATApDataObj,
LPD3DXFILEDATApParentDataObj,
DWORDdepth,
void**pData)
LPD3DXFILEDATApSubDataObj;
boolparseResult=true;
HRESULThr;
//获取子模板数目
DWORDchildCount;
pDataObj->
//遍历模板并分析
//取子模板对象
hr=pDataObj->
pSubDataObj);
//分析子模板
parseResult=ParseObject(pSubDataObj,pDataObj,depth+1,pData);
//释放数据对象
(pSubDataObj);
//出现错误,停止分析
就那么简单,相信大家都看得明白。
通过重载ParseObject方法,我们以判断当前分析的模板类型,然后创建实际的模板对象,从文档中复制数据。
有了上面的工具,我们就可以自己来读取和解析x格式的骨骼动画文件了。
下面我们就来看看如何重载ParseObject方法来获得我们感兴趣的数据,不要担心,绝对简单。
仔细看代码,你会发现只需要做一件事情,判断当前数据段的类型(通过GUID),分配对应的结构对象,然后从数据段拷贝数据(所有SDK自定义模板的GUID都在头文件rmxfguid.h中定义,你需要把它加入你的工程中。
所有预定义模板在这里可以找到)。
先来看看如何获取当前数据段的GUID,
GUIDobjGUID;
GetType(&
objGUID);
简单吧,下面开始我们的分析之旅。
x动画文件中骨骼是用Frame模板定义的,
templateFrame
3D82AB46-62DA-11cf-AB39-0020AF71E433>
FrameTransformMatrixframeTransformMatrix;
//骨骼相对于父节点的坐标变换矩阵
Meshmesh;
//骨骼的Mesh
}
只有两个字段。
FrameTransformMatrix就是一个matrix。
Mesh稍微复杂,详细格式大家自己参考MSDN,我们也会有专门的代码来加载Mesh,现在关注Frame。
为了加载Frame,我们要在程序中定义一个和Frame模板对应的数据结构,SDK中经默认提供了一个,那就是D3DXFRAME,
typedefstruct_D3DXFRAME
LPSTRName;
//骨骼名称
D3DXMATRIXTransformationMatrix;
//相对与父节点的坐标变换矩阵
LPD3DXMESHCONTAINERpMeshContainer;
//LPD3DXMESHCONTAINER对象,用来
//加载MESH,还有一些附加属性,见SDK
struct_D3DXFRAME*pFrameSibling;
//兄弟节点指针,和下面的子节点指针
//一块作用构成骨骼的层次结构。
struct_D3DXFRAME*pFrameFirstChild;
//子节点指针.
}D3DXFRAME,*LPD3DXFRAME;
这样一个结构已经足够容纳Frame模板中的数据并形成一个层次结构,不过为了我们程序的需要,我们还需要其他字段,为此我们通常会扩展D3DXFRAME,
typedefstruct_D3DXFRAME_EX:
publicD3DXFRAME
{
D3DXMATRIXmatCombined;
//存储当前节点相对于根节点的位置偏移矩阵,沿着到
//到根骨骼的路径把所有的坐标变换矩阵相乘得到。
D3DXMATRIXmatOriginal;
//在播放动画的时候有可能会改变原来结构中的
//TransformationMatrix,因此我们声名一个新的字段
//将原来的坐标变换矩阵保存起来以便在需要的时候恢
//复回去。
...//忽略一些方法定义
我知道有些人已经按捺不住了,那么动手吧,
//判断当前分析的是不是Frame节点
if(objGUID==TID_D3DRMFrame)
//引用对象直接返回,不需要做分析。
一个数据段实际定义一次后可以
//被其他模板引用,例如后面的Animation动画模板就会引用这里的Frame
//节点,标识动画关联的骨骼。
if(pDataObj->
IsReference())
returntrue;
//创建D3DXFRAME_EX结构,准备拷贝数据
D3DXFRAME_EX*pFrame=newD3DXFRAME_EX();
//拷贝名称
pFrame->
Name=GetObjectName(pDataObj);
//注意观察文件就可以发现一个Frame要么是根Frame,父节点不存在,
//要么作为某个Frame的下级Frame而存在。
if(NULL==pData)
//作为根节点的兄弟节点加入链表。
pFrameSibling=m_pRootFrame;
m_pRootFrame=pFrame;
pFrame=NULL;
//将自定义数据指针指向自己,供子
//节点引用。
pData=(void**)&
m_pRootFrame;
else
//作为传入节点的子节点
D3DXFRAME_EX*pDataFrame=(D3DXFRAME_EX*)(*pData);
pFrameSibling=pDataFrame->
pFrameFirstChild;
pDataFrame->
pFrameFirstChild=pFrame;
结束了!
是不是很简单,呵呵,记住我们只需要做一件事情,判断类型,分配匹配的对象然后拷贝数据,下面来分析Frame中的matrix,
//frame的坐标变换矩阵,因为matrix必然属于某个Frame所以pData必须有效
elseif(objGUID==TID_D3DRMFrameTransformMatrix&
&
pData)
//我们可以肯定pData指向某个Frame
//先取得缓冲区大小,应该是个标准的4x4矩阵
DWORDsize=0;
LPCVOIDbuffer=NULL;
Lock(&
size,&
buffer);
//拷贝数据
if(size==sizeof(D3DXMATRIX))
memcpy(&
TransformationMatrix,buffer,size);
Unlock();
matOriginal=pDataFrame->
TransformationMatrix;
这样大家应该对其他类型的模板数据分析代码都应该大致猜的出来了。
具体的代码我就不在这里提供,只是简单的介绍一下它们的作用和关系,大家可以参考最后附上的工程。
Frame--
骨骼。
正如大家已经看到的那样,我们可以用pFrameSibling和pFrameFirstChild两个字段来构成骨骼的层次结构。
骨骼模板包含了当前骨骼相对父骨骼的坐标变换矩阵和骨骼对应的模型
Mesh--
模型。
角色的顶点数据,包含vertexbuffer,indexbuffer等。
我们可以直接用普通的ID3DXMesh来加载其中的数据。
除此之外,Mesh中还包含了SkinWeight模板。
SkinWeight--
骨骼关联的顶点已经该骨骼的坐标变换对该顶点的权重。
实际中我们并不需要特殊处理这类模板数据,ID3DXMesh已经包含了对应的代码。
AnimationSet--
动画集合。
例如角色的各种动作“Kill”,“Jump”等等,包含多个Animation。
Animation--
动画。
由对应骨骼的名称和一组AnimationKey组成。
AnimationKey--
动画键。
包含一组时间戳以及在对应时间戳应用到骨骼上的平移、缩放、旋转向量或者复合的坐标变换矩阵。
以上就是我们需要了解的全部了。
至此,所有原料都已经准备齐全,各位大厨们下一步要做的就是骨骼动画这道小菜啦!
我们的目标是根据骨骼动画来更新模型。
看看手上的材料,
1)骨骼动画数据。
上一节中我们已经读出了AnimationSet、Animation和AnimationKey这些动画数据,我们现在要做的就是把它们应用到骨骼上面去。
AnimationSet只是标明了我们要播放的动画名称,关键的处理在Animation和AnimationKey上面。
Animation包含了所对应的骨骼名称,下属的AnimationKey包含了坐标变换的类型以及对应的时间戳,我们也把AnimationKey看做一个关键帧。
下面要做的就是根据当前时间判断动画落在哪两个关键帧中间,例如key1和key2,然后求出插值系数scaler,
scaler=(当前时间-key1.时间)/(key2.时间-key1.时间)
求出插值系数后,骨骼的当前位置就可以用下面的方法求出,注意各种key类型求插值的方法不一样,
switch(Key的类型)
case旋转:
...
//四元数插值
D3DXQUATERNIONRotationQuaternion;
D3DXQuaternionSlerp(
RotationQuaternion,
pAnimationKey->
pQuaternionKeys[Key1].value,
pQuaternionKeys[Key2].value,
Scaler);
//应用旋转矩阵
D3DXMATRIXRotationMatrix;
D3DXMatrixRotationQuaternion(&
RotationMatrix,&
RotationQuaternion);
pAnimation->
pBone->
TransformationMatrix*=RotationMatrix;
case平移和缩放:
//矢量插值
D3DXVECTOR3InterpolatedVector=
pVectorKeys[Key1].value+Scaler*
(pAnimationKey->
pVectorKeys[Key2].value-pAnimationKey->
pVectorKeys[Key1].value);
if(pAnimationKey->
Type==XAnimationKey:
:
KeyType:
Scaling)
//应用缩放矩阵
D3DXMATRIXScalingMatrix;
D3DXMatrixScaling(
ScalingMatrix,InterpolatedVector.x,InterpolatedVector.y,InterpolatedVector.z);
TransformationMatrix*=ScalingMatrix;
//应用平移矩阵
D3DXMATRIXTranslationMatrix;
D3DXMatrixTranslation(
TranslationMatrix,InterpolatedVector.x,InterpolatedVector.y,InterpolatedVector.z);
TransformationMatrix*=TranslationMatrix;
case坐标变换矩阵:
//矩阵插值
D3DXMATRIXTransformMatrix=
pMatrixKeys[Key1].value+Scaler*
pMatrixKeys[Key2].value-pAnimationKey->
pMatrixKeys[Key1].value);
//应用坐标变换矩阵
TransformationMatrix*=TransformMatrix;
}//switch
这样我们就把根据当前时间计算出来的插值坐标变换矩阵应用到骨骼上了。
2)骨骼数据。
在从文件中读出来的时候,我们已经利用pFrameSibling和pFrameFirstChild两个字段构造了一个层次结构。
注意骨骼中的TransformationMatrix包含的是当前骨骼相对于父骨骼的坐标变换,应用到mesh上的时候,我们需要的相对于根骨骼的坐标变换。
因此我们要做一下处理,简单的一个递归调用,
//名称:
UpdateHierarchy
//描述:
计算本节点及所有兄弟、子节点相对于根节点的偏移矩阵
voidUpdateHierarchy(D3DXMATRIX*matTrans=NULL)
D3DXMATRIXmatIdentity;
//根节点的偏移矩阵为单位矩阵
if(NULL==ma
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 骨骼 动画