goat3dgfx
diff src/assload.cc @ 0:1873dfd13f2d
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Thu, 14 Nov 2013 05:27:09 +0200 |
parents | |
children | 7d6b667821cf |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/assload.cc Thu Nov 14 05:27:09 2013 +0200 1.3 @@ -0,0 +1,437 @@ 1.4 +#include <stdio.h> 1.5 +#include "assload.h" 1.6 +#include "logger.h" 1.7 +#include "datapath.h" 1.8 + 1.9 +#ifdef USE_ASSIMP 1.10 + 1.11 +#include <vector> 1.12 +#include <map> 1.13 +#include "assimp/cimport.h" 1.14 +#include "assimp/scene.h" 1.15 +#include "assimp/postprocess.h" 1.16 +#include "texman.h" 1.17 +#include "material.h" 1.18 + 1.19 +using namespace std; 1.20 + 1.21 +static bool load_material(Material *mat, const aiMaterial *aimat); 1.22 +static Object *load_node(const aiScene *aiscn, const aiNode *ainode); 1.23 +static Mesh *load_mesh(const aiScene *aiscn, const aiMesh *aimesh); 1.24 +static Curve *load_curve(const aiScene *aiscn, const aiMesh *aimesh); 1.25 +static bool load_bones(Mesh *mesh, const aiMesh *aimesh); 1.26 + 1.27 +static Vector3 assimp_vector(const aiVector3D &v); 1.28 +static Quaternion assimp_quat(const aiQuaternion &q); 1.29 +static Matrix4x4 assimp_matrix(const aiMatrix4x4 &aim); 1.30 +static long assimp_time(const aiAnimation *anim, double aitime); 1.31 +static void print_hierarchy(const aiNode *node); 1.32 + 1.33 +static map<string, Object*> obj_by_name; 1.34 +static map<aiMesh*, Mesh*> mesh_by_aimesh; 1.35 + 1.36 +bool load_ass(Scene *scn, const char *fname) 1.37 +{ 1.38 + static bool init_done; 1.39 + 1.40 + if(!init_done) { 1.41 + static aiLogStream log_stream = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT, 0); 1.42 + aiAttachLogStream(&log_stream); 1.43 + //aiEnableVerboseLogging(1); 1.44 + init_done = true; 1.45 + } 1.46 + 1.47 + unsigned int proc_flags = aiProcess_JoinIdenticalVertices | 1.48 + aiProcess_CalcTangentSpace | 1.49 + aiProcess_Triangulate | 1.50 + aiProcess_SortByPType | 1.51 + aiProcess_FlipUVs; 1.52 + 1.53 + const aiScene *aiscn = aiImportFile(datafile_path(fname).c_str(), proc_flags); 1.54 + if(!aiscn) { 1.55 + error_log("failed to load file: %s\n", fname); 1.56 + return false; 1.57 + } 1.58 + 1.59 + info_log("NODE HIERARCHY:\n"); 1.60 + print_hierarchy(aiscn->mRootNode); 1.61 + info_log("-------------------\n"); 1.62 + 1.63 + Vector3 root_pos, root_scaling(1.0, 1.0, 1.0); 1.64 + Quaternion root_rot; 1.65 + 1.66 + if(aiscn->mRootNode) { 1.67 + Matrix4x4 root_matrix = assimp_matrix(aiscn->mRootNode->mTransformation); 1.68 + root_pos = root_matrix.get_translation(); 1.69 + root_rot = root_matrix.get_rotation_quat(); 1.70 + root_scaling = root_matrix.get_scaling(); 1.71 + } 1.72 + 1.73 + // load all meshes 1.74 + for(unsigned int i=0; i<aiscn->mNumMeshes; i++) { 1.75 + aiMesh *aimesh = aiscn->mMeshes[i]; 1.76 + Mesh *mesh; 1.77 + Curve *curve; 1.78 + 1.79 + switch(aimesh->mPrimitiveTypes) { 1.80 + case aiPrimitiveType_TRIANGLE: 1.81 + if((mesh = load_mesh(aiscn, aimesh))) { 1.82 + mesh_by_aimesh[aimesh] = mesh; 1.83 + scn->meshes.push_back(mesh); 1.84 + } 1.85 + break; 1.86 + 1.87 + case aiPrimitiveType_LINE: 1.88 + if((curve = load_curve(aiscn, aimesh))) { 1.89 + scn->curves.push_back(curve); 1.90 + } 1.91 + break; 1.92 + 1.93 + default: 1.94 + error_log("unsupported primitive type: %u\n", aimesh->mPrimitiveTypes); 1.95 + break; 1.96 + } 1.97 + } 1.98 + 1.99 + // load all the nodes recursively 1.100 + for(unsigned int i=0; i<aiscn->mRootNode->mNumChildren; i++) { 1.101 + Object *obj = load_node(aiscn, aiscn->mRootNode->mChildren[i]); 1.102 + if(obj) { 1.103 + Object *dummy = new Object; 1.104 + dummy->set_name((string("dummyroot_") + string(obj->get_name())).c_str()); 1.105 + dummy->set_position(root_pos); 1.106 + dummy->set_rotation(root_rot); 1.107 + dummy->set_scaling(root_scaling); 1.108 + dummy->add_child(obj); 1.109 + 1.110 + obj = dummy; 1.111 + scn->objects.push_back(obj); 1.112 + } 1.113 + } 1.114 + 1.115 + // load and attach the bones to the meshes 1.116 + for(unsigned int i=0; i<aiscn->mNumMeshes; i++) { 1.117 + aiMesh *aimesh = aiscn->mMeshes[i]; 1.118 + 1.119 + Mesh *mesh = mesh_by_aimesh[aimesh]; 1.120 + load_bones(mesh, aimesh); 1.121 + } 1.122 + 1.123 + obj_by_name.clear(); 1.124 + mesh_by_aimesh.clear(); 1.125 + 1.126 + aiReleaseImport(aiscn); 1.127 + return true; 1.128 +} 1.129 + 1.130 +static bool load_material(Material *mat, const aiMaterial *aimat) 1.131 +{ 1.132 + aiColor4D aicol; 1.133 + float shin, shin_str; 1.134 + 1.135 + if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_DIFFUSE, &aicol) == 0) { 1.136 + mat->diffuse = Vector3(aicol[0], aicol[1], aicol[2]); 1.137 + } 1.138 + if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_SPECULAR, &aicol) == 0) { 1.139 + mat->specular = Vector3(aicol[0], aicol[1], aicol[2]); 1.140 + } 1.141 + 1.142 + unsigned int count = 1; 1.143 + if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS_STRENGTH, &shin_str, &count) != 0) { 1.144 + shin_str = 1.0; 1.145 + } 1.146 + if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS, &shin, &count) == 0) { 1.147 + // XXX can't remember how I came up with this... 1.148 + mat->shininess = shin * shin_str * 0.0001 * 128.0; 1.149 + } 1.150 + 1.151 + // load textures 1.152 + struct { int type; aiTextureType aitype; } textypes[] = { 1.153 + {TEX_DIFFUSE, aiTextureType_DIFFUSE}, 1.154 + {TEX_NORMAL, aiTextureType_NORMALS}, 1.155 + {TEX_SPECULAR, aiTextureType_SPECULAR} 1.156 + }; 1.157 + 1.158 + for(int i=0; i<sizeof textypes / sizeof *textypes; i++) { 1.159 + aiString aipath; 1.160 + 1.161 + if(aiGetMaterialTexture(aimat, textypes[i].aitype, 0, &aipath) == 0) { 1.162 + char *tmp, *fname = aipath.data; 1.163 + 1.164 + if((tmp = strrchr(fname, '/'))) { 1.165 + fname = tmp + 1; 1.166 + } 1.167 + if((tmp = strrchr(fname, '\\'))) { 1.168 + fname = tmp + 1; 1.169 + } 1.170 + 1.171 + if(*fname) { 1.172 + mat->tex[textypes[i].type] = texset.get(fname); 1.173 + } 1.174 + } 1.175 + } 1.176 + 1.177 + return true; 1.178 +} 1.179 + 1.180 +static Object *load_node(const aiScene *aiscn, const aiNode *ainode) 1.181 +{ 1.182 + Object *obj = new Object; 1.183 + obj->set_name(ainode->mName.data); 1.184 + 1.185 + if(ainode->mNumMeshes) { 1.186 + if(ainode->mNumMeshes > 1) { 1.187 + info_log("%s warning: node %s has more than one meshes (%u)\n", __FUNCTION__, 1.188 + ainode->mName.data, ainode->mNumMeshes); 1.189 + } 1.190 + 1.191 + aiMesh *aimesh = aiscn->mMeshes[ainode->mMeshes[0]]; 1.192 + obj->set_mesh(mesh_by_aimesh[aimesh]); 1.193 + 1.194 + // also grab the material of this mesh 1.195 + load_material(&obj->material, aiscn->mMaterials[aimesh->mMaterialIndex]); 1.196 + } 1.197 + 1.198 + // if there are animations, grab the first and try to use it 1.199 + if(aiscn->mNumAnimations) { 1.200 + aiAnimation *aianim = aiscn->mAnimations[0]; 1.201 + aiNodeAnim *ainodeanim = 0; 1.202 + for(unsigned int i=0; i<aianim->mNumChannels; i++) { 1.203 + if(strcmp(aianim->mChannels[i]->mNodeName.data, ainode->mName.data) == 0) { 1.204 + ainodeanim = aianim->mChannels[i]; 1.205 + break; 1.206 + } 1.207 + } 1.208 + 1.209 + if(ainodeanim) { 1.210 + // load all position (translation) keyframes 1.211 + for(unsigned int i=0; i<ainodeanim->mNumPositionKeys; i++) { 1.212 + Vector3 pos = assimp_vector(ainodeanim->mPositionKeys[i].mValue); 1.213 + long msec = assimp_time(aianim, ainodeanim->mPositionKeys[i].mTime); 1.214 + obj->set_position(pos, msec); 1.215 + } 1.216 + 1.217 + // load all rotation keyframes 1.218 + for(unsigned int i=0; i<ainodeanim->mNumRotationKeys; i++) { 1.219 + Quaternion rot = assimp_quat(ainodeanim->mRotationKeys[i].mValue); 1.220 + if(rot.length_sq() < SMALL_NUMBER) { 1.221 + continue; 1.222 + } 1.223 + rot.normalize(); 1.224 + long msec = assimp_time(aianim, ainodeanim->mRotationKeys[i].mTime); 1.225 + obj->set_rotation(rot, msec); 1.226 + } 1.227 + 1.228 + // load all scaling keyframes 1.229 + for(unsigned int i=0; i<ainodeanim->mNumScalingKeys; i++) { 1.230 + Vector3 scale = assimp_vector(ainodeanim->mScalingKeys[i].mValue); 1.231 + long msec = assimp_time(aianim, ainodeanim->mScalingKeys[i].mTime); 1.232 + obj->set_scaling(scale, msec); 1.233 + } 1.234 + 1.235 + obj->set_extrapolator(EXTRAP_REPEAT); // loop animation 1.236 + } else { 1.237 + Matrix4x4 local_matrix = assimp_matrix(ainode->mTransformation); 1.238 + obj->set_local_matrix(local_matrix); 1.239 + } 1.240 + } 1.241 + 1.242 + /* recurse to all children */ 1.243 + for(unsigned int i=0; i<ainode->mNumChildren; i++) { 1.244 + Object *child = load_node(aiscn, ainode->mChildren[i]); 1.245 + if(child) { 1.246 + obj->add_child(child); 1.247 + } 1.248 + } 1.249 + 1.250 + obj_by_name[obj->get_name()] = obj; 1.251 + return obj; 1.252 +} 1.253 + 1.254 +static Mesh *load_mesh(const aiScene *aiscn, const aiMesh *aimesh) 1.255 +{ 1.256 + Mesh *mesh = new Mesh; 1.257 + 1.258 + int num_verts = aimesh->mNumVertices; 1.259 + int num_faces = aimesh->mNumFaces; 1.260 + 1.261 + mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, (float*)aimesh->mVertices); 1.262 + 1.263 + if(aimesh->mNormals) { 1.264 + mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, (float*)aimesh->mNormals); 1.265 + } 1.266 + if(aimesh->mTangents) { 1.267 + mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, (float*)aimesh->mTangents); 1.268 + } 1.269 + if(aimesh->mTextureCoords[0]) { 1.270 + mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 3, num_verts, (float*)aimesh->mTextureCoords[0]); 1.271 + } 1.272 + 1.273 + if(aimesh->mBones) { 1.274 + float *weights = mesh->set_attrib_data(MESH_ATTR_BONEWEIGHTS, 4, num_verts, 0); 1.275 + float *boneidx = mesh->set_attrib_data(MESH_ATTR_BONEIDX, 4, num_verts, 0); 1.276 + 1.277 + memset(weights, 0, num_verts * 4 * sizeof *weights); 1.278 + memset(boneidx, 0, num_verts * 4 * sizeof *boneidx); 1.279 + 1.280 + int *vertex_bone_count = new int[num_verts]; 1.281 + memset(vertex_bone_count, 0, num_verts * sizeof *vertex_bone_count); 1.282 + 1.283 + for(unsigned int i=0; i<aimesh->mNumBones; i++) { 1.284 + aiBone *aibone = aimesh->mBones[i]; 1.285 + 1.286 + // for every vertex affected by this bone: 1.287 + for(unsigned int j=0; j<aibone->mNumWeights; j++) { 1.288 + aiVertexWeight *aiweight = aibone->mWeights + j; 1.289 + int vidx = aiweight->mVertexId; 1.290 + int vert_boneidx = vertex_bone_count[vidx]; 1.291 + if(vert_boneidx >= 4) { 1.292 + error_log("WARNING vertex with more than 4 bones found\n"); 1.293 + continue; 1.294 + } 1.295 + 1.296 + weights[vidx * 4 + vert_boneidx] = aiweight->mWeight; 1.297 + boneidx[vidx * 4 + vert_boneidx] = (float)i; 1.298 + vertex_bone_count[vidx]++; 1.299 + } 1.300 + } 1.301 + 1.302 + delete [] vertex_bone_count; 1.303 + 1.304 + // normalize weights 1.305 + for(int i=0; i<num_verts; i++) { 1.306 + 1.307 + float wsum = 0.0f; 1.308 + 1.309 + for(int j=0; j<4; j++) { 1.310 + wsum += weights[i * 4 + j]; 1.311 + } 1.312 + 1.313 + if(1.0 - wsum > 1e-4) { 1.314 + error_log("WARNING vertex with weights < 1 (%f), normalizing...\n", wsum); 1.315 + 1.316 + if(wsum < 1e-6) { 1.317 + // this is clearly broken, let's use the first bone in full 1.318 + weights[i * 4] = 1.0; 1.319 + } else { 1.320 + weights[i * 4] /= wsum; 1.321 + weights[i * 4 + 1] /= wsum; 1.322 + weights[i * 4 + 2] /= wsum; 1.323 + weights[i * 4 + 3] /= wsum; 1.324 + } 1.325 + } 1.326 + } 1.327 + } 1.328 + 1.329 + unsigned int *iptr = mesh->set_index_data(num_faces * 3); 1.330 + for(int i=0; i<num_faces; i++) { 1.331 + for(int j=0; j<3; j++) { 1.332 + *iptr++ = aimesh->mFaces[i].mIndices[j]; 1.333 + } 1.334 + } 1.335 + 1.336 + return mesh; 1.337 +} 1.338 + 1.339 +static Curve *load_curve(const aiScene *aiscn, const aiMesh *aimesh) 1.340 +{ 1.341 + Curve *curve = new Curve; 1.342 + 1.343 + for(unsigned int i=0; i<aimesh->mNumVertices; i++) { 1.344 + Vector3 pt = assimp_vector(aimesh->mVertices[i]); 1.345 + curve->add_point(pt); 1.346 + } 1.347 + info_log("loaded curve with %d points\n", aimesh->mNumVertices); 1.348 + 1.349 + return curve; 1.350 +} 1.351 + 1.352 +static bool load_bones(Mesh *mesh, const aiMesh *aimesh) 1.353 +{ 1.354 + if(!aimesh->mNumBones) { 1.355 + return false; 1.356 + } 1.357 + 1.358 + for(unsigned int i=0; i<aimesh->mNumBones; i++) { 1.359 + aiBone *aibone = aimesh->mBones[i]; 1.360 + Object *obj = obj_by_name[aibone->mName.data]; 1.361 + if(!obj) { 1.362 + error_log("bone %s not found\n", aibone->mName.data); 1.363 + continue; 1.364 + } 1.365 + 1.366 + obj->set_bone_matrix(assimp_matrix(aibone->mOffsetMatrix)); 1.367 + mesh->add_bone(obj); 1.368 + 1.369 + info_log("adding bone: %s\n", obj->get_name()); 1.370 + } 1.371 + 1.372 + return true; 1.373 +} 1.374 + 1.375 +static Vector3 assimp_vector(const aiVector3D &v) 1.376 +{ 1.377 + return Vector3(v[0], v[1], v[2]); 1.378 +} 1.379 + 1.380 +static Quaternion assimp_quat(const aiQuaternion &q) 1.381 +{ 1.382 + return Quaternion(q.w, Vector3(q.x, q.y, q.z)); 1.383 +} 1.384 + 1.385 +static Matrix4x4 assimp_matrix(const aiMatrix4x4 &aim) 1.386 +{ 1.387 + Matrix4x4 m; 1.388 + memcpy(m[0], &aim, 16 * sizeof(float)); 1.389 + return m; 1.390 +} 1.391 + 1.392 +/* convert an assimp keyframe time (ticks) into milliseconds */ 1.393 +static long assimp_time(const aiAnimation *anim, double aitime) 1.394 +{ 1.395 + double sec; 1.396 + if(anim->mTicksPerSecond < 1e-6) { 1.397 + // assume time is in frames? 1.398 + sec = aitime / 30.0; 1.399 + } else { 1.400 + sec = aitime / anim->mTicksPerSecond; 1.401 + } 1.402 + return (long)(sec * 1000.0); 1.403 +} 1.404 + 1.405 +static void print_hierarchy(const aiNode *node) 1.406 +{ 1.407 + static int lvl; 1.408 + static int lvlopen[256]; 1.409 + 1.410 + for(int i=0; i<lvl; i++) { 1.411 + putchar(' '); 1.412 + if(lvlopen[i]) { 1.413 + putchar(i >= lvl - 1 ? '+' : '|'); 1.414 + } else { 1.415 + putchar(i >= lvl - 1 ? '+' : ' '); 1.416 + } 1.417 + } 1.418 + info_log("- \"%s\"\n", node->mName.data); 1.419 + 1.420 + lvlopen[lvl] = 1; 1.421 + 1.422 + lvl++; 1.423 + for(unsigned int i=0; i<node->mNumChildren; i++) { 1.424 + if(i == node->mNumChildren - 1) { 1.425 + lvlopen[lvl - 1] = 0; 1.426 + } 1.427 + print_hierarchy(node->mChildren[i]); 1.428 + } 1.429 + lvl--; 1.430 +} 1.431 + 1.432 +#else // !defined USE_ASSIMP 1.433 + 1.434 +bool load_ass(Scene *scn, const char *fname) 1.435 +{ 1.436 + error_log("load_ass: assimp support not compiled in\n"); 1.437 + return false; 1.438 +} 1.439 + 1.440 +#endif