goat3dgfx

annotate src/assload.cc @ 18:6f82b9b6d6c3

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