conworlds

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