OpenGL一个重要应用就是能够读取外部的3D模型文件,比如OBJ,MD2,MD3,3DS等。在我之前的日志里已经写过一篇关于OpenGL读取Obj格式的类,详情可以看“Qt下学习OpenGL之OBJ模型”。而这次我要介绍的是3DS文件的读取。
接下来要贴出的代码已经在Qt4下成功运行,不过里面需要依赖一个CBMPLoader的类,这个可以从《OpenGL游戏编程》这本书里看到,也可以在我博客里找到对应的代码。
最后,还补充一下个人的看法。对于3DS格式,相比OBJ的话,其多了光照、贴图、材质等信息,但如果是工业标准,并且对于贴图和材质没有特别的要求的话,使用代码给每个不同物体写上一个单一颜色就好了。这样速度很快,速度是很重要的因素啊。
好了,贴代码了。
3DSLoader.h内容:
- #ifndef __3DSLOADER_H__
- #define __3DSLOADER_H__
-
- #include "Stdafx.h"
- #include "Vector.h"
- #include "CBMPLoader.h"
-
-
-
-
- #define PRIMARY 0x4D4D
-
-
- #define OBJECTINFO 0x3D3D /* 网格对象的版本号 */
- #define VERSION 0x0002 /* .3ds文件的版本 */
- #define EDITKEYFRAME 0xB000 /* 所有关键帧信息的头部 */
-
-
- #define MATERIAL 0xAFFF /* 纹理信息 */
- #define OBJECT 0x4000 /* 对象的面、顶点等信息 */
-
-
- #define MATNAME 0xA000 /* 材质名称 */
- #define MATDIFFUSE 0xA020 /* 对象/材质的颜色 */
- #define MATMAP 0xA200 /* 新材质的头部 */
- #define MATMAPFILE 0xA300 /* 保存纹理的文件名 */
- #define OBJ_MESH 0x4100 /* 新的网格对象 */
-
-
- #define OBJ_VERTICES 0x4110 /* 对象顶点 */
- #define OBJ_FACES 0x4120 /* 对象的面 */
- #define OBJ_MATERIAL 0x4130 /* 对象的材质 */
- #define OBJ_UV 0x4140 /* 对象的UV纹理坐标 */
-
-
- struct tFace
- {
- int vertIndex[3];
- int coordIndex[3];
- };
-
-
- struct tMatInfo
- {
- char strName[255];
- char strFile[255];
- unsigned char color[3];
- int texureId;
- float uTile;
- float vTile;
- float uOffset;
- float vOffset;
- } ;
-
-
- struct t3DObject
- {
- int numOfVerts;
- int numOfFaces;
- int numTexVertex;
- int materialID;
- bool bHasTexture;
- char strName[255];
- Vector3 *pVerts;
- Vector3 *pNormals;
- Vector2 *pTexVerts;
- tFace *pFaces;
- };
-
-
- struct t3DModel
- { int numOfObjects;
- int numOfMaterials;
- QVectorpMaterials;
- QVector pObject;
- };
-
-
- struct tChunk
- {
- unsigned short int ID;
- unsigned int length;
- unsigned int bytesRead;
- };
-
- #define MAX_TEXTURES 100
-
-
- class C3DSLoader
- {
- public:
-
- C3DSLoader();
- virtual ~C3DSLoader();
- void Draw();
- void Init(char *filename);
-
- private:
-
- int GetString(char *);
-
-
- bool Import3DS(t3DModel *pModel, char *strFileName);
-
-
- void LoadTexture(char* filename, GLuint textureArray[], GLuint textureID);
-
-
- void ReadChunk(tChunk *);
-
-
- void ReadNextChunk(t3DModel *pModel, tChunk *);
-
-
- void ReadNextObjChunk(t3DModel *pModel,t3DObject *pObject,tChunk *);
-
-
- void ReadNextMatChunk(t3DModel *pModel, tChunk *);
-
-
- void ReadColor(tMatInfo *pMaterial, tChunk *pChunk);
-
-
- void ReadVertices(t3DObject *pObject, tChunk *);
-
-
- void ReadVertexIndices(t3DObject *pObject,tChunk *);
-
-
- void ReadUVCoordinates(t3DObject *pObject,tChunk *);
-
-
- void ReadObjMat(t3DModel *pModel,t3DObject *pObject,tChunk *pPreChunk);
-
-
- void ComputeNormals(t3DModel *pModel);
-
-
- void CleanUp();
-
- FILE *m_FilePointer;
- tChunk *m_CurrentChunk;
- tChunk *m_TempChunk;
- GLuint m_textures[MAX_TEXTURES];
- t3DModel m_3DModel;
- CBMPLoader m_BMPTexture;
-
- };
-
- #endif
3DSLoader.cpp内容:
- #include "Stdafx.h"
- #include "3DSLoader.h"
-
-
- C3DSLoader::C3DSLoader()
- {
- m_CurrentChunk = new tChunk;
- m_TempChunk = new tChunk;
- m_3DModel.numOfObjects = 0;
- m_3DModel.numOfMaterials = 0;
- for(int i=0;i0)
- LoadTexture(m_3DModel.pMaterials[i].strFile,m_textures, i);
- m_3DModel.pMaterials[i].texureId = i;
- }
- }
-
-
- void C3DSLoader::Draw()
- {
-
- glPushAttrib(GL_CURRENT_BIT);
- glDisable(GL_TEXTURE_2D);
-
-
-
- for(int i = 0; i < m_3DModel.numOfObjects; i++)
- {
-
- if(m_3DModel.pObject.size() <= 0)
- break;
- t3DObject *pObject = &m_3DModel.pObject[i];
-
- if(pObject->bHasTexture)
- {
- glEnable(GL_TEXTURE_2D);
- glBindTexture(GL_TEXTURE_2D, m_textures[pObject->materialID]);
- }
- else
- glDisable(GL_TEXTURE_2D);
-
- glColor3ub(255, 255, 255);
-
-
-
- glBegin(GL_TRIANGLES);
-
-
- for(int j = 0; j < pObject->numOfFaces; j++)
- {
-
- for(int tex = 0; tex < 3; tex++)
- {
- int index = pObject->pFaces[j].vertIndex[tex];
-
-
- glNormal3f(pObject->pNormals[index].x,pObject->pNormals[index].y,
- pObject->pNormals[index].z);
-
-
- if(pObject->bHasTexture)
- {
-
- if(pObject->pTexVerts)
- glTexCoord2f(pObject->pTexVerts[index].x,pObject->pTexVerts[index].y);
- }
- else
- {
- if(m_3DModel.pMaterials.size() && pObject->materialID>= 0)
- {
- unsigned char *pColor = m_3DModel.pMaterials[pObject->materialID].color;
- glColor3ub(pColor[0],pColor[1],pColor[2]);
- }
- }
- glVertex3f(pObject->pVerts[index].x,pObject->pVerts[index].y,pObject->pVerts[index].z);
- }
- }
- glEnd();
-
- }
-
-
- glEnable(GL_TEXTURE_2D);
-
-
- glPopAttrib();
- }
-
- void C3DSLoader::LoadTexture(char* filename, GLuint textureArray[], GLuint textureID)
- {
- if(!filename)
- return;
-
- if(!this->m_BMPTexture.LoadBitmap(filename))
- {
- QMessageBox::warning(0,QObject::tr("Load Bitmap Error"),QObject::tr("Load Bitmap Error"));
- return;
- }
-
- glGenTextures(1,&m_textures[textureID]);
-
- glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
- glBindTexture(GL_TEXTURE_2D, m_textures[textureID]);
-
-
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR);
-
-
- gluBuild2DMipmaps(GL_TEXTURE_2D, 3, m_BMPTexture.imageWidth,m_BMPTexture.imageHeight
- , GL_RGB, GL_UNSIGNED_BYTE, this->m_BMPTexture.image);
-
- }
-
-
- bool C3DSLoader::Import3DS(t3DModel *pModel,char *strFileName)
- {
- char strMessage[255] = {0};
-
-
- m_FilePointer = fopen(strFileName, "rb");
-
-
- if(!m_FilePointer)
- {
- sprintf(strMessage, "Cann't find the file: %s!", strFileName);
- QMessageBox::information(NULL,
- "Error",
- strMessage,
- QMessageBox::Yes | QMessageBox::No,
- QMessageBox::Yes);
- return false;
- }
-
-
- ReadChunk(m_CurrentChunk);
-
-
- if (m_CurrentChunk->ID != PRIMARY)
- {
- QMessageBox::information(NULL,
- "Error",
- "Cann't Loading the Function",
- QMessageBox::Yes | QMessageBox::No,
- QMessageBox::Yes);
- return false;
- }
-
-
- ReadNextChunk(pModel, m_CurrentChunk);
-
-
- ComputeNormals(pModel);
-
-
- CleanUp();
-
- return true;
- }
-
-
- int C3DSLoader::GetString(char *pBuffer)
- {
- int index = 0;
-
-
- fread(pBuffer, 1, 1, m_FilePointer);
-
-
- while (*(pBuffer + index++) != 0)
- {
-
- fread(pBuffer + index, 1, 1, m_FilePointer);
- }
-
-
- return strlen(pBuffer) + 1;
- }
-
-
- void C3DSLoader::ReadChunk(tChunk *pChunk)
- {
-
- pChunk->bytesRead = fread(&pChunk->ID, 1, 2, m_FilePointer);
-
-
- pChunk->bytesRead += fread(&pChunk->length, 1, 4, m_FilePointer);
-
- }
-
-
- void C3DSLoader::ReadNextChunk(t3DModel *pModel, tChunk *pPreChunk)
- {
- t3DObject newObject = {0};
- tMatInfo newTexture = {0};
- unsigned int version = 0;
- int buffer[50000] = {0};
- m_CurrentChunk = new tChunk;
-
-
- while (pPreChunk->bytesRead < pPreChunk->length)
- {
-
- ReadChunk(m_CurrentChunk);
-
-
- switch (m_CurrentChunk->ID)
- {
-
-
- case VERSION:
-
-
- m_CurrentChunk->bytesRead += fread(&version
- , 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
-
-
- if (version > 0x03)
- QMessageBox::information(NULL,
- "Warning",
- "The 3DS File Version is wrong",
- QMessageBox::Yes | QMessageBox::No,
- QMessageBox::Yes);
- break;
-
-
- case OBJECTINFO:
-
-
- ReadChunk(m_TempChunk);
-
-
- m_TempChunk->bytesRead += fread(&version, 1
- , m_TempChunk->length - m_TempChunk->bytesRead, m_FilePointer);
-
-
- m_CurrentChunk->bytesRead += m_TempChunk->bytesRead;
-
-
- ReadNextChunk(pModel, m_CurrentChunk);
- break;
-
-
- case MATERIAL:
-
-
- pModel->numOfMaterials++;
-
-
- pModel->pMaterials.push_back(newTexture);
-
-
- ReadNextMatChunk(pModel, m_CurrentChunk);
- break;
-
-
- case OBJECT:
-
-
- pModel->numOfObjects++;
-
-
- pModel->pObject.push_back(newObject);
-
-
- memset(&(pModel->pObject[pModel->numOfObjects - 1])
- , 0, sizeof(t3DObject));
-
-
- m_CurrentChunk->bytesRead
- += GetString(pModel->pObject[pModel->numOfObjects - 1].strName);
-
-
- ReadNextObjChunk(pModel
- , &(pModel->pObject[pModel->numOfObjects - 1]), m_CurrentChunk);
- break;
-
-
- case EDITKEYFRAME:
-
-
- m_CurrentChunk->bytesRead += fread(buffer, 1
- , m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
- break;
-
- default:
-
- m_CurrentChunk->bytesRead += fread(buffer, 1
- , m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
- break;
- }
-
-
- pPreChunk->bytesRead += m_CurrentChunk->bytesRead;
- }
-
-
- delete m_CurrentChunk;
- m_CurrentChunk = pPreChunk;
- }
-
-
- void C3DSLoader::ReadNextObjChunk(t3DModel *pModel
- , t3DObject *pObject, tChunk *pPreChunk)
- {
-
- int buffer[50000] = {0};
-
-
- m_CurrentChunk = new tChunk;
-
-
- while (pPreChunk->bytesRead < pPreChunk->length)
- {
-
- ReadChunk(m_CurrentChunk);
-
-
- switch (m_CurrentChunk->ID)
- {
-
- case OBJ_MESH:
-
- ReadNextObjChunk(pModel, pObject, m_CurrentChunk);
- break;
-
-
- case OBJ_VERTICES:
- ReadVertices(pObject, m_CurrentChunk);
- break;
-
-
- case OBJ_FACES:
- ReadVertexIndices(pObject, m_CurrentChunk);
- break;
-
-
- case OBJ_MATERIAL:
-
- ReadObjMat(pModel, pObject, m_CurrentChunk);
- break;
-
-
- case OBJ_UV:
-
- ReadUVCoordinates(pObject, m_CurrentChunk);
- break;
-
- default:
-
- m_CurrentChunk->bytesRead += fread(buffer, 1
- , m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
- break;
- }
-
- pPreChunk->bytesRead += m_CurrentChunk->bytesRead;
- }
-
- delete m_CurrentChunk;
- m_CurrentChunk = pPreChunk;
- }
-
-
- void C3DSLoader::ReadNextMatChunk(t3DModel *pModel, tChunk *pPreChunk)
- {
-
- int buffer[50000] = {0};
-
-
- m_CurrentChunk = new tChunk;
-
-
- while (pPreChunk->bytesRead < pPreChunk->length)
- {
-
- ReadChunk(m_CurrentChunk);
-
- switch (m_CurrentChunk->ID)
- {
- case MATNAME:
-
- m_CurrentChunk->bytesRead
- += fread(pModel->pMaterials[pModel->numOfMaterials - 1].strName, 1
- , m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
- break;
- case MATDIFFUSE:
- ReadColor(&(pModel->pMaterials[pModel->numOfMaterials - 1]), m_CurrentChunk);
- break;
- case MATMAP:
-
- ReadNextMatChunk(pModel, m_CurrentChunk);
- break;
- case MATMAPFILE:
-
- m_CurrentChunk->bytesRead
- += fread(pModel->pMaterials[pModel->numOfMaterials - 1].strFile, 1
- , m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
- break;
- default:
-
- m_CurrentChunk->bytesRead += fread(buffer, 1
- , m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
- break;
- }
-
- pPreChunk->bytesRead += m_CurrentChunk->bytesRead;
- }
-
- delete m_CurrentChunk;
- m_CurrentChunk = pPreChunk;
- }
-
-
- void C3DSLoader::ReadColor(tMatInfo *pMaterial, tChunk *pChunk)
- {
-
- ReadChunk(m_TempChunk);
-
- m_TempChunk->bytesRead += fread(pMaterial->color, 1
- , m_TempChunk->length - m_TempChunk->bytesRead, m_FilePointer);
-
- pChunk->bytesRead += m_TempChunk->bytesRead;
- }
-
-
- void C3DSLoader::ReadVertexIndices(t3DObject *pObject, tChunk *pPreChunk)
- {
- unsigned short index = 0;
-
- pPreChunk->bytesRead += fread(&pObject->numOfFaces, 1, 2, m_FilePointer);
-
- pObject->pFaces = new tFace [pObject->numOfFaces];
- memset(pObject->pFaces, 0, sizeof(tFace) * pObject->numOfFaces);
-
- for(int i = 0; i < pObject->numOfFaces; i++)
- { for(int j = 0; j < 4; j++)
- {
-
- pPreChunk->bytesRead += fread(&index, 1, sizeof(index), m_FilePointer);
- if(j < 3)
- {
-
- pObject->pFaces[i].vertIndex[j] = index;
- }
- }
- }
- }
-
-
- void C3DSLoader::ReadUVCoordinates(t3DObject *pObject, tChunk *pPreChunk)
- {
-
- pPreChunk->bytesRead += fread(&pObject->numTexVertex, 1, 2, m_FilePointer);
-
-
- pObject->pTexVerts = new Vector2[pObject->numTexVertex];
-
-
- pPreChunk->bytesRead += fread(pObject->pTexVerts, 1
- , pPreChunk->length - pPreChunk->bytesRead, m_FilePointer);
- }
-
-
- void C3DSLoader::ReadVertices(t3DObject *pObject, tChunk *pPreChunk)
- {
-
- pPreChunk->bytesRead += fread(&(pObject->numOfVerts), 1, 2, m_FilePointer);
-
-
- pObject->pVerts = new Vector3 [pObject->numOfVerts];
- memset(pObject->pVerts, 0, sizeof(Vector3) * pObject->numOfVerts);
-
-
- pPreChunk->bytesRead += fread(pObject->pVerts, 1
- , pPreChunk->length - pPreChunk->bytesRead, m_FilePointer);
-
-
- for(int i = 0; i < pObject->numOfVerts; i++)
- {
-
- float fTempY = pObject->pVerts[i].y;
-
- pObject->pVerts[i].y = pObject->pVerts[i].z;
-
- pObject->pVerts[i].z = -fTempY;
- }
- }
-
-
- void C3DSLoader::ReadObjMat(t3DModel *pModel, t3DObject *pObject, tChunk *pPreChunk)
- {
- char strMaterial[255] = {0};
- int buffer[50000] = {0};
-
-
- pPreChunk->bytesRead += GetString(strMaterial);
-
-
- for(int i = 0; i < pModel->numOfMaterials; i++)
- {
-
- if(strcmp(strMaterial, pModel->pMaterials[i].strName) == 0)
- {
-
- pObject->materialID = i;
-
-
- if(strlen(pModel->pMaterials[i].strFile) > 0) {
-
-
- pObject->bHasTexture = true;
- }
- break;
- }
- else
- {
-
- pObject->materialID = -1;
- }
- }
- pPreChunk->bytesRead += fread(buffer, 1
- , pPreChunk->length - pPreChunk->bytesRead, m_FilePointer);
- }
-
-
- void C3DSLoader::ComputeNormals(t3DModel *pModel)
- {
- Vector3 vVector1, vVector2, vNormal, vPoly[3];
-
-
- if(pModel->numOfObjects <= 0)
- return;
-
-
- for(int index = 0; index < pModel->numOfObjects; index++)
- {
-
- t3DObject *pObject = &(pModel->pObject[index]);
-
-
- Vector3 *pNormals = new Vector3 [pObject->numOfFaces];
- Vector3 *pTempNormals = new Vector3 [pObject->numOfFaces];
- pObject->pNormals = new Vector3 [pObject->numOfVerts];
-
-
- for(int i=0; i < pObject->numOfFaces; i++)
- { vPoly[0] = pObject->pVerts[pObject->pFaces[i].vertIndex[0]];
- vPoly[1] = pObject->pVerts[pObject->pFaces[i].vertIndex[1]];
- vPoly[2] = pObject->pVerts[pObject->pFaces[i].vertIndex[2]];
-
-
- vVector1 = vPoly[0] - vPoly[2];
- vVector2 = vPoly[2] - vPoly[1];
- vNormal = vVector1.crossProduct(vVector2);
- pTempNormals[i] = vNormal;
- vNormal = vNormal.normalize();
- pNormals[i] = vNormal;
- }
-
-
- Vector3 vSum(0.0,0.0,0.0);
- Vector3 vZero = vSum;
- int shared=0;
-
-
- for (int i = 0; i < pObject->numOfVerts; i++)
- { for (int j = 0; j < pObject->numOfFaces; j++)
- {
- if (pObject->pFaces[j].vertIndex[0] == i ||
- pObject->pFaces[j].vertIndex[1] == i ||
- pObject->pFaces[j].vertIndex[2] == i)
- {
- vSum = vSum + pTempNormals[j];
- shared++;
- }
- }
- pObject->pNormals[i] = vSum / float(-shared);
-
-
- pObject->pNormals[i] = pObject->pNormals[i].normalize();
- vSum = vZero;
- shared = 0;
- }
-
- delete [] pTempNormals;
- delete [] pNormals;
- }
- }
使用方法:
-
- C3DSLoader m_3DS;
-
- m_3DS.Init("d:\plane.3ds");
-
- m_3DS.Draw();
附上贴图:
(民警小刘) |