MD2帧动画模型载入._第1页
MD2帧动画模型载入._第2页
免费预览已结束,剩余18页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、1. MD2 文件简介MD2 文件是 Quake2 专用模型文件。他是基于帧动画实现模型的 动态活动的(播放多个连续的帧来实现 3D 模型的动态展示)。因此, 一个MD2 文件,实际上是有多个帧组成的 MD2 模型文件(* md2) 以及对应的纹理文件(*. pcx ,*bmp 等)组成。如图:模型tris. ID2砧图嗣.xh 完藝业示的模型其中 MD2 模型文件(*. md2)又有两部分组成,头文件和数据区头文件的格式是固定的,定义了 MD2 文件的基本信息,如文件的标志,版本号,MD2 文件中含有几个帧等信息。头文件结构如下(对每个 MD2 文件都是一样的):/* MD2 文件头*/st

2、ruct tMd2Headerintmagic;/*文件标志*/其值必须为 IDP2intversion;/*文件版本号*/其值为 8intskinWidth;/*纹理宽度*/intskinHeight;/*纹理高度*/intframeSize;/*每一帧的字节数*/intnumSkins;/*纹理数目*/intnumVertices;/*顶点数目(每一帧中都是一样的)*/intnumTexCoords;/*纹理坐标数目*/intnumTriangles;/*三角行数目*/intnumGlCommands;/* gl 命令数目*/intnumFrames;/*总帧数*/int offsetSk

3、ins;/* 纹理的偏移位置 */int offsetTexCoords;/* 纹理坐标的偏移位置 */int offsetTriangles;/* 三角形索引的偏移位置 */int offsetFrames;/* 第一帧的偏移位置 */int offsetGlCommands;/* OPenGL 命令的偏移位置 */int offsetEnd;/* 文件结尾偏移位置 */;而对于 MD2 文件的数据区,则是不同的 MD2 有不同的大小(很明 显,不同的模型所含的帧数,纹理数目等数据是不同的)。对于读取 MD2 的数据区时还应该注意,由于MD2 原文件对帧的顶点信息(将float 存储为 BY

4、TE 型 ) , 纹理坐标信息 (int 存储为 short) 都进行 了压缩,因此在读取上面两个信息时,都要进行解压缩。他们的解压公式为:帧的顶点解压 :x = 缩放比例 * v0 + 偏移量;y = 缩放比例 * v1 + 偏移量z = 缩放比例 * v2 + 偏移量 其中缩放比例,偏移量在帧结构体中定义。纹理坐标解压 :Like for vertices, data is compresed. Here we use short (2 bytes) instead offloat (4 bytes) for storing texture coordinates. But to use

5、them, we mustconvert them to float because texture coordinates range from 0.0 to 1.0,and if we kept short values, we could have only 0 or 1 and any intermediatevalue! So howto uncompress them? Its quite simple. Divide the short value by the texturesize :公式如下: RealSTi.s = (float) texCoordi.s / header

6、.skinwidth; RealSTi.t= (float)texCoordi.t / header.skinheight;2. 读取 MD2 文件前面说过,MD2 文件是基于帧动画的模型文件,因此,一个 *.md2 文件实际上是一个有许多帧的集合体(每个帧又由许多三角形组成一 个网络,每个三角形又由 3 的顶点组成,每个顶点又由 (x,y,z) 组成)。 对于MD2 文件的读取,实质上是我们把 MD2 文件中的帧一个个读取到 我们自定义的 MD2 文件对象中。 当我们把所有的帧都统一存储在我们 定义的 MD2文件对象(*pModel)后,我们对于 MD2 文件的读取就完成 了。但事实是这样的

7、吗,当然不是!我们此时只是存储了一个个静止 的帧(其实就是一个个摆出静止动作的 3D 模型),我们下面的任务, 就是区分这些帧都是属于那些动作的, (如 016 帧属于跑步的动作, 1720 帧属于射击的动作, 2032 帧属于敬礼的动作) 并将它们都链 接到MD2文件对象 (*pModel)的帧信息链表中(在程序中我们使用 ParseAnimations (t3Dmodel *pModel)来完成对帧分类的功能 )。完成 了对帧分类的任务后,我们就可以在程序中运用特定的算法,让 MD2 文件对象对特定的动作命令做出响应。(如对射击命令,他会自动的找到射击的开始帧 17,并播放 1720 帧)

