Many fixes (credits Nuclear)
[hair] / src / mesh.cc
1 #include <GL/glew.h>
2
3 #include <assert.h>
4
5 #include <assimp/cimport.h>
6 #include <assimp/postprocess.h>
7 #include <assimp/scene.h>
8 #include <assimp/mesh.h>
9
10 #include <float.h>
11 #include <imago2.h>
12
13 #include "mesh.h"
14
15 static bool check_tex_opaque(unsigned int tex);
16
17 Mesh::Mesh()
18 {
19         vbo_vertices = 0;
20         vbo_normals = 0;
21         vbo_texcoords = 0;
22         vbo_colors = 0;
23         ibo = 0;
24
25         num_vertices = 0;
26         num_indices = 0;
27
28         mtl.tex = 0;
29         mtl.diffuse = Vec3(1, 1, 1);
30         mtl.shininess = 50;
31 }
32
33 Mesh::~Mesh()
34 {
35         if(vbo_vertices)
36                 glDeleteBuffers(1, &vbo_vertices);
37         if(vbo_normals)
38                 glDeleteBuffers(1, &vbo_normals);
39         if(vbo_texcoords)
40                 glDeleteBuffers(1, &vbo_texcoords);
41         if(vbo_colors)
42                 glDeleteBuffers(1, &vbo_colors);
43         if(ibo)
44                 glDeleteBuffers(1, &ibo);
45
46         vertices.clear();
47         normals.clear();
48         texcoords.clear();
49         colors.clear();
50 }
51
52 void Mesh::draw() const
53 {
54         /* set material */
55         float diff[4] = {
56                 mtl.diffuse.x, mtl.diffuse.y, mtl.diffuse.z, 1.0
57         };
58         glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, diff);
59
60         float spec[4] = {
61                 mtl.specular.x, mtl.specular.y, mtl.specular.z, 1.0
62         };
63         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, spec);
64
65         glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, mtl.shininess);
66
67         glPushAttrib(GL_ENABLE_BIT);
68         if(mtl.tex) {
69                 glBindTexture(GL_TEXTURE_2D, mtl.tex);
70                 glEnable(GL_TEXTURE_2D);
71
72                 if(!mtl.tex_opaque) {
73                         glEnable(GL_BLEND);
74                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
75                 }
76         }
77
78         glBindBuffer(GL_ARRAY_BUFFER, vbo_vertices);
79         glVertexPointer(3, GL_FLOAT, 0, 0);
80
81         if(vbo_normals) {
82                 glBindBuffer(GL_ARRAY_BUFFER, vbo_normals);
83                 glNormalPointer(GL_FLOAT, 0, 0);
84                 glEnableClientState(GL_NORMAL_ARRAY);
85         }
86
87         if(vbo_texcoords) {
88                 glBindBuffer(GL_ARRAY_BUFFER, vbo_texcoords);
89                 glTexCoordPointer(2, GL_FLOAT, 0, 0);
90                 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
91         }
92
93         if(vbo_colors) {
94                 glBindBuffer(GL_ARRAY_BUFFER, vbo_colors);
95                 glColorPointer(3, GL_FLOAT, 0, 0);
96                 glEnableClientState(GL_COLOR_ARRAY);
97         }
98
99         glBindBuffer(GL_ARRAY_BUFFER, 0);
100
101         glEnableClientState(GL_VERTEX_ARRAY);
102
103         if(ibo) {
104                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
105                 glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, 0);
106                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
107         } else {
108                 glDrawArrays(GL_TRIANGLES, 0, num_vertices);
109         }
110
111         glDisableClientState(GL_VERTEX_ARRAY);
112         glDisableClientState(GL_NORMAL_ARRAY);
113         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
114         glDisableClientState(GL_COLOR_ARRAY);
115
116         glPopAttrib();
117 }
118
119 void Mesh::update_vbo(unsigned int which)
120 {
121         if((which & MESH_NORMAL) && !normals.empty()) {
122                 if(!vbo_normals) {
123                         glGenBuffers(1, &vbo_normals);
124                 }
125                 glBindBuffer(GL_ARRAY_BUFFER, vbo_normals);
126                 if(num_vertices != (int)normals.size()) {
127                         glBufferData(GL_ARRAY_BUFFER, normals.size() * 3 * sizeof(float),
128                                         &normals[0], GL_STATIC_DRAW);
129                 }
130                 else {
131                         glBufferSubData(GL_ARRAY_BUFFER, 0, normals.size() * 3 * sizeof(float),
132                                         &normals[0]);
133                 }
134         }
135
136         if((which & MESH_TEXCOORDS) && !texcoords.empty()) {
137                 if(!vbo_texcoords) {
138                         glGenBuffers(1, &vbo_texcoords);
139                 }
140                 glBindBuffer(GL_ARRAY_BUFFER, vbo_texcoords);
141                 if(num_vertices != (int)texcoords.size()) {
142                         glBufferData(GL_ARRAY_BUFFER, texcoords.size() * 2 * sizeof(float),
143                                         &texcoords[0], GL_STATIC_DRAW);
144                 }
145                 else {
146                         glBufferSubData(GL_ARRAY_BUFFER, 0, texcoords.size() * 2 * sizeof(float),
147                                         &texcoords[0]);
148                 }
149         }
150
151         if((which & MESH_COLOR) && !colors.empty()) {
152                 if(!vbo_colors) {
153                         glGenBuffers(1, &vbo_colors);
154                 }
155                 glBindBuffer(GL_ARRAY_BUFFER, vbo_colors);
156                 if(num_vertices != (int)colors.size()) {
157                         glBufferData(GL_ARRAY_BUFFER, colors.size() * 3 * sizeof(float),
158                                         &colors[0], GL_STATIC_DRAW);
159                 }
160                 else {
161                         glBufferSubData(GL_ARRAY_BUFFER, 0, colors.size() * 3 * sizeof(float),
162                                         &colors[0]);
163                 }
164         }
165
166
167         if(which & MESH_VERTEX) {
168                 assert(!vertices.empty());
169                 if(!vbo_vertices) {
170                         glGenBuffers(1, &vbo_vertices);
171                 }
172                 glBindBuffer(GL_ARRAY_BUFFER, vbo_vertices);
173                 if(num_vertices != (int)vertices.size()) {
174                         glBufferData(GL_ARRAY_BUFFER, vertices.size() * 3 * sizeof(float),
175                                         &vertices[0], GL_STATIC_DRAW);
176                 }
177                 else {
178                         glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.size() * 3 * sizeof(float),
179                                         &vertices[0]);
180                 }
181                 num_vertices = vertices.size();
182         }
183
184         if((which & MESH_INDEX) && !indices.empty()) {
185                 if(!ibo) {
186                         glGenBuffers(1, &ibo);
187                 }
188                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
189                 if(num_indices != (int)indices.size()) {
190                         glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * 2,
191                                         &indices[0], GL_STATIC_DRAW);
192                 }
193                 else {
194                         glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indices.size() * 2,
195                                         &indices[0]);
196                 }
197                 num_indices = indices.size();
198         }
199 }
200
201 std::vector<Mesh*> load_meshes(const char *fname)
202 {
203         std::vector<Mesh*> meshes;
204         unsigned int ai_flags = aiProcessPreset_TargetRealtime_Quality;
205         const aiScene *scene = aiImportFile(fname, ai_flags);
206
207         for(unsigned int j=0; j<scene->mNumMeshes; j++) {
208                 aiMesh *amesh = scene->mMeshes[j];
209                 aiMaterial *amtl = scene->mMaterials[amesh->mMaterialIndex];
210
211                 if(!amesh->HasPositions() || !amesh->mNumFaces)
212                         continue;
213
214                 Mesh *mesh = new Mesh;
215                 mesh->name = std::string(amesh->mName.C_Str());
216                 printf("loading mesh: %s\n", mesh->name.c_str());
217
218                 for(unsigned int i=0; i<amesh->mNumVertices; i++) {
219                         Vec3 vertex = Vec3(amesh->mVertices[i].x,
220                                         amesh->mVertices[i].y,
221                                         amesh->mVertices[i].z);
222                         mesh->vertices.push_back(vertex);
223                 }
224                 printf(" %u vertices\n", amesh->mNumVertices);
225
226                 if(amesh->HasNormals()) {
227                         for(unsigned int i=0; i<amesh->mNumVertices; i++) {
228                                 Vec3 normal = Vec3(amesh->mNormals[i].x,
229                                                            amesh->mNormals[i].y,
230                                                                    amesh->mNormals[i].z);
231                                 mesh->normals.push_back(normal);
232                         }
233                 }
234
235                 if(amesh->HasTextureCoords(0)) {
236                         for(unsigned int i=0; i<amesh->mNumVertices; i++) {
237                                 Vec2 tc = Vec2(amesh->mTextureCoords[0][i].x,
238                                                            1.0f - amesh->mTextureCoords[0][i].y);
239                                 mesh->texcoords.push_back(tc);
240                         }
241                 }
242
243                 if(amesh->HasVertexColors(0)) {
244                         for(unsigned int i=0; i<amesh->mNumVertices; i++) {
245                                 Vec3 color = Vec3(amesh->mColors[0][i].r,
246                                                                   amesh->mColors[0][i].g,
247                                                                   amesh->mColors[0][i].b);
248                                 mesh->colors.push_back(color);
249                         }
250                 }
251
252                 for(unsigned int i=0; i<amesh->mNumFaces; i++) {
253                         for(int j=0; j<3; j++) {
254                                 mesh->indices.push_back(amesh->mFaces[i].mIndices[j]);
255                         }
256                 }
257                 printf(" %d faces\n", amesh->mNumFaces);
258
259                 aiColor4D acol;
260                 aiGetMaterialColor(amtl, AI_MATKEY_COLOR_DIFFUSE, &acol);
261                 mesh->mtl.diffuse = Vec3(acol.r, acol.g, acol.b);
262
263                 float sstr;
264                 aiGetMaterialFloat(amtl, AI_MATKEY_SHININESS_STRENGTH, &sstr);
265
266                 aiGetMaterialColor(amtl, AI_MATKEY_COLOR_SPECULAR, &acol);
267                 mesh->mtl.specular = sstr * Vec3(acol.r, acol.g, acol.b) * 0.3;
268
269                 float shin;
270                 aiGetMaterialFloat(amtl, AI_MATKEY_SHININESS, &shin);
271                 mesh->mtl.shininess = shin * 6;
272
273                 aiString astr;
274                 if(aiGetMaterialTexture(amtl, aiTextureType_DIFFUSE, 0, &astr) == 0) {
275                         char *fname = astr.data;
276                         char *slash;
277                         char *path;
278
279                         if((slash = strrchr(fname, '/'))) {
280                                 fname = slash + 1;
281                         }
282                         if((slash = strrchr(fname, '\\'))) {
283                                 fname = slash + 1;
284                         }
285                         path = new char[strlen(fname) + 6];
286                         sprintf(path, "data/%s", fname);
287                         if(!(mesh->mtl.tex = img_gltexture_load(path))) {
288                                 fprintf(stderr, "Failed to load texture %s\n", path);
289                         } else {
290                                 mesh->mtl.tex_opaque = check_tex_opaque(mesh->mtl.tex);
291                                 printf(" texture: %s (%s)\n", path, mesh->mtl.tex_opaque ? "opaque" : "transparent");
292                         }
293                         delete [] path;
294                 }
295
296                 meshes.push_back(mesh);
297         }
298
299         aiReleaseImport(scene);
300         return meshes;
301 }
302
303 void Mesh::calc_bbox()
304 {
305         if (vertices.empty()) {
306                 bbox.v0 = Vec3(0, 0, 0);
307                 bbox.v1 = Vec3(0, 0, 0);
308
309                 return;
310         }
311
312         bbox.v0 = Vec3(FLT_MAX, FLT_MAX, FLT_MAX);
313         bbox.v1 = -bbox.v0;
314
315         for(size_t i=0; i<vertices.size(); i++) {
316                 for(int j=0; j<3; j++) {
317                         if(vertices[i][j] < bbox.v0[j])
318                                 bbox.v0[j] = vertices[i][j];
319                         if(vertices[i][j] > bbox.v1[j])
320                                 bbox.v1[j] = vertices[i][j];
321                 }
322         }
323 }
324
325 static bool check_tex_opaque(unsigned int tex)
326 {
327         int xsz, ysz;
328         uint32_t *pixels, *pptr;
329
330         glBindTexture(GL_TEXTURE_2D, tex);
331         glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &xsz);
332         glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &ysz);
333         assert(xsz > 0 && ysz > 0);
334
335         pptr = pixels = new uint32_t[xsz * ysz];
336         glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
337
338         for(int i=0; i<xsz * ysz; i++) {
339                 if((*pptr++ & 0xff000000) != 0xff000000) {
340                         delete [] pixels;
341                         return false;
342                 }
343         }
344         delete [] pixels;
345         return true;
346 }