OpenGL读取3DS模型(Qt版)

OpenGL一个重要应用就是能够读取外部的3D模型文件,比如OBJ,MD2,MD3,3DS等。在我之前的日志里已经写过一篇关于OpenGL读取Obj格式的类,详情可以看“Qt下学习OpenGL之OBJ模型”。而这次我要介绍的是3DS文件的读取。

  接下来要贴出的代码已经在Qt4下成功运行,不过里面需要依赖一个CBMPLoader的类,这个可以从《OpenGL游戏编程》这本书里看到,也可以在我博客里找到对应的代码。

  最后,还补充一下个人的看法。对于3DS格式,相比OBJ的话,其多了光照、贴图、材质等信息,但如果是工业标准,并且对于贴图和材质没有特别的要求的话,使用代码给每个不同物体写上一个单一颜色就好了。这样速度很快,速度是很重要的因素啊。

  好了,贴代码了。
3DSLoader.h内容:

view source
print ?
001 #ifndef __3DSLOADER_H__
002 #define __3DSLOADER_H__
003   
004 #include "Stdafx.h"
005 #include "Vector.h"
006 #include "CBMPLoader.h"
007   
008 /* 下面是定义一些块的ID号 */
009   
010 /*  基本块(Primary Chunk),位于文件的开始 */
011 #define PRIMARY       0x4D4D
012   
013 /*  主块(Main Chunks) */
014 #define OBJECTINFO    0x3D3D        /* 网格对象的版本号 */
015 #define VERSION       0x0002        /* .3ds文件的版本 */
016 #define EDITKEYFRAME  0xB000        /* 所有关键帧信息的头部 */
017   
018 /*  对象的次级定义 */
019 #define MATERIAL      0xAFFF        /* 纹理信息 */
020 #define OBJECT        0x4000        /* 对象的面、顶点等信息 */
021   
022 /*  材质的次级定义 */
023 #define MATNAME       0xA000        /* 材质名称 */
024 #define MATDIFFUSE    0xA020        /* 对象/材质的颜色 */
025 #define MATMAP        0xA200        /* 新材质的头部 */
026 #define MATMAPFILE    0xA300        /* 保存纹理的文件名 */
027 #define OBJ_MESH      0x4100            /* 新的网格对象 */
028   
029 /* 网格对象的次级定义 */
030 #define OBJ_VERTICES  0x4110        /* 对象顶点 */
031 #define OBJ_FACES     0x4120        /* 对象的面 */
032 #define OBJ_MATERIAL  0x4130        /* 对象的材质 */
033 #define OBJ_UV        0x4140        /* 对象的UV纹理坐标 */
034   
035 /* 面的结构定义 */
036 struct tFace
037 {
038         int vertIndex[3];       /* 顶点索引 */
039         int coordIndex[3];      /* 纹理坐标索引 */
040 };
041   
042 /* 材质信息结构体 */
043 struct tMatInfo
044 {
045         char  strName[255];     /* 纹理名称 */
046         char  strFile[255];     /* 纹理文件名称 */
047         unsigned char  color[3];    /* 对象的RGB颜色 */
048         int   texureId;         /* 纹理ID */
049         float uTile;            /* u 重复 */
050         float vTile;            /* v 重复 */
051         float uOffset;          /* u 纹理偏移 */
052         float vOffset;          /* v 纹理偏移 */
053 } ;
054   
055 /* 对象信息结构体 */
056 struct t3DObject
057 {
058         int  numOfVerts;        /* 模型中顶点的数目 */
059         int  numOfFaces;        /* 模型中面的数目 */
060         int  numTexVertex;      /* 模型中纹理坐标的数目 */
061         int  materialID;        /* 纹理ID */
062         bool bHasTexture;       /* 是否具有纹理映射 */
063         char strName[255];      /* 对象的名称 */
064         Vector3  *pVerts;       /* 对象的顶点 */
065         Vector3  *pNormals;     /* 对象的法向量 */
066         Vector2  *pTexVerts;        /* 纹理UV坐标 */
067         tFace *pFaces;          /* 对象的面信息 */
068 };
069   
070 /* 模型信息结构体 */
071 struct t3DModel
072 {   int numOfObjects;       /* 模型中对象的数目 */
073         int numOfMaterials;     /* 模型中材质的数目 */
074         QVectorpMaterials;  /* 材质链表信息 */
075         QVector pObject;    /* 模型中对象链表信息 */
076 };
077   
078 /* 块信息的结构 */
079 struct tChunk
080 {
081         unsigned short int ID;       /* 块的ID */
082         unsigned int length;         /* 块的长度 */
083         unsigned int bytesRead;      /* 需要读的块数据的字节数 */
084 };
085   
086 #define MAX_TEXTURES  100
087   
088 /* 3DS文件载入类 */
089 class C3DSLoader
090 {
091 public:
092         /* 构造函数 */
093         C3DSLoader();
094         virtual ~C3DSLoader();
095         void Draw();//显示3ds模型
096         void Init(char *filename);
097   
098 private:
099         /* 读一个字符串 */
100         int  GetString(char *);
101   
102         /* 装载3ds文件到模型结构中 */
103         bool Import3DS(t3DModel *pModel, char *strFileName);
104   
105         /*  从文件中创建纹理 */
106         void LoadTexture(char* filename, GLuint textureArray[], GLuint textureID);
107   
108         /* 读取一个块 */
109         void ReadChunk(tChunk *);
110   
111         /* 读取下一个块 */
112         void ReadNextChunk(t3DModel *pModel, tChunk *);
113   
114         /* 读取下一个对象 */
115         void ReadNextObjChunk(t3DModel *pModel,t3DObject *pObject,tChunk *);
116   
117         /* 读取下一个材质块 */
118         void ReadNextMatChunk(t3DModel *pModel, tChunk *);
119   
120         /* 读取对象颜色的RGB值 */
121         void ReadColor(tMatInfo *pMaterial, tChunk *pChunk);
122   
123         /* 读取对象的顶点信息 */
124         void ReadVertices(t3DObject *pObject, tChunk *);
125   
126         /* 读取对象的面信息 */
127         void ReadVertexIndices(t3DObject *pObject,tChunk *);
128   
129         /* 读取对象的纹理坐标 */
130         void ReadUVCoordinates(t3DObject *pObject,tChunk *);
131   
132         /* 读取赋予对象的材质 */
133         void ReadObjMat(t3DModel *pModel,t3DObject *pObject,tChunk *pPreChunk);
134   
135         /* 计算对象顶点的法向量 */
136         void ComputeNormals(t3DModel *pModel);
137   
138         /* 释放内存,关闭文件 */
139         void CleanUp();
140   
141         FILE         *m_FilePointer;              /*< 文件指针 */
142         tChunk       *m_CurrentChunk;             /*< 读取过程中当前块 */
143         tChunk       *m_TempChunk;                /*< 临时块 */
144         GLuint       m_textures[MAX_TEXTURES];    /*< 纹理 */
145         t3DModel     m_3DModel;                   /*< 模型 */
146         CBMPLoader   m_BMPTexture;                /* 载入位图 */
147   
148 };
149   
150 #endif

