参考

源代码:

解释:

CLoad3DS.h为加载3DMax模型的头文件,CLoad3DS.cpp为加载3DMax模型的实现文件,

nehewidget.h为Qt下使用OpenGL头文件,nehewidget.cpp为Qt下使用OpenGL实现文件。

注意:

1.3D模型和纹理图片资源需要放在源代码同一目录下的Data目录中,即/Data/3DS和/Data/pic下。

2.图标和其他纹理图片存放在Resources文件夹下。

CLoad3DS.h:

#ifndef _CLoad3DS_h_
#define _CLoad3DS_h_#include <windows.h>
#include <cassert>
#include <cmath>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <vector>                 #include <olectl.h>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <direct.h>//初始化OpenGL环境
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>#pragma   comment(lib,"opengl32.lib")
#pragma   comment(lib,"glu32.lib")#define PICPATH "\\Data\\pic\\"  //纹理资源的地址// 基本块(Primary Chunk),位于文件的开始
#define PRIMARY 0x4D4D// 主块(Main Chunks)
#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 OBJECT_MESH 0x4100    // 新的网格对象// OBJECT_MESH的次级定义
#define OBJECT_VERTICES 0x4110      // 对象顶点
#define OBJECT_FACES    0x4120      // 对象的面
#define OBJECT_MATERIAL    0x4130   // 对象的材质
#define OBJECT_UV      0x4140       // 对象的UV纹理坐标// 下面的宏定义计算一个矢量的长度
#define Mag(Normal) (sqrt(Normal.x*Normal.x + Normal.y*Normal.y + Normal.z*Normal.z))
#define MAX_TEXTURES 100            // 最大的纹理数目using namespace std;
class NBVector3
{
public:NBVector3() {}NBVector3(float X, float Y, float Z){x = X; y = Y; z = Z;}inline NBVector3 operator+(NBVector3 vVector){return NBVector3(vVector.x + x, vVector.y + y, vVector.z + z);}inline NBVector3 operator-(NBVector3 vVector){return NBVector3(x - vVector.x, y - vVector.y, z - vVector.z);}inline NBVector3 operator-(){return NBVector3(-x, -y, -z);}inline NBVector3 operator*(float num){return NBVector3(x * num, y * num, z * num);}inline NBVector3 operator/(float num){return NBVector3(x / num, y / num, z / num);}inline NBVector3 operator^(const NBVector3 &rhs) const{return NBVector3(y * rhs.z - rhs.y * z, rhs.x * z - x * rhs.z, x * rhs.y - rhs.x * y);}union{struct{float x;float y;float z;};float v[3];};
};// 定义2D点类,用于保存模型的UV纹理坐标
class CVector2
{
public:float x, y;
};// 面的结构定义
struct tFace
{int vertIndex[3];      // 顶点索引int coordIndex[3];     // 纹理坐标索引
};// 材质信息结构体
struct tMaterialInfo
{char strName[255];      // 纹理名称char strFile[255];      // 如果存在纹理映射,则表示纹理文件名称BYTE color[3];          // 对象的RGB颜色int texureId;           // 纹理IDfloat uTile;            // u 重复float vTile;            // v 重复float uOffset;          // u 纹理偏移float vOffset;          // v 纹理偏移
} ;// 对象信息结构体
struct t3DObject
{int numOfVerts;      // 模型中顶点的数目int numOfFaces;      // 模型中面的数目int numTexVertex;    // 模型中纹理坐标的数目int materialID;      // 纹理IDbool bHasTexture;    // 是否具有纹理映射char strName[255];   // 对象的名称NBVector3 *pVerts;   // 对象的顶点NBVector3 *pNormals; // 对象的法向量CVector2 *pTexVerts; // 纹理UV坐标tFace *pFaces;       // 对象的面信息
};// 模型信息结构体
struct t3DModel
{UINT texture[MAX_TEXTURES];int numOfObjects;                  // 模型中对象的数目int numOfMaterials;                // 模型中材质的数目vector<tMaterialInfo> pMaterials;  // 材质链表信息vector<t3DObject> pObject;         // 模型中对象链表信息
};struct tIndices
{             unsigned short a, b, c, bVisible;
};// 保存块信息的结构
struct tChunk
{unsigned short int ID;           // 块的ID   unsigned int length;             // 块的长度unsigned int bytesRead;          // 需要读的块数据的字节数
};typedef struct tagBoundingBoxStruct
{NBVector3  BoxPosMaxVertex;NBVector3  BoxNegMaxVertex;
} BoundingBoxVertex2;// 下面的函数求两点决定的矢量
NBVector3 Vector(NBVector3 vPoint1, NBVector3 vPoint2);
// 下面的函数两个矢量相加
NBVector3 AddVector(NBVector3 vVector1, NBVector3 vVector2);
// 下面的函数处理矢量的缩放
NBVector3 DivideVectorByScaler(NBVector3 vVector1, float Scaler);
// 下面的函数返回两个矢量的叉积
NBVector3 Cross(NBVector3 vVector1, NBVector3 vVector2);
// 下面的函数归一化矢量
NBVector3 Normalize(NBVector3 vNormal);//
#define FRAND   (((float)rand()-(float)rand())/RAND_MAX)
#define Clamp(x, min, max)  x = (x<min  ? min : x<max ? x : max);
#define SQUARE(x)  (x)*(x)
struct vector3_t
{vector3_t(float x, float y, float z) : x(x), y(y), z(z) {}vector3_t(const vector3_t &v) : x(v.x), y(v.y), z(v.z) {}vector3_t() : x(0.0f), y(0.0f), z(0.0f) {}vector3_t& operator=(const vector3_t &rhs){x = rhs.x;y = rhs.y;z = rhs.z;return *this;}// vector addvector3_t operator+(const vector3_t &rhs) const{return vector3_t(x + rhs.x, y + rhs.y, z + rhs.z);}// vector subtractvector3_t operator-(const vector3_t &rhs) const{return vector3_t(x - rhs.x, y - rhs.y, z - rhs.z);}// scalar multiplicationvector3_t operator*(const float scalar) const{return vector3_t(x * scalar, y * scalar, z * scalar);}// dot productfloat operator*(const vector3_t &rhs) const{return x * rhs.x + y * rhs.y + z * rhs.z;}// cross productvector3_t operator^(const vector3_t &rhs) const{return vector3_t(y * rhs.z - rhs.y * z, rhs.x * z - x * rhs.z, x * rhs.y - rhs.x * y);}float& operator[](int index){return v[index];}float Length(){float length = (float)sqrt(SQUARE(x) + SQUARE(y) + SQUARE(z));return (length != 0.0f) ? length : 1.0f;}/*****************************************************************************Normalize()Helper function to normalize vectors*****************************************************************************/vector3_t Normalize(){*this = *this * (1.0f/Length());return *this;}union{struct{float x;float y;float z;};float v[3];};
};// CLoad3DS类处理所有的装入代码
class CLoad3DS
{
public:CLoad3DS();                // 初始化数据成员// 装入3ds文件到模型结构中bool Import3DS(t3DModel *pModel, char *strFileName);private:// 读入一个纹理int BuildTexture(char *szPathName, GLuint &texid);// 读一个字符串int GetString(char *);// 读下一个块void ReadChunk(tChunk *);// 读下一个块void ProcessNextChunk(t3DModel *pModel, tChunk *);// 读下一个对象块void ProcessNextObjectChunk(t3DModel *pModel, t3DObject *pObject, tChunk *);// 读下一个材质块void ProcessNextMaterialChunk(t3DModel *pModel, tChunk *);// 读对象颜色的RGB值void ReadColorChunk(tMaterialInfo *pMaterial, tChunk *pChunk);// 读对象的顶点void ReadVertices(t3DObject *pObject, tChunk *);// 读对象的面信息void ReadVertexIndices(t3DObject *pObject, tChunk *);// 读对象的纹理坐标void ReadUVCoordinates(t3DObject *pObject, tChunk *);// 读赋予对象的材质名称void ReadObjectMaterial(t3DModel *pModel, t3DObject *pObject, tChunk *pPreviousChunk);// 计算对象顶点的法向量void ComputeNormals(t3DModel *pModel);// 关闭文件,释放内存空间void CleanUp();// 文件指针FILE *m_FilePointer;tChunk *m_CurrentChunk;tChunk *m_TempChunk;
};
void changeObject(float trans[10]);
void drawModel(t3DModel Model,bool touming,bool outTex);
#endif

