Qopengl assimp使用骨骼动画代码
时间: 2023-10-13 18:06:41 浏览: 38
以下是使用OpenGL和Assimp库实现骨骼动画的代码示例:
1. 导入模型和动画
```
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
// 导入模型
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile("model.dae", aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace);
// 导入动画
const aiAnimation* animation = scene->mAnimations[0];
float ticksPerSecond = animation->mTicksPerSecond != 0 ? animation->mTicksPerSecond : 25.0f;
float timeInSeconds = animation->mDuration / ticksPerSecond;
```
2. 定义骨骼结构
```
struct Bone {
std::string name;
glm::mat4 offsetMatrix;
glm::mat4 finalTransformation;
};
struct VertexBoneData {
int ids[4];
float weights[4];
};
struct Mesh {
std::vector<glm::vec3> positions;
std::vector<glm::vec3> normals;
std::vector<glm::vec2> texCoords;
std::vector<VertexBoneData> boneData;
std::vector<unsigned int> indices;
std::vector<Bone> bones;
};
```
3. 解析模型和骨骼数据
```
void processMesh(const aiMesh* mesh, Mesh& result) {
for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
glm::vec3 position(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z);
glm::vec3 normal(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z);
glm::vec2 texCoords(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y);
VertexBoneData boneData;
boneData.ids[0] = -1;
boneData.ids[1] = -1;
boneData.ids[2] = -1;
boneData.ids[3] = -1;
boneData.weights[0] = 0.0f;
boneData.weights[1] = 0.0f;
boneData.weights[2] = 0.0f;
boneData.weights[3] = 0.0f;
for (unsigned int j = 0; j < mesh->mNumBones; j++) {
aiBone* bone = mesh->mBones[j];
for (unsigned int k = 0; k < bone->mNumWeights; k++) {
if (bone->mWeights[k].mVertexId == i) {
if (boneData.ids[0] == -1) {
boneData.ids[0] = j;
boneData.weights[0] = bone->mWeights[k].mWeight;
} else if (boneData.ids[1] == -1) {
boneData.ids[1] = j;
boneData.weights[1] = bone->mWeights[k].mWeight;
} else if (boneData.ids[2] == -1) {
boneData.ids[2] = j;
boneData.weights[2] = bone->mWeights[k].mWeight;
} else if (boneData.ids[3] == -1) {
boneData.ids[3] = j;
boneData.weights[3] = bone->mWeights[k].mWeight;
}
}
}
}
result.positions.push_back(position);
result.normals.push_back(normal);
result.texCoords.push_back(texCoords);
result.boneData.push_back(boneData);
}
for (unsigned int i = 0; i < mesh->mNumFaces; i++) {
aiFace face = mesh->mFaces[i];
for (unsigned int j = 0; j < face.mNumIndices; j++) {
result.indices.push_back(face.mIndices[j]);
}
}
}
void processNode(const aiNode* node, const aiScene* scene, Mesh& result) {
for (unsigned int i = 0; i < node->mNumMeshes; i++) {
const aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
processMesh(mesh, result);
}
for (unsigned int i = 0; i < node->mNumChildren; i++) {
processNode(node->mChildren[i], scene, result);
}
}
void loadMesh(const std::string& fileName, Mesh& result) {
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(fileName.c_str(), aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace | aiProcess_LimitBoneWeights);
processNode(scene->mRootNode, scene, result);
}
```
4. 计算骨骼动画矩阵
```
void calculateBoneTransform(const aiNode* node, glm::mat4 parentTransform, std::unordered_map<std::string, glm::mat4>& boneTransforms) {
std::string nodeName(node->mName.data);
glm::mat4 nodeTransform(aiMatrix4x4ToGlm(&node->mTransformation));
const aiAnimation* animation = scene->mAnimations[0];
float ticksPerSecond = animation->mTicksPerSecond != 0 ? animation->mTicksPerSecond : 25.0f;
float timeInTicks = fmod(currentTime, timeInSeconds) * ticksPerSecond;
glm::mat4 animationTransform(1.0f);
if (boneTransforms.find(nodeName) != boneTransforms.end()) {
animationTransform = boneTransforms[nodeName];
}
glm::mat4 globalTransform = parentTransform * nodeTransform * animationTransform;
if (boneMap.find(nodeName) != boneMap.end()) {
unsigned int boneIndex = boneMap[nodeName];
glm::mat4 boneOffset = mesh.bones[boneIndex].offsetMatrix;
glm::mat4 finalTransformation = globalInverseTransform * globalTransform * boneOffset;
mesh.bones[boneIndex].finalTransformation = finalTransformation;
boneTransforms[nodeName] = finalTransformation;
}
for (unsigned int i = 0; i < node->mNumChildren; i++) {
calculateBoneTransform(node->mChildren[i], globalTransform, boneTransforms);
}
}
```
5. 更新顶点位置和法线
```
void updateVertexPositions() {
for (unsigned int i = 0; i < mesh.positions.size(); i++) {
glm::vec3 position(0.0f);
glm::vec3 normal(0.0f);
for (unsigned int j = 0; j < 4; j++) {
int boneId = mesh.boneData[i].ids[j];
float weight = mesh.boneData[i].weights[j];
if (boneId >= 0 && boneId < mesh.bones.size()) {
glm::mat4 boneTransform = mesh.bones[boneId].finalTransformation;
position += weight * glm::vec3(boneTransform * glm::vec4(mesh.positions[i], 1.0f));
normal += weight * glm::vec3(boneTransform * glm::vec4(mesh.normals[i], 0.0f));
}
}
vertices[i].position = position;
vertices[i].normal = normal;
}
}
```
6. 绘制模型
```
void drawModel() {
glUseProgram(shaderProgram);
// 设置uniform变量
glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, glm::value_ptr(modelMatrix));
glUniformMatrix4fv(viewMatrixLocation, 1, GL_FALSE, glm::value_ptr(viewMatrix));
glUniformMatrix4fv(projectionMatrixLocation, 1, GL_FALSE, glm::value_ptr(projectionMatrix));
glUniformMatrix4fv(globalInverseTransformLocation, 1, GL_FALSE, glm::value_ptr(globalInverseTransform));
for (unsigned int i = 0; i < mesh.bones.size(); i++) {
std::string boneName = mesh.bones[i].name;
glm::mat4 boneTransform = mesh.bones[i].finalTransformation;
std::string uniformName = "boneTransforms[" + std::to_string(i) + "]";
GLuint uniformLocation = glGetUniformLocation(shaderProgram, uniformName.c_str());
glUniformMatrix4fv(uniformLocation, 1, GL_FALSE, glm::value_ptr(boneTransform));
}
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, mesh.indices.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
```