fixed bugs in the resource manager and removed the hardcoded textures
[laserbrain_demo] / src / sceneload.cc
1 #include <stdio.h>
2 #include <assert.h>
3 #include <vector>
4 #include <map>
5 #include <gmath/gmath.h>
6 #include <assimp/cimport.h>
7 #include <assimp/postprocess.h>
8 #include <assimp/scene.h>
9 #include <assimp/mesh.h>
10 #include <assimp/material.h>
11 #include <assimp/anim.h>
12 #include <assimp/vector3.h>
13 #include <assimp/matrix4x4.h>
14 #include <assimp/quaternion.h>
15 #include "scene.h"
16 #include "objmesh.h"
17 #include "datamap.h"
18
19 static bool load_material(Scene *scn, Material *mat, const aiMaterial *aimat);
20 static SceneNode *load_node(Scene *scn, const aiScene *aiscn, unsigned int flags, const aiNode *ainode);
21 static Mesh *load_mesh(Scene *scn, const aiScene *aiscn, unsigned int flags, const aiMesh *aimesh);
22 static void print_nodes(SceneNode *node, int lvl = 0);
23 /*static const char *mprop_semantic(int x);
24 static int count_textures(const aiMaterial *aimat);*/
25 static int assimp_textype(aiTextureType type);
26 static const char *assimp_textypestr(aiTextureType type);
27
28 static Mat4 assimp_matrix(const aiMatrix4x4 &aim);
29
30 /*static Vec3 assimp_vector(const aiVector3D &v);
31 static Quat assimp_quat(const aiQuaternion &q);
32 static long assimp_time(const aiAnimation *anim, double aitime);
33 static void print_hierarchy(const aiNode *node);
34 */
35
36 static std::map<std::string, SceneNode*> node_by_name;
37 static std::map<aiMesh*, Mesh*> mesh_by_aimesh;
38
39 bool Scene::load(const char *fname, unsigned int flags)
40 {
41         unsigned int ppflags = aiProcess_CalcTangentSpace |
42                 aiProcess_GenNormals |
43                 aiProcess_JoinIdenticalVertices |
44                 aiProcess_Triangulate |
45                 aiProcess_SortByPType |
46                 aiProcess_GenUVCoords |
47                 //aiProcess_PreTransformVertices |
48                 aiProcess_TransformUVCoords;
49
50         if(flags & SCNLOAD_FLIPTEX) {
51                 ppflags |= aiProcess_FlipUVs;
52         }
53
54         printf("Loading scene file: %s\n", fname);
55
56         const aiScene *aiscn = aiImportFile(fname, ppflags);
57         if(!aiscn) {
58                 fprintf(stderr, "failed to load scene file: %s\n", fname);
59                 return false;
60         }
61
62         // assimp adds its own root node, which might have transformations
63         Vec3 root_pos, root_scaling(1.0, 1.0, 1.0);
64         Quat root_rot;
65
66         if(aiscn->mRootNode) {
67                 Mat4 root_matrix = assimp_matrix(aiscn->mRootNode->mTransformation);
68                 root_pos = root_matrix.get_translation();
69                 root_rot = root_matrix.get_rotation();
70                 root_scaling = root_matrix.get_scaling();
71
72                 printf("assimp root node: %s\n", aiscn->mRootNode->mName.data);
73                 printf("  pos: %f %f %f\n", root_pos.x, root_pos.y, root_pos.z);
74                 printf("  rot: %f %+f %+f %+f\n", root_rot.w, root_rot.x, root_rot.y, root_rot.z);
75                 printf("  scaling: %f %f %f\n", root_scaling.x, root_scaling.y, root_scaling.z);
76         }
77
78         // load all meshes
79         for(unsigned int i=0; i<aiscn->mNumMeshes; i++) {
80                 aiMesh *aimesh = aiscn->mMeshes[i];
81                 Mesh *mesh;
82
83                 switch(aimesh->mPrimitiveTypes) {
84                 case aiPrimitiveType_TRIANGLE:
85                         if((mesh = load_mesh(this, aiscn, flags, aimesh))) {
86                                 mesh_by_aimesh[aimesh] = mesh;
87                                 meshes.push_back(mesh);
88                         }
89                         break;
90
91                 default:
92                         fprintf(stderr, "unsupported primitive type: %u\n", aimesh->mPrimitiveTypes);
93                         break;
94                 }
95         }
96
97         if(!nodes) {
98                 nodes = new SceneNode;
99                 nodes->set_name("root");
100                 nodes->set_position(root_pos);
101                 nodes->set_rotation(root_rot);
102                 nodes->set_scaling(root_scaling);
103         }
104
105         // load all the nodes recursively
106         for(unsigned int i=0; i<aiscn->mRootNode->mNumChildren; i++) {
107                 SceneNode *node = load_node(this, aiscn, flags, aiscn->mRootNode->mChildren[i]);
108                 if(node) {
109                         nodes->add_child(node);
110                 }
111         }
112
113         node_by_name.clear();
114         mesh_by_aimesh.clear();
115
116         aiReleaseImport(aiscn);
117         printf("loaded scene file: %s, %d meshes\n", fname, (int)meshes.size());
118         nodes->update(0);
119         print_nodes(nodes);
120         return true;
121 }
122
123 static bool load_material(Scene *scn, Material *mat, const aiMaterial *aimat)
124 {
125         aiString name;
126         aiColor4D aicol;
127         float shin, shin_str;
128
129         if(aiGetMaterialString(aimat, AI_MATKEY_NAME, &name) == 0) {
130                 mat->name = name.data;
131         } else {
132                 mat->name = "unknown";
133         }
134         printf("load_material: %s\n", mat->name.c_str());
135
136         if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_DIFFUSE, &aicol) == 0) {
137                 mat->diffuse = Vec3(aicol[0], aicol[1], aicol[2]);
138         }
139         if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_SPECULAR, &aicol) == 0) {
140                 mat->specular = Vec3(aicol[0], aicol[1], aicol[2]);
141         }
142
143         unsigned int count = 1;
144         if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS_STRENGTH, &shin_str, &count) != 0) {
145                 shin_str = 1.0;
146         }
147         if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS, &shin, &count) == 0) {
148                 // XXX can't remember how I came up with this...
149                 mat->shininess = shin * shin_str * 0.0001 * 128.0;
150         }
151
152         // load textures
153
154         const int num_tex_types = aiTextureType_UNKNOWN + 1;
155         for(int i=0; i<num_tex_types; i++) {
156                 aiTextureType aitype = (aiTextureType)i;
157                 int count = aiGetMaterialTextureCount(aimat, aitype);
158
159                 for(int j=0; j<count; j++) {
160                         aiString aipath;
161                         if(aiGetMaterialTexture(aimat, aitype, j, &aipath) != 0) {
162                                 continue;
163                         }
164
165                         char *fname;
166                         int nsize = datamap_path_size(aipath.data);
167                         if(nsize) {
168                                 fname = new char[nsize];
169                                 datamap_lookup(aipath.data, fname, nsize);
170                         } else {
171                                 fname = new char[strlen(aipath.data) + 1];
172                                 char *dptr = fname;
173                                 char *sptr = aipath.data;
174                                 do {
175                                         *dptr++ = *sptr == '\\' ? '/' : *sptr;
176                                 } while(*sptr++);
177                         }
178
179                         int textype = assimp_textype(aitype);
180                         printf("loading %s texture: %s\n", assimp_textypestr(aitype), fname);
181
182                         Texture *tex = scn->texset->get_texture(fname, TEX_2D);
183                         assert(tex);
184                         mat->textures.push_back(tex);
185
186                         printf("   DBG(%p)\n", (void*)tex);
187
188                         if(textype != MTL_TEX_UNKNOWN && !mat->stdtex[textype]) {
189                                 mat->stdtex[textype] = tex;
190                         }
191                 }
192         }
193
194         return true;
195 }
196
197 static SceneNode *load_node(Scene *scn, const aiScene *aiscn, unsigned int flags, const aiNode *ainode)
198 {
199         SceneNode *node = new SceneNode;
200         node->set_name(ainode->mName.data);
201
202         // transformation
203         Mat4 matrix = assimp_matrix(ainode->mTransformation);
204         Vec3 pos = matrix.get_translation();
205         Quat rot = matrix.get_rotation();
206         Vec3 scale = matrix.get_scaling();
207
208         node->set_position(pos);
209         node->set_rotation(rot);
210         node->set_scaling(scale);
211         node->dbg_xform = matrix;
212
213         // meshes
214         for(unsigned int i=0; i<ainode->mNumMeshes; i++) {
215                 aiMesh *aimesh = aiscn->mMeshes[ainode->mMeshes[i]];
216
217                 Mesh *mesh = mesh_by_aimesh[aimesh];
218                 if(mesh) {
219                         ObjMesh *obj = new ObjMesh;
220                         obj->mesh = mesh;
221                         // also grab the material of this mesh
222                         load_material(scn, &obj->mtl, aiscn->mMaterials[aimesh->mMaterialIndex]);
223
224                         node->add_object(obj);
225                         scn->objects.push_back(obj);
226                 }
227         }
228
229         /* recurse to all children */
230         for(unsigned int i=0; i<ainode->mNumChildren; i++) {
231                 SceneNode *child = load_node(scn, aiscn, flags, ainode->mChildren[i]);
232                 if(child) {
233                         node->add_child(child);
234                 }
235         }
236
237         node_by_name[node->get_name()] = node;
238         return node;
239 }
240
241 static Mesh *load_mesh(Scene *scn, const aiScene *aiscn, unsigned int flags, const aiMesh *aimesh)
242 {
243         Mesh *mesh = new Mesh;
244
245         int num_verts = aimesh->mNumVertices;
246         int num_faces = aimesh->mNumFaces;
247
248         mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, (float*)aimesh->mVertices);
249
250         if(aimesh->mNormals) {
251                 mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, (float*)aimesh->mNormals);
252         }
253         if(aimesh->mTangents) {
254                 mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, (float*)aimesh->mTangents);
255         }
256         if(aimesh->mTextureCoords[0]) {
257                 mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 3, num_verts, (float*)aimesh->mTextureCoords[0]);
258         }
259         if(aimesh->mTextureCoords[1]) {
260                 mesh->set_attrib_data(MESH_ATTR_TEXCOORD2, 3, num_verts, (float*)aimesh->mTextureCoords[1]);
261         }
262
263         if(flags & SCNLOAD_FLIPYZ) {
264                 Vec3 *vptr = (Vec3*)mesh->get_attrib_data(MESH_ATTR_VERTEX);
265                 for(int i=0; i<num_verts; i++) {
266                         *vptr = vptr->xzy();
267                         ++vptr;
268                 }
269
270                 Vec3 *nptr = (Vec3*)mesh->get_attrib_data(MESH_ATTR_NORMAL);
271                 for(int i=0; i<num_verts; i++) {
272                         *nptr = nptr->xzy();
273                         ++nptr;
274                 }
275
276                 Vec3 *tptr = (Vec3*)mesh->get_attrib_data(MESH_ATTR_TANGENT);
277                 for(int i=0; i<num_verts; i++) {
278                         *tptr = tptr->xzy();
279                         ++tptr;
280                 }
281         }
282
283         unsigned int *iptr = mesh->set_index_data(num_faces * 3);
284         for(int i=0; i<num_faces; i++) {
285                 iptr[0] = aimesh->mFaces[i].mIndices[0];
286                 iptr[1] = aimesh->mFaces[i].mIndices[flags & SCNLOAD_FLIPYZ ? 2 : 1];
287                 iptr[2] = aimesh->mFaces[i].mIndices[flags & SCNLOAD_FLIPYZ ? 1 : 2];
288                 iptr += 3;
289         }
290
291         AABox bbox = mesh->get_aabbox();
292         printf("mesh bounding box: %f %f %f -> %f %f %f\n", bbox.min.x, bbox.min.y, bbox.min.z,
293                         bbox.max.x, bbox.max.y, bbox.max.z);
294
295         return mesh;
296 }
297
298 static void print_nodes(SceneNode *node, int lvl)
299 {
300         Vec3 pos = node->get_node_position();
301         Quat rot = node->get_node_rotation();
302         Vec3 scale = node->get_node_scaling();
303
304         const char *type = node->get_num_objects() > 0 ? "mesh node" : "null node";
305
306         for(int i=0; i<lvl; i++) {
307                 fputs("  ", stdout);
308         }
309         printf("%s[%s] p(%g %g %g) rq(%g %+gi %+gj %+gk) s(%g %g %g)\n", type, node->get_name(),
310                         pos.x, pos.y, pos.z, rot.w, rot.x, rot.y, rot.z, scale.x, scale.y, scale.z);
311
312         if(node->get_num_objects()) {
313                 Mat4 xform = node->get_matrix();
314                 xform.print(stdout);
315         }
316
317         int nchld = node->get_num_children();
318         for(int i=0; i<nchld; i++) {
319                 print_nodes(node->get_child(i), lvl + 1);
320         }
321 }
322
323 static int assimp_textype(aiTextureType type)
324 {
325         switch(type) {
326         case aiTextureType_DIFFUSE:
327                 return MTL_TEX_DIFFUSE;
328         case aiTextureType_SPECULAR:
329                 return MTL_TEX_SPECULAR;
330         case aiTextureType_NORMALS:
331                 return MTL_TEX_NORMALMAP;
332         case aiTextureType_LIGHTMAP:
333         case aiTextureType_EMISSIVE:
334                 return MTL_TEX_LIGHTMAP;
335         case aiTextureType_REFLECTION:
336                 return MTL_TEX_ENVMAP;
337         default:
338                 break;
339         }
340         return MTL_TEX_UNKNOWN;
341 }
342
343 static const char *assimp_textypestr(aiTextureType type)
344 {
345         switch(type) {
346         case aiTextureType_DIFFUSE:
347                 return "diffuse";
348         case aiTextureType_SPECULAR:
349                 return "specular";
350         case aiTextureType_NORMALS:
351                 return "normalmap";
352         case aiTextureType_LIGHTMAP:
353         case aiTextureType_EMISSIVE:
354                 return "lightmap";
355         case aiTextureType_REFLECTION:
356                 return "envmap";
357         default:
358                 break;
359         }
360         return "unknown";
361 }
362
363 static Mat4 assimp_matrix(const aiMatrix4x4 &aim)
364 {
365         Mat4 m;
366         memcpy(m[0], &aim, 16 * sizeof(float));
367         return transpose(m);
368 }