OpenGL读取3DS模型(Qt版)

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

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

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

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

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/* 面的结构定义 */
036struct tFace
037{
038        int vertIndex[3];       /* 顶点索引 */
039        int coordIndex[3];      /* 纹理坐标索引 */
040};
041  
042/* 材质信息结构体 */
043struct 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/* 对象信息结构体 */
056struct 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/* 模型信息结构体 */
071struct t3DModel
072{   int numOfObjects;       /* 模型中对象的数目 */
073        int numOfMaterials;     /* 模型中材质的数目 */
074        QVectorpMaterials;  /* 材质链表信息 */
075        QVector pObject;    /* 模型中对象链表信息 */
076};
077  
078/* 块信息的结构 */
079struct 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文件载入类 */
089class C3DSLoader
090{
091public:
092        /* 构造函数 */
093        C3DSLoader();
094        virtual ~C3DSLoader();
095        void Draw();//显示3ds模型
096        void Init(char *filename);
097  
098private:
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内容:

001#include "Stdafx.h"
002#include "3DSLoader.h"
003  
004/* 构造函数 */
005C3DSLoader::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模型 */
018void 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  
082void 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文件到模型结构中 */
109bool 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/*  读入一个字符串 */
155int 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号和它的字节长度 */
174void 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文件的主要部分 */
185void 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/* 处理所有的文件中对象的信息 */
289void 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/* 处理所有的材质信息 */
348void 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颜色 */
393void 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/* 读入顶点索引 */
404void 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坐标 */
422void 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/*  读入对象的顶点 */
435void 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/* 读入对象的材质名称 */
460void 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/* 计算对象的法向量 */
495void 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}

使用方法:

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

附上贴图:

 

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值