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