文章来源:http://www.zwqxin.com/archives/opengl/3ds-cload3ds-view.html
CLoad3DS类是Sourceforge中的一个开源项目,作用在于帮助开发者学会简单的对3DS文件的载入(OpenGL)程序。虽然有更成熟更强大的3dslib库,但是平时写写Demo中,对模型载入的要求一般比较低,这时候只把CLoad3DS类包含到程序就够了。——ZwqXin.com 上篇文章:3DS文件结构的初步认识 中谈到的就是这个类。
本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
原文地址:http://www.zwqxin.com/archives/opengl/3ds-cload3ds-view.html
这个类只用到了3DS文件中的一小部分(chunk):顶点信息,面信息,纹理信息,材质信息,和一些标志信息(见上篇日志)。接下来我们首先看看它是怎样把3DS文件里的数据存储到实际内存中的:
- //上有CVector3D,CVector2D类用于保存一个顶点和一个纹理坐标,这是底层的存储结构。
- //这里给出了这么一个事实:一个模型由好几部分组成,譬如一个人体由手脚头身等等部分组成,每个部分就是3DS中单独命名的一个对象;因此说,模型由一系列对象组成,每个对象由一系列三角面片组成
- // 面的结构定义《-由顶点构
- struct tFace
- {
- int vertIndex[3]; // 顶点索引
- int coordIndex[3]; // 纹理坐标索引
- };
- // 对象信息结构体《-由面构
- struct t3DObject
- {
- int numOfVerts; // 模型中顶点的数目
- int numOfFaces; // 模型中面的数目
- int numTexVertex; // 模型中纹理坐标的数目
- int materialID; // 纹理ID
- bool bHasTexture; // 是否具有纹理映射
- char strName[255]; // 对象的名称
- CVector3D *pVerts; // 对象的顶点
- CVector3D *pNormals; // 对象的法向量
- CVector2D *pTexVerts; // 纹理UV坐标
- tFace *pFaces; // 对象的面信息
- };
- // 模型信息结构体《-由对象构,包含材质
- struct t3DModel
- {
- UINT texture[MAX_TEXTURES];
- bool Textured; //是否使用纹理
- int numOfObjects; // 模型中对象的数目
- int numOfMaterials; // 模型中材质的数目
- vector<tMaterialInfo> pMaterials; // 材质链表信息
- vector<t3DObject> pObject; // 模型中对象链表信息
- };
- // 接下来是材质信息结构体,描述材质
- 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 纹理偏移
- } ;
- // 这个与前面的不同,它是针对3DS文件而非模型实体。也就是描述块。你将看到bytesRead的精确计算对获得块内正确数据的重要性
- //保存块信息的结构
- struct tChunk
- {
- unsigned short int ID; // 块的ID
- unsigned int length; // 块的长度
- unsigned int bytesRead; // 需要读的块数据的字节数
- };
- //实现中我们就用以上数据结构描述整个模型和读取过程了:
- //构造一个临时模型对象,它仅存在于读取过程中
- t3DModel Model3DS;
- //构造两个临时存放chunk的结构
- tChunk *m_CurrentChunk;
- tChunk *m_TempChunk;
对模型的操作分为两部分:装载模型(初始化时把3DS文件中我们所需数据,通过上述数据结构读入内存供程序随时调用)和渲染模型(把模型画出来,并进行移转缩等调整)。核心分别为ImportModel函数和RenderModel函数。先看前者:
- // 打开一个3ds文件,读出其中的内容
- //省略了非主要的内容,你现在可以看到一个初始化步骤做了哪些事情:
- bool CLoad3DS::ImportModel(GLuint Model_id, char *strFileName)
- { .........
- m_FilePointer = fopen(strFileName, "rb");//1.打开文件,让文件指针指向
- ......
- // 2.将文件的第一块读出并判断是否是3ds文件(0x4D4D)
- ReadChunk(m_CurrentChunk);
- ........
- // 3.通过调用下面的递归函数,将对象读出
- ProcessNextChunk(&Model3DS, m_CurrentChunk);
- // 4.在读完整个3ds文件之后,计算顶点的法线
- ComputeNormals(&Model3DS);
- .......
- return true;
- }
其中的核心当然是第3步了。在进入这个核心之前,看看CLoad3DS类是怎样读数据的:
- // 下面函数读入块的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);
- }
- //fread函数,针对每次函数调用,4参数分别表示:读入的数据所存入的位置,每次读多少字节,读多少次,文件指针(所以中间两参数的乘积就是调用一次fread要读入的数据量了);返回实际成功读入了的字节数。
- //因此,一次成功的ReadChunk将把参数(tChunk 类型的块结构)中的bytesRead加6。这6字节包含一个块最开头的ID号(存入ID)和长度(存入length),这样文件指针(fread会让其指向下一个文件数据块开头)接下来将要面对的就是实际数据了。不明白者看此。
请继续收看:一个读取3DS文件的类CLoad3DS浅析Ⅱ
本文参考资料(大部分中文注释编写者),若没找错的话,应该是这里(http://blog.youkuaiyun.com/hardVB)呵呵.
本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
原文地址:http://www.zwqxin.com/archives/opengl/3ds-cload3ds-view.html