lib3ds大家看了园子里发过的Tutorial后可能都会使用了,这里我展示了一个小小的优化技巧,把一个3DS文件中所有Objects的顶点、纹理坐标、向量都捆绑在一个Batch里进行渲染。局限是,如果你的3DS文件使用了多个贴图,那么还是无法使用这个技巧。或者你如同John Camark一样,发明那个MetaTexture技术,把多个贴图放到一个纹理中。
我对比过STL版本的和C版本的代码,STL慢了不止一秒,而C代码使用了最快速与直接的memcpy,效率当然比STL容器高的多,不过代价是,调试非常麻烦,动不动就出错。
关键是把所有的索引Index放在一起,正确的组合所有的顶点。代码如下,有些脏:
1
static 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

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

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了,还节省纹理通道,非常实用!