作者: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是指一种将真实数据通过首尾的两个Mark来进行标示的数据结构体。比如,下图这样的结构:
我们真实的需要保存的数据是struct,但是在struct的上面和下面分别有两个标示符,begin(名字只做示例说明,在代码中并不一定就是begin)标记中会存放一个标示符,告知我们下面的struct是什么样的结构,并且一共有多大。在结构保存完毕之后,我们在struct后面加上一个end标记,表示对这块的Chunk已经读取完毕。同样的事情持续的发生在下面的空间中,直到我们将所有要保存的结构都保存完毕为止。同时,我们还需要明确一点,在每一个CHUNK中,我们可以继续嵌套一个CHUNK(这就是Chunk Based System的强大之处)。
使用这样的结构简单,容易进行读写操作,并且是2进制的文件,更加节省空间。
在ZFXEngine中,我们使用.CBF文件格式。这个格式是我们自己定义的文件格式类型。我们希望这个格式能够支持如下的几个功能:
为此,我们分别定义了如下的几个结构体:
<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>上面所有的格式表示的就是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>比如,我们可以这样来构建一个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,在将数据保存到.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是一款免费的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>
在有了我们自己定义的.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, "1322600812@qq.com",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文件,并且最终将它进行渲染出来。
原文地址:http://blog.csdn.net/i_dovelemon/article/details/39341393