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