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