⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lesson2.cpp

📁 一个简单的导入3ds文件格式的例子
💻 CPP
📖 第 1 页 / 共 4 页
字号:
  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(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::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 CVector3 [pObject->numOfVerts];
  memset(pObject->pVerts, 0, sizeof(CVector3) * 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;
  }
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -