ZFXEngine开发笔记之3D模型格式支持(1)

作者: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文件,并且最终将它进行渲染出来。

时间: 2024-10-03 01:12:37

ZFXEngine开发笔记之3D模型格式支持(1)的相关文章

cesiumjs开发实践(七) 3D模型

cesium中支持载入3D模型,不过只支持gltf格式.gltf是khronos组织(起草OpenGL标准的那家)定义的一种交换格式,用于互联网或移动设备上展现3d内容,充分支持opengl,webgl,opengles图形加速标准. gltf目前可以由collada格式转换而来,官网上也提供了一个转换工具(https://www.khronos.org/gltf).这个工具比较坑爹,只能和collada文件放在同一个目录才工作,转换后的结果文件也必须在同一目录,就是说不能指定输入和输出路径,当

ZFXEngine开发笔记-SSE版本的ZFXVector

SSE介绍 在学习3D游戏编程大师技巧的时候,就了解到,可是使用一种称之为"单指令,多数据(SIMD)"的技术来编写3D数学库.通过这样的方法,可以将我们经常使用的诸如向量计算,矩阵变换等操作加快很多倍.这次,在学习3D引擎开发的时候,也用到了这个技术.SIMD是一种技术的名称,而并不是具体的工具.实现这种技术,不同的CPU厂商推出了不同的技术,像MMX, 3DNow!, SSE, SSE2, SSE3....由于我的计算机上使用的是Intel的处理器,它支持MMX,SSE,SSE2,

ZFXEngine开发笔记之Shadow Volume

作者:i_dovelemon 来源:CSDN 日期:2014 / 10 / 20 主题: Shadow Volume 引言 游戏中,往往有很多的光影效果.想要营造出很好的光影效果,物体在光源照射下的阴影就必不可少.本节内容就向大家讲述如何构建阴影. Shadow Volume 构建阴影的方法有很多,常用的两种方法是Shadow Volume和Shadow mapping.本片博文将向大家讲述使用Shadow Volume来构建阴影的方法. Shadow Volume的算法是由Frank Crow

ZFXEngine开发笔记之Bump Mapping(2)

作者:i_dovelemon 日期: 2014 / 9 / 13 来源 : CSDN 主题 :Bump Mapping, Tangent Space, Normal Map, Height Map 引言 在上篇文章中,我讲述了如何根据高度图来创建法线图.并且承诺在后面会讲述3D几何学中的重要工具Tangent Space的相关知识.今天,就在这里,向大家讲述如何接下来的工作部分. Tangent Space 我们知道,在前篇文章中,讲述的法线图中的法线是在纹理图的空间中也就是Tangent Sp

ZFXEngine开发笔记- GPU硬件架构

作者:i_dovelemon 日期:2014 / 8 / 31 来源: CSDN博客 文章: GPU硬件架构 引言 在3D图形学中,可编程渲染管道的出现,无疑是一种创举.在下面的文章中,会向大家简要的介绍现如今的可编程渲染管道中最重要的Vertex Shader和Pixel Shader的硬件架构以及如何使用汇编语言来编写Shader. Vertex Shader 在硬件上,Vertex Shader中所有的运算都在一个名为vertex arithmetic logic unit(ALU)中进行

ZFXEngine开发笔记之Bump Mapping(1)

作者:i_dovelemon 日期:2014 / 9 / 7 来源:CSDN博客 主题:Bump Mapping, Tangent Space, Normal Map, Height Map 引言 我们知道,在真实世界中,任何物体的表面都不是非常光滑的,这种粗糙的表面效果,在光源的照射下会有明暗的感觉.那么,如何在3D图形中模拟这种效果了?是通过建模的方法,为模型的表面建造出粗糙的表面?这种方法很难做出真实的粗糙感出来,毕竟表面的粗糙程度是很细微的效果,很难用建模的方式建模出来.所以,只能用其他

ZFXEngine开发笔记之Omni Light

作者:i_dovelemon 日期:2014 / 9 / 5 来源:CSDN博客 主题:Per-pixel lighting, Omni light, early-z, Multi-pass, Assembly Shader 引言 在第一人称射击游戏中,经常需要点光源来完成光照计算.所以,这篇文章中将会讲述如何创建这个点光源,并且如何设计让引擎可以支持多光源渲染. Omni light介绍 所谓的Omni light,就是指点光源.不过在这里,我们进行点光源的光照计算的时候,没有用到顶点的法线来

【Kinect开发笔记之(二)】Kinect for windows发展历程

新版本SDK和旧版本的SDK完全兼容,如果您之前安装过旧版本的,可以直接安装新版本的SDK,但是如果您之前的开发版本是Beta版的,则需要卸载之后再安装新版本.在Kinect for Windows SDK 1.0版本中,SDK和示例文件是打包一起安装的.而在之后的版本,为了可以分别升级,微软把这两者分开独立为Kinect for Windows SDK和Kinect for Windows Developer Toolkit这两部分,所以需要分别下载安装, Kinect for Windows

8月7号晚7点Autodesk北京办公室,我们来聊聊HTML5/ WebGL 3D 模型浏览技术

Autodesk 发布了一款完全无需插件的三维模型浏览器 Autodesk 360 Viewer,大家有没有兴趣,下班后过来聊聊吧!   8月7号 周四, 19:00~21:00 Autodesk北京办公室, 北京市朝阳区东大桥路9号侨福芳草地大厦 A 栋9层   报名: http://huiyi.csdn.net/activity/product/goods_list?project_id=1248  或: http://www.meetup.com/Beijing-3D-Modeling-C