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

📄 lesson2.cpp

📁 一个简单的导入3ds文件格式的例子
💻 CPP
📖 第 1 页 / 共 4 页
字号:
/*
 *		This Code Was Created By Jeff Molofee 2000
 *		A HUGE Thanks To Fredric Echols For Cleaning Up
 *		And Optimizing The Base Code, Making It More Flexible!
 *		If You've Found This Code Useful, Please Let Me Know.
 *		Visit My Site At nehe.gamedev.net
 */


#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <fstream>
#include <vector>
#include <gl\gl.h>                    
#include <gl\glu.h>                    
#include <gl\glaux.h>
#include <crtdbg.h>

#include <olectl.h>              
#include <math.h>              

using namespace std;

#define MAX_TEXTURES 100                // 最大的纹理数目

// 定义3D点的类,用于保存模型中的顶点
class CVector3 
{
public:
  float x, y, z;
};

// 定义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;			// 纹理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 
{
  UINT texture[MAX_TEXTURES];
  int numOfObjects;          // 模型中对象的数目
  int numOfMaterials;          // 模型中材质的数目
  vector<tMaterialInfo> pMaterials;  // 材质链表信息
  vector<t3DObject> pObject;      // 模型中对象链表信息
};

#ifndef _3DS_H
#define _3DS_H

// 基本块(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纹理坐标

struct tIndices 
{              
  unsigned short a, b, c, bVisible;  
};

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

// 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;
};

#endif

// 读入一个纹理
int CLoad3DS::BuildTexture(char *szPathName, GLuint &texid)
{
  HDC      hdcTemp;                        // The DC To Hold Our Bitmap
  HBITMAP    hbmpTemp;                        // Holds The Bitmap Temporarily
  IPicture  *pPicture;                        // IPicture Interface
  OLECHAR    wszPath[MAX_PATH+1];                  // Full Path To Picture (WCHAR)
  char    szPath[MAX_PATH+1];                    // Full Path To Picture
  long    lWidth;                          // Width In Logical Units
  long    lHeight;                        // Height In Logical Units
  long    lWidthPixels;                      // Width In Pixels
  long    lHeightPixels;                      // Height In Pixels
  GLint    glMaxTexDim ;                      // Holds Maximum Texture Size

  if (strstr(szPathName, "http://"))                  // If PathName Contains http:// Then...
  {
    strcpy(szPath, szPathName);                    // Append The PathName To szPath
  }
  else                                // Otherwise... We Are Loading From A File
  {
    GetCurrentDirectory(MAX_PATH, szPath);              // Get Our Working Directory
    strcat(szPath, "\\");                      // Append "\" After The Working Directory
    strcat(szPath, szPathName);                    // Append The PathName
  }

  MultiByteToWideChar(CP_ACP, 0, szPath, -1, wszPath, MAX_PATH);    // Convert From ASCII To Unicode
  HRESULT hr = OleLoadPicturePath(wszPath, 0, 0, 0, IID_IPicture, (void**)&pPicture);

  if(FAILED(hr))                            // If Loading Failed
    return FALSE;                          // Return False

  hdcTemp = CreateCompatibleDC(GetDC(0));                // Create The Windows Compatible Device Context
  if(!hdcTemp)                            // Did Creation Fail?
  {
    pPicture->Release();                      // Decrements IPicture Reference Count
    return FALSE;                          // Return False (Failure)
  }

  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glMaxTexDim);          // Get Maximum Texture Size Supported
  
  pPicture->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 Two
  if (lWidthPixels <= glMaxTexDim) // Is Image Width Less Than Or Equal To Cards Limit
    lWidthPixels = 1 << (int)floor((log((double)lWidthPixels)/log(2.0f)) + 0.5f); 
  else // Otherwise Set Width To "Max Power Of Two" That The Card Can Handle
    lWidthPixels = glMaxTexDim;
 
  if (lHeightPixels <= glMaxTexDim) // Is Image Height Greater Than Cards Limit
    lHeightPixels = 1 << (int)floor((log((double)lHeightPixels)/log(2.0f)) + 0.5f);
  else // Otherwise Set Height To "Max Power Of Two" That The Card Can Handle
    lHeightPixels = glMaxTexDim;
  
  //  Create A Temporary Bitmap
  BITMAPINFO  bi = {0};                        // The Type Of Bitmap We Request
  DWORD    *pBits = 0;                        // Pointer To The Bitmap Bits

  bi.bmiHeader.biSize      = sizeof(BITMAPINFOHEADER);        // Set Structure Size
  bi.bmiHeader.biBitCount    = 32;                  // 32 Bit
  bi.bmiHeader.biWidth    = lWidthPixels;              // Power Of Two Width
  bi.bmiHeader.biHeight    = lHeightPixels;            // Make Image Top Up (Positive Y-Axis)
  bi.bmiHeader.biCompression  = BI_RGB;                // RGB Encoding
  bi.bmiHeader.biPlanes    = 1;                  // 1 Bitplane

  //  Creating A Bitmap This Way Allows Us To Specify Color Depth And Gives Us Imediate Access To The Bits
  hbmpTemp = CreateDIBSection(hdcTemp, &bi, DIB_RGB_COLORS, (void**)&pBits, 0, 0);
  
  if(!hbmpTemp)                            // Did Creation Fail?
  {
    DeleteDC(hdcTemp);                        // Delete The Device Context
    pPicture->Release();                      // Decrements IPicture Reference Count
    return FALSE;                          // Return False (Failure)
  }

  SelectObject(hdcTemp, hbmpTemp);                  // Select Handle To Our Temp DC And Our Temp Bitmap Object

  // Render The IPicture On To The Bitmap
  pPicture->Render(hdcTemp, 0, 0, lWidthPixels, lHeightPixels, 0, lHeight, lWidth, -lHeight, 0);

  // Convert From BGR To RGB Format And Add An Alpha Value Of 255
  for(long i = 0; i < lWidthPixels * lHeightPixels; i++)        // Loop Through All Of The Pixels
  {
    BYTE* pPixel  = (BYTE*)(&pBits[i]);              // Grab The Current Pixel
    BYTE 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 Black
      pPixel[3]  = 0;                      // Set The Alpha Value To 0
    else                              // Otherwise
      pPixel[3]  = 255;                      // Set The Alpha Value To 255
  }

  glGenTextures(1, &texid);                      // Create The Texture

  // Typical Texture Generation Using Data From The Bitmap
  glBindTexture(GL_TEXTURE_2D, texid);                // Bind To The Texture ID
  glTexParameteri(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 Object
  DeleteDC(hdcTemp);                          // Delete The Device Context

  pPicture->Release();                        // Decrements IPicture Reference Count

  return TRUE;                            // Return True (All Good)

}


// 构造函数的功能是初始化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);            // 关闭当前的文件指针

⌨️ 快捷键说明

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