早就想自己做个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”的结构如下所示:
c. 读取的思路是:首先根据偏移量和长度找到一个块的标识符,然后据此来判断它是什么块,遇到我们需要的块,就进一步读取,如果不需要,直接跳过这一块,读取下面的块。
d. 一个简单的Loader程序不需要什么都读取,只需要读基本的图元就行了。比如点数据,面数据,和纹理数据。我的Laoder主要包含的就是这三个信息。当然,如果你要读到这三者,起码要把先前的父块内容读入才行,否则就被跳过了。因此你一共要读个7块数据。这7者信息如下:
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值表示坐标。这些纹理点的次序和上面的实点的次序是一一对应的。每一个实点都对应一个纹理点。
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}
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。我要向作者表示感谢,虽然他永远也不知道,而且这小子从下一篇教程起就开始收费了。