HLSL SkinMesh 学习笔记
16.2.1 SkinnedMesh Overview
成员变量:
mSkinnedMesh:一个指向保存网格的指针
mRoot: 一个指向骨骼框架根节点的指针
MMaxVertInfluences: 保存每个点最多受多少个骨骼的影响
mNumBones:整个骨架中的骨头数量
mSkinInfo: 一个指向ID3DXSkinInfo的指针,ID3DXSkinInfo里面保存了一些与蒙皮相关的信息,比如说每块骨骼的偏移、顶点的权重和骨头的矩阵索引等
mAnimCtrl: 一个指向ID3DXAnimationController的指针,ID3DXAnimationController是用来更改骨骼信息的.
mFinalXForms: 保存每块骨骼最后一次改变的矩阵。
mToRootXFormPtrs: 一个指针数组,指针指向每个骨骼toRoot的矩阵。(in addition, we organize these pointers so that the ith to-root matrix corresponds with the ith offfset matrix. 这句话不太懂,offset矩阵和to-root矩阵有什么关系吗?)
MAX_NUM_BONES_SUPPORTED: 定义了网格中最多拥有多少块骨骼
主要方法:
getFinalXFormArray: 获得一个指向数组的指针,该数组保存了最后一次变化的矩阵
updata: 更新网格位置
draw: 画网格(书中原话:Draws the skinned mesh)
findModeWithMesh: 在骨骼树找到含有且只含有mesh的节点。
hasMormals: 如果.X文件中已经计算出normal数据则返回true
buildSkinnedMesh: 分配空间并且将混合权重和矩阵索引保存进去
buildToRootXFormPrtArray: 遍历骨骼树,返回指向每个骨骼的to-root矩阵的指针们。
buildToRootXForms: 用递归的方法计算每个骨骼的to-root矩阵。
16.2.2 D3DXMESHCONTAINER
D3DXMESHCONTAINER给我们提供一个能够关联网格的框架(其实是一个结构体,保存网格们信息)
typedef struct _D3DXMESHCONTAINER { LPSTR Name; D3DXMESHDATA MeshData; LPD3DXMATERIAL pMaterials; LPD3DXEFFECTINSTANCE pEffects; DWORD NumMaterials; DWORD *pAdjacency; LPD3DXSKININFO pSkinInfo; struct _D3DXMESHCONTAINER *pNextMeshContainer; } D3DXMESHCONTAINER, *LPD3DXMESHCONTAINER;
Name: mesh的名字
MeshData: D3DXMESHCONTAINER是一个通用结构体,可以是ID3DXMesh, ID3DXPMesh(progressive mesh)或者ID3DXPatchMesh. D3DXMESHDATA便用来指明用的是什么类型的mesh并且还包含一个valid(不知道翻译为啥)的指针。
pMaterials: 一个指向D3DMATERAIL结构数组的指针
pEffects: 一个指向D3DEFFECTINSTANCE结构的指针
NumMaterials: material数组元素个数
pAdjacency: 一个指向网格adjacency信息的指针
pSkinInfo: 一个指向ID3DXSkinInfo接口的指针,包含顶点渲染的信息(这些信息包含顶点权重值、顶点骨骼索引等,这些信息从.X文件中获得),ID3DXSkinInfo是D3DX库提供给我们的加载与蒙皮骨骼相关信息的类。
pNextMeshContainer: 指向下一个网格的指针(网格们以linked list的形式保存)
16.2.3 ID3DXAnimationController
一个控制骨骼运动的接口,骨骼保存一些关键帧和介于两个关键帧之间的位置的计算方法。我们已知t时刻骨骼的位置,我们需要计算的是t+△t时刻骨骼的位置,当△t=1/60s时,骨骼运动在我们看来就是连续的了。
方法:HRESULT ID3DXAnimationController::AdvanceTime(
DOUBLE TimeDelta,
LPD3DXANMATIONCALLBACKHANDLE pCallbackHandler );
其中,TimeDelta就是△t,pCallbackHandler设置为null即可.
一旦animation controller改变了骨骼的位置,它会将更改的数据写入D3DFRAME::TransformationMatrix,我们可以通过该方法获得骨骼树中每块骨骼的位置。
16.2.4 ID3DXAllocateHierarchy
为了创建和销毁骨骼树,我们需要实现ID3DXAllocateHierarchy接口,里面有四个抽象函数,可以根据自己的需要重写适合自己的方法。
16.2.5 D3DXLoadMesh HierarchyFromX and D3DXFrameDestroy
当我们实现了ID3DXAllocateHierarchy接口后,我们就可以使用D3DLoadMeshHierarchyFromX方法了。
HRESULT WINAPI D3DXLoadMeshHierarchyFromX( LPCSTR Filename, //.X文件的名字 DWORD MeshOptions, //D3DXMESH_32BIT: The mesh will use 32-bit indices. //D3DXMESH_SYSTEMMEM: The mesh will be placed in the system memory pool. //D3DXMESH_MANAGED: The mesh will be placed in the managed memory pool. //D3DXMESH_WRITEONLY: The mesh's data will only be written to and not read from. //D3DXMESH_DYNAMIC: The mesh's buffers will be made dynamic. LPDIRECT3DDEVICE9 pDevice, //与该mesh相关联的设备 LPD3DXALLOCATEHIERARCHY pAlloc, //一个指向class的指针,该类是ID3DXA11ocateHierarchy 类的实现类,表明这个该以什么具体的方法加载该文件 LPD3DXLOADUSERDATA pUserDataLoader, //和上一个参数类似,可以忽略它 LPD3DXFRAME* ppFrameHierarchy, //返回一个指向骨骼树root的指针 LPD3DXANIMATIONCONTROLLER* ppAnimController); //返回一个指向保存该骨骼data的ID3DXAnimationController的指针
}
16.2.7 Converting to a Skinned Mesh
通过以上的方法我们已经获取mesh container,但是我们还不知道骨骼顶点权重和渲染混合信息。此时我们首先克隆mesh信息到包含位置和材质顶点format(mesh to vertex format)。第二步:将mesh信息转换为包含混合权重和骨骼索引信息用来渲染的vertex format.
在实际的代码中:我们
第一步是buildSkinnedMesh(获取vertex的位置和贴图信息?不确定)
第二步:在我们添加混合权重和骨骼索引之前,我们对mesh进行一些优化,将mesh保存到顶点缓冲区。
第三步:在第二步中优化方法中,我们rearrange顶点数组,所以当前的顶点数组是不正确的,此时我们需要指明remapped的顶点(相当于一个哈希么?),此时通过ID3DXSkinInfo::Remap来实现映射。
16.2.8 Building the To-Root Transform Matrix Array
用一个for循环求得所有子骨骼相对于root矩阵的Transform matrix。
16.2.9 Initialization Summarized
总结初始化工作具体的步骤:
①实现ID3DAllocate-Hierarchy接口,以便可以使用和重写它的一些方法。
②使用ID3DAllocate-Hierarchy接口中的D3DXLoadMeshHierarchyFromX方法加载.X文件中的数据.
③寻找包含skin data的框架,保存在mesh container里面
④将skinned mesh转换成我们需要的包含顶点权重和骨骼索引信息的格式,使ID3DXSkinInfo::ConvertToIndexedBlendedMesh方法。
⑤建立一个数组,里面保存了每个bones的to-root矩阵。