Small Tips : 如何整合3DS模型中的Obj
lib3ds大家看了园子里发过的Tutorial后可能都会使用了,这里我展示了一个小小的优化技巧,把一个3DS文件中所有Objects的顶点、纹理坐标、向量都捆绑在一个Batch里进行渲染。局限是,如果你的3DS文件使用了多个贴图,那么还是无法使用这个技巧。或者你如同John Camark一样,发明那个MetaTexture技术,把多个贴图放到一个纹理中。
我对比过STL版本的和C版本的代码,STL慢了不止一秒,而C代码使用了最快速与直接的memcpy,效率当然比STL容器高的多,不过代价是,调试非常麻烦,动不动就出错。
关键是把所有的索引Index放在一起,正确的组合所有的顶点。代码如下,有些脏:
1static void InitModel()
2{
3 Lib3dsFile* file = 0;
4 file = lib3ds_file_load("E:\\MyModels\\ELEPHANT\\ELEPHANT.3DS");
5
6 deque<GLfloat> _V;
7 deque<GLfloat> _T;
8 deque<GLushort> _I;
9 deque<GLfloat> _N;
10
11 deque<GLushort> _n;
12 _n.push_front(0);
13 _n.pop_back();
14 for(deque<ushort>::reverse_iterator r_itr = _n.rbegin();r_itr!=_n.rend();r_itr++){
15 for( deque<ushort>::reverse_iterator f_itr = r_itr + 1; f_itr != _n.rend() ;f_itr++)
16 *r_itr += *f_itr;
17 //printf("%d\n",*r_itr);
18 }
19 //Get All meshes information
20 Lib3dsMesh *m = 0;
21 int VerNum = 0,TexNum = 0,TriNum = 0,IdxNum=0;
22
23 int MeshNum = 0;
24 for( m=file->meshes; m!=0; m=m->next){
25 VerNum += m->points;
26 TexNum += m->texels;
27 TriNum += m->faces;
28 IdxNum += m->faces*3;
29 MeshNum++;
30 _n.push_back(m->points);
31 };
32 VerticesPtr = new GLfloat[VerNum * 3];
33 TexCoordsPtr = new GLfloat[TexNum * 2];
34 NormalPtr = new GLfloat[VerNum * 3];
35 //NormalPtr = new GLfloat[TriNum * 3 * 3];
36 IndexPtr = new GLushort[IdxNum];
37 IndexNum = IdxNum;
38
39
40 printf("<--- mesh num : %d,tri,pointsNum : %d --->\n",MeshNum,VerNum);
41
42 _n.push_front(0);
43 _n.pop_back();
44 for(deque<ushort>::reverse_iterator r_itr = _n.rbegin();r_itr!=_n.rend();r_itr++){
45 for( deque<ushort>::reverse_iterator f_itr = r_itr + 1; f_itr != _n.rend() ;f_itr++)
46 *r_itr += *f_itr;
47 //printf("%d\n",*r_itr);
48 }
49
50 int offset[4] = {0,0,0,0};
51 int j = 0;
52 for( m=file->meshes; m!=0; m=m->next){
53 //copy VerticesPtr
54 for( int i = 0;i<m->points;i++ ){
55 memcpy(&(VerticesPtr[offset[0]*3]),&(m->pointL[i].pos[0]),sizeof(float)*3);
56 offset[0]++;
57 };
58 //copy Texcoords
59 for( int i=0;i<m->texels;i++ ){
60 memcpy(&(TexCoordsPtr[offset[1]*2]),&(m->texelL[i][0]),sizeof(float)*2);
61 offset[1]++;
62 };
63
64 Lib3dsVector* normalL = (Lib3dsVector*)malloc(3*sizeof(Lib3dsVector)*m->faces);
65 lib3ds_mesh_calculate_normals(m,normalL);
66
67 for( int i=0;i<m->faces;i++ ){
68 ushort _0 = m->faceL[i].points[0] + _n[j];
69 ushort _1 = m->faceL[i].points[1] + _n[j];
70 ushort _2 = m->faceL[i].points[2] + _n[j];
71
72 IndexPtr[ offset[2] ] = _0;
73 offset[2]++;
74 IndexPtr[ offset[2] ] = _1;
75 offset[2]++;
76 IndexPtr[ offset[2] ] = _2;
77 offset[2]++;
78 memcpy(&(NormalPtr[ _0 ]),&(normalL[i*3 + 0]),sizeof(float)*3);
79 memcpy(&(NormalPtr[ _1 ]),&(normalL[i*3 + 1]),sizeof(float)*3);
80 memcpy(&(NormalPtr[ _2 ]),&(normalL[i*3 + 2]),sizeof(float)*3);
81 };
82 printf("J : %d\t _n[j] : %d\n",j,_n[j]);
83 j++;
84
85 free(normalL);
86 };
87
88 for(int i=0;i<10;i++)
89 printf("%f %f %f\t",NormalPtr[i*3 + 0],NormalPtr[i*3 + 1],NormalPtr[i*3 + 2]);
90 //printf("%d ",IndexPtr[i]);
91 printf("IndexNum : %d\n",_I.size());
92
93
94};
2{
3 Lib3dsFile* file = 0;
4 file = lib3ds_file_load("E:\\MyModels\\ELEPHANT\\ELEPHANT.3DS");
5
6 deque<GLfloat> _V;
7 deque<GLfloat> _T;
8 deque<GLushort> _I;
9 deque<GLfloat> _N;
10
11 deque<GLushort> _n;
12 _n.push_front(0);
13 _n.pop_back();
14 for(deque<ushort>::reverse_iterator r_itr = _n.rbegin();r_itr!=_n.rend();r_itr++){
15 for( deque<ushort>::reverse_iterator f_itr = r_itr + 1; f_itr != _n.rend() ;f_itr++)
16 *r_itr += *f_itr;
17 //printf("%d\n",*r_itr);
18 }
19 //Get All meshes information
20 Lib3dsMesh *m = 0;
21 int VerNum = 0,TexNum = 0,TriNum = 0,IdxNum=0;
22
23 int MeshNum = 0;
24 for( m=file->meshes; m!=0; m=m->next){
25 VerNum += m->points;
26 TexNum += m->texels;
27 TriNum += m->faces;
28 IdxNum += m->faces*3;
29 MeshNum++;
30 _n.push_back(m->points);
31 };
32 VerticesPtr = new GLfloat[VerNum * 3];
33 TexCoordsPtr = new GLfloat[TexNum * 2];
34 NormalPtr = new GLfloat[VerNum * 3];
35 //NormalPtr = new GLfloat[TriNum * 3 * 3];
36 IndexPtr = new GLushort[IdxNum];
37 IndexNum = IdxNum;
38
39
40 printf("<--- mesh num : %d,tri,pointsNum : %d --->\n",MeshNum,VerNum);
41
42 _n.push_front(0);
43 _n.pop_back();
44 for(deque<ushort>::reverse_iterator r_itr = _n.rbegin();r_itr!=_n.rend();r_itr++){
45 for( deque<ushort>::reverse_iterator f_itr = r_itr + 1; f_itr != _n.rend() ;f_itr++)
46 *r_itr += *f_itr;
47 //printf("%d\n",*r_itr);
48 }
49
50 int offset[4] = {0,0,0,0};
51 int j = 0;
52 for( m=file->meshes; m!=0; m=m->next){
53 //copy VerticesPtr
54 for( int i = 0;i<m->points;i++ ){
55 memcpy(&(VerticesPtr[offset[0]*3]),&(m->pointL[i].pos[0]),sizeof(float)*3);
56 offset[0]++;
57 };
58 //copy Texcoords
59 for( int i=0;i<m->texels;i++ ){
60 memcpy(&(TexCoordsPtr[offset[1]*2]),&(m->texelL[i][0]),sizeof(float)*2);
61 offset[1]++;
62 };
63
64 Lib3dsVector* normalL = (Lib3dsVector*)malloc(3*sizeof(Lib3dsVector)*m->faces);
65 lib3ds_mesh_calculate_normals(m,normalL);
66
67 for( int i=0;i<m->faces;i++ ){
68 ushort _0 = m->faceL[i].points[0] + _n[j];
69 ushort _1 = m->faceL[i].points[1] + _n[j];
70 ushort _2 = m->faceL[i].points[2] + _n[j];
71
72 IndexPtr[ offset[2] ] = _0;
73 offset[2]++;
74 IndexPtr[ offset[2] ] = _1;
75 offset[2]++;
76 IndexPtr[ offset[2] ] = _2;
77 offset[2]++;
78 memcpy(&(NormalPtr[ _0 ]),&(normalL[i*3 + 0]),sizeof(float)*3);
79 memcpy(&(NormalPtr[ _1 ]),&(normalL[i*3 + 1]),sizeof(float)*3);
80 memcpy(&(NormalPtr[ _2 ]),&(normalL[i*3 + 2]),sizeof(float)*3);
81 };
82 printf("J : %d\t _n[j] : %d\n",j,_n[j]);
83 j++;
84
85 free(normalL);
86 };
87
88 for(int i=0;i<10;i++)
89 printf("%f %f %f\t",NormalPtr[i*3 + 0],NormalPtr[i*3 + 1],NormalPtr[i*3 + 2]);
90 //printf("%d ",IndexPtr[i]);
91 printf("IndexNum : %d\n",_I.size());
92
93
94};
解释一下思路。3DS文件如果有多个OBJ,那么每一个OBJ都会维护一个自己的索引Index,而且都是从0开始。如果我们仅仅把顶点放在一起,那么是没有意义的,无法得到完整的Mesh。所以我们就必须迭代计算前一个Obj有多少个顶点,并且把当前Obj的索引都加上前面的数据。举例如下。
3个Obj:
Obj 0 1 2
顶点数 10 20 30
三角形数目 5 8 14
新索引起点 0 5 13
deque容器我留在了代码中,如果你不放心可以使用它们来装载数据,看看速度究竟有多么的迟缓。
如果每个Obj都有自己的纹理,那么就很麻烦了。在你的纹理加载代码中,需要额外的声明一个能够装得下所有纹理的“巨大纹理”,拷贝所有的图象数据到这个大纹理中,记得各自的位置。在整合模型的阶段,处理纹理坐标,根据各自的纹理所在“巨大纹理”图上的位置进行缩放。然后就OK了,还节省纹理通道,非常实用!