Lookof 's Wild

Last of the Wild

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
早就想自己做个3dmax的读取器了,前几天找了点资料学习了一下,经过三天的努力,今晚终于告罄!简是简单了点,而且没有设置材质,但有纹理信息,而且效果还挺好。特地抓了一张图贴来晒晒。


我的M60主战坦克!

a. 3ds文件存储的基本单元叫“chunk”,我们就是读这样一块一块的信息。目录树如下,加粗体的是chunk的标识符,而缩进风格体现了块的父子关系:

MAIN CHUNK 0x4D4D
   3D EDITOR CHUNK 0x3D3D
      OBJECT BLOCK 0x4000
         TRIANGULAR MESH 0x4100
            VERTICES LIST 0x4110
            FACES DESCRIPTION 0x4120
               FACES MATERIAL 0x4130
            MAPPING COORDINATES LIST 0x4140
               SMOOTHING GROUP LIST 0x4150
            LOCAL COORDINATES SYSTEM 0x4160
         LIGHT 0x4600
            SPOTLIGHT 0x4610
         CAMERA 0x4700
      MATERIAL BLOCK 0xAFFF
         MATERIAL NAME 0xA000
         AMBIENT COLOR 0xA010
         DIFFUSE COLOR 0xA020
         SPECULAR COLOR 0xA030
         TEXTURE MAP 1 0xA200
         BUMP MAP 0xA230
         REFLECTION MAP 0xA220
         [SUB CHUNKS FOR EACH MAP]
            MAPPING FILENAME 0xA300
            MAPPING PARAMETERS 0xA351
      KEYFRAMER CHUNK 0xB000
         MESH INFORMATION BLOCK 0xB002
         SPOT LIGHT INFORMATION BLOCK 0xB007
         FRAMES (START AND END) 0xB008
            OBJECT NAME 0xB010
            OBJECT PIVOT POINT 0xB013
            POSITION TRACK 0xB020
            ROTATION TRACK 0xB021
            SCALE TRACK 0xB022
            HIERARCHY POSITION 0xB030


b. 每一个“chunk”的结构如下所示:

偏移量 长度  
0 2 块标识符
2 4 块长: 块数据 + 子块内容
6 n 块数据
6+n m S子块

c. 读取的思路是:首先根据偏移量和长度找到一个块的标识符,然后据此来判断它是什么块,遇到我们需要的块,就进一步读取,如果不需要,直接跳过这一块,读取下面的块。

d. 一个简单的Loader程序不需要什么都读取,只需要读基本的图元就行了。比如点数据,面数据,和纹理数据。我的Laoder主要包含的就是这三个信息。当然,如果你要读到这三者,起码要把先前的父块内容读入才行,否则就被跳过了。因此你一共要读个7块数据。这7者信息如下:

MAIN CHUNK
Identifier 0x4d4d 
Length 0 + sub-chunks length
Chunk father None
Sub chunks 3D EDITOR CHUNK
Data None
3D EDITOR CHUNK
Identifier 0x3D3D 
Length 0 + sub-chunks length
Chunk father MAIN CHUNK
Sub chunks OBJECT BLOCK, MATERIAL BLOCK, KEYFRAMER CHUNK
Data None
OBJECT BLOCK
Identifier 0x4000
Length Object name length + sub-chunks length
Chunk father 3D EDITOR CHUNK
Sub chunks TRIANGULAR MESH, LIGHT, CAMERA
Data Object name
TRIANGULAR MESH
Identifier 0x4100
Length 0 + sub-chunks length
Chunk father OBJECT BLOCK
Sub chunks VERTICES LIST, FACES DESCRIPTION, MAPPING COORDINATES LIST
Data None
VERTICES LIST(点数据在这)
Identifier 0x4110
Length varying + sub-chunks length
Chunk father TRIANGULAR MESH
Sub chunks None
Data Vertices number (unsigned short)
Vertices list: x1,y1,z1,x2,y2,z2 etc. (for each vertex: 3*float)
FACES DESCRIPTION(面数据在这)
Identifier 0x4120
Length varying + sub-chunks length
Chunk father TRIANGULAR MESH
Sub chunks FACES MATERIAL
Data Polygons number (unsigned short)
Polygons list: a1,b1,c1,a2,b2,c2 etc. (for each point: 3*unsigned short)
Face flag: face options, sides visibility etc. (unsigned short)
MAPPING COORDINATES LIST(贴图数据在这)
Identifier 0x4140
Length varying + sub-chunks length
Chunk father TRIANGULAR MESH
Sub chunks SMOOTHING GROUP LIST
Data Vertices number (unsigned short)
Mapping coordinates list: u1,v1,u2,v2 etc. (for each vertex: 2*float)

