一个读取3DS文件的类CLoad3DS浅析Ⅰ

文章来源: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/), 转载请注明
      原文地址:http://www.zwqxin.com/archives/opengl/3ds-cload3ds-view.html

这个类只用到了3DS文件中的一小部分(chunk):顶点信息,面信息,纹理信息,材质信息,和一些标志信息(见上篇日志)。接下来我们首先看看它是怎样把3DS文件里的数据存储到实际内存中的:

  1. //上有CVector3D,CVector2D类用于保存一个顶点和一个纹理坐标,这是底层的存储结构。
  2.  
  3. //这里给出了这么一个事实:一个模型由好几部分组成,譬如一个人体由手脚头身等等部分组成,每个部分就是3DS中单独命名的一个对象;因此说,模型由一系列对象组成,每个对象由一系列三角面片组成
  4.  
  5. // 面的结构定义《-由顶点构
  6. struct tFace
  7. {
  8.   int vertIndex[3];      // 顶点索引
  9.   int coordIndex[3];      // 纹理坐标索引
  10. };
  11.  
  12. // 对象信息结构体《-由面构
  13. struct t3DObject 
  14. {
  15.   int numOfVerts;      // 模型中顶点的数目
  16.   int numOfFaces;      // 模型中面的数目
  17.   int numTexVertex;      // 模型中纹理坐标的数目
  18.   int materialID;      // 纹理ID
  19.   bool bHasTexture;      // 是否具有纹理映射
  20.   char strName[255];      // 对象的名称
  21.   CVector3D *pVerts;      // 对象的顶点
  22.   CVector3D *pNormals;    // 对象的法向量
  23.   CVector2D *pTexVerts;    // 纹理UV坐标
  24.   tFace *pFaces;        // 对象的面信息
  25. };
  26.  
  27. // 模型信息结构体《-由对象构,包含材质
  28. struct t3DModel 
  29. {
  30.   UINT texture[MAX_TEXTURES];
  31.   bool  Textured;           //是否使用纹理
  32.   int numOfObjects;          // 模型中对象的数目
  33.   int numOfMaterials;          // 模型中材质的数目
  34.   vector pMaterials;  // 材质链表信息
  35.   vector pObject;      // 模型中对象链表信息
  36. };
  37.  
  38. // 接下来是材质信息结构体,描述材质
  39. struct tMaterialInfo
  40. {
  41.   char strName[255];      // 纹理名称
  42.   char strFile[255];      // 如果存在纹理映射,则表示纹理文件名称
  43.   BYTE color[3];        // 对象的RGB颜色
  44.   int texureId;        // 纹理ID
  45.   float uTile;        // u 重复
  46.   float vTile;        // v 重复
  47.   float uOffset;       // u 纹理偏移
  48.   float vOffset;        // v 纹理偏移
  49. } ;
  50.  
  51. // 这个与前面的不同,它是针对3DS文件而非模型实体。也就是描述块。你将看到bytesRead的精确计算对获得块内正确数据的重要性
  52. //保存块信息的结构
  53. struct tChunk
  54. {
  55.   unsigned short int ID;          // 块的ID    
  56.   unsigned int length;          // 块的长度
  57.   unsigned int bytesRead;          // 需要读的块数据的字节数
  58. };
  59. //实现中我们就用以上数据结构描述整个模型和读取过程了:
  60.  //构造一个临时模型对象,它仅存在于读取过程中
  61. t3DModel Model3DS;
  62.  
  63. //构造两个临时存放chunk的结构
  64.   tChunk *m_CurrentChunk;
  65.   tChunk *m_TempChunk;

对模型的操作分为两部分:装载模型(初始化时把3DS文件中我们所需数据,通过上述数据结构读入内存供程序随时调用)和渲染模型(把模型画出来,并进行移转缩等调整)。核心分别为ImportModel函数和RenderModel函数。先看前者:

  1. // 打开一个3ds文件,读出其中的内容
  2. //省略了非主要的内容,你现在可以看到一个初始化步骤做了哪些事情:
  3. bool CLoad3DS::ImportModel(GLuint Model_id, char *strFileName)
  4. { .........
  5.     m_FilePointer = fopen(strFileName, "rb");//1.打开文件,让文件指针指向
  6.    ......
  7.   // 2.将文件的第一块读出并判断是否是3ds文件(0x4D4D)
  8.   ReadChunk(m_CurrentChunk);
  9.   ........
  10.     // 3.通过调用下面的递归函数,将对象读出
  11.   ProcessNextChunk(&Model3DS, m_CurrentChunk);
  12.  
  13.   // 4.在读完整个3ds文件之后,计算顶点的法线
  14.   ComputeNormals(&Model3DS);
  15.    .......
  16.   return true;
  17. }

其中的核心当然是第3步了。在进入这个核心之前,看看CLoad3DS类是怎样读数据的:

  1. // 下面函数读入块的ID号和它的字节长度
  2. void CLoad3DS::ReadChunk(tChunk *pChunk)
  3. {
  4.   // 读入块的ID号,占用了2个字节。块的ID号象OBJECT或MATERIAL一样,说明了在块中所包含的内容
  5.   pChunk->bytesRead = fread(&pChunk->ID, 1, 2, m_FilePointer);
  6.  
  7.   // 然后读入块占用的长度,包含了四个字节
  8.   pChunk->bytesRead += fread(&pChunk->length, 1, 4, m_FilePointer);
  9. }
  10. //fread函数,针对每次函数调用,4参数分别表示:读入的数据所存入的位置,每次读多少字节,读多少次,文件指针(所以中间两参数的乘积就是调用一次fread要读入的数据量了);返回实际成功读入了的字节数。
  11. //因此,一次成功的ReadChunk将把参数(tChunk 类型的块结构)中的bytesRead加6。这6字节包含一个块最开头的ID号(存入ID)和长度(存入length),这样文件指针(fread会让其指向下一个文件数据块开头)接下来将要面对的就是实际数据了。不明白者看此。

请继续收看:一个读取3DS文件的类CLoad3DS浅析Ⅱ
 

本文参考资料(大部分中文注释编写者),若没找错的话,应该是这里(http://blog.csdn.net/hardVB)呵呵.

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
      原文地址:http://www.zwqxin.com/archives/opengl/3ds-cload3ds-view.html


你可能感兴趣的:(OpenGL,C/C++,综合)