nuclear@13: #include nuclear@13: #include "assload.h" nuclear@13: #include "logger.h" nuclear@13: nuclear@13: #ifdef USE_ASSIMP nuclear@13: nuclear@13: #include nuclear@13: #include nuclear@13: #include "assimp/cimport.h" nuclear@13: #include "assimp/scene.h" nuclear@13: #include "assimp/postprocess.h" nuclear@13: #include "texman.h" nuclear@13: #include "material.h" nuclear@13: #include "scene.h" nuclear@13: nuclear@13: using namespace std; nuclear@13: nuclear@13: static bool load_material(Material *mat, const aiMaterial *aimat); nuclear@13: static Object *load_node(const aiScene *aiscn, const aiNode *ainode); nuclear@13: static Mesh *load_mesh(const aiScene *aiscn, const aiMesh *aimesh); nuclear@13: static Curve *load_curve(const aiScene *aiscn, const aiMesh *aimesh); nuclear@13: static bool load_bones(Mesh *mesh, const aiMesh *aimesh); nuclear@13: nuclear@13: static Vector3 assimp_vector(const aiVector3D &v); nuclear@13: static Quaternion assimp_quat(const aiQuaternion &q); nuclear@13: static Matrix4x4 assimp_matrix(const aiMatrix4x4 &aim); nuclear@13: static long assimp_time(const aiAnimation *anim, double aitime); nuclear@13: static void print_hierarchy(const aiNode *node); nuclear@13: nuclear@13: static map obj_by_name; nuclear@13: static map mesh_by_aimesh; nuclear@13: nuclear@13: bool load_ass(Scene *scn, const char *fname) nuclear@13: { nuclear@13: static bool init_done; nuclear@13: nuclear@13: if(!init_done) { nuclear@13: static aiLogStream log_stream = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT, 0); nuclear@13: aiAttachLogStream(&log_stream); nuclear@13: //aiEnableVerboseLogging(1); nuclear@13: init_done = true; nuclear@13: } nuclear@13: nuclear@13: unsigned int proc_flags = aiProcess_JoinIdenticalVertices | nuclear@13: aiProcess_CalcTangentSpace | nuclear@13: aiProcess_Triangulate | nuclear@13: aiProcess_SortByPType | nuclear@13: aiProcess_FlipUVs; nuclear@13: nuclear@13: const aiScene *aiscn = aiImportFile(datafile_path(fname).c_str(), proc_flags); nuclear@13: if(!aiscn) { nuclear@13: error_log("failed to load file: %s\n", fname); nuclear@13: return false; nuclear@13: } nuclear@13: nuclear@13: info_log("NODE HIERARCHY:\n"); nuclear@13: print_hierarchy(aiscn->mRootNode); nuclear@13: info_log("-------------------\n"); nuclear@13: nuclear@13: Vector3 root_pos, root_scaling(1.0, 1.0, 1.0); nuclear@13: Quaternion root_rot; nuclear@13: nuclear@13: if(aiscn->mRootNode) { nuclear@13: Matrix4x4 root_matrix = assimp_matrix(aiscn->mRootNode->mTransformation); nuclear@13: root_pos = root_matrix.get_translation(); nuclear@13: root_rot = root_matrix.get_rotation_quat(); nuclear@13: root_scaling = root_matrix.get_scaling(); nuclear@13: } nuclear@13: nuclear@13: // load all meshes nuclear@13: for(unsigned int i=0; imNumMeshes; i++) { nuclear@13: aiMesh *aimesh = aiscn->mMeshes[i]; nuclear@13: Mesh *mesh; nuclear@13: Curve *curve; nuclear@13: nuclear@13: switch(aimesh->mPrimitiveTypes) { nuclear@13: case aiPrimitiveType_TRIANGLE: nuclear@13: if((mesh = load_mesh(aiscn, aimesh))) { nuclear@13: mesh_by_aimesh[aimesh] = mesh; nuclear@13: scn->meshes.push_back(mesh); nuclear@13: } nuclear@13: break; nuclear@13: nuclear@13: case aiPrimitiveType_LINE: nuclear@13: if((curve = load_curve(aiscn, aimesh))) { nuclear@13: scn->curves.push_back(curve); nuclear@13: } nuclear@13: break; nuclear@13: nuclear@13: default: nuclear@13: error_log("unsupported primitive type: %u\n", aimesh->mPrimitiveTypes); nuclear@13: break; nuclear@13: } nuclear@13: } nuclear@13: nuclear@13: // load all the nodes recursively nuclear@13: for(unsigned int i=0; imRootNode->mNumChildren; i++) { nuclear@13: Object *obj = load_node(aiscn, aiscn->mRootNode->mChildren[i]); nuclear@13: if(obj) { nuclear@13: Object *dummy = new Object; nuclear@13: dummy->set_name((string("dummyroot_") + string(obj->get_name())).c_str()); nuclear@13: dummy->set_position(root_pos); nuclear@13: dummy->set_rotation(root_rot); nuclear@13: dummy->set_scaling(root_scaling); nuclear@13: dummy->add_child(obj); nuclear@13: nuclear@13: obj = dummy; nuclear@13: scn->objects.push_back(obj); nuclear@13: } nuclear@13: } nuclear@13: nuclear@13: // load and attach the bones to the meshes nuclear@13: for(unsigned int i=0; imNumMeshes; i++) { nuclear@13: aiMesh *aimesh = aiscn->mMeshes[i]; nuclear@13: nuclear@13: Mesh *mesh = mesh_by_aimesh[aimesh]; nuclear@13: load_bones(mesh, aimesh); nuclear@13: } nuclear@13: nuclear@13: obj_by_name.clear(); nuclear@13: mesh_by_aimesh.clear(); nuclear@13: nuclear@13: aiReleaseImport(aiscn); nuclear@13: return true; nuclear@13: } nuclear@13: nuclear@13: static bool load_material(Material *mat, const aiMaterial *aimat) nuclear@13: { nuclear@13: aiColor4D aicol; nuclear@13: float shin, shin_str; nuclear@13: nuclear@13: if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_DIFFUSE, &aicol) == 0) { nuclear@13: mat->diffuse = Vector3(aicol[0], aicol[1], aicol[2]); nuclear@13: } nuclear@13: if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_SPECULAR, &aicol) == 0) { nuclear@13: mat->specular = Vector3(aicol[0], aicol[1], aicol[2]); nuclear@13: } nuclear@13: nuclear@13: unsigned int count = 1; nuclear@13: if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS_STRENGTH, &shin_str, &count) != 0) { nuclear@13: shin_str = 1.0; nuclear@13: } nuclear@13: if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS, &shin, &count) == 0) { nuclear@13: // XXX can't remember how I came up with this... nuclear@13: mat->shininess = shin * shin_str * 0.0001 * 128.0; nuclear@13: } nuclear@13: nuclear@13: // load textures nuclear@13: struct { int type; aiTextureType aitype; } textypes[] = { nuclear@13: {TEX_DIFFUSE, aiTextureType_DIFFUSE}, nuclear@13: {TEX_NORMAL, aiTextureType_NORMALS}, nuclear@13: {TEX_SPECULAR, aiTextureType_SPECULAR} nuclear@13: }; nuclear@13: nuclear@13: for(int i=0; itex[textypes[i].type] = texset.get(fname); nuclear@13: } nuclear@13: } nuclear@13: } nuclear@13: nuclear@13: return true; nuclear@13: } nuclear@13: nuclear@13: static Object *load_node(const aiScene *aiscn, const aiNode *ainode) nuclear@13: { nuclear@13: Object *obj = new Object; nuclear@13: obj->set_name(ainode->mName.data); nuclear@13: nuclear@13: if(ainode->mNumMeshes) { nuclear@13: if(ainode->mNumMeshes > 1) { nuclear@13: info_log("%s warning: node %s has more than one meshes (%u)\n", __FUNCTION__, nuclear@13: ainode->mName.data, ainode->mNumMeshes); nuclear@13: } nuclear@13: nuclear@13: aiMesh *aimesh = aiscn->mMeshes[ainode->mMeshes[0]]; nuclear@13: obj->set_mesh(mesh_by_aimesh[aimesh]); nuclear@13: nuclear@13: // also grab the material of this mesh nuclear@13: load_material(&obj->material, aiscn->mMaterials[aimesh->mMaterialIndex]); nuclear@13: } nuclear@13: nuclear@13: // if there are animations, grab the first and try to use it nuclear@13: if(aiscn->mNumAnimations) { nuclear@13: aiAnimation *aianim = aiscn->mAnimations[0]; nuclear@13: aiNodeAnim *ainodeanim = 0; nuclear@13: for(unsigned int i=0; imNumChannels; i++) { nuclear@13: if(strcmp(aianim->mChannels[i]->mNodeName.data, ainode->mName.data) == 0) { nuclear@13: ainodeanim = aianim->mChannels[i]; nuclear@13: break; nuclear@13: } nuclear@13: } nuclear@13: nuclear@13: if(ainodeanim) { nuclear@13: // load all position (translation) keyframes nuclear@13: for(unsigned int i=0; imNumPositionKeys; i++) { nuclear@13: Vector3 pos = assimp_vector(ainodeanim->mPositionKeys[i].mValue); nuclear@13: long msec = assimp_time(aianim, ainodeanim->mPositionKeys[i].mTime); nuclear@13: obj->set_position(pos, msec); nuclear@13: } nuclear@13: nuclear@13: // load all rotation keyframes nuclear@13: for(unsigned int i=0; imNumRotationKeys; i++) { nuclear@13: Quaternion rot = assimp_quat(ainodeanim->mRotationKeys[i].mValue); nuclear@13: if(rot.length_sq() < SMALL_NUMBER) { nuclear@13: continue; nuclear@13: } nuclear@13: rot.normalize(); nuclear@13: long msec = assimp_time(aianim, ainodeanim->mRotationKeys[i].mTime); nuclear@13: obj->set_rotation(rot, msec); nuclear@13: } nuclear@13: nuclear@13: // load all scaling keyframes nuclear@13: for(unsigned int i=0; imNumScalingKeys; i++) { nuclear@13: Vector3 scale = assimp_vector(ainodeanim->mScalingKeys[i].mValue); nuclear@13: long msec = assimp_time(aianim, ainodeanim->mScalingKeys[i].mTime); nuclear@13: obj->set_scaling(scale, msec); nuclear@13: } nuclear@13: nuclear@13: obj->set_extrapolator(EXTRAP_REPEAT); // loop animation nuclear@13: } else { nuclear@13: Matrix4x4 local_matrix = assimp_matrix(ainode->mTransformation); nuclear@13: obj->set_local_matrix(local_matrix); nuclear@13: } nuclear@13: } nuclear@13: nuclear@13: /* recurse to all children */ nuclear@13: for(unsigned int i=0; imNumChildren; i++) { nuclear@13: Object *child = load_node(aiscn, ainode->mChildren[i]); nuclear@13: if(child) { nuclear@13: obj->add_child(child); nuclear@13: } nuclear@13: } nuclear@13: nuclear@13: obj_by_name[obj->get_name()] = obj; nuclear@13: return obj; nuclear@13: } nuclear@13: nuclear@13: static Mesh *load_mesh(const aiScene *aiscn, const aiMesh *aimesh) nuclear@13: { nuclear@13: Mesh *mesh = new Mesh; nuclear@13: nuclear@13: int num_verts = aimesh->mNumVertices; nuclear@13: int num_faces = aimesh->mNumFaces; nuclear@13: nuclear@13: mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, (float*)aimesh->mVertices); nuclear@13: nuclear@13: if(aimesh->mNormals) { nuclear@13: mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, (float*)aimesh->mNormals); nuclear@13: } nuclear@13: if(aimesh->mTangents) { nuclear@13: mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, (float*)aimesh->mTangents); nuclear@13: } nuclear@13: if(aimesh->mTextureCoords[0]) { nuclear@13: mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 3, num_verts, (float*)aimesh->mTextureCoords[0]); nuclear@13: } nuclear@13: nuclear@13: if(aimesh->mBones) { nuclear@13: float *weights = mesh->set_attrib_data(MESH_ATTR_BONEWEIGHTS, 4, num_verts, 0); nuclear@13: float *boneidx = mesh->set_attrib_data(MESH_ATTR_BONEIDX, 4, num_verts, 0); nuclear@13: nuclear@13: memset(weights, 0, num_verts * 4 * sizeof *weights); nuclear@13: memset(boneidx, 0, num_verts * 4 * sizeof *boneidx); nuclear@13: nuclear@13: int *vertex_bone_count = new int[num_verts]; nuclear@13: memset(vertex_bone_count, 0, num_verts * sizeof *vertex_bone_count); nuclear@13: nuclear@13: for(unsigned int i=0; imNumBones; i++) { nuclear@13: aiBone *aibone = aimesh->mBones[i]; nuclear@13: nuclear@13: // for every vertex affected by this bone: nuclear@13: for(unsigned int j=0; jmNumWeights; j++) { nuclear@13: aiVertexWeight *aiweight = aibone->mWeights + j; nuclear@13: int vidx = aiweight->mVertexId; nuclear@13: int vert_boneidx = vertex_bone_count[vidx]; nuclear@13: if(vert_boneidx >= 4) { nuclear@13: error_log("WARNING vertex with more than 4 bones found\n"); nuclear@13: continue; nuclear@13: } nuclear@13: nuclear@13: weights[vidx * 4 + vert_boneidx] = aiweight->mWeight; nuclear@13: boneidx[vidx * 4 + vert_boneidx] = (float)i; nuclear@13: vertex_bone_count[vidx]++; nuclear@13: } nuclear@13: } nuclear@13: nuclear@13: delete [] vertex_bone_count; nuclear@13: nuclear@13: // normalize weights nuclear@13: for(int i=0; i 1e-4) { nuclear@13: error_log("WARNING vertex with weights < 1 (%f), normalizing...\n", wsum); nuclear@13: nuclear@13: if(wsum < 1e-6) { nuclear@13: // this is clearly broken, let's use the first bone in full nuclear@13: weights[i * 4] = 1.0; nuclear@13: } else { nuclear@13: weights[i * 4] /= wsum; nuclear@13: weights[i * 4 + 1] /= wsum; nuclear@13: weights[i * 4 + 2] /= wsum; nuclear@13: weights[i * 4 + 3] /= wsum; nuclear@13: } nuclear@13: } nuclear@13: } nuclear@13: } nuclear@13: nuclear@13: unsigned int *iptr = mesh->set_index_data(num_faces * 3); nuclear@13: for(int i=0; imFaces[i].mIndices[j]; nuclear@13: } nuclear@13: } nuclear@13: nuclear@13: return mesh; nuclear@13: } nuclear@13: nuclear@13: static Curve *load_curve(const aiScene *aiscn, const aiMesh *aimesh) nuclear@13: { nuclear@13: Curve *curve = new Curve; nuclear@13: nuclear@13: for(unsigned int i=0; imNumVertices; i++) { nuclear@13: Vector3 pt = assimp_vector(aimesh->mVertices[i]); nuclear@13: curve->add_point(pt); nuclear@13: } nuclear@13: info_log("loaded curve with %d points\n", aimesh->mNumVertices); nuclear@13: nuclear@13: return curve; nuclear@13: } nuclear@13: nuclear@13: static bool load_bones(Mesh *mesh, const aiMesh *aimesh) nuclear@13: { nuclear@13: if(!aimesh->mNumBones) { nuclear@13: return false; nuclear@13: } nuclear@13: nuclear@13: for(unsigned int i=0; imNumBones; i++) { nuclear@13: aiBone *aibone = aimesh->mBones[i]; nuclear@13: Object *obj = obj_by_name[aibone->mName.data]; nuclear@13: if(!obj) { nuclear@13: error_log("bone %s not found\n", aibone->mName.data); nuclear@13: continue; nuclear@13: } nuclear@13: nuclear@13: obj->set_bone_matrix(assimp_matrix(aibone->mOffsetMatrix)); nuclear@13: mesh->add_bone(obj); nuclear@13: nuclear@13: info_log("adding bone: %s\n", obj->get_name()); nuclear@13: } nuclear@13: nuclear@13: return true; nuclear@13: } nuclear@13: nuclear@13: static Vector3 assimp_vector(const aiVector3D &v) nuclear@13: { nuclear@13: return Vector3(v[0], v[1], v[2]); nuclear@13: } nuclear@13: nuclear@13: static Quaternion assimp_quat(const aiQuaternion &q) nuclear@13: { nuclear@13: return Quaternion(q.w, Vector3(q.x, q.y, q.z)); nuclear@13: } nuclear@13: nuclear@13: static Matrix4x4 assimp_matrix(const aiMatrix4x4 &aim) nuclear@13: { nuclear@13: Matrix4x4 m; nuclear@13: memcpy(m[0], &aim, 16 * sizeof(float)); nuclear@13: return m; nuclear@13: } nuclear@13: nuclear@13: /* convert an assimp keyframe time (ticks) into milliseconds */ nuclear@13: static long assimp_time(const aiAnimation *anim, double aitime) nuclear@13: { nuclear@13: double sec; nuclear@13: if(anim->mTicksPerSecond < 1e-6) { nuclear@13: // assume time is in frames? nuclear@13: sec = aitime / 30.0; nuclear@13: } else { nuclear@13: sec = aitime / anim->mTicksPerSecond; nuclear@13: } nuclear@13: return (long)(sec * 1000.0); nuclear@13: } nuclear@13: nuclear@13: static void print_hierarchy(const aiNode *node) nuclear@13: { nuclear@13: static int lvl; nuclear@13: static int lvlopen[256]; nuclear@13: nuclear@13: for(int i=0; i= lvl - 1 ? '+' : '|'); nuclear@13: } else { nuclear@13: putchar(i >= lvl - 1 ? '+' : ' '); nuclear@13: } nuclear@13: } nuclear@13: info_log("- \"%s\"\n", node->mName.data); nuclear@13: nuclear@13: lvlopen[lvl] = 1; nuclear@13: nuclear@13: lvl++; nuclear@13: for(unsigned int i=0; imNumChildren; i++) { nuclear@13: if(i == node->mNumChildren - 1) { nuclear@13: lvlopen[lvl - 1] = 0; nuclear@13: } nuclear@13: print_hierarchy(node->mChildren[i]); nuclear@13: } nuclear@13: lvl--; nuclear@13: } nuclear@13: nuclear@13: #else // !defined USE_ASSIMP nuclear@13: nuclear@13: bool load_ass(Scene *scn, const char *fname) nuclear@13: { nuclear@13: error_log("load_ass: assimp support not compiled in\n"); nuclear@13: return false; nuclear@13: } nuclear@13: nuclear@13: #endif