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