Direct X 12 – Character Animation 角色动画

23.4 从文件中读取动画数据

在demo中,我们使用text文件来存储3D蒙皮网格模型的动画数据,并且将该类型的文件称为.m3d文件。该格式只是便于我们进行读取,并没有考虑性能的因素。

23.4.1 Header

在.m3d格式的开头,我们分别定义了,材质,顶点,三角形,骨头以及动画的数量:

#Material 3
#Vertices 3121
#Triangles 4062
#Bones 44
#AnimationClips 15

23.4.2 材质

.m3d格式的下一部分是一组材质。以下为两个材质示例:

Name: soldier_head
Diffuse: 1 1 1
Fresnel0: 0.05 0.05 0.05
Roughness: 0.5
AlphaClip: 0
MaterialTypeName: SkinnedData
DiffuseMap: head_diiff.dds
NormalMap: head_norm.dds

Name: soldier_jacket
Diffuse: 1 1 1
Fresnel0: 0.05 0.05 0.05
Roughness: 0.8
AlphaClip: 0
MaterialTypeName: Skinned
DiffuseMap: jacket_diff.dds
NormalMap: jacket_norm.dds

文件中包含我们所熟悉的材质数据,例如漫反射,粗糙度等等,但是也包含其他信息,例如使用的贴图,是否启用α剪裁以及材质类型名称。材质类型名称决定了我们将使用哪一种着色器程序处理该材质。例如,“Skinned”类型意味着该材质需要以支持蒙皮的着色器进行处理。

23.4.3 子集(Subset)

一个网格模型由一个或者多个subset组成。subset表示在网格模型中能使用同一个材质进行渲染绘制的一组三角形。同时,subset与材质之间一一对应,也就是说第i个subset对应第i个材质。同时,第i个subset定义一组连续的三角形,这些三角形都应该使用第i个材质进行绘制。

SubsetID: 0 VertexStart: 0 VertexCount: 3915 FaceStart: 0 FaceCount: 7230
SubsetID: 1 VertexStart: 3915 VertexCount: 2984 FaceStart: 7230 FaceCount: 4449
SubsetID: 2 VertexStart: 6899 VertexCount: 4270 FaceStart: 11679 FaceCount: 6579
SubsetID: 3 VertexStart: 11169 VertexCount: 2305 FaceStart: 18258 FaceCount: 3807
SubsetID: 4 VertexStart: 13474 VertexCount: 274 FaceStart: 22065 FaceCount: 7230

23.4.4 顶点数据与三角形

之后的两组数据为顶点与索引(3个索引构成一个三角形):

Position: -14.34667 90.44742 -12.08929 
Tangent: -0.3069077 0.2750875 0.9111171 1 
Normal: -0.3731041 -0.9154652 0.150721 
Tex-Coords: 0.21795 0.105219 
BlendWeights: 0.483457 0.483457 0.0194 0.013686 
BlendIndices: 3 2 39 34   
Position: -15.87868 94.60355 9.362272 
Tangent: -0.3069076 0.2750875 0.9111172 1 
Normal: -0.3731041 -0.9154652 0.150721 
Tex-Coords: 0.278234 0.091931 
BlendWeights: 0.4985979 0.4985979 0.002804151 0 
BlendIndices: 39 2 3 0

***************三角形********************* 
0 1 2 
3 4 5 
6 7 8 
9 10 11 
12 13 14
...

23.4.5 Bone Offset Transform

***************BoneOffsets******************* 
BoneOffset0 -0.8669753 0.4982096 0.01187624 0  
0.04897417 0.1088907 -0.9928461 0
-0.4959392 -0.8601914 -0.118805 0  
-10.94755 -14.61919 90.63506 1   
BoneOffset1 1 4.884964E-07 3.025227E-07 0  
-3.145564E-07 2.163151E-07 -1 0  
4.884964E-07 0.9999997 -9.59325E-08 0  
3.284225 7.236738 1.556451 1 
...

23.4.6 层级

下列数据存储了层级数组,数组中第i个成员对应其父节点在数组中的索引。

***************BoneHierarchy***************** 
ParentIndexOfBone0: -1 
ParentIndexOfBone1: 0 
ParentIndexOfBone2: 1 
ParentIndexOfBone3: 2 
ParentIndexOfBone4: 3 
ParentIndexOfBone5: 4 
ParentIndexOfBone6: 5 
ParentIndexOfBone7: 6 
ParentIndexOfBone8: 7 
ParentIndexOfBone9: 7 
ParentIndexOfBone10: 7 
ParentIndexOfBone11: 7 
ParentIndexOfBone12: 6 
ParentIndexOfBone13: 12 
...

23.4.7 动画数据

最后一部分我们将读取animation clips。每一个动画都有一个可读取的名字以及一组key frames关键帧,其对应骨骼中的每一根骨头。每一个关键帧存储了时间点,表示骨头位置变化的位移向量,表示骨头缩放的缩放向量,以及表示骨头朝向的四元数。

