goat3dgfx

view src/assload.cc @ 16:f61cc1df533c

added viewscn example (under dev)
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 30 Nov 2013 20:53:26 +0200
parents 1873dfd13f2d
children
line source
1 #include <stdio.h>
2 #include "assload.h"
3 #include "logger.h"
4 #include "datapath.h"
6 #ifdef USE_ASSIMP
8 #include <vector>
9 #include <map>
10 #include "assimp/cimport.h"
11 #include "assimp/scene.h"
12 #include "assimp/postprocess.h"
13 #include "texman.h"
14 #include "material.h"
15 #include "scene.h"
17 using namespace std;
18 using namespace goatgfx;
20 static bool load_material(Material *mat, const aiMaterial *aimat);
21 static Object *load_node(const aiScene *aiscn, const aiNode *ainode);
22 static Mesh *load_mesh(const aiScene *aiscn, const aiMesh *aimesh);
23 static Curve *load_curve(const aiScene *aiscn, const aiMesh *aimesh);
24 static bool load_bones(Mesh *mesh, const aiMesh *aimesh);
26 static Vector3 assimp_vector(const aiVector3D &v);
27 static Quaternion assimp_quat(const aiQuaternion &q);
28 static Matrix4x4 assimp_matrix(const aiMatrix4x4 &aim);
29 static long assimp_time(const aiAnimation *anim, double aitime);
30 static void print_hierarchy(const aiNode *node);
32 static map<string, Object*> obj_by_name;
33 static map<aiMesh*, Mesh*> mesh_by_aimesh;
35 bool load_ass(Scene *scn, const char *fname)
36 {
37 static bool init_done;
39 if(!init_done) {
40 static aiLogStream log_stream = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT, 0);
41 aiAttachLogStream(&log_stream);
42 //aiEnableVerboseLogging(1);
43 init_done = true;
44 }
46 unsigned int proc_flags = aiProcess_JoinIdenticalVertices |
47 aiProcess_CalcTangentSpace |
48 aiProcess_Triangulate |
49 aiProcess_SortByPType |
50 aiProcess_FlipUVs;
52 const aiScene *aiscn = aiImportFile(datafile_path(fname).c_str(), proc_flags);
53 if(!aiscn) {
54 error_log("failed to load file: %s\n", fname);
55 return false;
56 }
58 info_log("NODE HIERARCHY:\n");
59 print_hierarchy(aiscn->mRootNode);
60 info_log("-------------------\n");
62 Vector3 root_pos, root_scaling(1.0, 1.0, 1.0);
63 Quaternion root_rot;
65 if(aiscn->mRootNode) {
66 Matrix4x4 root_matrix = assimp_matrix(aiscn->mRootNode->mTransformation);
67 root_pos = root_matrix.get_translation();
68 root_rot = root_matrix.get_rotation_quat();
69 root_scaling = root_matrix.get_scaling();
70 }
72 // load all meshes
73 for(unsigned int i=0; i<aiscn->mNumMeshes; i++) {
74 aiMesh *aimesh = aiscn->mMeshes[i];
75 Mesh *mesh;
76 Curve *curve;
78 switch(aimesh->mPrimitiveTypes) {
79 case aiPrimitiveType_TRIANGLE:
80 if((mesh = load_mesh(aiscn, aimesh))) {
81 mesh_by_aimesh[aimesh] = mesh;
82 scn->meshes.push_back(mesh);
83 }
84 break;
86 case aiPrimitiveType_LINE:
87 if((curve = load_curve(aiscn, aimesh))) {
88 scn->curves.push_back(curve);
89 }
90 break;
92 default:
93 error_log("unsupported primitive type: %u\n", aimesh->mPrimitiveTypes);
94 break;
95 }
96 }
98 // load all the nodes recursively
99 for(unsigned int i=0; i<aiscn->mRootNode->mNumChildren; i++) {
100 Object *obj = load_node(aiscn, aiscn->mRootNode->mChildren[i]);
101 if(obj) {
102 Object *dummy = new Object;
103 dummy->set_name((string("dummyroot_") + string(obj->get_name())).c_str());
104 dummy->set_position(root_pos);
105 dummy->set_rotation(root_rot);
106 dummy->set_scaling(root_scaling);
107 dummy->add_child(obj);
109 obj = dummy;
110 scn->objects.push_back(obj);
111 }
112 }
114 // load and attach the bones to the meshes
115 for(unsigned int i=0; i<aiscn->mNumMeshes; i++) {
116 aiMesh *aimesh = aiscn->mMeshes[i];
118 Mesh *mesh = mesh_by_aimesh[aimesh];
119 load_bones(mesh, aimesh);
120 }
122 obj_by_name.clear();
123 mesh_by_aimesh.clear();
125 aiReleaseImport(aiscn);
126 return true;
127 }
129 static bool load_material(Material *mat, const aiMaterial *aimat)
130 {
131 aiColor4D aicol;
132 float shin, shin_str;
134 if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_DIFFUSE, &aicol) == 0) {
135 mat->diffuse = Vector3(aicol[0], aicol[1], aicol[2]);
136 }
137 if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_SPECULAR, &aicol) == 0) {
138 mat->specular = Vector3(aicol[0], aicol[1], aicol[2]);
139 }
141 unsigned int count = 1;
142 if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS_STRENGTH, &shin_str, &count) != 0) {
143 shin_str = 1.0;
144 }
145 if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS, &shin, &count) == 0) {
146 // XXX can't remember how I came up with this...
147 mat->shininess = shin * shin_str * 0.0001 * 128.0;
148 }
150 // load textures
151 struct { int type; aiTextureType aitype; } textypes[] = {
152 {TEX_DIFFUSE, aiTextureType_DIFFUSE},
153 {TEX_NORMAL, aiTextureType_NORMALS},
154 {TEX_SPECULAR, aiTextureType_SPECULAR}
155 };
157 for(int i=0; i<sizeof textypes / sizeof *textypes; i++) {
158 aiString aipath;
160 if(aiGetMaterialTexture(aimat, textypes[i].aitype, 0, &aipath) == 0) {
161 char *tmp, *fname = aipath.data;
163 if((tmp = strrchr(fname, '/'))) {
164 fname = tmp + 1;
165 }
166 if((tmp = strrchr(fname, '\\'))) {
167 fname = tmp + 1;
168 }
170 if(*fname) {
171 mat->tex[textypes[i].type] = texset.get(fname);
172 }
173 }
174 }
176 return true;
177 }
179 static Object *load_node(const aiScene *aiscn, const aiNode *ainode)
180 {
181 Object *obj = new Object;
182 obj->set_name(ainode->mName.data);
184 if(ainode->mNumMeshes) {
185 if(ainode->mNumMeshes > 1) {
186 info_log("%s warning: node %s has more than one meshes (%u)\n", __FUNCTION__,
187 ainode->mName.data, ainode->mNumMeshes);
188 }
190 aiMesh *aimesh = aiscn->mMeshes[ainode->mMeshes[0]];
191 obj->set_mesh(mesh_by_aimesh[aimesh]);
193 // also grab the material of this mesh
194 load_material(&obj->material, aiscn->mMaterials[aimesh->mMaterialIndex]);
195 }
197 // if there are animations, grab the first and try to use it
198 if(aiscn->mNumAnimations) {
199 aiAnimation *aianim = aiscn->mAnimations[0];
200 aiNodeAnim *ainodeanim = 0;
201 for(unsigned int i=0; i<aianim->mNumChannels; i++) {
202 if(strcmp(aianim->mChannels[i]->mNodeName.data, ainode->mName.data) == 0) {
203 ainodeanim = aianim->mChannels[i];
204 break;
205 }
206 }
208 if(ainodeanim) {
209 // load all position (translation) keyframes
210 for(unsigned int i=0; i<ainodeanim->mNumPositionKeys; i++) {
211 Vector3 pos = assimp_vector(ainodeanim->mPositionKeys[i].mValue);
212 long msec = assimp_time(aianim, ainodeanim->mPositionKeys[i].mTime);
213 obj->set_position(pos, msec);
214 }
216 // load all rotation keyframes
217 for(unsigned int i=0; i<ainodeanim->mNumRotationKeys; i++) {
218 Quaternion rot = assimp_quat(ainodeanim->mRotationKeys[i].mValue);
219 if(rot.length_sq() < SMALL_NUMBER) {
220 continue;
221 }
222 rot.normalize();
223 long msec = assimp_time(aianim, ainodeanim->mRotationKeys[i].mTime);
224 obj->set_rotation(rot, msec);
225 }
227 // load all scaling keyframes
228 for(unsigned int i=0; i<ainodeanim->mNumScalingKeys; i++) {
229 Vector3 scale = assimp_vector(ainodeanim->mScalingKeys[i].mValue);
230 long msec = assimp_time(aianim, ainodeanim->mScalingKeys[i].mTime);
231 obj->set_scaling(scale, msec);
232 }
234 obj->set_extrapolator(EXTRAP_REPEAT); // loop animation
235 } else {
236 Matrix4x4 local_matrix = assimp_matrix(ainode->mTransformation);
237 obj->set_local_matrix(local_matrix);
238 }
239 }
241 /* recurse to all children */
242 for(unsigned int i=0; i<ainode->mNumChildren; i++) {
243 Object *child = load_node(aiscn, ainode->mChildren[i]);
244 if(child) {
245 obj->add_child(child);
246 }
247 }
249 obj_by_name[obj->get_name()] = obj;
250 return obj;
251 }
253 static Mesh *load_mesh(const aiScene *aiscn, const aiMesh *aimesh)
254 {
255 Mesh *mesh = new Mesh;
257 int num_verts = aimesh->mNumVertices;
258 int num_faces = aimesh->mNumFaces;
260 mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, (float*)aimesh->mVertices);
262 if(aimesh->mNormals) {
263 mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, (float*)aimesh->mNormals);
264 }
265 if(aimesh->mTangents) {
266 mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, (float*)aimesh->mTangents);
267 }
268 if(aimesh->mTextureCoords[0]) {
269 mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 3, num_verts, (float*)aimesh->mTextureCoords[0]);
270 }
272 if(aimesh->mBones) {
273 float *weights = mesh->set_attrib_data(MESH_ATTR_BONEWEIGHTS, 4, num_verts, 0);
274 float *boneidx = mesh->set_attrib_data(MESH_ATTR_BONEIDX, 4, num_verts, 0);
276 memset(weights, 0, num_verts * 4 * sizeof *weights);
277 memset(boneidx, 0, num_verts * 4 * sizeof *boneidx);
279 int *vertex_bone_count = new int[num_verts];
280 memset(vertex_bone_count, 0, num_verts * sizeof *vertex_bone_count);
282 for(unsigned int i=0; i<aimesh->mNumBones; i++) {
283 aiBone *aibone = aimesh->mBones[i];
285 // for every vertex affected by this bone:
286 for(unsigned int j=0; j<aibone->mNumWeights; j++) {
287 aiVertexWeight *aiweight = aibone->mWeights + j;
288 int vidx = aiweight->mVertexId;
289 int vert_boneidx = vertex_bone_count[vidx];
290 if(vert_boneidx >= 4) {
291 error_log("WARNING vertex with more than 4 bones found\n");
292 continue;
293 }
295 weights[vidx * 4 + vert_boneidx] = aiweight->mWeight;
296 boneidx[vidx * 4 + vert_boneidx] = (float)i;
297 vertex_bone_count[vidx]++;
298 }
299 }
301 delete [] vertex_bone_count;
303 // normalize weights
304 for(int i=0; i<num_verts; i++) {
306 float wsum = 0.0f;
308 for(int j=0; j<4; j++) {
309 wsum += weights[i * 4 + j];
310 }
312 if(1.0 - wsum > 1e-4) {
313 error_log("WARNING vertex with weights < 1 (%f), normalizing...\n", wsum);
315 if(wsum < 1e-6) {
316 // this is clearly broken, let's use the first bone in full
317 weights[i * 4] = 1.0;
318 } else {
319 weights[i * 4] /= wsum;
320 weights[i * 4 + 1] /= wsum;
321 weights[i * 4 + 2] /= wsum;
322 weights[i * 4 + 3] /= wsum;
323 }
324 }
325 }
326 }
328 unsigned int *iptr = mesh->set_index_data(num_faces * 3);
329 for(int i=0; i<num_faces; i++) {
330 for(int j=0; j<3; j++) {
331 *iptr++ = aimesh->mFaces[i].mIndices[j];
332 }
333 }
335 return mesh;
336 }
338 static Curve *load_curve(const aiScene *aiscn, const aiMesh *aimesh)
339 {
340 Curve *curve = new Curve;
342 for(unsigned int i=0; i<aimesh->mNumVertices; i++) {
343 Vector3 pt = assimp_vector(aimesh->mVertices[i]);
344 curve->add_point(pt);
345 }
346 info_log("loaded curve with %d points\n", aimesh->mNumVertices);
348 return curve;
349 }
351 static bool load_bones(Mesh *mesh, const aiMesh *aimesh)
352 {
353 if(!aimesh->mNumBones) {
354 return false;
355 }
357 for(unsigned int i=0; i<aimesh->mNumBones; i++) {
358 aiBone *aibone = aimesh->mBones[i];
359 Object *obj = obj_by_name[aibone->mName.data];
360 if(!obj) {
361 error_log("bone %s not found\n", aibone->mName.data);
362 continue;
363 }
365 obj->set_bone_matrix(assimp_matrix(aibone->mOffsetMatrix));
366 mesh->add_bone(obj);
368 info_log("adding bone: %s\n", obj->get_name());
369 }
371 return true;
372 }
374 static Vector3 assimp_vector(const aiVector3D &v)
375 {
376 return Vector3(v[0], v[1], v[2]);
377 }
379 static Quaternion assimp_quat(const aiQuaternion &q)
380 {
381 return Quaternion(q.w, Vector3(q.x, q.y, q.z));
382 }
384 static Matrix4x4 assimp_matrix(const aiMatrix4x4 &aim)
385 {
386 Matrix4x4 m;
387 memcpy(m[0], &aim, 16 * sizeof(float));
388 return m;
389 }
391 /* convert an assimp keyframe time (ticks) into milliseconds */
392 static long assimp_time(const aiAnimation *anim, double aitime)
393 {
394 double sec;
395 if(anim->mTicksPerSecond < 1e-6) {
396 // assume time is in frames?
397 sec = aitime / 30.0;
398 } else {
399 sec = aitime / anim->mTicksPerSecond;
400 }
401 return (long)(sec * 1000.0);
402 }
404 static void print_hierarchy(const aiNode *node)
405 {
406 static int lvl;
407 static int lvlopen[256];
409 for(int i=0; i<lvl; i++) {
410 putchar(' ');
411 if(lvlopen[i]) {
412 putchar(i >= lvl - 1 ? '+' : '|');
413 } else {
414 putchar(i >= lvl - 1 ? '+' : ' ');
415 }
416 }
417 info_log("- \"%s\"\n", node->mName.data);
419 lvlopen[lvl] = 1;
421 lvl++;
422 for(unsigned int i=0; i<node->mNumChildren; i++) {
423 if(i == node->mNumChildren - 1) {
424 lvlopen[lvl - 1] = 0;
425 }
426 print_hierarchy(node->mChildren[i]);
427 }
428 lvl--;
429 }
431 #else // !defined USE_ASSIMP
433 using namespace goatgfx;
435 bool load_ass(Scene *scn, const char *fname)
436 {
437 error_log("load_ass: assimp support not compiled in\n");
438 return false;
439 }
441 #endif