CLoad3DS.cpp:

#include "CLoad3DS.h"#pragma warning (disable: 4996)// 下面的函数求两点决定的矢量
NBVector3 Vector(NBVector3 vPoint1, NBVector3 vPoint2)
{NBVector3 vVector;             vVector.x = vPoint1.x - vPoint2.x;     vVector.y = vPoint1.y - vPoint2.y;     vVector.z = vPoint1.z - vPoint2.z;     return vVector;
}// 下面的函数两个矢量相加
NBVector3 AddVector(NBVector3 vVector1, NBVector3 vVector2)
{NBVector3 vResult;             vResult.x = vVector2.x + vVector1.x;   vResult.y = vVector2.y + vVector1.y;   vResult.z = vVector2.z + vVector1.z;   return vResult;
}// 下面的函数处理矢量的缩放
NBVector3 DivideVectorByScaler(NBVector3 vVector1, float Scaler)
{NBVector3 vResult;             vResult.x = vVector1.x / Scaler;     vResult.y = vVector1.y / Scaler;     vResult.z = vVector1.z / Scaler;       return vResult;
}// 下面的函数返回两个矢量的叉积
NBVector3 Cross(NBVector3 vVector1, NBVector3 vVector2)
{NBVector3 vCross;                  vCross.x = ((vVector1.y * vVector2.z) - (vVector1.z * vVector2.y));vCross.y = ((vVector1.z * vVector2.x) - (vVector1.x * vVector2.z));vCross.z = ((vVector1.x * vVector2.y) - (vVector1.y * vVector2.x));return vCross;
}// 下面的函数归一化矢量
NBVector3 Normalize(NBVector3 vNormal)
{double Magnitude;              Magnitude = Mag(vNormal);          // 获得矢量的长度  vNormal.x /= (float)Magnitude;       vNormal.y /= (float)Magnitude;       vNormal.z /= (float)Magnitude;         return vNormal;
}// 读入一个纹理
int CLoad3DS::BuildTexture(char *szPathName, GLuint &texid)
{HDC      hdcTemp;                        // The DC To Hold Our BitmapHBITMAP    hbmpTemp;                     // Holds The Bitmap TemporarilyLPPICTURE  pPicture;                     // IPicture Interface,此处较参考网址的博主的代码做了修改OLECHAR    wszPath[MAX_PATH+1];          // Full Path To Picture (WCHAR)char    szPath[MAX_PATH+1];         // Full Path To Picturelong    lWidth;                          // Width In Logical Unitslong    lHeight;                         // Height In Logical Unitslong    lWidthPixels;                    // Width In Pixelslong    lHeightPixels;                   // Height In PixelsGLint    glMaxTexDim ;                   // Holds Maximum Texture Sizeif (strstr(szPathName, "http://"))       // If PathName Contains http:// Then...{strcpy(szPath, szPathName);          // Append The PathName To szPath}else                                     // Otherwise... We Are Loading From A File{getcwd(szPath,MAX_PATH);              // Get Our Working Directory,此处较参考网址的博主的代码做了修改strcat(szPath, PICPATH);              // Append "\" After The Working Directorystrcat(szPath, szPathName);           // Append The PathName}MultiByteToWideChar(CP_ACP, 0, szPath, -1, wszPath, MAX_PATH);    // Convert From ASCII To UnicodeHRESULT hr = OleLoadPicturePath(wszPath, 0, 0, 0, IID_IPicture, reinterpret_cast<LPVOID *>(&pPicture));//此处较参考网址的博主的代码做了修改if(FAILED(hr))                             // If Loading Failedreturn FALSE;                          // Return FalsehdcTemp = CreateCompatibleDC(GetDC(0));    // Create The Windows Compatible Device Contextif(!hdcTemp)                               // Did Creation Fail?{pPicture->Release();                   // Decrements IPicture Reference Countreturn FALSE;                          // Return False (Failure)}glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glMaxTexDim);   // Get Maximum Texture Size SupportedpPicture->get_Width(&lWidth);                       // Get IPicture Width (Convert To Pixels)lWidthPixels  = MulDiv(lWidth, GetDeviceCaps(hdcTemp, LOGPIXELSX), 2540);pPicture->get_Height(&lHeight);                     // Get IPicture Height (Convert To Pixels)lHeightPixels  = MulDiv(lHeight, GetDeviceCaps(hdcTemp, LOGPIXELSY), 2540);// Resize Image To Closest Power Of Twoif (lWidthPixels <= glMaxTexDim) // Is Image Width Less Than Or Equal To Cards LimitlWidthPixels = 1 << (int)floor((log((double)lWidthPixels)/log(2.0f)) + 0.5f);else // Otherwise Set Width To "Max Power Of Two" That The Card Can HandlelWidthPixels = glMaxTexDim;if (lHeightPixels <= glMaxTexDim) // Is Image Height Greater Than Cards LimitlHeightPixels = 1 << (int)floor((log((double)lHeightPixels)/log(2.0f)) + 0.5f);else // Otherwise Set Height To "Max Power Of Two" That The Card Can HandlelHeightPixels = glMaxTexDim;//  Create A Temporary BitmapBITMAPINFO  bi = {0};                       // The Type Of Bitmap We RequestDWORD    *pBits = 0;                        // Pointer To The Bitmap Bitsbi.bmiHeader.biSize      = sizeof(BITMAPINFOHEADER); // Set Structure Sizebi.bmiHeader.biBitCount  = 32;                       // 32 Bitbi.bmiHeader.biWidth     = lWidthPixels;             // Power Of Two Widthbi.bmiHeader.biHeight    = lHeightPixels;            // Make Image Top Up (Positive Y-Axis)bi.bmiHeader.biCompression  = BI_RGB;                // RGB Encodingbi.bmiHeader.biPlanes    = 1;                        // 1 Bitplane//  Creating A Bitmap This Way Allows Us To Specify Color Depth And Gives Us Imediate Access To The BitshbmpTemp = CreateDIBSection(hdcTemp, &bi, DIB_RGB_COLORS, (void**)&pBits, 0, 0);if(!hbmpTemp)                                 // Did Creation Fail?{DeleteDC(hdcTemp);                        // Delete The Device ContextpPicture->Release();                      // Decrements IPicture Reference Countreturn FALSE;                             // Return False (Failure)}SelectObject(hdcTemp, hbmpTemp);              // Select Handle To Our Temp DC And Our Temp Bitmap Object// Render The IPicture On To The BitmappPicture->Render(hdcTemp, 0, 0, lWidthPixels, lHeightPixels, 0, lHeight, lWidth, -lHeight, 0);// Convert From BGR To RGB Format And Add An Alpha Value Of 255for(long i = 0; i < lWidthPixels * lHeightPixels; i++)        // Loop Through All Of The Pixels{BYTE* pPixel  = (BYTE*)(&pBits[i]);              // Grab The Current PixelBYTE temp    = pPixel[0];                        // Store 1st Color In Temp Variable (Blue)pPixel[0]    = pPixel[2];                        // Move Red Value To Correct Position (1st)pPixel[2]    = temp;                             // Move Temp Value To Correct Blue Position (3rd)// This Will Make Any Black Pixels, Completely Transparent    (You Can Hardcode The Value If You Wish)if ((pPixel[0]==0) && (pPixel[1]==0) && (pPixel[2]==0))      // Is Pixel Completely BlackpPixel[3]  = 0;                         // Set The Alpha Value To 0else                                        // OtherwisepPixel[3]  = 255;                       // Set The Alpha Value To 255}glGenTextures(1, &texid);                       // Create The Texture// Typical Texture Generation Using Data From The BitmapglBindTexture(GL_TEXTURE_2D, texid);                            // Bind To The Texture IDglTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // (Modify This For The Type Of Filtering You Want)glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // (Modify This For The Type Of Filtering You Want)glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, lWidthPixels, lHeightPixels, 0, GL_RGBA, GL_UNSIGNED_BYTE, pBits);  // (Modify This If You Want Mipmaps)DeleteObject(hbmpTemp);                        // Delete The ObjectDeleteDC(hdcTemp);                             // Delete The Device ContextpPicture->Release();                           // Decrements IPicture Reference Countprintf( "load %s!" , szPath );// Return True (All is okay)return TRUE;
}// 构造函数的功能是初始化tChunk数据
CLoad3DS::CLoad3DS()
{m_CurrentChunk = new tChunk;        // 初始化并为当前的块分配空间m_TempChunk = new tChunk;          // 初始化一个临时块并分配空间
}// 打开一个3ds文件,读出其中的内容,并释放内存
bool CLoad3DS::Import3DS(t3DModel *pModel, char *strFileName)
{char strMessage[255] = {0};// 打开一个3ds文件m_FilePointer = fopen(strFileName,"rb");// 确保所获得的文件指针合法if(!m_FilePointer){sprintf(strMessage, "Unable to find the file: %s!", strFileName);MessageBox(NULL, (LPWSTR)strMessage, (LPWSTR)"Error", MB_OK);return false;}// 当文件打开之后,首先应该将文件最开始的数据块读出以判断是否是一个3ds文件// 如果是3ds文件的话,第一个块ID应该是PRIMARY// 将文件的第一块读出并判断是否是3ds文件ReadChunk(m_CurrentChunk);// 确保是3ds文件if (m_CurrentChunk->ID != PRIMARY){sprintf(strMessage, "Unable to load PRIMARY chuck from file: %s!", strFileName);MessageBox(NULL, (LPWSTR)strMessage, (LPWSTR)"Error", MB_OK);return false;}// 现在开始读入数据,ProcessNextChunk()是一个递归函数// 通过调用下面的递归函数,将对象读出ProcessNextChunk(pModel, m_CurrentChunk);// 在读完整个3ds文件之后,计算顶点的法线ComputeNormals(pModel);// 释放内存空间CleanUp();return true;
}// 下面的函数释放所有的内存空间,并关闭文件
void CLoad3DS::CleanUp()
{fclose(m_FilePointer);            // 关闭当前的文件指针delete m_CurrentChunk;            // 释放当前块delete m_TempChunk;              // 释放临时块
}// 下面的函数读出3ds文件的主要部分
void CLoad3DS::ProcessNextChunk(t3DModel *pModel, tChunk *pPreviousChunk)
{t3DObject newObject = {0};          // 用来添加到对象链表tMaterialInfo newTexture = {0};     // 用来添加到材质链表unsigned int version = 0;           // 保存文件版本int buffer[50000] = {0};            // 用来跳过不需要的数据m_CurrentChunk = new tChunk;        // 为新的块分配空间   // 下面每读一个新块,都要判断一下块的ID,如果该块是需要的读入的,则继续进行// 如果是不需要读入的块,则略过// 继续读入子块,直到达到预定的长度while (pPreviousChunk->bytesRead < pPreviousChunk->length){// 读入下一个块ReadChunk(m_CurrentChunk);// 判断块的ID号switch (m_CurrentChunk->ID){case VERSION:              // 文件版本号// 在该块中有一个无符号短整型数保存了文件的版本// 读入文件的版本号,并将字节数添加到bytesRead变量中m_CurrentChunk->bytesRead += fread(&version, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);// 如果文件版本号大于3,给出一个警告信息if (version > 0x03)MessageBox(NULL, (LPWSTR)"This 3DS file is over version 3 so it may load incorrectly", (LPWSTR)"Warning", MB_OK);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;// 进入下一个块ProcessNextChunk(pModel, m_CurrentChunk);break;case MATERIAL:              // 材质信息// 材质的数目递增pModel->numOfMaterials++;// 在纹理链表中添加一个空白纹理结构pModel->pMaterials.push_back(newTexture);// 进入材质装入函数ProcessNextMaterialChunk(pModel, m_CurrentChunk);break;case OBJECT:              // 对象的名称// 该块是对象信息块的头部,保存了对象了名称// 对象数递增pModel->numOfObjects++;// 添加一个新的tObject节点到对象链表中pModel->pObject.push_back(newObject);// 初始化对象和它的所有数据成员memset(&(pModel->pObject[pModel->numOfObjects - 1]), 0, sizeof(t3DObject));// 获得并保存对象的名称,然后增加读入的字节数m_CurrentChunk->bytesRead += GetString(pModel->pObject[pModel->numOfObjects - 1].strName);// 进入其余的对象信息的读入ProcessNextObjectChunk(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;}// 增加从最后块读入的字节数pPreviousChunk->bytesRead += m_CurrentChunk->bytesRead;}// 释放当前块的内存空间delete m_CurrentChunk;m_CurrentChunk = pPreviousChunk;
}// 下面的函数处理所有的文件中对象的信息
void CLoad3DS::ProcessNextObjectChunk(t3DModel *pModel, t3DObject *pObject, tChunk *pPreviousChunk)
{int buffer[50000] = {0};          // 用于读入不需要的数据// 对新的块分配存储空间m_CurrentChunk = new tChunk;// 继续读入块的内容直至本子块结束while (pPreviousChunk->bytesRead < pPreviousChunk->length){// 读入下一个块ReadChunk(m_CurrentChunk);// 区别读入是哪种块switch (m_CurrentChunk->ID){case OBJECT_MESH:          // 正读入的是一个新块// 使用递归函数调用,处理该新块ProcessNextObjectChunk(pModel, pObject, m_CurrentChunk);break;case OBJECT_VERTICES:        // 读入是对象顶点ReadVertices(pObject, m_CurrentChunk);break;case OBJECT_FACES:          // 读入的是对象的面ReadVertexIndices(pObject, m_CurrentChunk);break;case OBJECT_MATERIAL:        // 读入的是对象的材质名称// 该块保存了对象材质的名称,可能是一个颜色,也可能是一个纹理映射。同时在该块中也保存了// 纹理对象所赋予的面// 下面读入对象的材质名称ReadObjectMaterial(pModel, pObject, m_CurrentChunk);     break;case OBJECT_UV:            // 读入对象的UV纹理坐标// 读入对象的UV纹理坐标ReadUVCoordinates(pObject, m_CurrentChunk);break;default:// 略过不需要读入的块m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);break;}// 添加从最后块中读入的字节数到前面的读入的字节中pPreviousChunk->bytesRead += m_CurrentChunk->bytesRead;}// 释放当前块的内存空间,并把当前块设置为前面块delete m_CurrentChunk;m_CurrentChunk = pPreviousChunk;
}// 下面的函数处理所有的材质信息
void CLoad3DS::ProcessNextMaterialChunk(t3DModel *pModel, tChunk *pPreviousChunk)
{int buffer[50000] = {0};          // 用于读入不需要的数据// 给当前块分配存储空间m_CurrentChunk = new tChunk;// 继续读入这些块,知道该子块结束while (pPreviousChunk->bytesRead < pPreviousChunk->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:            // 对象的R G B颜色ReadColorChunk(&(pModel->pMaterials[pModel->numOfMaterials - 1]), m_CurrentChunk);break;case MATMAP:              // 纹理信息的头部// 进入下一个材质块信息ProcessNextMaterialChunk(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;}// 添加从最后块中读入的字节数pPreviousChunk->bytesRead += m_CurrentChunk->bytesRead;}// 删除当前块,并将当前块设置为前面的块delete m_CurrentChunk;m_CurrentChunk = pPreviousChunk;
}// 下面函数读入块的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);
}// 下面的函数读入一个字符串
int CLoad3DS::GetString(char *pBuffer)
{int index = 0;// 读入一个字节的数据fread(pBuffer, 1, 1, m_FilePointer);// 直到结束while (*(pBuffer + index++) != 0) {// 读入一个字符直到NULLfread(pBuffer + index, 1, 1, m_FilePointer);}// 返回字符串的长度return strlen(pBuffer) + 1;
}// 下面的函数读入RGB颜色
void CLoad3DS::ReadColorChunk(tMaterialInfo *pMaterial, tChunk *pChunk)
{// 读入颜色块信息ReadChunk(m_TempChunk);// 读入RGB颜色m_TempChunk->bytesRead += fread(pMaterial->color, 1, m_TempChunk->length - m_TempChunk->bytesRead, m_FilePointer);// 增加读入的字节数pChunk->bytesRead += m_TempChunk->bytesRead;
}// 下面的函数读入顶点索引
void CLoad3DS::ReadVertexIndices(t3DObject *pObject, tChunk *pPreviousChunk)
{unsigned short index = 0;  // 用于读入当前面的索引// 读入该对象中面的数目pPreviousChunk->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++){// 读入当前面的第一个点pPreviousChunk->bytesRead += fread(&index, 1, sizeof(index), m_FilePointer);if(j < 3){// 将索引保存在面的结构中pObject->pFaces[i].vertIndex[j] = index;}}}
}// 下面的函数读入对象的UV坐标
void CLoad3DS::ReadUVCoordinates(t3DObject *pObject, tChunk *pPreviousChunk)
{// 为了读入对象的UV坐标,首先需要读入UV坐标的数量,然后才读入具体的数据// 读入UV坐标的数量pPreviousChunk->bytesRead += fread(&pObject->numTexVertex, 1, 2, m_FilePointer);// 分配保存UV坐标的内存空间pObject->pTexVerts = new CVector2 [pObject->numTexVertex];// 读入纹理坐标pPreviousChunk->bytesRead += fread(pObject->pTexVerts, 1, pPreviousChunk->length - pPreviousChunk->bytesRead, m_FilePointer);
}// 读入对象的顶点
void CLoad3DS::ReadVertices(t3DObject *pObject, tChunk *pPreviousChunk)
{// 在读入实际的顶点之前,首先必须确定需要读入多少个顶点。// 读入顶点的数目pPreviousChunk->bytesRead += fread(&(pObject->numOfVerts), 1, 2, m_FilePointer);// 分配顶点的存储空间,然后初始化结构体pObject->pVerts = new NBVector3 [pObject->numOfVerts];memset(pObject->pVerts, 0, sizeof(NBVector3) * pObject->numOfVerts);// 读入顶点序列pPreviousChunk->bytesRead += fread(pObject->pVerts, 1, pPreviousChunk->length - pPreviousChunk->bytesRead, m_FilePointer);// 现在已经读入了所有的顶点。// 因为3D Studio Max的模型的Z轴是指向上的,因此需要将y轴和z轴翻转过来。// 具体的做法是将Y轴和Z轴交换,然后将Z轴反向。// 遍历所有的顶点for(int i = 0; i < pObject->numOfVerts; i++){// 保存Y轴的值float fTempY = pObject->pVerts[i].y;// 设置Y轴的值等于Z轴的值pObject->pVerts[i].y = pObject->pVerts[i].z;// 设置Z轴的值等于-Y轴的值pObject->pVerts[i].z = -fTempY;}
}// 下面的函数读入对象的材质名称
void CLoad3DS::ReadObjectMaterial(t3DModel *pModel, t3DObject *pObject, tChunk *pPreviousChunk)
{char strMaterial[255] = {0};      // 用来保存对象的材质名称int buffer[50000] = {0};        // 用来读入不需要的数据// 材质或者是颜色,或者是对象的纹理,也可能保存了象明亮度、发光度等信息。// 下面读入赋予当前对象的材质名称pPreviousChunk->bytesRead += GetString(strMaterial);// 遍历所有的纹理for(int i = 0; i < pModel->numOfMaterials; i++){//如果读入的纹理与当前的纹理名称匹配if(strcmp(strMaterial, pModel->pMaterials[i].strName) == 0){// 设置材质IDpObject->materialID = i;// 判断是否是纹理映射,如果strFile是一个长度大于1的字符串,则是纹理if(strlen(pModel->pMaterials[i].strFile) > 0) {//载入纹理BuildTexture(pModel->pMaterials[i].strFile, pModel->texture[pObject->materialID]);// 设置对象的纹理映射标志pObject->bHasTexture = true;char strMessage[100];sprintf(strMessage, "file name : %s!", pModel->pMaterials[i].strFile);printf( "%s\n" , strMessage );
//              MessageBox(NULL, strMessage, "Error", MB_OK);} break;}else{// 如果该对象没有材质,则设置ID为-1pObject->materialID = -1;}}pPreviousChunk->bytesRead += fread(buffer, 1, pPreviousChunk->length - pPreviousChunk->bytesRead, m_FilePointer);
}     // 下面的这些函数主要用来计算顶点的法向量,顶点的法向量主要用来计算光照
// 下面的函数用于计算对象的法向量
void CLoad3DS::ComputeNormals(t3DModel *pModel)
{NBVector3 vVector1, vVector2, vNormal, vPoly[3];// 如果模型中没有对象,则返回if(pModel->numOfObjects <= 0)return;// 遍历模型中所有的对象for(int index = 0; index < pModel->numOfObjects; index++){// 获得当前的对象t3DObject *pObject = &(pModel->pObject[index]);// 分配需要的存储空间NBVector3 *pNormals    = new NBVector3 [pObject->numOfFaces];NBVector3 *pTempNormals  = new NBVector3 [pObject->numOfFaces];pObject->pNormals    = new NBVector3 [pObject->numOfVerts];int i=0;// 遍历对象的所有面for(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 = Vector(vPoly[0], vPoly[2]);    // 获得多边形的矢量vVector2 = Vector(vPoly[2], vPoly[1]);    // 获得多边形的第二个矢量vNormal = Cross(vVector1, vVector2);    // 获得两个矢量的叉积pTempNormals[i] = vNormal;          // 保存非规范化法向量vNormal = Normalize(vNormal);        // 规范化获得的叉积pNormals[i] = vNormal;            // 将法向量添加到法向量列表中}// 下面求顶点法向量NBVector3 vSum (0.0, 0.0, 0.0);NBVector3 vZero = vSum;int shared=0;// 遍历所有的顶点for (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 = AddVector(vSum, pTempNormals[j]);shared++;               }}pObject->pNormals[i] = DivideVectorByScaler(vSum, float(-shared));// 规范化最后的顶点法向pObject->pNormals[i] = Normalize(pObject->pNormals[i]); vSum = vZero;               shared = 0;                   }// 释放存储空间,开始下一个对象delete [] pTempNormals;delete [] pNormals;}
}
// 对模型进行移动变换等操作
void changeObject(float trans[10])
{glTranslatef(trans[0],trans[1],trans[2]);glScalef(trans[3],trans[4],trans[5]);glRotatef(trans[6],trans[7],trans[8],trans[9]);
}
// 绘制模型
void drawModel(t3DModel Model,bool touming,bool outTex)
{  if( touming ){glEnable(GL_BLEND);glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);glColor4f(1,1,1,0.5);}for(int i = 0; i < Model.numOfObjects; i++){t3DObject *pObject = &Model.pObject[i];if(!outTex){if(pObject->bHasTexture){glEnable(GL_TEXTURE_2D);glBindTexture(GL_TEXTURE_2D, Model.texture[pObject->materialID]);}else{glDisable(GL_TEXTURE_2D);glColor3ub(255, 255, 255);}}glBegin(GL_TRIANGLES);         for(int j = 0; j < pObject->numOfFaces; j++){for(int whichVertex = 0; whichVertex < 3; whichVertex++){int index = pObject->pFaces[j].vertIndex[whichVertex];glNormal3f(pObject->pNormals[ index ].x, pObject->pNormals[ index ].y, pObject->pNormals[ index ].z);if(pObject->bHasTexture){if(pObject->pTexVerts){glColor3f(1.0,1.0,1.0);glTexCoord2f(pObject->pTexVerts[ index ].x, pObject->pTexVerts[ index ].y);}}else{                  if(Model.pMaterials.size() && pObject->materialID >= 0){BYTE *pColor = Model.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();}if( touming )glDisable(GL_BLEND);
}

nehewidget.h:

#ifndef NEHEWIDGET_H
#define NEHEWIDGET_H#include <QGLWidget>
#include <QKeyEvent>
#include <QTimer>
#include <QImage>
#include <QMessageBox>
#include <QIcon>
#include <QDebug>
#include <GL/glu.h>
#include <CLoad3DS.h>class NeHeWidget : public QGLWidget
{Q_OBJECT
public:explicit NeHeWidget(QWidget *parent = 0);~NeHeWidget();protected:void initializeGL();void paintGL();void resizeGL(int w, int h);void keyPressEvent(QKeyEvent *);private:bool fullscreen;float xRot,yRot,zRot;GLuint texture[3];void initLight();void drawObject();void init3DMAX();signals:public slots:};#endif // NEHEWIDGET_H

nehewidget.cpp:

#include "nehewidget.h"GLfloat lightAmbient[4] = { 0.5, 0.5, 0.5, 1.0 };
GLfloat lightDiffuse[4] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat lightPosition[4] = { 0.0, 0.0, 2.0, 1.0 };CLoad3DS *gothicLoader=new CLoad3DS;
t3DModel gothicModel;NeHeWidget::NeHeWidget(QWidget *parent) :QGLWidget(parent)
{this->setWindowTitle(tr("OpenGL加载3DS模型"));this->setWindowIcon(QIcon(":/Textures/Resources/Desert.jpg"));resize(640,480);xRot = 0.0;zRot = 0.0;yRot = 0.0;
}NeHeWidget::~NeHeWidget()
{}//*************************************
// 作    者:  朱兴宇
// 时    间:  2015/4/16 15:08
// 权    限:  protected
// 返    回:  void
// 方法说明:  OpenGL初始化
//*************************************
void NeHeWidget::initializeGL()
{//使用阴影平滑glShadeModel(GL_SMOOTH);//黑色清屏r,g,b,alphaglClearColor(0.0,0.0,0.0,0.0);//设置深度缓存glClearDepth(1.0);//启用深度缓存glEnable(GL_DEPTH_TEST);//启用深度测试的类型glDepthFunc(GL_LEQUAL);//使用透视glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);//初始化灯光initLight();//初始化3D模型init3DMAX();
}//*************************************
// 作    者:  朱兴宇
// 时    间:  2015/4/16 15:07
// 权    限:  protected
// 返    回:  void
// 方法说明:  窗口大小变化
//*************************************
void NeHeWidget::resizeGL(int w, int h)
{if(h == 0){h = 1;}                                   // Far//重置当前视口glViewport(0,0,(GLint)w,(GLint)h);//选择投影矩阵glMatrixMode(GL_PROJECTION);glLoadIdentity();gluPerspective(60.0, (GLfloat)w/(GLfloat)h, 1.0, 100.0);//建立透视投影矩阵glMatrixMode(GL_MODELVIEW);glLoadIdentity();
}//*************************************
// 作    者:  朱兴宇
// 时    间:  2015/4/16 15:17
// 权    限:  protected
// 返    回:  void
// 方法说明:  绘制
//*************************************
void NeHeWidget::paintGL()
{//清楚屏幕颜色和深度glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);drawObject();
}//*************************************
// 作    者:  朱兴宇
// 时    间:  2015/4/16 15:06
// 权    限:  private
// 返    回:  void
// 方法说明:  绘制物体
//*************************************
void NeHeWidget::drawObject()
{glLoadIdentity();glTranslatef(0.0,0.0,-30.0);glRotatef(xRot,1.0,0.0,0.0);glRotatef(yRot,0.0,1.0,0.0);glTranslatef(0.0,-3.0,0.0);glScaled(0.25,0.25,0.25);drawModel(gothicModel,true,false); glBlendFunc( GL_SRC_ALPHA, GL_ONE );
}//*************************************
// 作    者:  朱兴宇
// 时    间:  2015/4/16 15:10
// 权    限:  protected
// 返    回:  void
// 方法说明:  鼠标响应事件
//*************************************
void NeHeWidget::keyPressEvent(QKeyEvent * e)
{switch(e->key()){case Qt::Key_F2:fullscreen = !fullscreen;if(fullscreen){showFullScreen();}else{showNormal();resize(640,480);}updateGL();break;case Qt::Key_Escape:close();break;case Qt::Key_Up:xRot+= 5.0;if (xRot>=360){xRot=0;}updateGL();break;case Qt::Key_Down:xRot -= 5.0;if (xRot<=0){xRot=360;}updateGL();break;case Qt::Key_Right:yRot += 5.0;if (yRot>=360){yRot=0;}updateGL();break;case Qt::Key_Left:yRot -= 5.0;if (yRot<=0){yRot=360;}updateGL();break;//启用灯光case Qt::Key_L:{static bool choose = false;if(choose){glDisable(GL_LIGHTING);choose = false;}else{glEnable(GL_LIGHTING);choose = true;}updateGL();}break;//启用混合case Qt::Key_B:{static bool blend = !blend;if ( blend ){glEnable(GL_BLEND);glDisable(GL_DEPTH_TEST);blend = false;}else{glDisable(GL_BLEND);glEnable(GL_DEPTH_TEST);blend = true;}updateGL();}break;}}//*************************************
// 作    者:  朱兴宇
// 时    间:  2015/4/16 15:07
// 权    限:  private
// 返    回:  void
// 方法说明:  初始化灯光
//*************************************
void NeHeWidget::initLight()
{glLightfv( GL_LIGHT1, GL_AMBIENT, lightAmbient );glLightfv( GL_LIGHT1, GL_DIFFUSE, lightDiffuse );glLightfv( GL_LIGHT1, GL_POSITION, lightPosition );glEnable( GL_LIGHT1 );
}//*************************************
// 作    者:  朱兴宇
// 时    间:  2015/4/16 22:00
// 权    限:  private
// 返    回:  void
// 方法说明:  初始化3DMAX导入模型
//*************************************
void NeHeWidget::init3DMAX()
{//导入模型 模型的文件夹尽量这样设置  //然后模型贴图 装在Data/3DS里面,一定要跟前面截图的文件夹名字一样,想改得去CLoad3DS文件里面改 gothicLoader->Import3DS(&gothicModel, "Data/3DS/GUTEMB_L.3DS");
}

main.cpp:

#include "nehewidget.h"
#include <QTextCodec>
#include <QtGui/QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);{QTextCodec *codec = QTextCodec::codecForName("utf8");QTextCodec::setCodecForLocale(codec);QTextCodec::setCodecForCStrings(codec);QTextCodec::setCodecForTr(codec);}NeHeWidget* w=new NeHeWidget;w->show();return a.exec();
}

dinlou.qrc:

<RCC><qresource prefix="/Textures"><file>Resources/text.bmp</file><file>Resources/Desert.jpg</file></qresource>
</RCC>

OpenGL导入3DMax模型(.3DS)相关推荐

  1. Windows下Qt开发环境:OpenGL导入3DMax模型(.3DS)

    参考:http://blog.csdn.net/cq361106306/article/details/41876541 效果: 源代码: 解释: CLoad3DS.h为加载3DMax模型的头文件,C ...

  2. Ogre导入3Dmax模型注意事项

    Ogre导入3Dmax模型注意事项 2010-03-01 12:53:26|  分类: Ogre |  标签: |字号大中小 订阅 1.由于Ogre与3Dmax坐标系统的不一致,可能会导致3Dmax导 ...

  3. UE4导入3dmax模型并在场景中添加第三人称角色

    1.3dmax安装Datasmith插件 插件下载位置 https://www.unrealengine.com/zh-CN/datasmith/plugins 2.3dmax导出模型 3.UE4导入 ...

  4. 在ArcGIS Pro2.5中导入3dmax 模型并且发布服务

    目录 一.需求描述 二.解决方法 一.需求描述 3dmax 数据导入arcgis pro中,带纹理. 这个是我的数据类型:.3ds+.jpg 二.解决方法 在arcmap (我用的是10.8版本的) ...

  5. unity导入3DMAX模型失真无法修改渲染方法解决方案

    今天搞了好久才发现是在这个地方设置(导入设置) 选择材质 在location处选择 使用扩展模式 然后接可以发现渲染方法可以修改了(此处个人选择手机版漫反射,比默认的清晰太多太多)

  6. bullet物理引擎与OpenGL结合 导入3D模型进行碰撞检测 以及画三角网格的坑

    原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/11681069.html 一.初始化世界以及模型 /// 冲突配置包含内存的默认设置,冲突设置. ...

  7. 如何连动作导入3dmax_教你如何将犀牛(Rhino)模型导入3dmax并进行渲染

    3dmax导入Rhino模型的方法如下: 1.首先打开一个模型文件,该模型在建模时已经分好了图层,可以看到每个层设置了颜色,然后在文件菜单下导出所选物体,选择物体,导出成3ds格式. 2.在3dmax ...

  8. OpenGL导入3DS专题

    最近一直在研究如何将3D模型导入OpenGL,略微有些进展,在附录部分把它记录下,希望能起到抛砖引玉的作用,有不足的地方,请大家指出. 1. 简述 如果想把做场景做的复杂,导入3D模型是一个必不可少的 ...

  9. 3dMax模型导入SuperMap

    目录 一.3dsmax2016 官方简体(64位)安装教程 二.超图 3ds MAX插件安装 2.1 插件下载 2.2 安装 三.模型导出 四.3dmax模型贴图路径的找回方法 五.SuperMap ...

  10. c4d怎么导入fbx_如何将3DMAX模型带材质导入C4D

    如何将3DMAX模型带材质导入C4D 有很多人都在问,苑长怎么将网站上3DMAX文件的模型使用在C4D里面呢,C4D外文名CINEMA 4D,也是很多三维爱好者经常使用的软件,我们今天就教大家如何将3 ...

最新文章

  1. Android 停止调试程序
  2. Java 设计模式之工厂模式
  3. OpenGL缩放转换
  4. soapui返回值类型都有哪些_法兰的类型都有哪些以及法兰的设计
  5. 站覆盖范围_武汉高铁站落地灯箱广告有什么投放价值?
  6. 问题五十:怎么用ray tracing画blobs
  7. NSString中如何正确判断包含一个变量字串NSString
  8. 计算机Excel电子表格处理文件,#excel2016打不开已恢复的文件#所有表格打不开怎么办...
  9. 如何对SNP设计引物: CAPS, dCAPS
  10. 利用ansys计算机械结构最小安全系数教程,利用ANSYS计算复杂薄壁杆件截面特性.pdf...
  11. 金税盘3.1、百旺V5、UKEY数据库
  12. 麒麟V10非root用户升级GCC5.4,并解决程序运行出现‘GLIBCXX_3.4.21‘ not found
  13. vue改变class名字_vue添加class类名
  14. html实现波浪,纯CSS实现波浪移动效果的示例
  15. python爬虫 — 爬取淘宝商品信息
  16. linux 搭建webserver-Goahead
  17. linux系统修改时区
  18. 《R语言数据分析》期末试题
  19. 微信如何批量自动添加好友
  20. notepad++ 下载地址(非官网,无捆绑插件,自己记录一下)

热门文章

  1. 创业者两大特征:喜欢折腾与坚持不懈
  2. 美国服务器电影网站版权问题,为什么说选择美国服务器做电影网站比较好
  3. Chromium网页Layer Tree绘制过程分析
  4. 眨眼信号分离及MATLAB实现
  5. PostgreSQL数据库部署之 :PostgreSQL pgadmin4 the application server could not be contacted
  6. java七年_阿里七年Java程序员的总结,献给还在迷茫中的你!
  7. 微信支付“下单账号和支付账号不一致,请核实后再支付”
  8. Java方法入参三个省略号...啥意思
  9. 百度地图android版v6.7,百度CarLife
  10. Linux中的preempt_count