8、。此处对于播放帧动画,还有一点要注意,由于每个帧都是一个个 静止的 3D 模型,因此在播放动画时,帧之间在切换时可能会产生抖 动或很僵硬的感觉, 因此需要 在帧之间进行插值运算 ,即在开始帧与 结束帧之间自动生成新的帧 (类似 flash 里自动生成的补间动画 ) 。帧间的插值数学公式为 ( 在程序中,重点是获得 t 值)p(t) = p(0) + t*(p(1)-p(0)其中: p(t) 为时刻 t 时模型上各点的位置p(0) 为动作开始 ( 如动作跑步 ) 时模型上各点的位置p(1)为动作结束时模型上各点的位置t 是 01 之间的数至此,我们已经完成了对帧的导入,以及播放连续帧以实现动画

9、的功能。但此时帧中的 3D 模型还没有进行纹理贴图!此时的动画, 只是运动的灰突突的模型,因此还要给MD2 文件对象贴图。由此,总结出载入 MD2 莫型文件的大体流程:1. 将每个帧载入到 MD2 莫型文件对象 t3DModle 中2. 对已载入的帧按照动作进行分类,并连接在 t3Dmodle 的帧信息链表中,为将来响应各种动作命令做准备3. 对帧中的模型贴图(因为 MD2 每一帧中模型的面和纹理坐标都 是以索引的形式提供的,所以 我们可以只对第一帧中的模型 面,纹理坐标信息进行计算和保存 ,对于其他帧可以引用第一帧的信息,这样可以节省很多内存 )3. MD2 文件载入的代码实现MD2 数据区

10、由四部分构成: 纹理数据, 纹理坐标, 三角形面,帧。因此对 MD2 数据区的读取,要分别定义指向这四块数据区域的指针 及代表这四块数据组成结构的结构体。读取帧数据 首先完成对帧数据区的读取工作,要定义帧的结构体。又由于帧 是由三角形构成的网络, 而三角形又是由顶点构成的, 因此要定义顶 点结构体和向量结构体 (Vector 原来已经定义过,此处直接用 )。注意 原数据中的顶点信息,和纹理坐标信息都需要解压缩, 先定义顶点,顶点有两种:由 MD2 文件中读出的未解压的顶点,解压后转换为程序中帧顶点格 式的顶点未解压的顶点结构 :(未解压用 _t 表示,同样的对于纹理坐标 )struct tMd

11、2Vertex_tBYTE vertex3; / 顶点坐标BYTE normalIndex; / 顶点的法向量 (此处 MD2 文件没有定义法向 /量信息)解压后的顶点结构 :struct tVertexfloat vertex3;float normal3;有了以上对顶点信息进行描述的准备,我们就可以构建帧结构体了。 帧结构体也分为两类:从 MD2 读取出的原始帧结构以及经过解压缩 等操作后我们在程序中实际用到的自定义帧结构。原始帧结构 : (其实更应该是帧中一个顶点的结构。一帧中顶点的总数等于头文件中的 frameSizeo 因此一帧的大小为:sizeof(tMd2Frame)乘以 fra

12、meSize MD2 文件的命名规则在这里有点乱.)struct tMd2Framefloat scale3;/解压因子float translate3;/解压因子char name16;/帧名字tMd2Vertex_t md2Vertices1; /由于是原始帧数据,此处的顶点/结构还是未解压的结构我们在程序中用到的自定义帧结构struct tFramechar name16; /帧名tVertex *pVertices; / 解压后的顶点至此,已经完成了对帧的结构体定义的工作, 下面就是对帧信息进 行载入。void ReadMD2Data()/定义数据缓冲区,来存储帧unsigned ch

13、ar bufferMD2_MAX_FRAMESIZE;/先对四个数据区分配内存m_pSkins = new tMd2Skinm_Header.numSkins;m_pTexCoords = new tMd2TexCoord m_Header.numTexCoords;m_pTriangles = new tMd2Facem_Header.numTriangles;m_pFrame = new tFramem_Header.numFrames; /程/ 序中实际用到/的帧结构指针/将指针定位到帧数据区fseek(m_FilePointer, m_Header.offsetFrames, SEEK

14、_SET);/循环读取每个帧 ,将原文件中的帧信息转存至我们自定义的帧中for(int i = 0; iname);/* 下面几行代码就是对帧顶点的解压缩与转换操作 */ /为自定义的i 帧的顶点分配内存,为下一步解压顶点做准备m_pFramei.pVertices = new tVertex m_Header.numVertices;/并定义指针指向自定义的 i 帧顶点tVertex* pVertices = m_pFramei.pVertices;/循环读取帧中的每个顶点进行解压缩和坐标转换操作for(j = 0; jmd2Verticesj.vertex0 *pFrame-scale0

15、+ pFrame-translate0;/*注意要交换 y, z 轴并将 z 轴反向(也许不用反向)*/pVerticesj.vertex2 = -1* (pFrame-md2Verticesj.vertex1* pFrame-scale1 + pFrame-translate1);pVerticesj.vertex1 = pFrame-md2Verticesj.vertex2 *pFrame-scale2 + pFrame-tra nslate2;OK!至吐匕为止,我们已经把所有的帧信息都存储到我们自定义的 帧结构了。将帧按不同动作分类上一步我们已经把帧的信息都载入到了m_pFrame 中,

16、为了实现帧动画的播放,现在就对 m_pFrame 指针指向的帧进行分类,将它们 分解为不同的动作序列,并记录开始帧与结束帧。对帧进行分类是根据帧的名字来进行的(上一步已通过strcpy(m_pF, pFrame-name 实现了对帧名字的存储)。帧的名字结构如下:动作名动作序号(2 位数)例如:帧 0run 01 从 1 开始帧 1run 02帧 2run 03帧 3run 04帧 4fire 01帧 5fire 02帧 6fire 03那么在对帧进行分类时,只需要分析帧的名字即可,将动作名相 同的帧归为一类,并记录下开始帧和结束帧。此处为了记录下动作信息,需要定义一个动

17、作信息结构体struct tAn imatio nlnfo char strName256; /动作名称int startFrame;起始帧int en dFrame;结束帧/*下面是分类帧的函数*/void ParseAnimation(t3Dmodel *pModle) /pModle 为最终的 3D 模型象tAnimationInfo animation; / 用来存储动作信息string strLastName = “”/* 现在开始依次分析每个帧的名字 */for(int i = 0 ;i m_Header.numFrame;i+)/将 m_pFrame 指针所指向的帧的名字拿出来依

18、次分析 stringstrName = m_pF;/* 分析该名字 */for(int j= 0; j = strName.length() - 2)/将字符串转换为数字 frame = atoi(&strNamej);/清除数字 strName.erase(j, strName.length - j);break; /跳出循环/* 如果该帧的名字与前一个不同或该帧为最后一帧 */if(strName != strLastName | i = = pModel-numObject -1)/* 检查是否为 0 号帧*/if(strLastName != “”) /如果

19、不是 0 号帧/复制帧的名称strcpy(animation.strName, strLastName.c_str(); /设置动作结束帧为 i-1ani mati on.en dFrame = i -1; /将该动作信息添加到动作信息列表中 pModel-pAnimation.push_back(animation);memet(&animation , 0 , sizeof(tAnimationInfo);/动作数加一pModel-numOfAnimations+;/if/ 设置开始帧为 i animation.startFrame = I;/if strLastName = st

20、rName;inttexureId;/纹理 ID自此,我们又完成了把动作信息加入到了模型中的函数。下面剩下的,就是将刚才的帧信息, 动作信息这些数据都保存到模型对象中, 而且 还要进行纹理坐标和顶点对应的处理。 这些最后的整合功能都被写在 了一个函数中,该函数调用了前面两个函数。将信息保存到模型结构中 在将信息保存到模型结构中前,要先定义一个对象信息结构体,其实就是帧结构的加强版,在对象信息结构体中,不光有帧信息,还 包括有纹理,面信息。对象信息结构体 :struct t3Dobjectint numOfVerts; /模型中顶点的数目int numOfFaces; /模型中面的数目int n

21、umTexVertex; / 模型中纹理坐标的数目int materialID;/纹理 IDbool bHasTexture; /该模型是否有纹理映射char strName256; /对象名称Vector3 *pVerts;/对象的顶点Vector3 *pNormals; / 对象的法向量Vector2 *pFace;/对象的面下面是我们最终的 模型信息结构体 struct t3DModelint numOfObjects;/模型中对象数int numOfMaterials;/模型中材质的数目int numOfAnimations;/模型中动作的数目int currentAnim;/动作索引

22、int currentFrame;/帧索引vector pAnimations; / 动作信息链表vectorpMaterials; /材质信息链表vectorp3DObject;/对象信息链表关于材质信息结构体 (tMaterialInfo )的定义struct tMaterialInfochar strName255; / 纹理名称char strFile255; /纹理文件名称BYTE color3;/对象的 RGB 颜色inttexureId;/纹理 ID下面,就开始构造将各种数据保存在模型信息中的函数void ConvertDataStructures(t3DModel *pMode

23、l)int j = 0, i = 0;/先给模型结构对象分配内存memset(pModel, 0 , sizeof(t3DModel);/* 下面开始填充模型信息对象 */pModel-numOfObjects = m_Header.numFrames;/产生动作信息ParseAnimations(pModel);/* 下面填充对象结构,最后再把对象结构加到模型对象链表中 */ for(i= 0; inumOfObjects; i +)/先定义对象结构 t3DObject currentFrame = 0;float uTile;float vTile;float uOffset;float

24、vOffset;/u 重复/v 重复/u 纹理偏移/v 纹理偏移/填充基本的信息currentFrame.numOfVerts = m_Header.numVertices;currentFrame.numOfTexVertex = m_Header.numTexCoords;currentFrame.numFaces = m_Header.numTriangles;/* 将帧的顶点信息读入到对象结构中 */先分配内存currentFrame.pVerts = new Vector3currentFrame.numOfVerts; /*将 前 面 存 储 在 m_pFrame 中 的 帧 的 顶 点 信 息 转 存 到 currentFrame 中 */for(j = 0; j 0) / 超出了第一帧/将对象信息添加到模型对象链表中pModel-pObject.push_back(currentFrame);continue;/* 以下的内容是对于纹理坐标与面的操作, 仅对第一帧才会 执行*/先分配内存currentFrame.pTexVerts=newVector2currentFrame.numTexVertex;currentFrame.pFaces = new tFacecurrentFrame.numOfFaces;/* 解压纹理坐标 */for(j =

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论