Qt版OpenGL读取3DS模型

14 篇文章 1 订阅

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"


/* 下面是定义一些块的ID号 */


/*  基本块(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 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]; /* 对象的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]; /* 对象的名称 */
        Vector3  *pVerts; /* 对象的顶点 */
        Vector3  *pNormals; /* 对象的法向量 */
        Vector2  *pTexVerts; /* 纹理UV坐标 */
        tFace *pFaces; /* 对象的面信息 */
};


/* 模型信息结构体 */
struct t3DModel
{ int numOfObjects; /* 模型中对象的数目 */
        int numOfMaterials; /* 模型中材质的数目 */
        QVectorpMaterials; /* 材质链表信息 */
        QVector pObject; /* 模型中对象链表信息 */
};


/* 块信息的结构 */
struct tChunk
{
        unsigned short int ID; /* 块的ID */
        unsigned int length; /* 块的长度 */
        unsigned int bytesRead; /* 需要读的块数据的字节数 */
};


#define MAX_TEXTURES  100


/* 3DS文件载入类 */
class C3DSLoader
{
public:
        /* 构造函数 */
        C3DSLoader();
        virtual ~C3DSLoader();
        void Draw();//显示3ds模型
        void Init(char *filename);


private:
        /* 读一个字符串 */
        int  GetString(char *);


        /* 装载3ds文件到模型结构中 */
        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 *);


        /* 读取对象颜色的RGB值 */
        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;/* 设置材质的纹理ID */
    }
}


/* 显示3ds模型 */
void C3DSLoader::Draw()
{
    /*保存现有颜色属实性 */
    glPushAttrib(GL_CURRENT_BIT);
    glDisable(GL_TEXTURE_2D);
    //glEnable(GL_LIGHT0);


    /* 遍历模型中所有的对象 */
    for(int i = 0; i < m_3DModel.numOfObjects; i++)
    {
        /* 如果对象的大小小于0,则退出 */
        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)
                {
                    /* 确定是否有UVW纹理坐标 */
                    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();
        /* 绘制结束 */
    }


    //glDisable(GL_LIGHT0);
    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);


}


/* 载入3DS文件到模型结构中 */
bool C3DSLoader::Import3DS(t3DModel *pModel,char *strFileName)
{
    char strMessage[255] = {0};


    /* 打开一个3ds文件 */
    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;
    }


    /* 将文件的第一块读出并判断是否是3ds文件 */
    ReadChunk(m_CurrentChunk);


    /* 确保是3ds文件 */
    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)
    {
        /* 读入一个字符直到NULL */
        fread(pBuffer + index, 1, 1, m_FilePointer);
    }


    /* 返回字符串的长度 */
    return strlen(pBuffer) + 1;
}


/* 读入块的ID号和它的字节长度 */
void C3DSLoader::ReadChunk(tChunk *pChunk)
{
    /* 读入块的ID号 */
    pChunk->bytesRead = fread(&pChunk->ID, 1, 2, m_FilePointer);


    /* 读入块占用的长度 */
    pChunk->bytesRead += fread(&pChunk->length, 1, 4, m_FilePointer);


}


/* 读出3ds文件的主要部分 */
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);


        /* 判断块的ID号 */
        switch (m_CurrentChunk->ID)
        {


            /* 文件版本号 */
        case VERSION:


            /* 读入文件的版本号 */
            m_CurrentChunk->bytesRead += fread(&version, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);


            /* 如果文件版本号大于3,给出一个警告信息 */
            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++;


            /* 添加一个新的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 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;


            /* 读入对象的UV纹理坐标 */
        case OBJ_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 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: /* 对象的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;
}


/* 读入RGB颜色 */
void C3DSLoader::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 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;
            }
        }
    }
}


/* 读入对象的UV坐标 */
void C3DSLoader::ReadUVCoordinates(t3DObject *pObject, tChunk *pPreChunk)
{
    /* 读入UV坐标的数量 */
    pPreChunk->bytesRead += fread(&pObject->numTexVertex, 1, 2, m_FilePointer);


    /* 分配保存UV坐标的内存空间 */
    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);


    /* 遍历所有的顶点将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 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)
        {
            /* 设置材质ID */
            pObject->materialID = i;


            /* 判断是否是纹理映射 */
            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);
}


/* 计算对象的法向量 */
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();

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值