3DSLoader.cpp内容:

view source
print ?
001 #include "Stdafx.h"
002 #include "3DSLoader.h"
003   
004 /* 构造函数 */
005 C3DSLoader::C3DSLoader()
006 {
007     m_CurrentChunk = new tChunk;     /* 为当前块分配空间 */
008     m_TempChunk = new tChunk;        /* 为临时块分配空间 */
009     m_3DModel.numOfObjects = 0;
010     m_3DModel.numOfMaterials = 0;
011     for(int i=0;i0)
012             LoadTexture(m_3DModel.pMaterials[i].strFile,m_textures, i);/* 使用纹理文件名称来装入位图 */
013         m_3DModel.pMaterials[i].texureId = i;/* 设置材质的纹理ID */
014     }
015 }
016   
017 /* 显示3ds模型 */
018 void C3DSLoader::Draw()
019 {
020     /*保存现有颜色属实性 */
021     glPushAttrib(GL_CURRENT_BIT);
022     glDisable(GL_TEXTURE_2D);
023     //glEnable(GL_LIGHT0);
024   
025     /* 遍历模型中所有的对象 */
026     for(int i = 0; i < m_3DModel.numOfObjects; i++)
027     {
028         /* 如果对象的大小小于0,则退出 */
029         if(m_3DModel.pObject.size() <= 0)             break;         /* 获得当前显示的对象 */         t3DObject *pObject = &m_3DModel.pObject[i];         /* 判断该对象是否有纹理映射 */         if(pObject->bHasTexture)
030         {
031             glEnable(GL_TEXTURE_2D);/* 打开纹理映射 */
032             glBindTexture(GL_TEXTURE_2D, m_textures[pObject->materialID]);
033         }
034         else
035             glDisable(GL_TEXTURE_2D);/* 关闭纹理映射 */
036   
037         glColor3ub(255, 255, 255);
038   
039         /* 开始绘制 */
040   
041             glBegin(GL_TRIANGLES);
042   
043         /* 遍历所有的面 */
044         for(int j = 0; j < pObject->numOfFaces; j++)
045         {
046             /* 遍历三角形的所有点 */
047             for(int tex = 0; tex < 3; tex++)             {                 /* 获得面对每个点的索引 */                 int index = pObject->pFaces[j].vertIndex[tex];
048   
049                 /* 给出法向量 */
050                 glNormal3f(pObject->pNormals[index].x,pObject->pNormals[index].y,
051                            pObject->pNormals[index].z);
052   
053                 /* 如果对象具有纹理 */
054                 if(pObject->bHasTexture)
055                 {
056                     /* 确定是否有UVW纹理坐标 */
057                     if(pObject->pTexVerts)
058                         glTexCoord2f(pObject->pTexVerts[index].x,pObject->pTexVerts[index].y);
059                 }
060                 else
061                 {
062                     if(m_3DModel.pMaterials.size() && pObject->materialID>= 0)
063                     {
064                         unsigned char *pColor = m_3DModel.pMaterials[pObject->materialID].color;
065                         glColor3ub(pColor[0],pColor[1],pColor[2]);
066                     }
067                 }
068                 glVertex3f(pObject->pVerts[index].x,pObject->pVerts[index].y,pObject->pVerts[index].z);
069             }
070         }
071         glEnd();
072         /* 绘制结束 */
073     }
074   
075     //glDisable(GL_LIGHT0);
076     glEnable(GL_TEXTURE_2D);
077   
078     /* 恢复前一属性 */
079     glPopAttrib();
080 }
081   
082 void C3DSLoader::LoadTexture(char* filename, GLuint textureArray[], GLuint textureID)
083 {
084     if(!filename)
085         return;
086   
087     if(!this->m_BMPTexture.LoadBitmap(filename))
088     {
089         QMessageBox::warning(0,QObject::tr("Load Bitmap Error"),QObject::tr("Load Bitmap Error"));
090         return;
091     }
092   
093     glGenTextures(1,&m_textures[textureID]);
094   
095     glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
096     glBindTexture(GL_TEXTURE_2D, m_textures[textureID]);
097   
098     /* 控制滤波 */
099     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
100     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR);
101   
102     /* 创建纹理 */
103     gluBuild2DMipmaps(GL_TEXTURE_2D, 3, m_BMPTexture.imageWidth,m_BMPTexture.imageHeight, GL_RGB,
104                       GL_UNSIGNED_BYTE, this->m_BMPTexture.image);
105   
106 }
107   
108 /* 载入3DS文件到模型结构中 */
109 bool C3DSLoader::Import3DS(t3DModel *pModel,char *strFileName)
110 {
111     char strMessage[255] = {0};
112   
113     /* 打开一个3ds文件 */
114     m_FilePointer = fopen(strFileName, "rb");
115   
116     /* 检查文件指针 */
117     if(!m_FilePointer)
118     {
119         sprintf(strMessage, "Cann't find the file: %s!", strFileName);
120         QMessageBox::information(NULL,
121                                  "Error",
122                                  strMessage,
123                                  QMessageBox::Yes | QMessageBox::No,
124                                  QMessageBox::Yes);
125         return false;
126     }
127   
128     /* 将文件的第一块读出并判断是否是3ds文件 */
129     ReadChunk(m_CurrentChunk);
130   
131     /* 确保是3ds文件 */
132     if (m_CurrentChunk->ID != PRIMARY)
133     {
134         QMessageBox::information(NULL,
135                                  "Error",
136                                  "Cann't Loading the Function",
137                                  QMessageBox::Yes | QMessageBox::No,
138                                  QMessageBox::Yes);
139         return false;
140     }
141   
142     /* 递归读出对象数据 */
143     ReadNextChunk(pModel, m_CurrentChunk);
144   
145     /* 计算顶点的法线 */
146     ComputeNormals(pModel);
147   
148     /* 释放内存空间 */
149     CleanUp();
150   
151     return true;
152 }
153   
154 /*  读入一个字符串 */
155 int C3DSLoader::GetString(char *pBuffer)
156 {
157     int index = 0;
158   
159     /* 读入一个字节的数据 */
160     fread(pBuffer, 1, 1, m_FilePointer);
161   
162     /* 直到结束 */
163     while (*(pBuffer + index++) != 0)
164     {
165         /* 读入一个字符直到NULL */
166         fread(pBuffer + index, 1, 1, m_FilePointer);
167     }
168   
169     /* 返回字符串的长度 */
170     return strlen(pBuffer) + 1;
171 }
172   
173 /* 读入块的ID号和它的字节长度 */
174 void C3DSLoader::ReadChunk(tChunk *pChunk)
175 {
176     /* 读入块的ID号 */
177     pChunk->bytesRead = fread(&pChunk->ID, 1, 2, m_FilePointer);
178   
179     /* 读入块占用的长度 */
180     pChunk->bytesRead += fread(&pChunk->length, 1, 4, m_FilePointer);
181   
182 }
183   
184 /* 读出3ds文件的主要部分 */
185 void C3DSLoader::ReadNextChunk(t3DModel *pModel, tChunk *pPreChunk)
186 {
187     t3DObject newObject = {0};                  /* 用来添加到对象链表 */
188     tMatInfo newTexture = {0};                  /* 用来添加到材质链表 */
189     unsigned int version = 0;                   /* 保存文件版本 */
190     int buffer[50000] = {0};                    /* 用来跳过不需要的数据 */
191     m_CurrentChunk = new tChunk;                /* 为新的块分配空间      */
192   
193     /* 继续读入子块 */
194     while (pPreChunk->bytesRead < pPreChunk->length)
195     {
196         /* 读入下一个块 */
197         ReadChunk(m_CurrentChunk);
198   
199         /* 判断块的ID号 */
200         switch (m_CurrentChunk->ID)
201         {
202   
203             /* 文件版本号 */
204         case VERSION:
205   
206             /* 读入文件的版本号 */
207             m_CurrentChunk->bytesRead += fread(&version, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
208   
209             /* 如果文件版本号大于3,给出一个警告信息 */
210             if (version > 0x03)
211                 QMessageBox::information(NULL,
212                                          "Warning",
213                                          "The 3DS File Version is wrong",
214                                          QMessageBox::Yes | QMessageBox::No,
215                                          QMessageBox::Yes);
216             break;
217   
218             /* 网格版本信息 */
219         case OBJECTINFO:
220   
221             /* 读入下一个块 */
222             ReadChunk(m_TempChunk);
223   
224             /* 获得网格的版本号 */
225             m_TempChunk->bytesRead += fread(&version, 1, m_TempChunk->length - m_TempChunk->bytesRead, m_FilePointer);
226   
227             /* 增加读入的字节数 */
228             m_CurrentChunk->bytesRead += m_TempChunk->bytesRead;
229   
230             /* 进入下一个块 */
231             ReadNextChunk(pModel, m_CurrentChunk);
232             break;
233   
234             /* 材质信息 */
235         case MATERIAL:
236   
237             /* 材质的数目递增 */
238             pModel->numOfMaterials++;
239   
240             /* 在纹理链表中添加一个空白纹理结构 */
241             pModel->pMaterials.push_back(newTexture);
242   
243             /* 进入材质装入函数 */
244             ReadNextMatChunk(pModel, m_CurrentChunk);
245             break;
246   
247             /* 对象名称 */
248         case OBJECT:
249   
250             /* 对象数递增 */
251             pModel->numOfObjects++;
252   
253             /* 添加一个新的tObject节点到对象链表中 */
254             pModel->pObject.push_back(newObject);
255   
256             /* 初始化对象和它的所有数据成员 */
257             memset(&(pModel->pObject[pModel->numOfObjects - 1]), 0, sizeof(t3DObject));
258   
259             /* 获得并保存对象的名称,然后增加读入的字节数 */
260             m_CurrentChunk->bytesRead += GetString(pModel->pObject[pModel->numOfObjects - 1].strName);
261   
262             /* 进入其余的对象信息的读入 */
263             ReadNextObjChunk(pModel, &(pModel->pObject[pModel->numOfObjects - 1]), m_CurrentChunk);
264             break;
265   
266             /* 关键帧 */
267         case EDITKEYFRAME:
268   
269             /* 跳过关键帧块的读入 */
270             m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
271             break;
272   
273         default:
274             /*  跳过所有忽略的块的内容的读入 */
275             m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
276             break;
277         }
278   
279         /* 增加从最后块读入的字节数 */
280         pPreChunk->bytesRead += m_CurrentChunk->bytesRead;
281     }
282   
283     /* 释放当前块的内存空间 */
284     delete m_CurrentChunk;
285     m_CurrentChunk = pPreChunk;
286 }
287   
288 /* 处理所有的文件中对象的信息 */
289 void C3DSLoader::ReadNextObjChunk(t3DModel *pModel, t3DObject *pObject, tChunk *pPreChunk)
290 {
291     /* 用于读入不需要的数据 */
292     int buffer[50000] = {0};
293   
294     /* 对新的块分配存储空间 */
295     m_CurrentChunk = new tChunk;
296   
297     /* 继续读入块的内容直至本子块结束 */
298     while (pPreChunk->bytesRead < pPreChunk->length)
299     {
300         /* 读入下一个块 */
301         ReadChunk(m_CurrentChunk);
302   
303         /* 区别读入是哪种块 */
304         switch (m_CurrentChunk->ID)
305         {
306             /* 正读入的是一个新块 */
307         case OBJ_MESH:
308             /* 使用递归函数调用,处理该新块 */
309             ReadNextObjChunk(pModel, pObject, m_CurrentChunk);
310             break;
311   
312             /* 读入是对象顶点 */
313         case OBJ_VERTICES:
314             ReadVertices(pObject, m_CurrentChunk);
315             break;
316   
317             /* 读入的是对象的面 */
318         case OBJ_FACES:
319             ReadVertexIndices(pObject, m_CurrentChunk);
320             break;
321   
322             /* 读入的是对象的材质名称 */
323         case OBJ_MATERIAL:
324             /* 读入对象的材质名称 */
325             ReadObjMat(pModel, pObject, m_CurrentChunk);
326             break;
327   
328             /* 读入对象的UV纹理坐标 */
329         case OBJ_UV:
330             /* 读入对象的UV纹理坐标 */
331             ReadUVCoordinates(pObject, m_CurrentChunk);
332             break;
333   
334         default:
335             /* 略过不需要读入的块 */
336             m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
337             break;
338         }
339         /* 添加从最后块中读入的字节数到前面的读入的字节中 */
340         pPreChunk->bytesRead += m_CurrentChunk->bytesRead;
341     }
342     /* 释放当前块的内存空间,并把当前块设置为前面块 */
343     delete m_CurrentChunk;
344     m_CurrentChunk = pPreChunk;
345 }
346   
347 /* 处理所有的材质信息 */
348 void C3DSLoader::ReadNextMatChunk(t3DModel *pModel, tChunk *pPreChunk)
349 {
350     /* 用于读入不需要的数据 */
351     int buffer[50000] = {0};
352   
353     /* 给当前块分配存储空间 */
354     m_CurrentChunk = new tChunk;
355   
356     /* 继续读入这些块 */
357     while (pPreChunk->bytesRead < pPreChunk->length)
358     {
359         /* 读入下一块 */
360         ReadChunk(m_CurrentChunk);
361         /* 判断读入的是什么块 */
362         switch (m_CurrentChunk->ID)
363         {
364         case MATNAME:                           /* 材质的名称 */
365             /* 读入材质的名称 */
366             m_CurrentChunk->bytesRead += fread(pModel->pMaterials[pModel->numOfMaterials - 1].strName, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
367             break;
368         case MATDIFFUSE:                        /* 对象的R G B颜色 */
369             ReadColor(&(pModel->pMaterials[pModel->numOfMaterials - 1]), m_CurrentChunk);
370             break;
371         case MATMAP:                            /* 纹理信息的头部 */
372             /* 下一个材质块信息 */
373             ReadNextMatChunk(pModel, m_CurrentChunk);
374             break;
375         case MATMAPFILE:                        /* 材质文件的名称 */
376             /* 读入材质的文件名称 */
377             m_CurrentChunk->bytesRead += fread(pModel->pMaterials[pModel->numOfMaterials - 1].strFile, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
378             break;
379         default:
380             /* 跳过不需要读入的块 */
381             m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
382             break;
383         }
384         /* 添加从最后块中读入的字节数 */
385         pPreChunk->bytesRead += m_CurrentChunk->bytesRead;
386     }
387     /* 删除当前块,并将当前块设置为前面的块 */
388     delete m_CurrentChunk;
389     m_CurrentChunk = pPreChunk;
390 }
391   
392 /* 读入RGB颜色 */
393 void C3DSLoader::ReadColor(tMatInfo *pMaterial, tChunk *pChunk)
394 {
395     /* 读入颜色块信息 */
396     ReadChunk(m_TempChunk);
397     /* 读入RGB颜色 */
398     m_TempChunk->bytesRead += fread(pMaterial->color, 1, m_TempChunk->length - m_TempChunk->bytesRead, m_FilePointer);
399     /* 增加读入的字节数 */
400     pChunk->bytesRead += m_TempChunk->bytesRead;
401 }
402   
403 /* 读入顶点索引 */
404 void C3DSLoader::ReadVertexIndices(t3DObject *pObject, tChunk *pPreChunk)
405 {
406     unsigned short index = 0;                   /* 用于读入当前面的索引 */
407     /* 读入该对象中面的数目 */
408     pPreChunk->bytesRead += fread(&pObject->numOfFaces, 1, 2, m_FilePointer);
409     /* 分配所有面的存储空间,并初始化结构 */
410     pObject->pFaces = new tFace [pObject->numOfFaces];
411     memset(pObject->pFaces, 0, sizeof(tFace) * pObject->numOfFaces);
412     /* 遍历对象中所有的面 */
413     for(int i = 0; i < pObject->numOfFaces; i++)
414     {   for(int j = 0; j < 4; j++)         {             /* 读入当前面的第一个点  */             pPreChunk->bytesRead += fread(&index, 1, sizeof(index), m_FilePointer);
415             if(j < 3)             {                 /* 将索引保存在面的结构中 */                 pObject->pFaces[i].vertIndex[j] = index;
416             }
417         }
418     }
419 }
420   
421 /* 读入对象的UV坐标 */
422 void C3DSLoader::ReadUVCoordinates(t3DObject *pObject, tChunk *pPreChunk)
423 {
424     /* 读入UV坐标的数量 */
425     pPreChunk->bytesRead += fread(&pObject->numTexVertex, 1, 2, m_FilePointer);
426   
427     /* 分配保存UV坐标的内存空间 */
428     pObject->pTexVerts = new Vector2[pObject->numTexVertex];
429   
430     /* 读入纹理坐标 */
431     pPreChunk->bytesRead += fread(pObject->pTexVerts, 1, pPreChunk->length - pPreChunk->bytesRead, m_FilePointer);
432 }
433   
434 /*  读入对象的顶点 */
435 void C3DSLoader::ReadVertices(t3DObject *pObject, tChunk *pPreChunk)
436 {
437     /* 读入顶点的数目 */
438     pPreChunk->bytesRead += fread(&(pObject->numOfVerts), 1, 2, m_FilePointer);
439   
440     /* 分配顶点的存储空间,然后初始化结构体 */
441     pObject->pVerts = new Vector3 [pObject->numOfVerts];
442     memset(pObject->pVerts, 0, sizeof(Vector3) * pObject->numOfVerts);
443   
444     /* 读入顶点序列 */
445     pPreChunk->bytesRead += fread(pObject->pVerts, 1, pPreChunk->length - pPreChunk->bytesRead, m_FilePointer);
446   
447     /* 遍历所有的顶点将Y轴和Z轴交换,然后将Z轴反向 */
448     for(int i = 0; i < pObject->numOfVerts; i++)
449     {
450         /* 保存Y轴的值 */
451         float fTempY = pObject->pVerts[i].y;
452         /* 设置Y轴的值等于Z轴的值 */
453         pObject->pVerts[i].y = pObject->pVerts[i].z;
454         /* 设置Z轴的值等于-Y轴的值  */
455         pObject->pVerts[i].z = -fTempY;
456     }
457 }
458   
459 /* 读入对象的材质名称 */
460 void C3DSLoader::ReadObjMat(t3DModel *pModel, t3DObject *pObject, tChunk *pPreChunk)
461 {
462     char strMaterial[255] = {0};            /* 用来保存对象的材质名称 */
463     int buffer[50000] = {0};                /* 用来读入不需要的数据 */
464   
465     /* 读入赋予当前对象的材质名称 */
466     pPreChunk->bytesRead += GetString(strMaterial);
467   
468     /* 遍历所有的纹理 */
469     for(int i = 0; i < pModel->numOfMaterials; i++)
470     {
471         /* 如果读入的纹理与当前的纹理名称匹配 */
472         if(strcmp(strMaterial, pModel->pMaterials[i].strName) == 0)
473         {
474             /* 设置材质ID */
475             pObject->materialID = i;
476   
477             /* 判断是否是纹理映射 */
478             if(strlen(pModel->pMaterials[i].strFile) > 0) {
479   
480                 /* 设置对象的纹理映射标志 */
481                 pObject->bHasTexture = true;
482             }
483             break;
484         }
485         else
486         {
487             /* 如果该对象没有材质,则设置ID为-1 */
488             pObject->materialID = -1;
489         }
490     }
491     pPreChunk->bytesRead += fread(buffer, 1, pPreChunk->length - pPreChunk->bytesRead, m_FilePointer);
492 }
493   
494 /* 计算对象的法向量 */
495 void C3DSLoader::ComputeNormals(t3DModel *pModel)
496 {
497     Vector3 vVector1, vVector2, vNormal, vPoly[3];
498   
499     /* 如果模型中没有对象,则返回 */
500     if(pModel->numOfObjects <= 0)
501         return;
502   
503     /* 遍历模型中所有的对象 */
504     for(int index = 0; index < pModel->numOfObjects; index++)
505     {
506         /* 获得当前的对象 */
507         t3DObject *pObject = &(pModel->pObject[index]);
508   
509         /* 分配需要的存储空间 */
510         Vector3 *pNormals       = new Vector3 [pObject->numOfFaces];
511         Vector3 *pTempNormals   = new Vector3 [pObject->numOfFaces];
512         pObject->pNormals        = new Vector3 [pObject->numOfVerts];
513   
514         /* 遍历对象的所有面 */
515         for(int i=0; i < pObject->numOfFaces; i++)
516         {   vPoly[0] = pObject->pVerts[pObject->pFaces[i].vertIndex[0]];
517             vPoly[1] = pObject->pVerts[pObject->pFaces[i].vertIndex[1]];
518             vPoly[2] = pObject->pVerts[pObject->pFaces[i].vertIndex[2]];
519   
520             /* 计算面的法向量 */
521             vVector1 = vPoly[0] - vPoly[2];             /* 获得多边形的矢量 */
522             vVector2 = vPoly[2] - vPoly[1];             /* 获得多边形的第二个矢量 */
523             vNormal  = vVector1.crossProduct(vVector2); /* 计算两个矢量的叉积 */
524             pTempNormals[i] = vNormal;
525             vNormal  = vNormal.normalize();             /* 规一化叉积 */
526             pNormals[i] = vNormal;                      /* 将法向量添加到法向量列表中 */
527         }
528   
529         /* 计算顶点法向量 */
530         Vector3 vSum(0.0,0.0,0.0);
531         Vector3 vZero = vSum;
532         int shared=0;
533   
534         /* 遍历所有的顶点 */
535         for (int i = 0; i < pObject->numOfVerts; i++)
536         {   for (int j = 0; j < pObject->numOfFaces; j++) /* 遍历所有的三角形面 */
537             {                                               /* 判断该点是否与其它的面共享 */
538                 if (pObject->pFaces[j].vertIndex[0] == i ||
539                     pObject->pFaces[j].vertIndex[1] == i ||
540                     pObject->pFaces[j].vertIndex[2] == i)
541                 {
542                     vSum = vSum + pTempNormals[j];
543                     shared++;
544                 }
545             }
546             pObject->pNormals[i] = vSum / float(-shared);
547   
548             /* 规一化顶点法向 */
549             pObject->pNormals[i] = pObject->pNormals[i].normalize();
550             vSum = vZero;
551             shared = 0;
552         }
553         /* 释放存储空间,开始下一个对象 */
554         delete [] pTempNormals;
555         delete [] pNormals;
556     }
557 }

使用方法:

view source
print ?
1 //使用很简单,声明对象
2   C3DSLoader m_3DS;
3 //初始化
4 m_3DS.Init("d:/plane.3ds");
5 //绘制模型
6 m_3DS.Draw();

附上贴图:

 

http://www.beyondc.cn/opengl-3ds-model-of-reading-qt-version.html

你可能感兴趣的:(qt学习)