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