#include "main.h"
#include "3ds.h"
// 构造函数的功能是初始化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, strMessage, "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, strMessage, "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, "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;
// 进入下一个块
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