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