***************AnimationClips**************** 
AnimationClip run_loop 
{    
    Bone0 #Keyframes: 18    
    {       
        Time: 0 Pos: 2.538344 101.6727 -0.52932         
        Scale: 1 1 1         
        Quat: 0.4042651 0.3919331 -0.5853591 0.5833637       
        Time: 0.0666666         
        Pos: 0.81979 109.6893 -1.575387         
        Scale: 0.9999998 0.9999998 0.9999998         
        Quat: 0.4460441 0.3467651 -0.5356012 0.6276384       
        ...    
    }      
    Bone1 #Keyframes: 18    
    {       
        Time: 0         
        Pos: 36.48329 1.210869 92.7378         
        Scale: 1 1 1         
        Quat: 0.126642 0.1367731 0.69105 0.6983587       
        Time: 0.0666666         
        Pos: 36.30672 -2.835898 93.15854         
        Scale: 1 1 1         
        Quat: 0.1284061 0.1335271 0.6239273 0.7592083       
        ...    
    }
    ...
}

AnimationClip walk_loop 
{    
    Bone0 #Keyframes: 33    
    {       
        Time: 0         
        Pos: 1.418595 98.13201 -0.051082         
        Scale: 0.9999985 0.999999 0.9999991         
        Quat: 0.3164562 0.6437552 -0.6428624 0.2686314       
        Time: 0.0333333         
        Pos: 0.956079 96.42985 -0.047988         
        Scale: 0.9999999 0.9999999 0.9999999         
        Quat: 0.3250651 0.6395872 -0.6386833 0.2781091              
        …    
    }      
    Bone1 #Keyframes: 33    
    {       
        Time: 0         
        Pos: -5.831432 2.521564 93.75848         
        Scale: 0.9999995 0.9999995 1         
        Quat: -0.033817 -0.000631005 0.9097761 0.4137191       
        Time: 0.0333333         
        Pos: -5.688324 2.551427 93.71078         
        Scale: 0.9999998 0.9999998 1         
        Quat: -0.033202 -0.0006390021 0.903874 0.426508       
        ...    
    }    
    ... 
}   
...

以下代码展示了我们如何从文件中读取animation clip:

void M3DLoader::ReadAnimationClips(
    std::ifstream& fin,
    UINT numBones,
    UINT numAnimationClips,
    std::unordered_map<std::string, AnimationClip>& animations)
{
    std::string ignore;
    fin >> ignore; // AnimationClips的header文字
    
    for(UINT clipIndex = 0; clipIndex < numAnimationClips; ++clipIndex) { std::string clipName; fin >> ignore >> clipName;
        
        fin >> ignore;

        AnimationClip clip;
        clip.BoneAnimations.resize(numBones);
        for(UINT boneIndex = 0; boneIndex < numBones; ++boneIndex) { ReadBoneKeyFrames(fin, numBOnes, clip.BoneAnimations[boneIndex]); } fin >> ignore;

        animations[clipName] = clip;
    }
}

void M3DLoader::ReadBoneKeyframes(
    std::ifstream& fin,
    UINT numBones,
    BoneAnimation& boneAnimation)
{
    std::string ignore;
    UINT numKeyframes = 0;
    fin >> ignore >> ignore >> numKeyframes;
    
    fin >> ignore;

    boneAnimation.Keyframes.resize(numKeyframes);
    for(UINT i = 0; i < numKeyframes; ++i) { float t = 0.f; XMFLOAT3 p(0.f, 0.f, 0.f); XMFLOAT3 s(1.f, 1.f, 1.f); XMFLOAT4 q(0.f, 0.f, 0.f, 1.f); fin >> ignore >> t;
        fin >> ignore >> p.x >> p.y >> p.z;
        fin >> ignore >> s.x >> s.y >> s.z;
        fin >> ignore >> q.x >> q.y >> q.z >> q.w;

        boneAnimation.Keyframes[i].TimePos = t;
        boneAnimation.Keyframes[i].Translation = p;
        boneAnimation.Keyframes[i].Scale = s;
        boneAnimation.Keyframes[i].RotationQuat = q;
    }

    fin >> ignore;
}

23.4.8 M3DLoader

从.m3d文件中读取数据的代码包含在LoadM3D.h/.cpp中,其中,函数LoadM3d如下所示:

bool M3DLoader::LoadM3d(
    const std::string& filename,
    std::vector<SkinnedVertex>& vertices,
    std::vector<USHORT>& indices,
    std::vector<M3dMaterial>& mats,
    SkinnedData& skinInfo)
{
    std::ifstream fin(filename);

    UINT numMaterials = 0;
    UINT numVertices = 0;
    UINT numTriangles = 0;
    UINT numBones = 0;
    UINT numAnimationClips = 0;

    std::string ignore;

    if(fin)
    {
        fin >> ignore;
        fin >> ignore >> numMaterials;
        fin >> ignore >> numVertices;
        fin >> ignore >> numTriangles;
        fin >> ignore >> numBones;
        fin >> ignore >> numAnimationClips;

        std::vector<XMFLOAT4X4> boneOffsets;
        std::vector<int> boneIndexToParentIndex;
        std::unordered_map<std::string, AnimationClip> animations;

        ReadMaterials(fin, numMaterials, mats);
        ReadSubsetTable(fin, numMaterials, subsets);
        ReadSkinnedVertices(fin, numVertices, vertices);
        ReadTriangles(fin, numTriangles, indices);
        ReadBoneOffsets(fin, numBones, boneOffsets);
        ReadBoneHierarchy(fin, numBones, boneIndexToParentIndex);
        ReadAnimationClips(fin, numBones, numAnimationClips, animations);

        skinInfo.Set(boneIndexToParentIndex, boneOffsets, animations);

        return true;
    }

    return false;
}

留下评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据