e. 稍作解释:OBJECT BLOCK块的Data里有内容,是一个以“\0”结尾的字符串,存放了该对象的名字,实际上从目录树上可以看出,接下来的点、面、纹理数据等都是该对象的子块。所以说该对象就是描述一个独立完整的模型的对象。不过你可以不用读它,当然,读了也没什么坏处。不过注意的是,不读是不是说要跳过呢?不然,即便不读,也要让指针经过它才能接着取到后面的数据,因此我说的“不读”的意思,是指这个信息没什么用,可以把它读到一个专门盛放废品的容器里。

再来看VERTICES LIST,它的Data里有两方面的数据,一是一共有多少个点,二是分别这些点的x、y、z坐标值是什么,这些信息正是你需要的。

再来看FACES DESCRIPTION,它的Data里有三方面的数据,一是一共有多少面,二是分别这些面是由哪三个点构成的,它用到了上面读到的点的数据。比如有一个面的信息是1,2,3,这就表示,这个面是由第1个点、第2个点和第3个点构成的。注意3ds里所有的面都是三角形,只需三个点。第三方面的内容face flag是专门为3dmax的编辑器使用的,我们不需要,所以要把它读到废品站里。

再来看MAPPING COORDINATES LIST,这是纹理坐标数据,由u、v值表示坐标。这些纹理点的次序和上面的实点的次序是一一对应的。每一个实点都对应一个纹理点。

f. 我的Laoder代码贴下:

  1#include"3dsloader.h"
  2#include<fstream>
  3
  4using namespace std;
  5
  6bool tri_dsLoader(char* f_name,Object* obj)
  7{
  8    //定义接受容器
  9    unsigned short    chunk_id;
 10    unsigned int      chunk_length;
 11    unsigned char     chunk_char[1];
 12    unsigned short    chunk_qty;
 13    unsigned short      i;
 14    unsigned short    chunk_face_flag_chuck;
 15    
 16
 17
 18    
 19    //新建输入流对象
 20    std::ifstream f_in;
 21
 22    
 23
 24    //打开文件
 25    f_in.open(f_name,ios::binary,0x10) ;  //注意第三个参数的取值,只能是0x10,0x20,0x30,0x40中的一个。其他值会报“invalid sha”错误
 26    if(f_in.fail()) return false;
 27
 28    //length表示文件长度
 29    f_in.seekg(0,ios::end);
 30    streampos length=f_in.tellg();
 31    f_in.seekg(0,ios::beg);
 32    
 33    //读取文件
 34    while(f_in.tellg()<length)
 35    {
 36        f_in.read((char*)&chunk_id,sizeof(unsigned short));
 37        f_in.read((char*)&chunk_length,sizeof(unsigned int));
 38
 39        switch(chunk_id)
 40        {
 41        case 0x4d4d:
 42            break;
 43
 44        case 0x3d3d:
 45            break;
 46
 47        case 0x4000:
 48            unsigned char*      p_name;
 49            p_name=obj->getP_name();
 50            do
 51            {
 52                f_in.read((char*)chunk_char,1);
 53                *p_name=chunk_char[0];
 54                ++p_name;
 55            }
while(chunk_char[0]!='\0');
 56            break;
 57
 58        case 0x4100:
 59            break;
 60
 61        case 0x4110:
 62            VertexCoord point;
 63            f_in.read((char*)&chunk_qty,2);
 64            obj->set_points_num(chunk_qty);
 65
 66            for(i=0;i<chunk_qty;++i)
 67            {
 68                f_in.read((char*)&point.x,4);
 69                f_in.read((char*)&point.y,4);
 70                f_in.read((char*)&point.z,4);
 71
 72                obj->set_points(i,point);
 73            }

 74            break;
 75
 76        case 0x4120:
 77            Triangle surface;
 78            f_in.read((char*)&chunk_qty,2);
 79            obj->set_surfaces_num(chunk_qty);
 80
 81            for(i=0;i<chunk_qty;++i)
 82            {
 83                f_in.read((char*)&surface.a,2);
 84                f_in.read((char*)&surface.b,2);
 85                f_in.read((char*)&surface.c,2);
 86
 87                obj->set_surfaces(i,surface);
 88
 89                f_in.read((char*)&chunk_face_flag_chuck,2);
 90            }

 91            break;
 92
 93        case 0x4140:
 94            MappingCoord texture;
 95            f_in.read((char*)&chunk_qty,2);
 96
 97            for(i=0;i<chunk_qty;++i)
 98            {
 99                f_in.read((char*)&texture.u,4);
100                f_in.read((char*)&texture.v,4);
101
102
103                obj->set_textures(i,texture);
104            }

105
106            break;
107
108        default:        
109            f_in.seekg(chunk_length-6,ios::cur);
110        }

111    }

112    f_in.close();
113    
114    return true;
115}

g. 我认为把别人的成果拿来说成是自己的是不道德的,因此我一定要交待下我获取此知识的途径:http://www.spacesimulator.net/tut4_3dsloader.html。我要向作者表示感谢,虽然他永远也不知道,而且这小子从下一篇教程起就开始收费了。


posted on 2009-03-27 23:04  lookof  阅读(2872)  评论(6编辑  收藏  举报