文章来源:http://www.zwqxin.com/archives/opengl/3ds-cload3ds-view.html
CLoad3DS类是Sourceforge中的一个开源项目,作用在于帮助开发者学会简单的对3DS文件的载入(OpenGL)程序。虽然有更成熟更强大的3dslib库,但是平时写写Demo中,对模型载入的要求一般比较低,这时候只把CLoad3DS类包含到程序就够了。——ZwqXin.com 上篇文章:3DS文件结构的初步认识 中谈到的就是这个类。
本文来源于 ZwqXin (http://www.zwqxin.com/),
这个类只用到了3DS文件中的一小部分(chunk):顶点信息,面信息,纹理信息,材质信息,和一些标志信息(见上篇日志)。接下来我们首先看看它是怎样把3DS文件里的数据存储到实际内存中的:
//上有CVector3D,CVector2D类用于保存一个顶点和一个纹理坐标,这是底层的存储结构。 //这里给出了这么一个事实:一个模型由好几部分组成,譬如一个人体由手脚头身等等部分组成,每个部分就是3DS中单独命名的一个对象;因此说,模型由一系列对象组成,每个对象由一系列三角面片组成 // 面的结构定义《-由顶点构struct tFace{ int vertIndex[3]; // 顶点索引 int coordIndex[3]; // 纹理坐标索引}; // 对象信息结构体《-由面构struct t3DObject { int numOfVerts; // 模型中顶点的数目 int numOfFaces; // 模型中面的数目 int numTexVertex; // 模型中纹理坐标的数目 int materialID; // 纹理ID bool bHasTexture; // 是否具有纹理映射 char strName[255]; // 对象的名称 CVector3D *pVerts; // 对象的顶点 CVector3D *pNormals; // 对象的法向量 CVector2D *pTexVerts; // 纹理UV坐标 tFace *pFaces; // 对象的面信息}; // 模型信息结构体《-由对象构,包含材质struct t3DModel { UINT texture[MAX_TEXTURES]; bool Textured; //是否使用纹理 int numOfObjects; // 模型中对象的数目 int numOfMaterials; // 模型中材质的数目 vector<tMaterialInfo> pMaterials; // 材质链表信息 vector<t3DObject> pObject; // 模型中对象链表信息}; // 接下来是材质信息结构体,描述材质struct tMaterialInfo{ char strName[255]; // 纹理名称 char strFile[255]; // 如果存在纹理映射,则表示纹理文件名称 BYTE color[3]; // 对象的RGB颜色 int texureId; // 纹理ID float uTile; // u 重复 float vTile; // v 重复 float uOffset; // u 纹理偏移 float vOffset; // v 纹理偏移} ; // 这个与前面的不同,它是针对3DS文件而非模型实体。也就是描述块。你将看到bytesRead的精确计算对获得块内正确数据的重要性//保存块信息的结构struct tChunk{ unsigned short int ID; // 块的ID unsigned int length; // 块的长度 unsigned int bytesRead; // 需要读的块数据的字节数};//实现中我们就用以上数据结构描述整个模型和读取过程了: //构造一个临时模型对象,它仅存在于读取过程中t3DModel Model3DS; //构造两个临时存放chunk的结构 tChunk *m_CurrentChunk; tChunk *m_TempChunk;对模型的操作分为两部分:装载模型(初始化时把3DS文件中我们所需数据,通过上述数据结构读入内存供程序随时调用)和渲染模型(把模型画出来,并进行移转缩等调整)。核心分别为ImportModel函数和RenderModel函数。先看前者:
// 打开一个3ds文件,读出其中的内容//省略了非主要的内容,你现在可以看到一个初始化步骤做了哪些事情:bool CLoad3DS::ImportModel(GLuint Model_id, char *strFileName){ ......... m_FilePointer = fopen(strFileName, "rb");//1.打开文件,让文件指针指向 ...... // 2.将文件的第一块读出并判断是否是3ds文件(0x4D4D) ReadChunk(m_CurrentChunk); ........ // 3.通过调用下面的递归函数,将对象读出 PRocessNextChunk(&Model3DS, m_CurrentChunk); // 4.在读完整个3ds文件之后,计算顶点的法线 ComputeNormals(&Model3DS); ....... return true;}其中的核心当然是第3步了。在进入这个核心之前,看看CLoad3DS类是怎样读数据的:
// 下面函数读入块的ID号和它的字节长度void CLoad3DS::ReadChunk(tChunk *pChunk){ // 读入块的ID号,占用了2个字节。块的ID号象OBJECT或MATERIAL一样,说明了在块中所包含的内容 pChunk->bytesRead = fread(&pChunk->ID, 1, 2, m_FilePointer); // 然后读入块占用的长度,包含了四个字节 pChunk->bytesRead += fread(&pChunk->length, 1, 4, m_FilePointer);}//fread函数,针对每次函数调用,4参数分别表示:读入的数据所存入的位置,每次读多少字节,读多少次,文件指针(所以中间两参数的乘积就是调用一次fread要读入的数据量了);返回实际成功读入了的字节数。//因此,一次成功的ReadChunk将把参数(tChunk 类型的块结构)中的bytesRead加6。这6字节包含一个块最开头的ID号(存入ID)和长度(存入length),这样文件指针(fread会让其指向下一个文件数据块开头)接下来将要面对的就是实际数据了。不明白者看此。请继续收看:一个读取3DS文件的类CLoad3DS浅析Ⅱ
本文参考资料(大部分中文注释编写者),若没找错的话,应该是这里(http://blog.csdn.net/hardVB)呵呵.
本文来源于 ZwqXin (http://www.zwqxin.com/),
新闻热点
疑难解答