foo
[laserbrain_demo] / src / sceneload.cc
1 #include <stdio.h>
2 #include <vector>
3 #include <map>
4 #include <gmath/gmath.h>
5 #include <assimp/cimport.h>
6 #include <assimp/postprocess.h>
7 #include <assimp/scene.h>
8 #include <assimp/mesh.h>
9 #include <assimp/material.h>
10 #include <assimp/anim.h>
11 #include <assimp/vector3.h>
12 #include <assimp/matrix4x4.h>
13 #include <assimp/quaternion.h>
14 #include "scene.h"
15 #include "objmesh.h"
16
17 static bool load_material(Material *mat, const aiMaterial *aimat);
18 static SceneNode *load_node(const aiScene *aiscn, unsigned int flags, const aiNode *ainode);
19 static Mesh *load_mesh(const aiScene *aiscn, unsigned int flags, const aiMesh *aimesh);
20
21 /*static Vec3 assimp_vector(const aiVector3D &v);
22 static Quat assimp_quat(const aiQuaternion &q);
23 static Mat4 assimp_matrix(const aiMatrix4x4 &aim);
24 static long assimp_time(const aiAnimation *anim, double aitime);
25 static void print_hierarchy(const aiNode *node);
26 */
27
28 static std::map<std::string, SceneNode*> node_by_name;
29 static std::map<aiMesh*, Mesh*> mesh_by_aimesh;
30
31 bool Scene::load(const char *fname, unsigned int flags)
32 {
33         unsigned int ppflags = aiProcess_CalcTangentSpace |
34                 aiProcess_GenNormals |
35                 aiProcess_JoinIdenticalVertices |
36                 aiProcess_Triangulate |
37                 aiProcess_SortByPType |
38                 aiProcess_TransformUVCoords | aiProcess_PreTransformVertices;
39
40         if(flags & SCNLOAD_FLIPTEX) {
41                 ppflags |= aiProcess_FlipUVs;
42         }
43
44         const aiScene *aiscn = aiImportFile(fname, ppflags);
45         if(!aiscn) {
46                 fprintf(stderr, "failed to load scene file: %s\n", fname);
47                 return false;
48         }
49
50         /*
51         Vec3 root_pos, root_scaling(1.0, 1.0, 1.0);
52         Quat root_rot;
53
54         if(aiscn->mRootNode) {
55                 Mat4 root_matrix = assimp_matrix(aiscn->mRootNode->mTransformation);
56                 root_pos = root_matrix.get_translation();
57                 root_rot = root_matrix.get_rotation_quat();
58                 root_scaling = root_matrix.get_scaling();
59         }*/
60
61         // load all meshes
62         for(unsigned int i=0; i<aiscn->mNumMeshes; i++) {
63                 aiMesh *aimesh = aiscn->mMeshes[i];
64                 Mesh *mesh;
65
66                 switch(aimesh->mPrimitiveTypes) {
67                 case aiPrimitiveType_TRIANGLE:
68                         if((mesh = load_mesh(aiscn, flags, aimesh))) {
69                                 mesh_by_aimesh[aimesh] = mesh;
70                                 meshes.push_back(mesh);
71                         }
72                         break;
73
74                 default:
75                         fprintf(stderr, "unsupported primitive type: %u\n", aimesh->mPrimitiveTypes);
76                         break;
77                 }
78         }
79
80         SceneNode *root = new SceneNode;
81
82         // load all the nodes recursively
83         for(unsigned int i=0; i<aiscn->mRootNode->mNumChildren; i++) {
84                 SceneNode *node = load_node(aiscn, flags, aiscn->mRootNode->mChildren[i]);
85                 if(node) {
86                         root->add_child(node);
87                 }
88         }
89
90         int nnodes = root->get_num_children();
91         if(nnodes <= 0) {
92                 delete root;
93         } else if(nnodes == 1) {
94                 nodes = root->get_child(0);
95                 root->remove_child(nodes);
96                 delete root;
97         } else {
98                 nodes = root;
99         }
100
101         node_by_name.clear();
102         mesh_by_aimesh.clear();
103
104         aiReleaseImport(aiscn);
105         printf("loaded scene file: %s, %d meshes\n", fname, (int)meshes.size());
106         return true;
107 }
108
109 static bool load_material(Material *mat, const aiMaterial *aimat)
110 {
111         aiColor4D aicol;
112         float shin, shin_str;
113
114         if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_DIFFUSE, &aicol) == 0) {
115                 mat->diffuse = Vec3(aicol[0], aicol[1], aicol[2]);
116         }
117         if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_SPECULAR, &aicol) == 0) {
118                 mat->specular = Vec3(aicol[0], aicol[1], aicol[2]);
119         }
120
121         unsigned int count = 1;
122         if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS_STRENGTH, &shin_str, &count) != 0) {
123                 shin_str = 1.0;
124         }
125         if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS, &shin, &count) == 0) {
126                 // XXX can't remember how I came up with this...
127                 mat->shininess = shin * shin_str * 0.0001 * 128.0;
128         }
129
130         /*
131         // load textures
132         struct { int type; aiTextureType aitype; } textypes[] = {
133                 {TEX_DIFFUSE, aiTextureType_DIFFUSE},
134                 {TEX_NORMAL, aiTextureType_NORMALS},
135                 {TEX_SPECULAR, aiTextureType_SPECULAR}
136         };
137
138         for(size_t i=0; i<sizeof textypes / sizeof *textypes; i++) {
139                 aiString aipath;
140
141                 if(aiGetMaterialTexture(aimat, textypes[i].aitype, 0, &aipath) == 0) {
142                         char *tmp, *fname = aipath.data;
143
144                         if((tmp = strrchr(fname, '/'))) {
145                                 fname = tmp + 1;
146                         }
147                         if((tmp = strrchr(fname, '\\'))) {
148                                 fname = tmp + 1;
149                         }
150
151                         if(*fname) {
152                                 mat->tex[textypes[i].type] = texset.get(fname);
153                         }
154                 }
155         }
156         */
157
158         return true;
159 }
160
161 static SceneNode *load_node(const aiScene *aiscn, unsigned int flags, const aiNode *ainode)
162 {
163         SceneNode *node = new SceneNode;
164         node->set_name(ainode->mName.data);
165
166         for(unsigned int i=0; i<ainode->mNumMeshes; i++) {
167                 aiMesh *aimesh = aiscn->mMeshes[ainode->mMeshes[0]];
168
169                 Mesh *mesh = mesh_by_aimesh[aimesh];
170                 if(mesh) {
171                         ObjMesh *obj = new ObjMesh;
172                         obj->mesh = mesh;
173                         // also grab the material of this mesh
174                         load_material(&obj->mtl, aiscn->mMaterials[aimesh->mMaterialIndex]);
175
176                         node->add_object(obj);
177                 }
178         }
179
180         /* recurse to all children */
181         for(unsigned int i=0; i<ainode->mNumChildren; i++) {
182                 SceneNode *child = load_node(aiscn, flags, ainode->mChildren[i]);
183                 if(child) {
184                         node->add_child(child);
185                 }
186         }
187
188         node_by_name[node->get_name()] = node;
189         return node;
190 }
191
192 static Mesh *load_mesh(const aiScene *aiscn, unsigned int flags, const aiMesh *aimesh)
193 {
194         Mesh *mesh = new Mesh;
195
196         int num_verts = aimesh->mNumVertices;
197         int num_faces = aimesh->mNumFaces;
198
199         mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, (float*)aimesh->mVertices);
200
201         if(aimesh->mNormals) {
202                 mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, (float*)aimesh->mNormals);
203         }
204         if(aimesh->mTangents) {
205                 mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, (float*)aimesh->mTangents);
206         }
207         if(aimesh->mTextureCoords[0]) {
208                 mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 3, num_verts, (float*)aimesh->mTextureCoords[0]);
209         }
210
211         if(flags & SCNLOAD_FLIPYZ) {
212                 Vec3 *vptr = (Vec3*)mesh->get_attrib_data(MESH_ATTR_VERTEX);
213                 for(int i=0; i<num_verts; i++) {
214                         *vptr = vptr->xzy();
215                         ++vptr;
216                 }
217
218                 Vec3 *nptr = (Vec3*)mesh->get_attrib_data(MESH_ATTR_NORMAL);
219                 for(int i=0; i<num_verts; i++) {
220                         *nptr = nptr->xzy();
221                         ++nptr;
222                 }
223
224                 Vec3 *tptr = (Vec3*)mesh->get_attrib_data(MESH_ATTR_TANGENT);
225                 for(int i=0; i<num_verts; i++) {
226                         *tptr = tptr->xzy();
227                         ++tptr;
228                 }
229         }
230
231         unsigned int *iptr = mesh->set_index_data(num_faces * 3);
232         for(int i=0; i<num_faces; i++) {
233                 iptr[0] = aimesh->mFaces[i].mIndices[0];
234                 iptr[1] = aimesh->mFaces[i].mIndices[flags & SCNLOAD_FLIPYZ ? 2 : 1];
235                 iptr[2] = aimesh->mFaces[i].mIndices[flags & SCNLOAD_FLIPYZ ? 1 : 2];
236                 iptr += 3;
237         }
238
239         return mesh;
240 }
241
242 #if 0
243 static Vec3 assimp_vector(const aiVector3D &v)
244 {
245         return Vec3(v[0], v[1], v[2]);
246 }
247
248 static Quat assimp_quat(const aiQuaternion &q)
249 {
250         return Quat(q.x, q.y, q.z, q.w);
251 }
252
253 static Mat4 assimp_matrix(const aiMatrix4x4 &aim)
254 {
255         Mat4 m;
256         memcpy(m[0], &aim, 16 * sizeof(float));
257         m.transpose();
258         return m;
259 }
260
261 /* convert an assimp keyframe time (ticks) into milliseconds */
262 static long assimp_time(const aiAnimation *anim, double aitime)
263 {
264         double sec;
265         if(anim->mTicksPerSecond < 1e-6) {
266                 // assume time is in frames?
267                 sec = aitime / 30.0;
268         } else {
269                 sec = aitime / anim->mTicksPerSecond;
270         }
271         return (long)(sec * 1000.0);
272 }
273
274 static void print_hierarchy(const aiNode *node)
275 {
276         static int lvl;
277         static int lvlopen[256];
278
279         for(int i=0; i<lvl; i++) {
280                 putchar(' ');
281                 if(lvlopen[i]) {
282                         putchar(i >= lvl - 1 ? '+' : '|');
283                 } else {
284                         putchar(i >= lvl - 1 ? '+' : ' ');
285                 }
286         }
287         printf("- \"%s\"\n", node->mName.data);
288
289         lvlopen[lvl] = 1;
290
291         lvl++;
292         for(unsigned int i=0; i<node->mNumChildren; i++) {
293                 if(i == node->mNumChildren - 1) {
294                         lvlopen[lvl - 1] = 0;
295                 }
296                 print_hierarchy(node->mChildren[i]);
297         }
298         lvl--;
299 }
300 #endif  /* 0 */