作者:i_dovelemon
来源:CSDN
日期:2014 / 9 / 17
主题:3D Format, Milk 3D Shape, Chunk Based System, Skeleton Animation
(文中以红色中字标示的文字,是整个文章的注意项,请读者留心)
引言
在3D游戏领域,艺术家们通过3D建模软件建立自己的艺术品模型和绚丽的3D场景。当我们想要在自己的游戏中使用这些模型的时候,我们就需要将这种模型变成能够被我们的引擎所识别的文件格式。每一个建模软件都有自己的文件格式,不同的游戏也会建立自己的3D模型格式。如此多的格式,给我们带来了很多麻烦。所以在本节,我们先设计一个自己引擎的3D格式,然后在编写一个格式转换的工具,将别的3D格式转换成我们想要的模型的格式。
Chunk Based System File 3D格式
Chunk Based System是指一种将真实数据通过首尾的两个Mark来进行标示的数据结构体。比如,下图这样的结构:
我们真实的需要保存的数据是struct,但是在struct的上面和下面分别有两个标示符,begin(名字只做示例说明,在代码中并不一定就是begin)标记中会存放一个标示符,告知我们下面的struct是什么样的结构,并且一共有多大。在结构保存完毕之后,我们在struct后面加上一个end标记,表示对这块的Chunk已经读取完毕。同样的事情持续的发生在下面的空间中,直到我们将所有要保存的结构都保存完毕为止。同时,我们还需要明确一点,在每一个CHUNK中,我们可以继续嵌套一个CHUNK(这就是Chunk
Based System的强大之处)。
使用这样的结构简单,容易进行读写操作,并且是2进制的文件,更加节省空间。
.CBF 3D格式
在ZFXEngine中,我们使用.CBF文件格式。这个格式是我们自己定义的文件格式类型。我们希望这个格式能够支持如下的几个功能:
- 支持顶点(Vertex)数据
- 支持面(Face)数据
- 支持Mesh数据
- 支持材质
- 支持骨骼动画(Joint)
为此,我们分别定义了如下的几个结构体:
<span style="font-family:Microsoft YaHei;">/** * Define the Chunk Header */ typedef struct _CHUNKHEAD { UCHAR ucIdentifier[4]; // identifier UCHAR ucName[32]; // name UCHAR ucAuthor[32]; // author UCHAR ucEmail[32]; // E-mail UCHAR ucType; // type UCHAR ucVersion; // version ULONG ulNumVertices; // number vertices ULONG ulNumIndices; // number indices ULONG ulNumFaces; // number faces ULONG ulNumMeshs; // number meshes UINT uiNumMaterial; // number material UINT uiNumJoints; // number joints float fAnimationFPS; // FPS float fCurrentTime; // current time UINT uiNumFrames; // number frames UINT uiNumAnimations; // number animations }CHUNKHEAD_S; typedef CHUNKHEAD_S* LPCHUNKHEAD ; // Chunk-Header /** * Define the VERTEX_3F_S */ typedef struct _VERTEX { float fXYZ[3]; // coordinates float fUV0[2]; // texture coordinates 1 float fUV1[2]; // texture coordinates 2 ZFXVector fNormal; // normal vector USHORT usReferences; // references UINT uiBoneID_A; // bone-ID 1 float fWeight_A; // weight 1 UINT uiBoneID_B; // bone-ID 2 float fWeight_B; // weight 2 BYTE byFlags; // flags }VERTEX_3F_S; typedef VERTEX_3F_S* LPVERTEX_3F; /** * Define the FACE */ typedef struct _FACE { ULONG ulIndices[3]; // indices ZFXVector fNormal; // normal vector ULONG ulMeshID; // mesh-ID UINT uiMaterialID; // material-ID BYTE byFlags; // flags }FACE_S; typedef FACE_S* LPFACE; /** * Define the Mesh */ typedef struct _MESH { char cName[32]; // name WORD wNumFaces; // number of faces PWORD pIndices; // face index UINT uiMaterialID; // material ID BYTE byFlags; // flags }MESH_S; typedef MESH_S* LPMESH; /** * Define the Material */ typedef struct _MATERIAL { char cName[32]; // name float fAmbient[4]; // ambient color float fDiffuse[4]; // diffuse color float fSpecular[4]; // specular color float fEmissive[4]; // emissive color float fSpecularPower; // specular power float fTransparency; // transparency char cTexture_1[128];// texture name char cTexture_2[128];// texture name BYTE byFlags; // flags }MATERIAL_S; typedef MATERIAL_S* LPMATERIAL ; /** * Define the Keyframe Rotation */ typedef struct _KF_ROT { float fTime; // time ZFXVector vRotation; // rotation }KF_ROT_S; typedef KF_ROT_S* LPKF_ROT; /** * Define the Keyframe Position */ typedef struct _KF_POS { float fTime; // time ZFXVector vPosition; // position }KF_POS_S; typedef KF_POS_S* LPKF_POS; /** * Define the JOINT */ typedef struct _JOING { char cName[32]; // descriptor char cParentName[32]; // parent descriptor WORD wParentID; // parent - ID ZFXVector vRotation; // rotation ZFXVector vPosition; // position WORD wNumKF_Rotation; // number of rotations WORD wNumKF_Position; // number of positions LPKF_ROT pKF_Rotation; // key frame rotations LPKF_POS pKF_Position; // key frame position bool bAnimated; // animated or not BYTE byFlags; // flags ZFXMatrix sMatrix; // matrix ZFXMatrix sMatrix_absolute; // matrix absolute ZFXMatrix sMatrix_relative; // matrix relative }JOINT_S; typedef JOINT_S* LPJOINT; /** * Define the Animation */ typedef struct _ANIMATION { char cName[64]; // name string float fStartFrame;// start frame float fEndFrame; // end frame bool bActive; // active or not }ANIMATION_S; typedef ANIMATION_S* LPANIMATION;</span>
这些结构体能够完整的描述一个3D模型,并且支持骨骼动画,完全满足我们的要求。
上面所有的格式表示的就是Chunk Based System里面的struct,是需要实际输出到文件中的数据。
除了上面的struct之外,我们还需要进行标记,下面是标记的结构定义:
<span style="font-family:Microsoft YaHei;">/** * Define the CHUNK in the CBF file */ typedef struct _CHUNK { WORD wIdentifier; ULONG ulSize ; }CHUNK_S; typedef CHUNK_S* LPCHUNK; /** * Define the CHUNK Identifier */ #define V1_BEGIN 0x0010 // File Begin #define V1_HEADER 0x0100 // Header #define V1_VERTEX 0x0200 // Vertices #define V1_FACE 0x0300 // Faces #define V1_MESH 0x0400 // Meshes #define V1_MATERIAL 0x0500 // Material #define V1_JOINT 0x0600 // Joints #define V1_JOINT_MAIN 0x0610 // Joints Main #define V1_JOINT_KEYFRAME_ROT 0x0620 // Keyf. - Rotation #define V1_JOINT_KEYFRAME_POS 0x0630 // Keyf. - Position #define V1_ANIMATION 0x0700 // Animation #define V1_END 0x9999 // End-Chunk</span>
Begin标记和End标记的结构体通用一个结构CHUNK_S,在这个结构体中有wIdentifier标示,用于存放在下面定义的标示符,从而来确定是否为end标记或者begin标记。读者请注意,begin标记并不只是V1_BEGIN这个,除了V1_END是作为end标记外,其他的所有标示符都可以用来作为begin标记,仔细理解我这里说的话。
比如,我们可以这样来构建一个Chunk:
<span style="font-family:Microsoft YaHei;">CHUNK_S sChunk; sChunk.wIdentifier = V1_HEADER ; sChunk.ulSize = sizeof(CHUNKHEAD_S) ; writetofile(sChunk); writetofile(header); CHUNK_S eChunk; eChunk.wIdentifer = V1_END ; eChunk.ulSize = 0 ; writetofile(eChunk);</span>
我们并没有强制规定CHUNK的顺序。但是请注意,CHUNKHEAD_S必须作为第一个Chunk保存在.CBF文件中。这是因为当我们读取.CBF文件的时候,我们需要根据CHUNKHEAD_S里面的成员数据来控制后面的读取流程。
上面大部分的结构都是单一的Chunk,在将数据保存到.CBF中的时候,只要简单的将结构体输出即可。但是对于那些结构中含有指针,指针又指向另外的内存的这样的结构,我们就需要使用嵌套的Chunk来保存该结构。
比如,上面结构体中的JOINT_S中,含有对KF_POS_S和KF_ROT_S的指针。我们在对JOINT_S进行序列化操作的时候,如果单单将JOINT_S的结构体输出,那么这两个指针所指向的数据并不会自动的进行序列化操作。所以,我们需要再另外的单独将这两个指针中的内容作为单独的Chunk来保存。同时为了维持,这两个单独的chunk和JOINT_S所形成的CHUNK之间的包含关系,我们就将这两个单独的Chunk保存在JOINT_S所形成的Chunk中,从而利用Chunk
Based Ssytem的可嵌套特性来保存它。
通过上面的描述,希望大家对我们的.CBF文件格式的数据组织方式有一个宏观的认识,具体的细节等我们详细讲解源代码的时候,再来向大家讲述。
Milk3D Shape软件及 Milk3D Shape ASCII文件格式解析
Milk3D Shape是一款免费的3D建模软件。大家可以到Milk3D Shape来进行下载。我第一个支持这种软件导出的格式,是因为它的格式支持骨骼动画,并且相对于3D Max和Maya来说,这款软件的操作更加的简单,快捷。所以,对于像我这样的非3D美工人员来说,非常适合使用。关于更多Milk3D的介绍,请读者自己到官网去了解。
在官网上,我们能够找到很多的插件或者开源代码。并且它也提供了一个C++版本的读取它的.ms3d格式的源码。我下载使用了下,发现它这个源码并没有处理读取纹理坐标,所以没办法使用在我们的引擎中。所以,我们只能够自己来编写自己的格式读取代码,实现最终转换为我们自定义的.CBF文件格式。
由于对.ms3d格式进行解析,在调试和资料不详实的情况下,很难开发出来,所以我们就针对Milk3D Shape导出的Milk3D Shape ASCII的txt文件进行解析,从而达到读取的目的。关于Milk3D ASCII的文件格式可以在它的官网上的MilkShape
3D 1.8.4 SDK处下载得到。下面就是他的文件格式的描述:
<span style="font-family:Microsoft YaHei;">// MilkShape 3D ASCII File Format Specification: 1 box, 1 material, a simple animation // this file format spec is outdated. // total frames Frames: 30 // current frame Frame: 1 // number of meshes Meshes: 1 // mesh: name, flags, material index "Box01" 0 0 // number of vertices 20 // vertex: flags, x, y, z, u, v, bone index 0 -10.375000 10.500000 13.750000 0.000000 0.000000 -1 0 -10.375000 -10.250000 13.750000 0.000000 1.000000 -1 0 10.375000 10.500000 13.750000 1.000000 0.000000 -1 0 10.375000 -10.250000 13.750000 1.000000 1.000000 -1 0 10.375000 10.500000 13.750000 0.000000 0.000000 -1 0 10.375000 -10.250000 13.750000 0.000000 1.000000 -1 0 10.375000 10.500000 -4.500000 1.000000 0.000000 -1 0 10.375000 -10.250000 -4.500000 1.000000 1.000000 -1 0 10.375000 10.500000 -4.500000 0.000000 0.000000 -1 0 10.375000 -10.250000 -4.500000 0.000000 1.000000 -1 0 -10.375000 10.500000 -4.500000 1.000000 0.000000 -1 0 -10.375000 -10.250000 -4.500000 1.000000 1.000000 -1 0 -10.375000 10.500000 -4.500000 0.000000 0.000000 -1 0 -10.375000 -10.250000 -4.500000 0.000000 1.000000 -1 0 -10.375000 10.500000 13.750000 1.000000 0.000000 -1 0 -10.375000 -10.250000 13.750000 1.000000 1.000000 -1 0 -10.375000 10.500000 13.750000 0.000000 1.000000 -1 0 10.375000 10.500000 13.750000 1.000000 1.000000 -1 0 -10.375000 -10.250000 13.750000 0.000000 0.000000 -1 0 10.375000 -10.250000 13.750000 1.000000 0.000000 -1 // number of normals 6 // normal: x, y, z 0.000000 0.000000 1.000000 1.000000 0.000000 0.000000 0.000000 0.000000 -1.000000 -1.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 -1.000000 0.000000 // number of triangles 12 // triangle: flags, vertex index1, vertex index2, vertex index3, normal index1, normal index 2, normal index 3, smoothing group 0 0 1 2 0 0 0 1 0 1 3 2 0 0 0 1 0 4 5 6 1 1 1 2 0 5 7 6 1 1 1 2 0 8 9 10 2 2 2 1 0 9 11 10 2 2 2 1 0 12 13 14 3 3 3 2 0 13 15 14 3 3 3 2 0 12 16 6 4 4 4 3 0 16 17 6 4 4 4 3 0 18 13 19 5 5 5 3 0 13 7 19 5 5 5 3 // number of materials Materials: 1 // material: name "Material01" // ambient 0.200000 0.200000 0.200000 0.800000 // diffuse 0.000000 0.501961 0.752941 0.800000 // specular 0.752941 0.752941 0.752941 0.800000 // emissive 0.000000 0.000000 0.000000 0.800000 // shininess 63.000000 // transparency 0.800000 // color map "D:\Eigene Dateien\Image2.tga" // alphamap "D:\Eigene Dateien\Image1.tga" // number of joints Bones: 3 // name "joint1" // parent name "" // joint: flags, posx, posy, posz, rotx, roty, rotz 0 0.250000 0.000000 0.000000 0.224024 1.570796 0.000000 // number of position keys 2 // position key: time, posx, posy, posz 1.000000 0.000000 0.000000 0.000000 30.000000 0.000000 0.000000 0.000000 // number of rotation keys 2 // rotation key: time, rotx, roty, rotz 1.000000 0.000000 0.000000 0.000000 30.000000 0.000000 0.000000 0.000000 "joint2" "joint1" 5 0.000000 0.000000 20.256172 -1.015218 0.000362 3.141593 3 1.000000 0.000000 0.000000 0.000000 15.000000 0.000000 0.000000 0.000000 30.000000 0.000000 0.000000 0.000000 3 1.000000 0.000000 0.000000 0.000000 15.000000 2.286381 -0.000548 -0.000365 30.000000 0.000000 0.000000 0.000000 "joint3" "joint2" 4 0.000000 0.000000 16.128391 0.000000 0.000000 0.000000 2 1.000000 0.000000 0.000000 0.000000 30.000000 0.000000 0.000000 0.000000 2 1.000000 0.000000 0.000000 0.000000 30.000000 0.000000 0.000000 0.000000 //num of animation Animations: 2 // start frame , end frame , animation name 1 30 "Walk1" 31 60 "Walk2" // you can build the joint matrix: // - rotate an identity matrix by the rotation values of the joint // - set the translation of the matrix to the position values. // the same for key matrices // the key matrices animate the reference matrices, so you have to multiply them, // to get the final local joint matrix. The get the final global joint matrix, // you have to multiply them by their parents.</span>
这个文件清晰的讲述了,在它的ASCII文件格式中各个结构存放的顺序以及各个结构的数据含义。在此就不再赘述。
格式转换程序设计
在有了我们自己定义的.CBF文件和Milk3D导出的ASCII文件之后,我们就可以自己设计一个程序用来将这两个格式进行转换。
在设计的时候,我们想到在未来可能会增加其他的格式,然后转换到.CBF,所以我们抽象一个公共的基类,提供一个需要统一实现的接口,如下所示:
<span style="font-family:Microsoft YaHei;">//----------------------------------------------------------------------------------- // declaration : Copyright (c), by XJ , 2014. All right reserved . // brief : This file will define the converter virtual class. Every real converter // will inherit from this class. // file : ZFXModelConverter.h // date : 2014 / 9 / 15 // author : XJ // version : 1.0 //------------------------------------------------------------------------------------ #pragma once class ZFXModelConverter { public: ZFXModelConverter(){} virtual ~ZFXModelConverter(){} public: /** * Brief : This method will convert the source file to the CBF file * sourceFileName: The file that need to be converted to CBF file * destFileName : The file that will save the CBF file's data * Return : True for success, false for failure */ virtual bool convertToCBF(const char* sourceFileName, const char* destFileName) = 0 ; };// end for ZFXModelConverter</span>
以后一旦我们需要进行增加其他的格式的时候,只要简单的根据这个接口,继承它,然后实现它就可以了。
下面是我们根据Milk ASCII定义的类:
<span style="font-family:Microsoft YaHei;">//--------------------------------------------------------------------------------- // declaration : Copyright (c), by XJ , 2014 . All right reserved . // brief : This file will define the MS3D format converter/ // file : MS3DConverter.h // date : 2014 / 9 / 15 // author : XJ // version : 1.0 //---------------------------------------------------------------------------------- #pragma once #include"ZFXModelConverter.h" #include"ZFXModelStructs.h" #include<fstream> #pragma comment(lib,"ZFX3D.lib") using namespace std ; using namespace ZFXEngine ; class MS3DConverter:public ZFXModelConverter { public: MS3DConverter(); ~MS3DConverter(); public: bool convertToCBF(const char* sourceFileName, const char* destFileName); private: void getHeader(ifstream*); void getMeshes(ifstream*); void getMaterials(ifstream*); void getJoints(ifstream*); void getAnimations(ifstream*); void getVertices(ifstream*, LPMESH); void getNormals(ifstream*, LPMESH); void getFaces(ifstream*, LPMESH); bool writeToCBF(const char* destFileName); bool writeHeader(FILE*); bool writeVertices(FILE*); bool writeMeshes(FILE*); bool writeFaces(FILE*); bool writeMaterials(FILE*); bool writeJoints(FILE*); bool writeAnimations(FILE*); bool writeMainJoints(FILE*, LPJOINT); bool writeKF_Pos(FILE*, LPJOINT); bool writeKF_Rot(FILE*, LPJOINT); private: CHUNKHEAD_S m_sChunkHeader ; // Header LPVERTEX_3F m_lpVertices; // Vertices LPFACE m_lpFaces; // Faces LPMESH m_lpMeshes; // Mesh LPMATERIAL m_lpMaterials; // Materials LPJOINT m_lpJoints ; // Joints LPANIMATION m_lpAnimation; // Animation ZFXVector *m_lpNormals ; // Normals ULONG m_ulCurVerticesNum; // Currrent Vertices ULONG m_ulTotalVerticesNum; // Total Vertices ULONG m_ulCurNormalsNum; // Current Normals ULONG m_ulTotalNormalsNum; // Total Normals ULONG m_ulCurFacesNum ; // Current Faces ULONG m_ulTotalFacesNum; // Total Faces ULONG m_ulCurMeshesNum; // Current Meshes };// end for MS3DConverter</span>
下面是这个类的完整实现代码:
<span style="font-family:Microsoft YaHei;">#include"MS3DConverter.h" #include<stdio.h> MS3DConverter::MS3DConverter() { m_sChunkHeader.fAnimationFPS = 0.0f ; m_sChunkHeader.fCurrentTime = 0 ; m_sChunkHeader.ucType = 0 ; m_sChunkHeader.ucVersion = 0 ; m_sChunkHeader.uiNumAnimations = 0 ; m_sChunkHeader.uiNumFrames = 0 ; m_sChunkHeader.uiNumJoints = 0 ; m_sChunkHeader.uiNumMaterial = 0 ; m_sChunkHeader.ulNumFaces = 0 ; m_sChunkHeader.ulNumIndices = 0 ; m_sChunkHeader.ulNumMeshs = 0 ; m_sChunkHeader.ulNumVertices = 0 ; m_lpVertices = NULL ; m_lpMeshes = NULL ; m_lpFaces = NULL ; m_lpMaterials = NULL ; m_lpJoints = NULL ; m_lpAnimation = NULL ; m_lpNormals = NULL ; m_ulCurVerticesNum = 0 ; m_ulTotalVerticesNum = 0 ; m_ulCurNormalsNum = 0 ; m_ulTotalNormalsNum = 0 ; m_ulCurFacesNum = 0 ; m_ulTotalFacesNum = 0 ; m_ulCurMeshesNum = 0 ; } MS3DConverter::~MS3DConverter() { //Vertices if(m_lpVertices) { free(m_lpVertices); m_lpVertices = NULL ; } //Meshes if(m_lpMeshes) { free(m_lpMeshes); m_lpMeshes = NULL ; } //Faces if(m_lpFaces) { free(m_lpFaces); m_lpFaces = NULL ; } //Materials if(m_lpMaterials) { free(m_lpMaterials); m_lpMaterials = NULL ; } //Joints if(m_lpJoints) { free(m_lpJoints); m_lpJoints = NULL ; } //Animation if(m_lpAnimation) { free(m_lpAnimation); m_lpAnimation = NULL ; } //Normal if(m_lpNormals) { free(m_lpNormals); m_lpNormals = NULL ; } } bool MS3DConverter::convertToCBF(const char* sourceFileName, const char* destFileName) { //Open source File ifstream srcFile; srcFile.open(sourceFileName); if(srcFile.fail()) return false ; //Read the data to model getHeader(&srcFile); getMeshes(&srcFile); getMaterials(&srcFile); getJoints(&srcFile); getAnimations(&srcFile); //Close source File srcFile.close(); //Save some value to chunk header memcpy(m_sChunkHeader.ucAuthor, "ZFXModelConverter", 18) ; memcpy(m_sChunkHeader.ucEmail, "[email protected]",18); memcpy(m_sChunkHeader.ucIdentifier, "CBF", 4); memcpy(m_sChunkHeader.ucName, destFileName, strlen(destFileName)+1); m_sChunkHeader.ucType = 0 ; m_sChunkHeader.ucVersion = 0 ; m_sChunkHeader.ulNumFaces = m_ulTotalFacesNum ; m_sChunkHeader.ulNumIndices = m_ulTotalFacesNum * 3; m_sChunkHeader.ulNumVertices = m_ulTotalVerticesNum ; m_sChunkHeader.fAnimationFPS = 60.0f ; return writeToCBF(destFileName); }// end for convertToCBF void MS3DConverter::getHeader(ifstream* srcFile) { // Variablen init char buffer[128]; size_t length = sizeof(buffer); UINT frames = 0 ; memset(buffer, 0, sizeof(buffer)); //get total frames while(!srcFile->eof()) { //Read a line from the file srcFile->getline(buffer, length); //Check if the line is "Frames: xxx" if(sscanf(buffer,"Frames: %d",&frames) == 1) { m_sChunkHeader.uiNumFrames = frames ; break ; } }// end while //get current frame while(!srcFile->eof()) { //Read a line from the file srcFile->getline(buffer,length); //Check if the line is "Frame: xxx" if(sscanf(buffer, "Frame: %d", &frames) == 1) { m_sChunkHeader.fCurrentTime = frames ; break ; } }// end while }// end for getHeader void MS3DConverter::getMeshes(ifstream* srcFile) { //Variablen init char buffer[128]; size_t length = sizeof(buffer); memset(buffer, 0, length); char meshName[64]; ULONG temp = 0 ; UINT flags = 0 ; int materialIndex = 0 ; //get the number of the mesh while(!srcFile->eof()) { //Read a line srcFile->getline(buffer, length); //Check if the line is "Meshes: xxx" if(sscanf(buffer, "Meshes: %d", &temp) == 1) { m_sChunkHeader.ulNumMeshs = temp ; break ; } }// end while //Allocate the memory for the meshes m_lpMeshes = (LPMESH)malloc(sizeof(MESH_S) * m_sChunkHeader.ulNumMeshs ) ; //get the meshes for( int i = 0 ; i < m_sChunkHeader.ulNumMeshs ; i ++) { //Read a line srcFile->getline(buffer, length); //Save current meshes m_ulCurMeshesNum = i ; //Check if the line is "'MeshName' flags materialindex" if(sscanf(buffer,"%s %d %d",meshName, &flags, &materialIndex) == 3) { m_lpMeshes[i].byFlags = flags ; memcpy(m_lpMeshes[i].cName, meshName+1, strlen(meshName) - 2); m_lpMeshes[i].cName[strlen(meshName) - 2] = '\0'; m_lpMeshes[i].uiMaterialID = materialIndex ; //get the vertices from this mesh getVertices(srcFile, &m_lpMeshes[i]); //get the normals from this mesh getNormals(srcFile, &m_lpMeshes[i]); //get the triangle from this mesh getFaces(srcFile, &m_lpMeshes[i]); //update counter m_ulCurVerticesNum = m_ulTotalVerticesNum ; m_ulCurNormalsNum = m_ulTotalNormalsNum ; m_ulCurFacesNum = m_ulTotalFacesNum ; continue ; } }// end for }// end for getMeshes void MS3DConverter::getVertices(ifstream* srcFile, LPMESH pMesh) { //Variablen init char buffer[128]; size_t length = sizeof(buffer); ULONG numV = 0 ; UINT flags = 0; float x = 0 , y = 0 , z = 0 ; float u = 0 , v = 0 ; UINT boneID = 0 ; //Get the vertice number in this mesh while(!srcFile->eof()) { //read a line srcFile->getline(buffer, length); //Check if this line is "xxx" if(sscanf(buffer, "%d",&numV) == 1) { //Add to total vertices number m_ulTotalVerticesNum += numV ; //Allocate the memory for the vertices if(m_lpVertices == NULL) m_lpVertices = (LPVERTEX_3F)malloc(sizeof(VERTEX_3F_S) * numV); else { m_lpVertices = (LPVERTEX_3F)realloc(m_lpVertices, sizeof(VERTEX_3F_S) * m_ulTotalVerticesNum); } if(m_lpVertices == NULL) return ; //read the vertices for(int i = 0 ; i < numV ; i ++) { srcFile->getline(buffer, length); //Check if this line is "flags x y z u v boneindex" if(sscanf(buffer,"%d %f %f %f %f %f %d",&flags, &x, &y, &z, &u, &v, &boneID) == 7) { m_lpVertices[i + m_ulCurVerticesNum].byFlags = flags ; m_lpVertices[i + m_ulCurVerticesNum].fUV0[0] = u ; m_lpVertices[i + m_ulCurVerticesNum].fUV0[1] = v ; m_lpVertices[i + m_ulCurVerticesNum].fWeight_A = 1.0f ; m_lpVertices[i + m_ulCurVerticesNum].fXYZ[0] = x ; m_lpVertices[i + m_ulCurVerticesNum].fXYZ[1] = y ; m_lpVertices[i + m_ulCurVerticesNum].fXYZ[2] = z ; m_lpVertices[i + m_ulCurVerticesNum].uiBoneID_A = boneID ; } }// end for vertices break ; }// end if }// end while }// end for getVertices void MS3DConverter::getNormals(ifstream* srcFile, LPMESH pMesh) { // Variablen init char buffer[128]; size_t length = sizeof(buffer); ULONG numN = 0 ; float x = 0 , y = 0 , z = 0 ; // read the normals from the file while(!srcFile->eof()) { //read a line srcFile->getline(buffer, length); //Check if the line is "xxxx" if(sscanf(buffer,"%d",&numN) == 1) { //Add to total normals m_ulTotalNormalsNum += numN ; //Alocate the memory for the normals if(m_lpNormals == NULL) m_lpNormals = (ZFXVector*)malloc(sizeof(ZFXVector) * numN); else { m_lpNormals = (ZFXVector*)realloc(m_lpNormals, sizeof(ZFXVector) * m_ulTotalNormalsNum); } if( m_lpNormals == NULL) return ; //read the normals for(int i = 0 ; i < numN ; i ++) { srcFile->getline(buffer, length); //Check if the line is "x y z" if(sscanf(buffer, "%f %f %f", &x, &y, &z) == 3) { m_lpNormals[i + m_ulCurNormalsNum].set(x,y,z); } }// end for break ; } }// end for while }// end for getNormals void MS3DConverter::getFaces(ifstream* srcFile, LPMESH pMesh) { // Variablen init char buffer[128]; size_t length = sizeof(buffer); ULONG numF = 0 ; UINT flags = 0 ; UINT v1 = 0 , v2 = 0, v3 = 0 ; UINT n1 = 0 , n2 = 0, n3 = 0 ; UINT group = 0 ; //read the triangle from the file while(!srcFile->eof()) { // read a line srcFile->getline(buffer, length); // check if the line is "xxx" if(sscanf(buffer, "%d", &numF) == 1) { //Add to total faces m_ulTotalFacesNum += numF ; //Save to the mesh pMesh->wNumFaces = numF ; //Allocate the memory for the faces if(m_lpFaces == NULL) m_lpFaces = (LPFACE)malloc(sizeof(FACE_S) * numF); else { m_lpFaces = (LPFACE)realloc(m_lpFaces, sizeof(FACE_S) * m_ulTotalFacesNum) ; } if( m_lpFaces == NULL) return ; // read the faces for( int i = 0 ; i < numF ; i ++) { //read a line srcFile->getline(buffer, length); //check if the line is "flags v1 v2 v3 nor1 nor2 nor3 group" if(sscanf(buffer, "%d %d %d %d %d %d %d %d", &flags, &v1, &v2, &v3, &n1, &n2, &n3, &group) == 8) { m_lpFaces[i + m_ulCurFacesNum].byFlags = flags ; m_lpFaces[i + m_ulCurFacesNum].uiMaterialID = pMesh->uiMaterialID ; m_lpFaces[i + m_ulCurFacesNum].ulIndices[0] = v1 + m_ulCurVerticesNum ; m_lpFaces[i + m_ulCurFacesNum].ulIndices[1] = v2 + m_ulCurVerticesNum ; m_lpFaces[i + m_ulCurFacesNum].ulIndices[2] = v3 + m_ulCurVerticesNum ; m_lpFaces[i + m_ulCurFacesNum].ulMeshID = m_ulCurMeshesNum ; //Save the normals for the vertices m_lpVertices[v1 + m_ulCurVerticesNum].fNormal = m_lpNormals[ n1 + m_ulCurNormalsNum]; m_lpVertices[v2 + m_ulCurVerticesNum].fNormal = m_lpNormals[ n2 + m_ulCurNormalsNum]; m_lpVertices[v3 + m_ulCurVerticesNum].fNormal = m_lpNormals[ n3 + m_ulCurNormalsNum]; //Save the normals for the faces ZFXVector ver0; ver0.set(m_lpVertices[v1 + m_ulCurVerticesNum].fXYZ[0], m_lpVertices[v1 + m_ulCurVerticesNum].fXYZ[1], m_lpVertices[v1 + m_ulCurVerticesNum].fXYZ[2]); ZFXVector ver1 ; ver1.set(m_lpVertices[v2 + m_ulCurVerticesNum].fXYZ[0], m_lpVertices[v2 + m_ulCurVerticesNum].fXYZ[1], m_lpVertices[v2 + m_ulCurVerticesNum].fXYZ[2]); ZFXVector ver2 ; ver2.set(m_lpVertices[v3 + m_ulCurVerticesNum].fXYZ[0], m_lpVertices[v3 + m_ulCurVerticesNum].fXYZ[1], m_lpVertices[v3 + m_ulCurVerticesNum].fXYZ[2]); ZFXVector v10 ; v10 = ver1 - ver0 ; ZFXVector v20 ; v20 = ver2 - ver0 ; ZFXVector normals; normals.cross(v10, v20); normals.normalize(); m_lpFaces[i + m_ulCurFacesNum].fNormal = normals ; } }// end for break ; }//end if }// end for while }// end for getFaces void MS3DConverter::getMaterials(ifstream* srcFile) { //Variablen init char buffer[128]; size_t length = sizeof(buffer); ULONG numM = 0 ; char materialName[64] ; float r = 0 , g = 0 , b = 0, a = 0 ; float shiness = 0 ; char texName[64]; //read the number of materials while(!srcFile->eof()) { srcFile->getline(buffer, length); //check if the line is "Materials: xxx" if(sscanf(buffer,"Materials: %d", &numM) == 1) { //Save to the chunk header m_sChunkHeader.uiNumMaterial = numM ; //Allocate the memory for the materials if(numM == 0) return ; m_lpMaterials = (LPMATERIAL)malloc(sizeof(MATERIAL_S) * numM); if(m_lpMaterials == NULL) return ; //read the materials from the file for(int i = 0 ; i < numM ; i ++) { //check if the line is "materialname" srcFile->getline(buffer, length); if(sscanf(buffer,"%s", materialName) == 1) { memcpy(m_lpMaterials[i].cName, materialName + 1, strlen(materialName) - 2) ; m_lpMaterials[i].cName[strlen(materialName) - 2] = '\0'; } //check the ambient color srcFile->getline(buffer, length); if(sscanf(buffer,"%f %f %f %f",&r, &g, &b, &a) == 4) { m_lpMaterials[i].fAmbient[0] = r ; m_lpMaterials[i].fAmbient[1] = g ; m_lpMaterials[i].fAmbient[2] = b ; m_lpMaterials[i].fAmbient[3] = a ; } //check the diffuse color srcFile->getline(buffer, length); if(sscanf(buffer,"%f %f %f %f",&r, &g, &b, &a) == 4) { m_lpMaterials[i].fDiffuse[0] = r ; m_lpMaterials[i].fDiffuse[1] = g ; m_lpMaterials[i].fDiffuse[2] = b ; m_lpMaterials[i].fDiffuse[3] = a ; } //check the specular srcFile->getline(buffer, length); if(sscanf(buffer,"%f %f %f %f",&r, &g, &b, &a) == 4) { m_lpMaterials[i].fSpecular[0] = r ; m_lpMaterials[i].fSpecular[1] = g ; m_lpMaterials[i].fSpecular[2] = b ; m_lpMaterials[i].fSpecular[3] = a ; } //check the emissive srcFile->getline(buffer, length); if(sscanf(buffer,"%f %f %f %f",&r, &g, &b, &a) == 4) { m_lpMaterials[i].fEmissive[0] = r ; m_lpMaterials[i].fEmissive[1] = g ; m_lpMaterials[i].fEmissive[2] = b ; m_lpMaterials[i].fEmissive[3] = a ; } //check the shiness srcFile->getline(buffer,length); if(sscanf(buffer,"%f", &shiness) == 1) { m_lpMaterials[i].fSpecularPower = shiness ; } //check the transparency srcFile->getline(buffer,length); if(sscanf(buffer,"%f", &shiness) == 1) { m_lpMaterials[i].fTransparency = shiness ; } //check the diffuse map srcFile->getline(buffer, length); if(sscanf(buffer,"%s", texName) == 1) { memcpy(m_lpMaterials[i].cTexture_1, texName + 1, strlen(texName) - 2); m_lpMaterials[i].cTexture_1[strlen(texName) - 2] = '\0'; } //check the alpha map srcFile->getline(buffer, length); if(sscanf(buffer,"%s", texName) == 1) { memcpy(m_lpMaterials[i].cTexture_2, texName + 1, strlen(texName) - 2); m_lpMaterials[i].cTexture_2[strlen(texName) - 2] = '\0'; } }// end for break ; }// end if }// end for while }// end for getMaterials void MS3DConverter::getJoints(ifstream* srcFile) { //Variablen init char buffer[128]; size_t length = sizeof(buffer); ULONG numJ = 0 ; char jointName[64]; UINT flags; float x = 0, y = 0 , z = 0; float rx = 0 ,ry = 0, rz = 0 ; ULONG numPosK = 0 , numRotK = 0; float time = 0 ; // read the joints from the file while(!srcFile->eof()) { //read a line srcFile->getline(buffer, length); //Check if the line is "Bones: xxx" if(sscanf(buffer, "Bones: %d", &numJ) == 1) { //Save the number of joints to chunk header m_sChunkHeader.uiNumJoints = numJ ; //Check if there is some joint if(numJ == 0) return ; //Allocate the memory for the joints m_lpJoints = (LPJOINT)malloc(sizeof(JOINT_S) * numJ); if(m_lpJoints == NULL) return ; memset(m_lpJoints, 0, sizeof(JOINT_S) * numJ); //read the joint for(int i = 0 ; i < numJ ; i ++) { //check if the line is "jointsname" srcFile->getline(buffer, length); if(sscanf(buffer,"%s", jointName) == 1) { memcpy(m_lpJoints[i].cName, jointName + 1, strlen(jointName) - 2); m_lpJoints[i].cName[ strlen(jointName) - 2] = '\0'; memset(jointName,0, sizeof(jointName)); } //check if the line is "parentName" srcFile->getline(buffer, length); if(sscanf(buffer, "%s", jointName) == 1) { memcpy(m_lpJoints[i].cParentName, jointName + 1, strlen(jointName) - 2); m_lpJoints[i].cParentName[ strlen(jointName) - 2] = '\0'; memset(jointName, 0, sizeof(jointName)); } //check if the line is "flags x y z rx ry rz" srcFile->getline(buffer, length); if(sscanf(buffer, "%d %f %f %f %f %f %f", &flags, &x, &y, &z, &rx, &ry, &rz) == 7) { m_lpJoints[i].byFlags = flags ; m_lpJoints[i].vPosition.set(x,y,z); m_lpJoints[i].vRotation.set(rx,ry,rz); } //read the pos_keframe srcFile->getline(buffer, length); if(sscanf(buffer, "%d", &numPosK) == 1) { //Save the number of pos keyframe m_lpJoints[i].wNumKF_Position = numPosK ; //Allocate the memory for the pos keyframe m_lpJoints[i].pKF_Position = (LPKF_POS)malloc(sizeof(KF_POS_S)*numPosK); if(m_lpJoints[i].pKF_Position == NULL) return ; //read the pos key frame for(int j = 0 ; j < numPosK ; j ++) { srcFile->getline(buffer, length); if(sscanf(buffer, "%f %f %f %f", &time, &x, &y, &z) != 4) return ; m_lpJoints[i].pKF_Position[j].fTime = time ; m_lpJoints[i].pKF_Position[j].vPosition.set(x,y,z); }// end for }// end if //read the rotation keframe srcFile->getline(buffer, length); if(sscanf(buffer, "%d", &numRotK) == 1) { //Save the number of pos keyframe m_lpJoints[i].wNumKF_Rotation = numRotK ; //Allocate the memory for the pos keyframe m_lpJoints[i].pKF_Rotation = (LPKF_ROT)malloc(sizeof(KF_ROT_S)*numRotK); if(m_lpJoints[i].pKF_Rotation == NULL) return ; //read the pos key frame for(int j = 0 ; j < numRotK ; j ++) { srcFile->getline(buffer, length); if(sscanf(buffer, "%f %f %f %f", &time, &rx, &ry, &rz) != 4) return ; m_lpJoints[i].pKF_Rotation[j].fTime = time ; m_lpJoints[i].pKF_Rotation[j].vRotation.set(rx,ry,rz); }// end for }// end if }// end for break ; }// end if }// end for while }// end for getJoints void MS3DConverter::getAnimations(ifstream* srcFile) { //Variablen init char buffer[128]; size_t length = sizeof(buffer); ULONG numA = 0 ; ULONG start = 0, end = 0 ; char animationName[64]; //read the animation while(!srcFile->eof()) { //read a line srcFile->getline(buffer, length); //check if the line is "Animations: xxx" if(sscanf(buffer,"Animations: %d", &numA) == 1) { //Save the number of animation to chunk header m_sChunkHeader.uiNumAnimations = numA ; //Allocate the memory for the animation m_lpAnimation = (LPANIMATION)malloc(sizeof(ANIMATION_S) * numA); if(m_lpAnimation == NULL) return ; //read the animation for(int i = 0 ; i < numA ; i ++) { //read a line srcFile->getline(buffer, length); //check if the line is "s e animationname" if(sscanf(buffer, "%d %d %s", &start, &end, animationName) == 3) { m_lpAnimation[i].fStartFrame = start ; m_lpAnimation[i].fEndFrame = end ; memcpy(m_lpAnimation[i].cName,animationName + 1, strlen(animationName) - 2); m_lpAnimation[i].cName[strlen(animationName) - 2] = '\0'; m_lpAnimation[i].bActive = false ; memset(animationName, 0, sizeof(animationName)); }// end if }// end for break ; } }// end for while }// end for getAnimations bool MS3DConverter::writeToCBF(const char* fileName) { //Open the file FILE * file = NULL ; file = fopen(fileName,"wb"); if(file == NULL) return false ; //Write the begin chunk to the cbf file CHUNK_S bChunk; bChunk.wIdentifier = V1_BEGIN; bChunk.ulSize = 0 ; fwrite(&bChunk, sizeof(CHUNK_S), 1, file); //Write to the CBF file if(!writeHeader(file)) { fclose(file); return false ; } if(!writeVertices(file)) { fclose(file); return false ; } if(!writeMeshes(file)) { fclose(file); return false ; } if(!writeFaces(file)) { fclose(file); return false ; } if(!writeMaterials(file)) { fclose(file); return false ; } if(!writeJoints(file)) { fclose(file); return false ; } if(!writeAnimations(file)) { fclose(file); return false ; } //write the end chunk to the cbf file CHUNK_S eChunk; eChunk.wIdentifier = V1_END ; eChunk.ulSize = 0 ; fwrite(&eChunk, sizeof(CHUNK_S), 1 , file); //Close the file fclose(file); return true ; }// end for writeToCBF bool MS3DConverter::writeHeader(FILE* file) { //Variablen init ULONG writeNum = 0 ; //create the start chunk CHUNK_S sChunk; sChunk.wIdentifier = V1_HEADER ; sChunk.ulSize = sizeof(CHUNKHEAD_S); //write the start chunk writeNum = fwrite(&sChunk, sizeof(CHUNK_S), 1, file); if(writeNum != 1) return false ; //write the header chunk writeNum = fwrite(&m_sChunkHeader, sizeof(CHUNKHEAD_S), 1, file); if(writeNum != 1) return false ; //create the end chunk CHUNK_S eChunk; eChunk.wIdentifier = V1_END ; eChunk.ulSize = 0 ; //write the end chunk writeNum = fwrite(&eChunk, sizeof(CHUNK_S), 1, file); if(writeNum != 1) return false ; return true ; }// end for writeHeader bool MS3DConverter::writeVertices(FILE* file) { //Variablen init ULONG writeNum = 0 ; //create the start chunk CHUNK_S sChunk ; sChunk.wIdentifier = V1_VERTEX ; sChunk.ulSize = sizeof(VERTEX_3F_S) * m_sChunkHeader.ulNumVertices ; //write the start chunk writeNum = fwrite(&sChunk, sizeof(CHUNK_S), 1, file); if(writeNum != 1) return false ; //write the vertices writeNum = fwrite(m_lpVertices, sizeof(VERTEX_3F_S), m_sChunkHeader.ulNumVertices, file); if( writeNum != m_sChunkHeader.ulNumVertices) return false ; //create the end chunk CHUNK_S eChunk; eChunk.wIdentifier = V1_END ; eChunk.ulSize = 0 ; //write the end chunk writeNum = fwrite(&eChunk, sizeof(CHUNK_S), 1, file); if(writeNum != 1) return false ; return true ; }// end for writeVertices bool MS3DConverter::writeMeshes(FILE* file) { //Variablen init ULONG writeNum = 0 ; //Check if there is some meshes if(m_sChunkHeader.ulNumMeshs == 0) return true ; //create the start chunk CHUNK_S sChunk ; sChunk.wIdentifier = V1_MESH ; sChunk.ulSize = m_sChunkHeader.ulNumMeshs * sizeof(MESH_S); //write the start chunk writeNum = fwrite(&sChunk, sizeof(CHUNK_S), 1, file); if(writeNum != 1) return false ; //write the meshes chunk writeNum = fwrite(m_lpMeshes, sizeof(MESH_S), m_sChunkHeader.ulNumMeshs, file); if(writeNum != m_sChunkHeader.ulNumMeshs) return false ; //create the end chunk CHUNK_S eChunk; eChunk.wIdentifier = V1_END ; eChunk.ulSize = 0 ; writeNum = fwrite(&eChunk, sizeof(CHUNK_S), 1, file); if(writeNum != 1) return false ; return true ; }// end for writeMeshes bool MS3DConverter::writeFaces(FILE* file) { //Variablen init ULONG writeNum = 0 ; //create the start chunk CHUNK_S sChunk ; sChunk.wIdentifier = V1_FACE ; sChunk.ulSize = sizeof(FACE_S) * m_sChunkHeader.ulNumFaces ; //write the start chunk writeNum = fwrite(&sChunk, sizeof(CHUNK_S), 1, file); if(writeNum != 1) return false ; //write the faces chunk writeNum = fwrite(m_lpFaces, sizeof(FACE_S), m_sChunkHeader.ulNumFaces, file); if( writeNum != m_sChunkHeader.ulNumFaces) return false ; //create the end chunk CHUNK_S eChunk ; eChunk.wIdentifier = V1_END ; eChunk.ulSize = 0 ; //write the end chunk writeNum = fwrite(&eChunk, sizeof(CHUNK_S), 1, file); if(writeNum != 1) return false ; return true ; }// end for writeFaces bool MS3DConverter::writeMaterials(FILE* file) { //Variablen init ULONG writeNum = 0 ; //Check if there is some materials if(m_sChunkHeader.uiNumMaterial == 0) return true ; //create the start chunk CHUNK_S sChunk; sChunk.wIdentifier = V1_MATERIAL ; sChunk.ulSize = sizeof(MATERIAL_S) * m_sChunkHeader.uiNumMaterial; //write the start chunk writeNum = fwrite(&sChunk, sizeof(CHUNK_S), 1, file); if(writeNum != 1) return false ; //write the materials writeNum = fwrite(m_lpMaterials, sizeof(MATERIAL_S), m_sChunkHeader.uiNumMaterial, file); if(writeNum != m_sChunkHeader.uiNumMaterial ) return false ; //create the end chunk CHUNK_S eChunk ; eChunk.wIdentifier = V1_END ; eChunk.ulSize = 0 ; //write the end chunk writeNum = fwrite(&eChunk, sizeof(CHUNK_S), 1, file); if(writeNum != 1) return false ; return true ; }// end for writeMaterials bool MS3DConverter::writeJoints(FILE* file) { //Variablen init ULONG writeNum = 0 ; ULONG size = 0 ; //check if there is some animation if(m_sChunkHeader.uiNumJoints == 0) return true ; //create the start chunk CHUNK_S sChunk; sChunk.wIdentifier = V1_JOINT ; //get the size of this chunk for(int i = 0 ; i < m_sChunkHeader.uiNumJoints ; i ++) { size += sizeof(JOINT_S) ; size += sizeof(KF_POS_S) * m_lpJoints[i].wNumKF_Position ; size += sizeof(KF_ROT_S) * m_lpJoints[i].wNumKF_Rotation ; }// end for sChunk.ulSize = size ; //write the start chunk writeNum = fwrite(&sChunk, sizeof(CHUNK_S), 1, file); if(writeNum != 1) return false ; //write the joints chunk for(int i = 0 ; i < m_sChunkHeader.uiNumJoints ; i ++) { //write the main joint if(!writeMainJoints(file, &m_lpJoints[i])) return false ; //write the pos keyframe if(!writeKF_Pos(file, &m_lpJoints[i])) return false ; //write the rot keyframe if(!writeKF_Rot(file, &m_lpJoints[i])) return false ; }// end for //create the end chunk CHUNK_S eChunk ; eChunk.wIdentifier = V1_END ; eChunk.ulSize = 0 ; //write the end chunk writeNum = fwrite(&eChunk, sizeof(CHUNK_S), 1, file); if(writeNum != 1) return false ; return true ; }// end for writeJoints bool MS3DConverter::writeMainJoints(FILE* file, LPJOINT pJoint) { //Variablen init ULONG writeNum = 0 ; //create the start chunk CHUNK_S sChunk; sChunk.wIdentifier = V1_JOINT_MAIN ; sChunk.ulSize = sizeof(JOINT_S); //write the start chunk writeNum = fwrite(&sChunk, sizeof(CHUNK_S), 1, file); if(writeNum != 1) return false ; //write the main joints chunk writeNum = fwrite(pJoint, sizeof(JOINT_S), 1, file); if(writeNum != 1) return false ; //create the end chunk CHUNK_S eChunk ; eChunk.wIdentifier = V1_END ; eChunk.ulSize = 0 ; //write the end chunk writeNum = fwrite(&eChunk, sizeof(CHUNK_S), 1, file); if(writeNum != 1) return false ; return true ; }// end for writeMainJoints bool MS3DConverter::writeKF_Pos(FILE* file, LPJOINT pJoint) { //Variablen init ULONG writeNum = 0 ; //create the start chunk CHUNK_S sChunk; sChunk.wIdentifier = V1_JOINT_KEYFRAME_POS ; sChunk.ulSize = sizeof(KF_POS_S) * pJoint->wNumKF_Position ; //write the start chunk writeNum = fwrite(&sChunk, sizeof(CHUNK_S), 1, file); if( writeNum != 1) return false ; //write the pos keyframe writeNum = fwrite(pJoint->pKF_Position, sizeof(KF_POS_S), pJoint->wNumKF_Position, file); if(writeNum != pJoint->wNumKF_Position) return false ; //create the end chunk CHUNK_S eChunk; eChunk.wIdentifier = V1_END ; eChunk.ulSize = 0 ; //write the end chunk writeNum = fwrite(&eChunk, sizeof(CHUNK_S), 1, file); if(writeNum != 1) return false ; return true ; }// end for writeKF_Pos bool MS3DConverter::writeKF_Rot(FILE* file, LPJOINT pJoint) { // Variablen init ULONG writeNum = 0; //create the start chunk CHUNK_S sChunk; sChunk.wIdentifier = V1_JOINT_KEYFRAME_ROT; sChunk.ulSize = sizeof(KF_ROT_S) * pJoint->wNumKF_Rotation ; //write the start chunk writeNum = fwrite(&sChunk, sizeof(CHUNK_S), 1, file); if(writeNum != 1) return false ; //write the rot keyframe writeNum = fwrite(pJoint->pKF_Rotation, sizeof(KF_ROT_S), pJoint->wNumKF_Rotation, file); if( writeNum != pJoint->wNumKF_Rotation) return false ; //create the end chunk CHUNK_S eChunk; eChunk.wIdentifier = V1_END; eChunk.ulSize = 0 ; //write the end chunk writeNum = fwrite(&eChunk, sizeof(CHUNK_S), 1, file); if(writeNum != 1) return false ; return true ; }// end for writeKF_Rot bool MS3DConverter::writeAnimations(FILE* file) { //Variablen init ULONG writeNum = 0 ; //Check if there is some animation if(m_sChunkHeader.uiNumAnimations == 0) return true ; //create the start chunk CHUNK_S sChunk ; sChunk.wIdentifier = V1_ANIMATION ; sChunk.ulSize = sizeof(ANIMATION_S) * m_sChunkHeader.uiNumAnimations ; //write the start chunk writeNum = fwrite(&sChunk, sizeof(CHUNK_S), 1, file); if(writeNum != 1) return false ; //write the animation writeNum = fwrite(m_lpAnimation, sizeof(ANIMATION_S), m_sChunkHeader.uiNumAnimations, file); if( writeNum != m_sChunkHeader.uiNumAnimations) return false ; //create the end chunk CHUNK_S eChunk ; eChunk.wIdentifier = V1_END ; eChunk.ulSize = 0 ; //write the end chunk writeNum = fwrite(&eChunk, sizeof(CHUNK_S), 1, file); if(writeNum != 1) return false ; return true ; }// end for writeAnimations</span>
由于代码实在太多,这里无法一一解释。大致的思路就是如下的步骤:
1.读取ASCII文件,使用sscanf函数,对读取的字符串进行解析,然后保存在类的成员变量中去
2.文件读取完毕之后,我们构建Chunk,使用fwrite函数对结构体进行输出。
在开发过程中遇到的问题:
对于读取,很简单,只要一个一个结构的解析,然后填充即可。没什么可说的。
但是对于将结构进行序列化的操作,出现了很多错误的地方。
首先,对于结构体进行序列化,往往会有如下的问题:
1.编译器的对齐设定。你定义的结构如果不是严格对齐的话,在将结构体保存到文件中的时候,编译器会自动的给你添加字符,从而是结构对齐。关于结构体对齐的文章,网上比比皆是,这里不再赘述。由于我这里都是使用VS2010进行编译,并且在读取和输出的时候,都是使用同一个结构体,所以这里暂时不考虑结构体对齐的问题。
2.结构体中带指针的问题。将结构体进行序列化时,结构体中的指针并不会跟着一起序列化。但是在上面我们已经讲过了,使用Chunk Based System完全就可以避免这个问题,我们只要将指针中的数据,单独抽取出来进行序列化即可。如果读者细心的读了我上面的代码,就会发现在对MESH_S的处理过程中,我并没有将MESH_S.pIndices这个结构单独抽取出来作为一个Chunk包含在MESH_S的chunk中。这是因为,在后期读取的时候,会根据FACE的描述,来自动的重新构建MESH_S。所以这里就省去了这部分的工作。
除了这两个大的问题之外,还在开发过程发现了fwrite函数在使用"w"控制符打开文件的时候,一旦要输出的数据中有0x0A这个数据,它的前面就会自动的多了0x0D这个数据。上网搜索之后发现,原来fwrite默认是使用ASCII码打开方式,也就是说你输出的数据,fwrite会认为是ASCII数据,但遇到0x0A时,也就是\n字符,编译器会自动的添加\r(0xOD)这个字符,从而构成回车键。明白了这个原因之后,我们只要使用"wb",以二进制的方式进行数据输出,就没有这个问题了。
好了,关于这个格式转换的工具就说到这里。如果有不明白的地方,欢迎大家在博客中指出!!!
下一节,会讲述ZFXEngine如何读取.CBF文件,并且最终将它进行渲染出来。