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