使用模型(X文件)

(本文原网址:http://hi.baidu.com/liu98/blog/item/80b6887ebe3ad73c0dd7da00.html)

使用模型
2008-03-05 22:04

在本章的前半部分,介绍了如何使用网格模型来建立地形或简单的三维图形。接下来,看看如何把三维建模软件生成的复杂三维模型导入程序中。流行的3D文件格式现在有很多种,比如.3ds、.max,.cof等,它们包含了许多信息,并且都不为像游戏引擎这样的实时环境所优化。游戏需要更快、更简单的文件格式。根据游戏类型的不同,每个游戏使用的文件格式也各不相同,比如,Quake引擎所使用的文件格式就是.md3的模型格式。

首先,看看最简单的3D文件格式.X,这是Direct3D所使用的格式,下面就研究一下.X格式的模型文件。

5.3.1 X文件格式

.X文件格式包含了两个最基本的部分:用户自定义的数据类型和层级关系。X文件是由模板(template)驱动的,模板定义了如何存储一个数据对象,这样用户便可以自己来定义具体的格式。预定义的模板位于rmxftmpl.h和rmxftmpl.x中,模板的标识符都在rmxfguid.h中,通用文件d3dfile.cpp包含了这两个头文件。模板所允许的数据类型被称为“可选成员(optional member)”,这些可选对象作为数据对象的子对象来保存。子对象可以是另一个数据对象,可以是对前一个数据对象的引用,也可以是一个二进制的对象。

X文件格式包含了下面几个主要部分。

1.首部(Header)

首部是X文件起始的部分,起始的4字节是魔数(magic number)“xof”,主版本号为03,次版本号也为03。“txt”代表文本文件格式,“bin”代表二进制文件格式。下面是一个典型的X文件的文件头:

xof 0303txt 0032

2.帧(Frame)

这个模板用来存储一个帧,帧是层次场景的构造元素。帧有它们自己的转换矩阵,还可以有自己的子对象(child objects)。帧也可以有子帧。在skin meshes中,骨骼(bone)指的就是一个帧。

3.变换矩阵(FrameTransformMatrix)

正所谓人如其名,这个变换矩阵是帧的变换。它在Frame模板里有实例。下面的文件片断描述一个帧chr_h_047,该帧带有一个子帧,有一个mesh格式的子对象:

Frame chr_h_047 {

   FrameTransformMatrix {

1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,

0.000000,0.000000,1.000000,0.000000,0.000000,-0.323429,-35.371239,

1.476196,1.000000;;

}

Frame {

   FrameTransformMatrix {

1.000000,0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,

0.000000,0.000000,0.000000,1.000000,0.000000,0.016750,0.671672,

-31.375168,1.000000;;

   }

   Mesh {

    }

4.网格模型(Mesh)

这个模板存储一个表态的mesh和mesh的材质。在skin meshes里,整个角色将只是一个mesh,由皮肤信息(skinning information)确定mesh中的每一个部分是如何受到骨骼的影响的。mesh在内部会分成几个子集,每一个子集将受到一个特定的骨骼集合的影响。

5.XskinMeshHeader

这个模板存储随mesh一起导出的(exported)关于皮肤信息的属性(nature)。这个模板包含在mesh模板里面。

template XSkinMeshHeader {

<3cf169ce-ff7c-44ab-93c0-f78f62d172e2>

WORD nMaxSkinWeightsPerVertex;

WORD nMaxSkinWeightsPerFace;

WORD nBones;

}

6.SkinWeights

真正的皮肤信息就存储在这里,这个模板定义了一个特定的骨骼能够去影响一个mesh。这个模板在每一个影响到mesh的骨骼里面都有实例。例如,有12个骨骼影响到mesh,mesh模板里将有12个SkinWeights模板的实例。

template SkinWeights {

<6f0d123b-bad2-4167-a0d0-80224f25fabb>

STRING transformNodeName;

DWORD nWeights;

array DWORD vertexIndices[nWeights];

array FLOAT weights[nWeights];

Matrix4x4 matrixOffset;

}

如果建立的是带有骨骼动画的模型,它和静态模型最大的区别就在于骨骼动画模型必须有XskinMeshHeader和SkinWeights模板。如果把这两个模板从任何一个skin mesh里面移走的话,就会得到一个静态的mesh(static mesh)。

7.VertexDuplicationIndices

模型的索引缓冲区格式:

template VertexDuplicationIndices {

<b8d65549-d7c9-4995-89cf-53a9a8b031e3>

DWORD nIndices;

DWORD nOriginalVertices;

array DWORD indices[nIndices];

}

8.材质和纹理

模型的材质和纹理信息都包含在Mesh中,可以直接从X文件里读取材质和纹理信息。纹理是以文件的形式存储的:

Material {

1.000000;1.000000;1.000000;1.000000;;

0.000000;

1.000000;1.000000;1.000000;;

0.000000;0.000000;0.000000;;

TextureFilename {

   ".//chr_h_047.bmp";

}

}

9.动画

在X文件里包含了一个动画集(AnimationSet),每个动画集里的动画都属于对某个框架的引用。例如:下面的X文件动画集就分别是对帧Scene_Root和chr_h_047的引用:

AnimationSet {

Animation {

   AnimationKey {

   4;

   2;

…0;16;1.000000,0.000000,0.000000,0.000000,0.000000,1.000000, 0.000000,0.000000,0.000000,0.000000,1.000000,0.000000,0.000000,0.000000,0.000000,1.000000;;,

     }

{ Scene_Root }

}

Animation {

    AnimationKey {

   4;

   2;

…   16160;16;1.000000,0.000000,0.000000,0.000000,0.000000,0.000000, -1.000000,0.000000,0.000000,1.000000,0.000000,0.000000,-0.323429,-35.371239,1.476196,1.000000;;;

}

{ chr_h_047 }

}

}

5.3.2 输出.X文件格式

可以有多种方法来得到X文件。首先需要使用三维建模软件来建立需要的三维模型,如3DS Max、MAYA。接着,可以使用三维建模软件的插件来导出X文件。

可以使用的导出插件包括XSkinExp、Panda等,从实用角度看,Panda更胜一筹。无论是导出静态模型,还是导出带有骨骼动画的动态模型,一般都不会出问题。这个插件可从下面这个网址下载:http://www.andytather.co.uk /Panda/directxmax_downloads.aspx。

Panda针对不同的3DSM ax版本,有不同版本的插件与之对应,在使用时只要把PandaDXExport*.dle(*对应的是版本)这个文件放在3DS Mas安装目录下的plugins目录中就可以了,3DS Mas在启动后会自动识别这个插件。

在3DS Max中打开一个模型,并单击“输出”按钮后,弹出如图5-10所示的保存对话框,选择Panda对应的保存格式,并单击“打开”按扭。在弹出的保存选项对话框中按图5-11~图5-14的指示进行选择即可。

图5-10 Panda插件的保存类型

图5-11 对象参数

图5-12 动画参数

图5-13 纹理的设置

图5-14 X文件格式的设置

经过一系列正确设置后,就会导出一个相应的X文件。同时这个X文件中用到的位图文件也会一并导出。输出文件后,应该检查一下文件内容对不对,这个时候可以用DirectX中自带的观看模型工具Mesh Viewer来看,如图5-15所示。

图5-15 用Mesh Viewer工具查看导出的模型

5.3.3 在Direct3D程序中载入X文件

在Direct3D中,3D模型都是以网格模型的形式使用的,它将数据从X文件中读出,通过函数D3DCreateMesh()来创建3D图形。不过通常情况下不用显式地调用函数D3DcreateMesh(),它已经被封装在其他Direct3D功能扩展库函数中。

1.从文件中读取网格模型

函数D3DXLoadMeshFromX()用于从X文件中提取多边形网格信息(包括顶点坐标、颜色、法线向量、纹理信息等),生成多边形网格。函数声明如下:

HRESULT D3DXLoadMeshFromX(        

    LPCTSTR pFilename,                   //X文件路径和文件名

    DWORD Options,                       //指定生成多边形网格属性

    LPDIRECT3DDEVICE9 pDevice,           //Direct3D设备指针

    LPD3DXBUFFER* ppAdjacency,           //存储临近多边形信息的内存地址

    LPD3DXBUFFER* ppMaterials,           //存储材质的内存地址

    LPD3DXBUFFER* ppEffectInstances, //存储模型特殊效果的内存地址

    DWORD* pNumMaterials,                //存储材质数目的内存地址

    LPD3DXMESH* ppMesh                   //存储生成的多边形网格的内存地址

);

其中,LPD3DXBUFFER可以存储如顶点缓冲区、材质、纹理等多种类型的Direct3D数据,而不必对每一种数据声明一个函数接口类型。

下面的函数片断从名为“game”的X文件里读取3D模型:

if( FAILED( D3DXLoadMeshFromX( "game.x", D3DXMESH_SYSTEMMEM,

        g_pd3dDevice, NULL, &pD3DXMtrlBuffer,NULL,

        &g_dwNumMaterials, &g_pMesh ) ) )

{

    return E_FAIL;

}

2.载入纹理和材质

函数D3DXLoadMeshFromX()调用成功后,参数pNumMaterials就会获得 X文件中三维模型的材质和纹理信息,而pNumMaterials会存储材质的数目。

下面的程序片断用于获取3D模型的材质和纹理信息。

【例5-5】得到模型中的材质和纹理信息:

// 从材质集合中把材质和纹理信息解压读取出来

D3DXMATERIAL* d3dxMaterials =

                (D3DXMATERIAL*)pD3DXMtrlBuffer->GetBufferPointer();

g_pMeshMaterials = new D3DMATERIAL9[g_dwNumMaterials];

g_pMeshTextures = new LPDIRECT3DTEXTURE9[g_dwNumMaterials];

for( DWORD i=0; i<g_dwNumMaterials; i++ )

{

    // 拷贝材质

    g_pMeshMaterials[i] = d3dxMaterials[i].MatD3D;

    // 设置材质漫反射的颜色

    g_pMeshMaterials[i].Ambient = g_pMeshMaterials[i].Diffuse;

    // 创建纹理

    if( FAILED( D3DXCreateTextureFromFile( g_pd3dDevice,

       d3dxMaterials[i].pTextureFilename, &g_pMeshTextures[i] ) ) )

    {

        g_pMeshTextures[i] = NULL;

    }

}

// 释放材质缓冲区的内容

pD3DXMtrlBuffer->Release();

3.绘制网格模型

最后,调用函数DrawSubset()绘制多边形网格的3D模型,如图5-12所示。

// 用循环绘制每一个网格面。

for( DWORD i=0; i<g_dwNumMaterials; i++ )

{

    // 设置材质和贴图

    g_pd3dDevice->SetMaterial( &g_pMeshMaterials[i] );

    g_pd3dDevice->SetTexture( 0, g_pMeshTextures[i] );

   // 绘制网格

    g_pMesh->DrawSubset( i );

}

图5-16 载入模型后的效果

5.3.4 三维模型动画

有了模型之后,要为三维模型添加相应的动作,实现游戏效果。Direct3D支持的三维模型动画很丰富,最主要的有三种:

(1)关节动画;

(2)单一网格模型动画;

(3)骨骼动画。

1.关节动画

关节动画中的角色由若干独立的部分组成。每一部分对应着一个独立的网格模型,不同的部分按照角色的特点组织成一个层次结构。比如说,一个人体模型可以由头,上身,左上臂,左前臂,左手,右上臂,右前臂,右手,左大腿,左小腿,左脚,右大腿,右小腿,右脚等各部分组成。而某个部分可能是另一个部分的子节点,同时又是另一个部分的父节点。比如前面的人体模型中,右前臂就是右上臂的子节点,同时也是右手的父节点。而右上臂是上身的子节点,上身则是躯体的子节点。通过改变不同部分之间的相对位置,比如夹角、位移等,就可以实现所需要的各种动画效果。

这类动画的优点很多。首先,在动画序列关键帧中只需要存储节点间的相对变化,因此动画文件占用的空间很小。其次,可以实现很多复杂的动画效果,如果应用程序支持反向动力学,还可以动态实现预先存储的动画序列之外的新的动画效果。

当然这类动画也有不少缺点。其中之一是由于角色模型是一个层次模型,要获得某一个部分相对于世界坐标的位置,必须从根结点开始,遍历该节点所有的父节点来累计计算模型世界变换。但最关键的问题是在不同部分的结合处往往会有很明显的接缝,这会严重地影响模型的真实感。

使用关节动画的游戏中,最著名的就是Quake2,该引擎使用.md3格式的三维模型。一个md3模型通常由身体的三个部分和一个武器模型组成。因此,需要打开3~4个模型并把它们放到一起。每个md3模型一般会有三个标志,这些标志对应它们所在的位置,例如,在头底部,通过tag_head标记将头部连接到身体上部。每个标记实际上是一个单面的几何体(一个直角三角形)。三角形中较长的垂直边必须面向前方,三角形的底部是一个支点(pivot point),这个支点决定各个模型将绕何处作旋转。标记是没有材质的,因此是不可见的。md3将角色动画存储在关键帧里,通过帧的刷新来实现动画效果,每个动画都是由各个部分的动画连接而成的。

2.单一网格模型动画(关键帧动画)

单一网格模型动画中的角色由一个完整的网格模型构成。在动画序列的关键帧中,记录着组成网格的各个顶点的新位置是相对于原位置的改变量。通过在相邻关键帧之间插值来直接改变该网格模型中各个顶点的位置,就可以实现动画效果,如图5-17所示。

与关节动画相比,单一网格模型动画的角色看上去更真实,也不会有关节动画所面临的接缝问题。由于没有使用层次模型,获得模型网格顶点在世界坐标中位置的计算量也很小。但是,这类动画的适应性很弱,角色很难通过实时计算来与环境进行良好的互动,以获得预先存储的动画序列之外的动画效果。另一方面,由于关键帧要存储网格模型所有的顶点信息,动画文件占用的空间特别大。

图5-17 关键帧动画

3.骨骼动画

骨骼动画是目前最为流行的三维模型动画,它可以看做关节动画和单一网格模型动画的结合。在骨骼动画中,一个角色由作为皮肤的单一网格模型和按照一定层次组织起来的骨骼组成。骨骼层次描述了角色的结构,就像关节动画中的不同部分一样,骨骼蒙皮动画中的骨骼按照角色的特点组成一个层次结构。相邻的骨骼通过关节相连,可以作相对的运动。通过改变相邻骨骼间的夹角、位移,组成角色的骨骼就可以做出不同的动作,实现不同的动画效果。

皮肤则作为一个网格蒙在骨骼之上,规定角色的外观。这里的皮肤不是固定不变的刚性网格,而是可以在骨骼影响下变化的可变形网格。组成皮肤的每一个顶点都会受到一个或多个骨骼的影响。在顶点受到多个骨骼影响的情况下,不同的骨骼按照与顶点的几何、物理关系确定对该顶点的影响权重,这一权重可以通过建模软件计算,也可以手工设置。通过计算影响该顶点的不同骨骼对它影响的加权和,就可以得到该顶点在世界坐标系中的正确位置。

动画文件中的关键帧一般保存着骨骼的位置、朝向等信息。通过在动画序列中相邻的两个关键帧间插值,可以确定某一时刻各个骨骼的新位置和新朝向。按照皮肤网格各个顶点中保存的影响它的骨骼索引和相应权重信息,可以计算出该顶点的新位置。这样就实现了在骨骼驱动下的单一皮肤网格变形动画,或者简单地说骨骼蒙皮动画。

骨骼蒙皮动画的效果比关节动画和单一网格动画更逼真,更生动。而且,随着3D硬件性能的提高,越来越多的相关计算可以通过硬件来完成,骨骼蒙皮动画已经成为各类实时动画(尤其是网络游戏图形显示技术)应用中使用最广泛的动画技术。

5.3.5 骨骼动画详解

现在研究一下如何在Direct3D程序中实现一个骨骼动画。首先需要载入建好的带有骨骼动画的三维模型;其次,需要将skin mesh连到骨骼上。在X文件里已经看到,X文件有帧的层次和一个或多个的Mesh包含皮肤信息的模板,需要一个个地加载这些模板,然后手动把mesh同它的骨骼(即帧)连接起来。

为了加载X文件的数据,需要用Direct3D自带的X文件的代码库。IDirectXFile是这个库的主接口,它有一个创建IDirectXFileEnumObject的方法。用IDirectXFileEnum Object接口的方法从一个特定的X文件里面获取数据。IDirectXFileEnumObject:: GetNextDataObject将会遍历X文件中的所有高层模板(top-level template),然后返回一个IDirectXFileData接口。IDirectXFileData用来在X文件中获取单个模型中的数据,使用的函数类似于IDirectXFileEnumObject::GetNextDataObject()。IDirectXFileData:: GetNextObject()遍历所有的子模板,然后返回一个IDirectXFileData接口。方法IDirectXFileData::GetData()用于获取来自模板的数据,但在可以获取数据之前,需要知道模板的类型。方法IDirectXFileData::GetID返回模板的GUID(global unique identifier)。例如,如果模板是Frame模板,那么GetID将会返回TID_D3DRMFrame,这在DX的头文件里有定义。如果需要模板实例的名字,GetName可以做到这一点。

在X文件中,将会发现一个GUID为TID_D3DRMMesh的模板,这意味着模板里面存有一个mesh。函数D3DXLoadSkinMeshFromXof将会加载skin mesh和所有其他的补充性数据,只需要向它传递一个IDirectXFileData的指针,用来存储得到的数据。

D3DXLoadSkinMeshFromXof返回给一个指向ID3DXSkinMesh对象的指针,这个对象含有skin mesh。在内部这个对象把mesh数据分组存储,每一组都由不同的骨骼进行转换(transform)。这个函数也返回一个被skin mesh使用的材质数据。需要把skin mesh和骨骼连接起来。D3DXLoadSkinMeshFromXof返回一个缓冲区,这个缓冲区包含所有影响到这个mesh的骨骼的名字,这个方法还返回另一个包含它们的变换的缓冲区。用名字来为特定的骨骼遍历所有的帧层次,骨骼转换会令人混淆,转换应该被包含在帧里面而不是这里。实际上,这个转换是骨骼的偏移量。什么是骨骼偏移量呢?skin mesh里所有的顶点是相对一个原点存储的,这个原点是mesh的原点而不是骨骼的自身坐标系原点,这就意味着要想得到骨骼对mesh的影响,应该用骨骼的当前变换和骨骼的原始变换之差使mesh变形。换句话说,应该把顶点转换到骨骼的局部坐标空间,然后再用新的转换把它们转换回mesh坐标空间。例如,假定有一个骨骼在(0, 50, 0),有一个顶点在(0, 51, 0),又假定这个顶点只受这个骨骼的影响,如果把骨骼从它的原始位置移到新的位置(0, 51, 0),顶点就应该移到(0, 52, 0),但是如果简单地把顶点乘以骨骼的变换,则顶点将会得到新的位置(0, 102, 0),这是个错误的位置。所以,用骨骼偏移量矩阵把顶点从它的原点转换到一个相对于骨骼的新位置。新位置是(0, 1, 0),这将由骨骼的当前矩阵转换到新的位置,即(0, 52, 0)。实际上,当使用一个骨骼时,用偏移量矩阵乘以它的当前的转换矩阵得到的结果作为世界变换矩阵。

回到ID3DXSkinMesh对象,这个对象持有skin mesh的原始形式数据。这个对象没有渲染skin mesh的任何功能,所以首先需要将这个mesh转换到ID3DXMesh对象。ConvertToBlendedMesh可以完成这个转换工作。从ConvertToBlendedMesh函数转换得来的ID3DXMesh对象和前者有不同点,它的顶点包含blending weights,所以需要打开vertex blending,然后在调用DrawSubset之后设置骨骼矩阵。正如前面所讲到的,mesh将会被分成几组子集(subset),每一个子集应该用特定的材质和特定的骨骼集合去渲染。结构D3DXBONECOMBINATION为每一个mesh子集指定材质,这个结构的一个队列(数组)也由ConvertToBlendedMesh函数中得到,需要做的是遍历这个数组,设置材质和骨骼,然后把材质的索引传给ID3DXMesh的DrawSubset进行渲染。

现在可以设计代码来实现骨骼动画了,其类结构如图5-18所示。

图5-18 骨骼动画中用到的类结构

CObject的目的在于提供一个树的机制:任何一个派生自CObject的对象都有链成一棵树的能力。CFrameNode是场景层次的构造元素而CMeshNode本身则拥有mesh。CMeshNode被包含在CFrameNode里面,而CFrameNode又被包含在CSkinMesh里面。整个场景由CSkinMesh开始,因为它拥有根帧(root frame)。所有与skin mesh相关的操作都将在CSkinMesh里面初始化。相应地,CSkinMesh将按需要把控制交给场景层次。因此,主程序将只是与CSkinMesh打交道,CFrameNode和CMeshNode将由CSkinMesh间接操作。

下面的伪码算法说明了场景是怎样从X文件基础上建立的。

【例5-6】通过X文件载入具有动画信息的模型(伪码):

CSKinMesh::CReate()

Begin

Initialize X file API

Register D3DRM templates

Open the X file

For every top level template in the X file

Begin

    Retrieve the X file data object

    Pass the data object to RootFrame.Load

End

Link the bones to the skin mesh(es)

End

CFrameNode::Load()

Begin

    Check the type of the data object

    If the type is Mesh

    Begin

        Create new CMeshNode object

        Attach the new object to the frame

    Pass the data object to CMeshNode::Create of the new mesh

    End

    Else if type is FrameTransformMatrix

        Load the transform matrix

    Else if type is Frame

    Begin

        Create the new CFrameNode object

        Attach the new object to this frame

        Set the name of the child frame to the name of the template

        For every child template of the current

        Begin

            Retrieve the X file data object

            Pass it to newframe.load

        End

    End

End

CMeshNode::Create()

Begin

    Set the name of the object to the name of the template

    Load the skin mesh

    Generate blended mesh from this skin mesh object

    Load materials

End

建立skin mesh之后,就可以开始渲染了。渲染操作由两个阶段构成。在第一个阶段里,计算骨骼的世界矩阵(通过矩阵相乘),并把它存储在CMeshNode对象里。在第二个阶段里面,skin mesh将被渲染。下面这个伪码算法显示了这个渲染过程。

【例5-7】渲染一个具有骨骼动画的模型(伪码):

CSkinMesh::Render()

Begin

    Calculate the world matrix of all the frames

    Call CMeshNode::Render of all mesh nodes in the hierarchy

End

CMeshNode::Render

Begin

    Enable vertex blending

    For every subset in the skin mesh

    Begin

        Set the bones’ transformation matrices to the device

        Set the material

        Render

    End

    Set vertex blending back disabled

End

X文件里的动画不是skin mesh独有的,它们对X文件里的任何的帧都是适用的。X文件存储了关键帧而程序则应用线性插值的方法产生中间帧。有4种动画关键帧:转动、缩放、位置、矩阵。函数D3DXQuaternionSlerp可以实现这种插值。在X文件里有专门的模块存储动画信息。

为了实现skin mesh,需要增加一个新的类。把这个类命名为CAnimationNode,这个类拥有帧码和指向目标帧的指针,这个类同样包含一个SetTime函数。这个函数会用从AnimationKey得到的新的时间值更新目标帧的变换矩阵。CAnimationNode的每 一个实例都将拥有Animation模板的一个实例。图5-19显示出代码的新的设计。

下面的伪码设计了相应的新算法。

【例5-8】对载入模型算法的改进(伪码):

CSkinMesh::Create()

Begin

    Initialize X file API

    Register D3DRM templates

    Open the X file

    For every top level template in the X file

    Begin

        Retrieve the X file data object

        Pass the data object to RootFrame.Load

    End

    Link the bones to the skin mesh(es)

    Link the bones to the animations

End

CFrameNode::Load()

Begin

    Check the type of the data object

    If the type is Mesh

    Begin

        Create new CMeshNode object

        Attach the new object to the frame

        Pass the data object to CMeshNode::Create of the new mesh

    End

    Else if type is FrameTransformationMatrix

        Load the transformation matrix

    Else if type is Frame

    Else if type is Animation

        Instruct CSkinMesh to load the new animation

    Begin

        Create new CFrameNode object

        Attach the new object to this frame

        Set the name of the child frame to the name of the template

        For every child template of the current

        Begin

            Retrieve the X file data object

            Pass it to newframe.Load

        End

    End

End

CSkinMesh::LoadAnimation()

Begin

    Create new CAnimationNode object

    Attach the new object to the link list

    For every child template

        Call CAnimationNode::Load for the new animation object

End

CAnimationNode::Load()

Begin

    Check the type of the data object

    If the type is a reference

    Begin

        Get the referenced template, which is a frame template

        Get the name of it

        Store the name

    End

    Else if type is data

    Begin

        Check the type of the animation key

        Load the key accordingly

    End

End

下面的SetTime函数就是展现动画能力的地方,CSkinMesh::SetTime 简单地调用动画对象的SetTime函数。

【例5-9】对渲染模型算法的封装(伪码):

CAnimationNode::SetTime()

Begin

    If a matrix key is available

    Begin

        Get the nearest matrix to the given time

        Set it to the target frame

    End

    Else

    Begin

        Prepare an identity matrix called TransMat

        If a scale key is available

        Begin

            Calculate the accurate scale value

            Prepare a scale matrix for this scale value

            Append the matrix to TransMat

        End

        If a rotation key is available

        Begin

            Calculate the accurate rotation quaternion

            Prepare a rotation matrix from this value

            Append the matrix to TransMat

        End

        If a position key is available

        Begin

            Calculate the accurate position value

            Prepare a matrix for it

            Append the matrix to TransMat

        End

        Set TransMat to the target frame

    End

End

你可能感兴趣的:(object,存储,animation,Matrix,Direct3D,网格)