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

本文介绍了ZFXEngine中自定义的Chunk Based System 3D格式.CBF,以及如何从Milk3D Shape的ASCII文件转换为.CBF格式。作者详细讨论了Chunk Based System的数据结构、.CBF格式的需求和设计,以及在处理骨骼动画、结构体序列化时遇到的问题和解决方案。最后,提到了格式转换程序的设计和开发中的一些注意事项。
摘要由CSDN通过智能技术生成

作者: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格式的源码。我下载使用了下,

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值