第一次想自己写一个系列的文章,写的不好,大家别骂啊 ,也欢迎交流;
从第三章开始才是真正的接触骨骼动画的内容,第一个例子讲解如何绘制一个简单的节点示意图(具体到人就是关节)
1.基本流程
1) D3DXLoadMeshHierarchyFromX(fileName, D3DXMESH_MANAGED, g_pDevice, &boneHierarchy, NULL, &m_pRootBone, NULL);
解释一下各个参数的意思:
fileName:骨骼文件名
boneHierarchy:应该是你自己实现的继承于ID3DXAllocateHierarchy并且已经实现了四个接口函数
m_pRootBone:返回的根节点
2)更新Bone中的CombinedTransformationMatrix,这里利用一个递归 子节点 = 子节点(D3DXFRAME中的TransformationMatrix) * 父节点 ;
其实Bone中的这个CombinedTransformationMatrix理论上可以不存在,加了这个数据会让绘制更方便操作也更易理解
D3DXFRAME中的TransformationMatrix 有人可能不太明白这个成员的意思:这个矩阵描述的是相对父节点的局部坐标矩阵
假设一个点现在处在父节点,那么这个节点 * TransformationMatrix就到达了当前节点(在局部坐标系下的表示)
而CombinedTransformationMatrix可以理解为相对世界的坐标系(言外之意 一个点通过这个矩阵变换 , 一步就可以变换到当前节点)
假设我们现在有一个如下图的骨骼结构,当你绘制节点4的时候,如果你没有CombinedTransformationMatrix这个成员 你不得不计算 1 * 2 然后才能得到节点4,
但是这也会有问题1节点有两个子节点2和3 ,你怎么知道应该走2呢,所以CombinedTransformationMatrix更适合在初始化的时候就被递归更新,这样以后每次用直接拿来用就可以了
3)Render(绘制)
这个没什么好说的 其实就是给一个实例图,每个小球都代表了一个节点, 子节点和父节点进行连线
2.注意事项:
我买的是英文原版的,但是为了加深学习的印象,我在DXUT框架下自己实现了一下,但运行之后发现有的引用计数不为0,我通过D3D文档中的骨骼例子发现
BoneHierarchyLoader boneHierarchy;
boneHierarchy.DestroyFrame(m_pRootBone);
这样调用只会释放掉一个,并不能将整棵树都释放掉
//应该调用下面的这个
BoneHierarchyLoader boneHierarchy;
D3DXFrameDestroy(m_pRootBone, &boneHierarchy);
所以每个例子我建议大家都应该自己再写一遍,特别是D3D这个本来就很有难度的东西,如果只是看看书,是不行的
下面我们看下代码部分:(不可能全部讲解,只跳重点的 大家可以去下载这本书的源代码 再结合本文分析 )
下面的内容 最好是你已经自己实现了一遍(哪怕是照着抄了一遍都可以)
//调用
D3DXMATRIX i;
D3DXMatrixIdentity(&i);
UpdateMatrices((Bone*)m_pRootBone, &i);
//实现
void SkinnedMesh::UpdateMatrices(Bone* bone, D3DXMATRIX *parentMatrix)
{
if(bone == NULL)return;(所有节点都已循环绘制完毕)
//计算当前的结合矩阵 = TransformationMatrix * 父矩阵
D3DXMatrixMultiply(&bone->CombinedTransformationMatrix,
&bone->TransformationMatrix,
parentMatrix);
//计算兄弟节点
if(bone->pFrameSibling)
{
UpdateMatrices((Bone*)bone->pFrameSibling,
parentMatrix);
}
//计算子节点
if(bone->pFrameFirstChild)
{
UpdateMatrices((Bone*)bone->pFrameFirstChild,
&bone->CombinedTransformationMatrix);
}
}
//调用
m_drone.RenderSkeleton(NULL, NULL, world);
//实现
通过一个球体 代表关节 然后在子节点和父节点之间用直线连接
void SkinnedMesh::RenderSkeleton(Bone* bone, Bone *parent, D3DXMATRIX world)
{
//Temporary function to render the bony hierarchy
if(bone == NULL)bone = (Bone*)m_pRootBone;
D3DXMATRIX r, s;
D3DXMatrixRotationYawPitchRoll(&r, -D3DX_PI * 0.5f, 0.0f, 0.0f);
//Draw line between bones
if(parent != NULL && bone->Name != NULL && parent->Name != NULL)
{
//绘制球体
g_pDevice->SetRenderState(D3DRS_LIGHTING, true);
//这个我有点疑问 一个球绕y轴旋转 会有什么用呢 不明白 欢迎大家给出解释
g_pDevice->SetTransform(D3DTS_WORLD, &( r* bone->CombinedTransformationMatrix * world ));
m_pSphereMesh->DrawSubset(0);
D3DXMATRIX w1 = bone->CombinedTransformationMatrix;
D3DXMATRIX w2 = parent->CombinedTransformationMatrix;
//绘制线
D3DXVECTOR3 thisBone = D3DXVECTOR3(w1(3, 0), w1(3, 1), w1(3, 2));
D3DXVECTOR3 ParentBone = D3DXVECTOR3(w2(3, 0), w2(3, 1), w2(3, 2));
if(D3DXVec3Length(&(thisBone - ParentBone)) < 2.0f)
{
g_pDevice->SetTransform(D3DTS_WORLD, &world);
VERTEX vert[] = {VERTEX(ParentBone, 0xffff0000), VERTEX(thisBone, 0xff00ff00)};
g_pDevice->SetRenderState(D3DRS_LIGHTING, false);
g_pDevice->SetFVF(VERTEX::FVF);
g_pDevice->DrawPrimitiveUP(D3DPT_LINESTRIP, 1, &vert[0], sizeof(VERTEX));
}
}
if(bone->pFrameSibling)RenderSkeleton((Bone*)bone->pFrameSibling, parent, world);
if(bone->pFrameFirstChild)RenderSkeleton((Bone*)bone->pFrameFirstChild, bone, world);
}
//总结
理解基本流程,理解CombinedTransformationMatrix的含义 D3DXFRAME中的TransformationMatrix的含义
写的很菜,不过花了我很长时间,自己学到了很多东西,希望大家指正,我建议大家至少读过那本龙书再来看骨骼动画,当然你也可以直接看,不过理解起来吃力。
其实有很多东西我没有讲,比如你load之后,是d3d内部帮你构建了所有的对象,并且将他们自动连接到一起(兄弟节点和子节点的赋值),不过这些我认为我不可能将的特别清楚,这些理论上可以自己实现,你可以自己串一个链,然后调用render,只要你构造正确,也完全没问题。大家有什么不明白的,可以qq交流,我也是小菜鸟,骨骼动画更是初涉
qq:379187940 欢迎交流指正