/* WARNING::Vertex vector must be sorted according to aimesh indices or shit goes south */ void load_mesh_bones(const aiMesh* mesh, std::unordered_map<GLuint, GLuint>& bone_map, std::unordered_map<std::string, GLuint>& bone_id_map, std::vector<Vertex>& vertices, std::vector<glm::mat4>& bone_info){ for (GLuint i = 0 ; i < mesh->mNumBones; ++i) { GLuint bone_index = 0; GLuint bone_id = 0; std::string bone_name(mesh->mBones[i]->mName.data); if (bone_id_map.find(bone_name) == bone_id_map.end()) { bone_id = bone_id_map.size(); bone_id_map[bone_name] = bone_id; } else{ bone_id = bone_id_map[bone_name]; } if (bone_map.find(bone_id) == bone_map.end()) { aiMatrix4x4 offset_aimatrix = mesh->mBones[i]->mOffsetMatrix; glm::mat4 offsetmatrix; for (GLuint x = 0; x < 4; ++x) { for (GLuint y = 0; y < 4; ++y) { offsetmatrix[x][y] = offset_aimatrix[x][y]; } } bone_index = bone_info.size(); bone_info.push_back(offsetmatrix); bone_map[bone_id] = bone_index; } for (GLuint j = 0 ; j < mesh->mBones[i]->mNumWeights; ++j) { /* TODO::Make sure this gets the right vertex id */ GLuint vertex_id = mesh->mBones[i]->mWeights[j].mVertexId; GLfloat weight = mesh->mBones[i]->mWeights[j].mWeight; if (!add_bone_to_vertex(vertices[vertex_id], bone_index)){ std::cout << __FILE__ << ":" << __LINE__ << ": " << "FATAL ERROR: Vertex can only be affected by four bones, id overflow in bone: " << bone_name << std::endl; errorlogger("FATAL ERROR: Vertex can only be affected by four bones, id overflow in bone: ", bone_name.c_str()); exit(EXIT_FAILURE); } if (!add_weight_to_vertex(vertices[vertex_id], weight)) { std::cout << __FILE__ << ":" << __LINE__ << ": " << "ERROR: Vertex can only be affected by four bones, weight overflow in bone: " << bone_name << std::endl; errorlogger("ERROR: Vertex can only be affected by four bones, weight overflow in bone: ", bone_name.c_str()); exit(EXIT_FAILURE); } } } }
Animation* ModelInterface::loadAnimation(const aiAnimation* ai_animation, const int index) { QString animation_name(ai_animation->mName.data); double duration = ai_animation->mDuration; //qDebug() << "Animation: " + animation_name + " duration " + QString::number(duration); Animation* animation = new Animation(animation_name, duration, index); animation->setBoneCount(bones->getBoneNames().size()); for(uint bone_index = 0; bone_index < ai_animation->mNumChannels; ++bone_index) { aiNodeAnim* channel = ai_animation->mChannels[bone_index]; QString bone_name(channel->mNodeName.data); if(!bones->hasBone(bone_name)) continue; int bone_id = bones->getBone(bone_name)->getId(); animation->registerBone(bone_id); for(uint i = 0; i < channel->mNumPositionKeys; ++i) { aiVectorKey pos_key = channel->mPositionKeys[i]; animation->addBonePosition(bone_id, float(pos_key.mTime), QVector3D(pos_key.mValue.x, pos_key.mValue.y, pos_key.mValue.z)); } for(uint i = 0; i < channel->mNumRotationKeys; ++i) { aiQuatKey rot_key = channel->mRotationKeys[i]; animation->addBoneRotation(bone_id, float(rot_key.mTime), QQuaternion(rot_key.mValue.w, rot_key.mValue.x, rot_key.mValue.y, rot_key.mValue.z)); } for(uint i = 0; i < channel->mNumScalingKeys; ++i) { aiVectorKey scale_key = channel->mScalingKeys[i]; animation->addBoneScaling(bone_id, float(scale_key.mTime), QVector3D(scale_key.mValue.x, scale_key.mValue.y, scale_key.mValue.z)); } } return animation; }
Bones* ModelInterface::loadBones(const aiScene* scene) { Bones* bones = new Bones(); for(uint mesh_index = 0; mesh_index < scene->mNumMeshes; ++mesh_index) { aiMesh* ai_mesh = scene->mMeshes[mesh_index]; if(ai_mesh->HasBones()) { for(uint bone_index = 0; bone_index < ai_mesh->mNumBones; ++bone_index) { aiBone* ai_bone = ai_mesh->mBones[bone_index]; QString bone_name(ai_bone->mName.data); if(!bones->hasBone(bone_name)) bones->createEmptyBone(bone_name, getMatrix(&ai_bone->mOffsetMatrix)); } } } return bones; }
Bone::Bone(FbxScene& scene) { //construct hierarchy *this = Bone { *(scene.GetRootNode()) }; std::map<std::string, std::pair<unsigned int, scm::math::mat4f> > bone_info{} ; unsigned num_bones = 0; for (unsigned int i = 0; i < scene.GetGeometryCount(); i++) { FbxGeometry* geo = scene.GetGeometry(i); if (geo->GetAttributeType() == FbxNodeAttribute::eMesh) { //check for skinning, use first skin deformer FbxSkin* skin; for (unsigned i = 0; i < geo->GetDeformerCount(); ++i) { FbxDeformer* defPtr = { geo->GetDeformer(i) }; if (defPtr->GetDeformerType() == FbxDeformer::eSkin) { skin = static_cast<FbxSkin*>(defPtr); break; } } if (!skin) { Logger::LOG_ERROR << "Mesh does not contain skin deformer" << std::endl; assert(false); } //one cluster corresponds to one bone for (unsigned i = 0; i < skin->GetClusterCount(); ++i) { FbxCluster* cluster = skin->GetCluster(i); FbxNode* node = cluster->GetLink(); if (!node) { Logger::LOG_ERROR << "associated node does not exist!" << std::endl; assert(false); } std::string bone_name(node->GetName()); //helper to check for matrix magnitude auto magnitude = [](scm::math::mat4f const & mat)->float { float mag = 0.0f; for (unsigned i = 0; i < 16; ++i) { mag += mat[i] * mat[i]; } return mag; } ; //reference pose of bone FbxAMatrix bind_transform; cluster->GetTransformLinkMatrix(bind_transform); //check if the clusters have an extra transformation FbxAMatrix cluster_transform; cluster->GetTransformMatrix(cluster_transform); if (magnitude(to_gua::mat4f(cluster_transform) - scm::math::mat4f::identity()) > 0.000000001f) { Logger::LOG_WARNING << "weight cluster of bone '" << bone_name << "' has transformation, animation will be skewed" << std::endl; } //add bone to list if it is not already included if (bone_info.find(bone_name) == bone_info.end()) { bone_info[bone_name] = std::make_pair( num_bones, to_gua::mat4f(bind_transform.Inverse() * cluster_transform)); ++num_bones; } } // traverse hierarchy and set accumulated values in the bone this->set_properties(bone_info); } } }