conworlds
view src/assload.cc @ 17:c814f77d177e
moved to SDL2
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Mon, 25 Aug 2014 22:02:08 +0300 |
parents | |
children |
line source
1 #include <stdio.h>
2 #include "assload.h"
3 #include "logger.h"
5 #ifdef USE_ASSIMP
7 #include <vector>
8 #include <map>
9 #include "assimp/cimport.h"
10 #include "assimp/scene.h"
11 #include "assimp/postprocess.h"
12 #include "texman.h"
13 #include "material.h"
14 #include "scene.h"
16 using namespace std;
18 static bool load_material(Material *mat, const aiMaterial *aimat);
19 static Object *load_node(const aiScene *aiscn, const aiNode *ainode);
20 static Mesh *load_mesh(const aiScene *aiscn, const aiMesh *aimesh);
21 static Curve *load_curve(const aiScene *aiscn, const aiMesh *aimesh);
22 static bool load_bones(Mesh *mesh, const aiMesh *aimesh);
24 static Vector3 assimp_vector(const aiVector3D &v);
25 static Quaternion assimp_quat(const aiQuaternion &q);
26 static Matrix4x4 assimp_matrix(const aiMatrix4x4 &aim);
27 static long assimp_time(const aiAnimation *anim, double aitime);
28 static void print_hierarchy(const aiNode *node);
30 static map<string, Object*> obj_by_name;
31 static map<aiMesh*, Mesh*> mesh_by_aimesh;
33 bool load_ass(Scene *scn, const char *fname)
34 {
35 static bool init_done;
37 if(!init_done) {
38 static aiLogStream log_stream = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT, 0);
39 aiAttachLogStream(&log_stream);
40 //aiEnableVerboseLogging(1);
41 init_done = true;
42 }
44 unsigned int proc_flags = aiProcess_JoinIdenticalVertices |
45 aiProcess_CalcTangentSpace |
46 aiProcess_Triangulate |
47 aiProcess_SortByPType |
48 aiProcess_FlipUVs;
50 const aiScene *aiscn = aiImportFile(datafile_path(fname).c_str(), proc_flags);
51 if(!aiscn) {
52 error_log("failed to load file: %s\n", fname);
53 return false;
54 }
56 info_log("NODE HIERARCHY:\n");
57 print_hierarchy(aiscn->mRootNode);
58 info_log("-------------------\n");
60 Vector3 root_pos, root_scaling(1.0, 1.0, 1.0);
61 Quaternion root_rot;
63 if(aiscn->mRootNode) {
64 Matrix4x4 root_matrix = assimp_matrix(aiscn->mRootNode->mTransformation);
65 root_pos = root_matrix.get_translation();
66 root_rot = root_matrix.get_rotation_quat();
67 root_scaling = root_matrix.get_scaling();
68 }
70 // load all meshes
71 for(unsigned int i=0; i<aiscn->mNumMeshes; i++) {
72 aiMesh *aimesh = aiscn->mMeshes[i];
73 Mesh *mesh;
74 Curve *curve;
76 switch(aimesh->mPrimitiveTypes) {
77 case aiPrimitiveType_TRIANGLE:
78 if((mesh = load_mesh(aiscn, aimesh))) {
79 mesh_by_aimesh[aimesh] = mesh;
80 scn->meshes.push_back(mesh);
81 }
82 break;
84 case aiPrimitiveType_LINE:
85 if((curve = load_curve(aiscn, aimesh))) {
86 scn->curves.push_back(curve);
87 }
88 break;
90 default:
91 error_log("unsupported primitive type: %u\n", aimesh->mPrimitiveTypes);
92 break;
93 }
94 }
96 // load all the nodes recursively
97 for(unsigned int i=0; i<aiscn->mRootNode->mNumChildren; i++) {
98 Object *obj = load_node(aiscn, aiscn->mRootNode->mChildren[i]);
99 if(obj) {
100 Object *dummy = new Object;
101 dummy->set_name((string("dummyroot_") + string(obj->get_name())).c_str());
102 dummy->set_position(root_pos);
103 dummy->set_rotation(root_rot);
104 dummy->set_scaling(root_scaling);
105 dummy->add_child(obj);
107 obj = dummy;
108 scn->objects.push_back(obj);
109 }
110 }
112 // load and attach the bones to the meshes
113 for(unsigned int i=0; i<aiscn->mNumMeshes; i++) {
114 aiMesh *aimesh = aiscn->mMeshes[i];
116 Mesh *mesh = mesh_by_aimesh[aimesh];
117 load_bones(mesh, aimesh);
118 }
120 obj_by_name.clear();
121 mesh_by_aimesh.clear();
123 aiReleaseImport(aiscn);
124 return true;
125 }
127 static bool load_material(Material *mat, const aiMaterial *aimat)
128 {
129 aiColor4D aicol;
130 float shin, shin_str;
132 if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_DIFFUSE, &aicol) == 0) {
133 mat->diffuse = Vector3(aicol[0], aicol[1], aicol[2]);
134 }
135 if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_SPECULAR, &aicol) == 0) {
136 mat->specular = Vector3(aicol[0], aicol[1], aicol[2]);
137 }
139 unsigned int count = 1;
140 if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS_STRENGTH, &shin_str, &count) != 0) {
141 shin_str = 1.0;
142 }
143 if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS, &shin, &count) == 0) {
144 // XXX can't remember how I came up with this...
145 mat->shininess = shin * shin_str * 0.0001 * 128.0;
146 }
148 // load textures
149 struct { int type; aiTextureType aitype; } textypes[] = {
150 {TEX_DIFFUSE, aiTextureType_DIFFUSE},
151 {TEX_NORMAL, aiTextureType_NORMALS},
152 {TEX_SPECULAR, aiTextureType_SPECULAR}
153 };
155 for(int i=0; i<sizeof textypes / sizeof *textypes; i++) {
156 aiString aipath;
158 if(aiGetMaterialTexture(aimat, textypes[i].aitype, 0, &aipath) == 0) {
159 char *tmp, *fname = aipath.data;
161 if((tmp = strrchr(fname, '/'))) {
162 fname = tmp + 1;
163 }
164 if((tmp = strrchr(fname, '\\'))) {
165 fname = tmp + 1;
166 }
168 if(*fname) {
169 mat->tex[textypes[i].type] = texset.get(fname);
170 }
171 }
172 }
174 return true;
175 }
177 static Object *load_node(const aiScene *aiscn, const aiNode *ainode)
178 {
179 Object *obj = new Object;
180 obj->set_name(ainode->mName.data);
182 if(ainode->mNumMeshes) {
183 if(ainode->mNumMeshes > 1) {
184 info_log("%s warning: node %s has more than one meshes (%u)\n", __FUNCTION__,
185 ainode->mName.data, ainode->mNumMeshes);
186 }
188 aiMesh *aimesh = aiscn->mMeshes[ainode->mMeshes[0]];
189 obj->set_mesh(mesh_by_aimesh[aimesh]);
191 // also grab the material of this mesh
192 load_material(&obj->material, aiscn->mMaterials[aimesh->mMaterialIndex]);
193 }
195 // if there are animations, grab the first and try to use it
196 if(aiscn->mNumAnimations) {
197 aiAnimation *aianim = aiscn->mAnimations[0];
198 aiNodeAnim *ainodeanim = 0;
199 for(unsigned int i=0; i<aianim->mNumChannels; i++) {
200 if(strcmp(aianim->mChannels[i]->mNodeName.data, ainode->mName.data) == 0) {
201 ainodeanim = aianim->mChannels[i];
202 break;
203 }
204 }
206 if(ainodeanim) {
207 // load all position (translation) keyframes
208 for(unsigned int i=0; i<ainodeanim->mNumPositionKeys; i++) {
209 Vector3 pos = assimp_vector(ainodeanim->mPositionKeys[i].mValue);
210 long msec = assimp_time(aianim, ainodeanim->mPositionKeys[i].mTime);
211 obj->set_position(pos, msec);
212 }
214 // load all rotation keyframes
215 for(unsigned int i=0; i<ainodeanim->mNumRotationKeys; i++) {
216 Quaternion rot = assimp_quat(ainodeanim->mRotationKeys[i].mValue);
217 if(rot.length_sq() < SMALL_NUMBER) {
218 continue;
219 }
220 rot.normalize();
221 long msec = assimp_time(aianim, ainodeanim->mRotationKeys[i].mTime);
222 obj->set_rotation(rot, msec);
223 }
225 // load all scaling keyframes
226 for(unsigned int i=0; i<ainodeanim->mNumScalingKeys; i++) {
227 Vector3 scale = assimp_vector(ainodeanim->mScalingKeys[i].mValue);
228 long msec = assimp_time(aianim, ainodeanim->mScalingKeys[i].mTime);
229 obj->set_scaling(scale, msec);
230 }
232 obj->set_extrapolator(EXTRAP_REPEAT); // loop animation
233 } else {
234 Matrix4x4 local_matrix = assimp_matrix(ainode->mTransformation);
235 obj->set_local_matrix(local_matrix);
236 }
237 }
239 /* recurse to all children */
240 for(unsigned int i=0; i<ainode->mNumChildren; i++) {
241 Object *child = load_node(aiscn, ainode->mChildren[i]);
242 if(child) {
243 obj->add_child(child);
244 }
245 }
247 obj_by_name[obj->get_name()] = obj;
248 return obj;
249 }
251 static Mesh *load_mesh(const aiScene *aiscn, const aiMesh *aimesh)
252 {
253 Mesh *mesh = new Mesh;
255 int num_verts = aimesh->mNumVertices;
256 int num_faces = aimesh->mNumFaces;
258 mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, (float*)aimesh->mVertices);
260 if(aimesh->mNormals) {
261 mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, (float*)aimesh->mNormals);
262 }
263 if(aimesh->mTangents) {
264 mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, (float*)aimesh->mTangents);
265 }
266 if(aimesh->mTextureCoords[0]) {
267 mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 3, num_verts, (float*)aimesh->mTextureCoords[0]);
268 }
270 if(aimesh->mBones) {
271 float *weights = mesh->set_attrib_data(MESH_ATTR_BONEWEIGHTS, 4, num_verts, 0);
272 float *boneidx = mesh->set_attrib_data(MESH_ATTR_BONEIDX, 4, num_verts, 0);
274 memset(weights, 0, num_verts * 4 * sizeof *weights);
275 memset(boneidx, 0, num_verts * 4 * sizeof *boneidx);
277 int *vertex_bone_count = new int[num_verts];
278 memset(vertex_bone_count, 0, num_verts * sizeof *vertex_bone_count);
280 for(unsigned int i=0; i<aimesh->mNumBones; i++) {
281 aiBone *aibone = aimesh->mBones[i];
283 // for every vertex affected by this bone:
284 for(unsigned int j=0; j<aibone->mNumWeights; j++) {
285 aiVertexWeight *aiweight = aibone->mWeights + j;
286 int vidx = aiweight->mVertexId;
287 int vert_boneidx = vertex_bone_count[vidx];
288 if(vert_boneidx >= 4) {
289 error_log("WARNING vertex with more than 4 bones found\n");
290 continue;
291 }
293 weights[vidx * 4 + vert_boneidx] = aiweight->mWeight;
294 boneidx[vidx * 4 + vert_boneidx] = (float)i;
295 vertex_bone_count[vidx]++;
296 }
297 }
299 delete [] vertex_bone_count;
301 // normalize weights
302 for(int i=0; i<num_verts; i++) {
304 float wsum = 0.0f;
306 for(int j=0; j<4; j++) {
307 wsum += weights[i * 4 + j];
308 }
310 if(1.0 - wsum > 1e-4) {
311 error_log("WARNING vertex with weights < 1 (%f), normalizing...\n", wsum);
313 if(wsum < 1e-6) {
314 // this is clearly broken, let's use the first bone in full
315 weights[i * 4] = 1.0;
316 } else {
317 weights[i * 4] /= wsum;
318 weights[i * 4 + 1] /= wsum;
319 weights[i * 4 + 2] /= wsum;
320 weights[i * 4 + 3] /= wsum;
321 }
322 }
323 }
324 }
326 unsigned int *iptr = mesh->set_index_data(num_faces * 3);
327 for(int i=0; i<num_faces; i++) {
328 for(int j=0; j<3; j++) {
329 *iptr++ = aimesh->mFaces[i].mIndices[j];
330 }
331 }
333 return mesh;
334 }
336 static Curve *load_curve(const aiScene *aiscn, const aiMesh *aimesh)
337 {
338 Curve *curve = new Curve;
340 for(unsigned int i=0; i<aimesh->mNumVertices; i++) {
341 Vector3 pt = assimp_vector(aimesh->mVertices[i]);
342 curve->add_point(pt);
343 }
344 info_log("loaded curve with %d points\n", aimesh->mNumVertices);
346 return curve;
347 }
349 static bool load_bones(Mesh *mesh, const aiMesh *aimesh)
350 {
351 if(!aimesh->mNumBones) {
352 return false;
353 }
355 for(unsigned int i=0; i<aimesh->mNumBones; i++) {
356 aiBone *aibone = aimesh->mBones[i];
357 Object *obj = obj_by_name[aibone->mName.data];
358 if(!obj) {
359 error_log("bone %s not found\n", aibone->mName.data);
360 continue;
361 }
363 obj->set_bone_matrix(assimp_matrix(aibone->mOffsetMatrix));
364 mesh->add_bone(obj);
366 info_log("adding bone: %s\n", obj->get_name());
367 }
369 return true;
370 }
372 static Vector3 assimp_vector(const aiVector3D &v)
373 {
374 return Vector3(v[0], v[1], v[2]);
375 }
377 static Quaternion assimp_quat(const aiQuaternion &q)
378 {
379 return Quaternion(q.w, Vector3(q.x, q.y, q.z));
380 }
382 static Matrix4x4 assimp_matrix(const aiMatrix4x4 &aim)
383 {
384 Matrix4x4 m;
385 memcpy(m[0], &aim, 16 * sizeof(float));
386 return m;
387 }
389 /* convert an assimp keyframe time (ticks) into milliseconds */
390 static long assimp_time(const aiAnimation *anim, double aitime)
391 {
392 double sec;
393 if(anim->mTicksPerSecond < 1e-6) {
394 // assume time is in frames?
395 sec = aitime / 30.0;
396 } else {
397 sec = aitime / anim->mTicksPerSecond;
398 }
399 return (long)(sec * 1000.0);
400 }
402 static void print_hierarchy(const aiNode *node)
403 {
404 static int lvl;
405 static int lvlopen[256];
407 for(int i=0; i<lvl; i++) {
408 putchar(' ');
409 if(lvlopen[i]) {
410 putchar(i >= lvl - 1 ? '+' : '|');
411 } else {
412 putchar(i >= lvl - 1 ? '+' : ' ');
413 }
414 }
415 info_log("- \"%s\"\n", node->mName.data);
417 lvlopen[lvl] = 1;
419 lvl++;
420 for(unsigned int i=0; i<node->mNumChildren; i++) {
421 if(i == node->mNumChildren - 1) {
422 lvlopen[lvl - 1] = 0;
423 }
424 print_hierarchy(node->mChildren[i]);
425 }
426 lvl--;
427 }
429 #else // !defined USE_ASSIMP
431 bool load_ass(Scene *scn, const char *fname)
432 {
433 error_log("load_ass: assimp support not compiled in\n");
434 return false;
435 }
437 #endif