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