OpenGL: 3ds模型显示
来源:互联网 发布:apache 限制访问目录 编辑:程序博客网 时间:2024/05/28 01:35
由3DS MAX导出的3ds模型的显示很简单。所谓代码之中,了无秘密。接口很简单:
存储:
模型信息和贴图信息,位于全局变量:
//纹理信息
UINT g_Texture[10][MAX_TEXTURES] = {0};
说明,其中MAX_TEXTURES 是100,表示最大的纹理数目。即一个模型文件,可以对应多个贴图。
//模型信息
t3DModel g_3DModel[10];
函数接口:
加载3D模型:
//入参 模型文件路径 模型数组g_3DModel的指定下标
void CLoad3DS::Init(char *filename,int j)
显示3D模型:
//入参 数组g_3DModel下标 显示坐标 缩放倍数
void CLoad3DS::show3ds(int j0,float tx,float ty,float tz,float size)
注意事项:3DS模型文件和对应的贴图文件要放在同一个文件夹中。3DS MAX系统单位的1毫米对应OPENGL中的一个单位。
附:接口类CLoad3DS的头文件和源文件
//////////////////////////////////////////////// 3ds.h /////////////////////////////////////////#ifndef _3DS_H#define _3DS_H#include <math.h>#include <vector>// 基本块(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 OBJ_MESH 0x4100 // 新的网格对象#define MAX_TEXTURES 100 // 最大的纹理数目// OBJ_MESH的次级定义#define OBJ_VERTICES 0x4110 // 对象顶点#define OBJ_FACES 0x4120 // 对象的面#define OBJ_MATERIAL 0x4130 // 对象的材质#define OBJ_UV 0x4140 // 对象的UV纹理坐标using namespace std;class CVector3 //定义3D点的类,用于保存模型中的顶点{ public: float x, y, z;};class CVector2 //定义2D点类,用于保存模型的UV纹理坐标{ public: float x, y;};struct tFace //面的结构定义{ int vertIndex[3]; // 顶点索引 int coordIndex[3]; // 纹理坐标索引};struct tMatInfo//材质信息结构体{ char strName[255]; // 纹理名称 char strFile[255]; // 如果存在纹理映射,则表示纹理文件名称 BYTE color[3]; // 对象的RGB颜色 int texureId; // 纹理ID float uTile; // u 重复 float vTile; // v 重复 float uOffset; // u 纹理偏移 float vOffset; // v 纹理偏移} ;struct t3DObject //对象信息结构体{ int numOfVerts; // 模型中顶点的数目 int numOfFaces; // 模型中面的数目 int numTexVertex; // 模型中纹理坐标的数目 int materialID; // 纹理ID bool bHasTexture; // 是否具有纹理映射 char strName[255]; // 对象的名称 CVector3 *pVerts; // 对象的顶点 CVector3 *pNormals; // 对象的法向量 CVector2 *pTexVerts; // 纹理UV坐标 tFace *pFaces; // 对象的面信息};struct t3DModel //模型信息结构体{ int numOfObjects; // 模型中对象的数目 int numOfMaterials; // 模型中材质的数目 vector<tMatInfo> pMaterials; // 材质链表信息 vector<t3DObject> pObject; // 模型中对象链表信息};struct tChunk //保存块信息的结构{ unsigned short int ID; // 块的ID unsigned int length; // 块的长度 unsigned int bytesRead; // 需要读的块数据的字节数};//////////////////////////////////////////////////////////////////////////class CLoad3DS// CLoad3DS类处理所有的装入代码{public: CLoad3DS(); // 初始化数据成员 virtual ~CLoad3DS(); void show3ds(int j0,float tx,float ty,float tz,float size);//显示3ds模型 void Init(char *filename,int j); void CleanUp(); // 关闭文件,释放内存空间private: bool Import3DS(t3DModel *pModel, char *strFileName);// 装入3ds文件到模型结构中 void CreateTexture(UINT textureArray[], LPSTR strFileName, int textureID);// 从文件中创建纹理 int GetString(char *); // 读一个字符串 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);// 读对象颜色的RGB值 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); // 计算对象顶点的法向量 FILE *m_FilePointer; // 文件指针 tChunk *m_CurrentChunk; tChunk *m_TempChunk;};#endif
//////////////////////////////////////////////// 3ds.cpp /////////////////////////////////////////#include "stdafx.h"#include "3ds.h"//纹理信息 UINT g_Texture[10][MAX_TEXTURES] = {0}; //模型信息t3DModel g_3DModel[10]; //绘制方式 int g_ViewMode = GL_TRIANGLES;bool g_bLighting = true; CLoad3DS::CLoad3DS(){ m_CurrentChunk = new tChunk; m_TempChunk = new tChunk;}CLoad3DS::~CLoad3DS(){ CleanUp(); for(int j = 0; j <10;j++) { for(int i = 0; i < g_3DModel[j].numOfObjects; i++) { delete [] g_3DModel[j].pObject[i].pFaces; delete [] g_3DModel[j].pObject[i].pNormals; delete [] g_3DModel[j].pObject[i].pVerts; delete [] g_3DModel[j].pObject[i].pTexVerts; } }}////////////////////////////////////////////////////////////////////////void CLoad3DS::Init(char *filename,int j)//{ Import3DS(&g_3DModel[j], filename); for(int i =0; i<g_3DModel[j].numOfMaterials;i++) { if(strlen(g_3DModel[j].pMaterials[i].strFile)>0) CreateTexture(g_Texture[j], g_3DModel[j].pMaterials[i].strFile, i); g_3DModel[j].pMaterials[i].texureId = i; }}void CLoad3DS::CreateTexture(UINT textureArray[], LPSTR strFileName, int textureID){ AUX_RGBImageRec *pBitmap = NULL; if(!strFileName) return; pBitmap = auxDIBImageLoad(strFileName); if(pBitmap == NULL) exit(0); glGenTextures(1, &textureArray[textureID]); glPixelStorei (GL_UNPACK_ALIGNMENT, 1); glBindTexture(GL_TEXTURE_2D, textureArray[textureID]); gluBuild2DMipmaps(GL_TEXTURE_2D, 3, pBitmap->sizeX, pBitmap->sizeY, GL_RGB, GL_UNSIGNED_BYTE, pBitmap->data); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR); if (pBitmap) { if (pBitmap->data) free(pBitmap->data); free(pBitmap); }}void CLoad3DS::show3ds(int j0,float tx,float ty,float tz,float size){ glPushAttrib(GL_CURRENT_BIT); glPushMatrix(); glDisable(GL_TEXTURE_2D); ::glTranslatef( tx, ty, tz); ::glScaled(size,size,size); glRotatef(90, 0, 1.0f, 0); //绘制模型 for(int i = 0; i < g_3DModel[j0].numOfObjects; i++) { if(g_3DModel[j0].pObject.size() <= 0) { break; } t3DObject *pObject = &g_3DModel[j0].pObject[i]; if(pObject->bHasTexture) { //如果这个物体有纹理,绑定纹理 glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, g_Texture[j0][pObject->materialID]); } else { //没有纹理,关闭贴图 glDisable(GL_TEXTURE_2D); } //指定颜色 glColor3ub(255, 255, 255); glBegin(g_ViewMode); //绘制这个物体的所有面片 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(g_3DModel[j0].pMaterials.size() && pObject->materialID>= 0) { BYTE *pColor = g_3DModel[j0].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); glPopMatrix(); glPopAttrib();}////////////////////////////////////////////////////////////////////加载模型bool CLoad3DS::Import3DS(t3DModel *pModel, char *strFileName){ char strMessage[255] = {0}; m_FilePointer = fopen(strFileName, "rb"); if(!m_FilePointer) { sprintf(strMessage, "Unable to find the file: %s!", strFileName); MessageBox(NULL, strMessage, "Error", MB_OK); return false; } ReadChunk(m_CurrentChunk); if (m_CurrentChunk->ID != PRIMARY) { sprintf(strMessage, "Unable to load PRIMARY chuck from file: %s!", strFileName); MessageBox(NULL, strMessage, "Error", MB_OK); return false; } ReadNextChunk(pModel, m_CurrentChunk); ComputeNormals(pModel); return true;}void CLoad3DS::CleanUp(){ fclose(m_FilePointer); delete m_CurrentChunk; delete m_TempChunk; }void CLoad3DS::ReadNextChunk(t3DModel *pModel, tChunk *pPreChunk){ t3DObject newObject = {0}; // 用来添加到对象链表 tMatInfo newTexture = {0}; // 用来添加到材质链表 unsigned int version = 0; // 保存文件版本 int buffer[50000] = {0}; // 用来跳过不需要的数据 m_CurrentChunk = new tChunk; // 为新的块分配空间 // 下面每读一个新块,都要判断一下块的ID,如果该块是需要的读入的,则继续进行 // 如果是不需要读入的块,则略过 // 继续读入子块,直到达到预定的长度 while (pPreChunk->bytesRead < pPreChunk->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, "This 3DS file is over version 3 so it may load incorrectly", "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; // 进入下一个块 ReadNextChunk(pModel, m_CurrentChunk); break; case MATERIAL: // 材质信息 // 材质的数目递增 pModel->numOfMaterials++; // 在纹理链表中添加一个空白纹理结构 pModel->pMaterials.push_back(newTexture); // 进入材质装入函数 ReadNextMatChunk(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); // 进入其余的对象信息的读入 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 CLoad3DS::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: // 读入对象的UV纹理坐标 // 读入对象的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 CLoad3DS::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: // 对象的R G B颜色 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;}// 下面函数读入块的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) { // 读入一个字符直到NULL fread(pBuffer + index, 1, 1, m_FilePointer); } // 返回字符串的长度 return strlen(pBuffer) + 1;}// 下面的函数读入RGB颜色void CLoad3DS::ReadColor(tMatInfo *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 *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; } } }}// 下面的函数读入对象的UV坐标void CLoad3DS::ReadUVCoordinates(t3DObject *pObject, tChunk *pPreChunk){ // 为了读入对象的UV坐标,首先需要读入UV坐标的数量,然后才读入具体的数据 // 读入UV坐标的数量 pPreChunk->bytesRead += fread(&pObject->numTexVertex, 1, 2, m_FilePointer); // 分配保存UV坐标的内存空间 pObject->pTexVerts = new CVector2 [pObject->numTexVertex]; // 读入纹理坐标 pPreChunk->bytesRead += fread(pObject->pTexVerts, 1, pPreChunk->length - pPreChunk->bytesRead, m_FilePointer);}// 读入对象的顶点void CLoad3DS::ReadVertices(t3DObject *pObject, tChunk *pPreChunk){ // 在读入实际的顶点之前,首先必须确定需要读入多少个顶点。 // 读入顶点的数目 pPreChunk->bytesRead += fread(&(pObject->numOfVerts), 1, 2, m_FilePointer); // 分配顶点的存储空间,然后初始化结构体 pObject->pVerts = new CVector3 [pObject->numOfVerts]; memset(pObject->pVerts, 0, sizeof(CVector3) * pObject->numOfVerts); // 读入顶点序列 pPreChunk->bytesRead += fread(pObject->pVerts, 1, pPreChunk->length - pPreChunk->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::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) { // 设置材质ID pObject->materialID = i; // 判断是否是纹理映射,如果strFile是一个长度大于1的字符串,则是纹理 if(strlen(pModel->pMaterials[i].strFile) > 0) { // 设置对象的纹理映射标志 pObject->bHasTexture = true; } break; } else { // 如果该对象没有材质,则设置ID为-1 pObject->materialID = -1; } } pPreChunk->bytesRead += fread(buffer, 1, pPreChunk->length - pPreChunk->bytesRead, m_FilePointer);} // 下面的这些函数主要用来计算顶点的法向量,顶点的法向量主要用来计算光照// 下面的宏定义计算一个矢量的长度#define Mag(Normal) (sqrt(Normal.x*Normal.x + Normal.y*Normal.y + Normal.z*Normal.z))// 下面的函数求两点决定的矢量CVector3 Vector(CVector3 vPoint1, CVector3 vPoint2){ CVector3 vVector; vVector.x = vPoint1.x - vPoint2.x; vVector.y = vPoint1.y - vPoint2.y; vVector.z = vPoint1.z - vPoint2.z; return vVector; }// 下面的函数两个矢量相加CVector3 AddVector(CVector3 vVector1, CVector3 vVector2){ CVector3 vResult; vResult.x = vVector2.x + vVector1.x; vResult.y = vVector2.y + vVector1.y; vResult.z = vVector2.z + vVector1.z; return vResult; }// 下面的函数处理矢量的缩放CVector3 DivideVectorByScaler(CVector3 vVector1, float Scaler){ CVector3 vResult; vResult.x = vVector1.x / Scaler; vResult.y = vVector1.y / Scaler; vResult.z = vVector1.z / Scaler; return vResult; }// 下面的函数返回两个矢量的叉积CVector3 Cross(CVector3 vVector1, CVector3 vVector2){ CVector3 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; }// 下面的函数规范化矢量CVector3 Normalize(CVector3 vNormal){ double Magnitude; Magnitude = Mag(vNormal); // 获得矢量的长度 vNormal.x /= (float)Magnitude; vNormal.y /= (float)Magnitude; vNormal.z /= (float)Magnitude; return vNormal; }// 下面的函数用于计算对象的法向量void CLoad3DS::ComputeNormals(t3DModel *pModel){ CVector3 vVector1, vVector2, vNormal, vPoly[3]; // 如果模型中没有对象,则返回 if(pModel->numOfObjects <= 0) return; // 遍历模型中所有的对象 for(int index = 0; index < pModel->numOfObjects; index++) { // 获得当前的对象 t3DObject *pObject = &(pModel->pObject[index]); // 分配需要的存储空间 CVector3 *pNormals = new CVector3 [pObject->numOfFaces]; CVector3 *pTempNormals = new CVector3 [pObject->numOfFaces]; pObject->pNormals = new CVector3 [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 = Vector(vPoly[0], vPoly[2]); // 获得多边形的矢量 vVector2 = Vector(vPoly[2], vPoly[1]); // 获得多边形的第二个矢量 vNormal = Cross(vVector1, vVector2); // 获得两个矢量的叉积 pTempNormals[i] = vNormal; // 保存非规范化法向量 vNormal = Normalize(vNormal); // 规范化获得的叉积 pNormals[i] = vNormal; // 将法向量添加到法向量列表中 } // 下面求顶点法向量 CVector3 vSum = {0.0, 0.0, 0.0}; CVector3 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; }}
感谢《学OPENGL编3D游戏》课件(来自www.gameres.com)
http://blog.csdn.net/programking/article/details/3943560
1 0
- OpenGL 3ds模型显示
- OpenGL 3ds模型显示
- OpenGL 3ds模型显示
- OpenGL: 3ds模型显示
- OpenGL 3DS建模显示
- OpenGL读取3DS模型(Qt版)
- OpenGL读取3DS模型(Qt版)
- Qt+OpenGL 3ds模型的导入
- Qt版OpenGL读取3DS模型
- Opengl 导入3Dmax制作的.3ds模型 并显示出来
- Opengl 导入3Dmax制作的.3ds模型 并显示出来
- OpenGL读取显示3ds,flt模型的一般过程(小结)
- OpenGL实现3DS文件中的模型自由旋转
- Opengl 浏览3ds模型,室外OCtree 和碰撞检测
- OpenGL实现3DS文件中的模型自由旋转
- OpenGL实现3DS文件中的模型自由旋转
- 3ds模型在OpenGL中的读取和重绘
- 3ds模型在OpenGL中的读取和重绘
- com关于引用计数
- dm368流程
- IE从服务器上获取json后转为下载提示的bug
- 使用maven创建web项目
- 4.4 视频缩略图显示
- OpenGL: 3ds模型显示
- HBase Shell输入命令无法删除问题解决技巧
- NFC学习笔记——三(在windows操作系统上安装libnfc)
- Suffix Structures - #256 (Div. 2) B (448B) 陷阱题
- 【matlab】频率加权函数曲线
- 关于ViewFlipper中GridView手势滑动滑的实现
- 多边心重心[nyoj 3 多边形重心问题, hdu 1115 Lifting the Stone]
- 毕业3年后重新起步----我晚了吗
- Linux下调整根目录的空间大小