qt opengl assimp 加载骨骼动画完整代码
时间: 2023-07-09 10:03:22 浏览: 84
骨骼动画演示程序
5星 · 资源好评率100%
以下是使用Qt、OpenGL和Assimp加载骨骼动画的基本代码。请注意,这只是一个示例,需要根据您的实际需求进行修改和优化。
```c++
#include <QApplication>
#include <QKeyEvent>
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QMatrix4x4>
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
GLWidget(QWidget *parent = nullptr)
: QOpenGLWidget(parent)
{
setFocusPolicy(Qt::StrongFocus);
}
protected:
void initializeGL() override
{
initializeOpenGLFunctions();
m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vertex.glsl");
m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/fragment.glsl");
m_shaderProgram.link();
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
m_texture = new QOpenGLTexture(QImage(":/textures/texture.png").mirrored());
m_texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
m_texture->setMagnificationFilter(QOpenGLTexture::Linear);
m_texture->setWrapMode(QOpenGLTexture::Repeat);
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(":/models/model.dae",
aiProcess_Triangulate |
aiProcess_FlipUVs |
aiProcess_GenSmoothNormals |
aiProcess_JoinIdenticalVertices |
aiProcess_LimitBoneWeights |
aiProcess_RemoveRedundantMaterials |
aiProcess_OptimizeMeshes |
aiProcess_ValidateDataStructure |
aiProcess_ImproveCacheLocality |
aiProcess_SortByPType |
aiProcess_FindDegenerates |
aiProcess_FindInvalidData |
aiProcess_RemoveComponent |
aiProcess_GenUVCoords |
aiProcess_TransformUVCoords |
aiProcess_FindInstances |
aiProcess_OptimizeGraph |
aiProcess_FixInfacingNormals |
aiProcess_SplitLargeMeshes |
aiProcess_Triangulate |
aiProcess_GenNormals |
aiProcess_SplitByBoneCount |
aiProcess_Debone);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
{
qDebug() << "Failed to load model:" << importer.GetErrorString();
return;
}
m_globalInverseTransform = glm::inverse(glm::make_mat4(reinterpret_cast<const float *>(&scene->mRootNode->mTransformation)));
if (scene->HasAnimations())
{
aiAnimation *animation = scene->mAnimations[0];
m_animationDuration = animation->mDuration;
m_ticksPerSecond = animation->mTicksPerSecond;
for (unsigned int i = 0; i < animation->mNumChannels; ++i)
{
aiNodeAnim *nodeAnim = animation->mChannels[i];
std::string nodeName = nodeAnim->mNodeName.C_Str();
for (unsigned int j = 0; j < nodeAnim->mNumPositionKeys; ++j)
{
const aiVectorKey &key = nodeAnim->mPositionKeys[j];
m_positions[nodeName][key.mTime] = glm::vec3(key.mValue.x, key.mValue.y, key.mValue.z);
}
for (unsigned int j = 0; j < nodeAnim->mNumRotationKeys; ++j)
{
const aiQuatKey &key = nodeAnim->mRotationKeys[j];
m_rotations[nodeName][key.mTime] = glm::quat(key.mValue.w, key.mValue.x, key.mValue.y, key.mValue.z);
}
for (unsigned int j = 0; j < nodeAnim->mNumScalingKeys; ++j)
{
const aiVectorKey &key = nodeAnim->mScalingKeys[j];
m_scales[nodeName][key.mTime] = glm::vec3(key.mValue.x, key.mValue.y, key.mValue.z);
}
}
}
processNode(scene->mRootNode, scene);
}
void resizeGL(int w, int h) override
{
glViewport(0, 0, w, h);
}
void paintGL() override
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_shaderProgram.bind();
QMatrix4x4 projection;
projection.perspective(45.0f, width() / float(height()), 0.1f, 100.0f);
QMatrix4x4 view;
view.translate(0.0f, 0.0f, -5.0f);
QMatrix4x4 model;
model = glm::make_mat4(reinterpret_cast<const float *>(&m_globalInverseTransform));
model.scale(0.01f);
m_shaderProgram.setUniformValue("projection", projection);
m_shaderProgram.setUniformValue("view", view);
m_shaderProgram.setUniformValue("model", model);
for (const auto &mesh : m_meshes)
{
m_texture->bind();
const auto &bones = mesh.bones;
for (unsigned int i = 0; i < bones.size(); ++i)
{
const auto &bone = bones[i];
QMatrix4x4 boneTransform;
boneTransform = glm::make_mat4(reinterpret_cast<const float *>(&bone.finalTransformation));
boneTransform = model * boneTransform;
QString uniformName = "bones[" + QString::number(i) + "]";
m_shaderProgram.setUniformValue(uniformName.toUtf8().constData(), boneTransform);
}
m_shaderProgram.setUniformValue("hasBones", bones.size() > 0);
glBindVertexArray(mesh.vao);
glDrawElements(GL_TRIANGLES, mesh.indices.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
m_shaderProgram.release();
}
void keyPressEvent(QKeyEvent *event) override
{
if (event->key() == Qt::Key_Escape)
QApplication::quit();
}
private:
struct Vertex
{
glm::vec3 position;
glm::vec3 normal;
glm::vec2 texCoord;
glm::vec3 tangent;
glm::vec3 bitangent;
};
struct BoneInfo
{
glm::mat4 offsetMatrix;
glm::mat4 finalTransformation;
};
struct Mesh
{
std::vector<Vertex> vertices;
std::vector<unsigned int> indices;
std::vector<BoneInfo> bones;
GLuint vao = 0;
GLuint vbo = 0;
GLuint ebo = 0;
};
QOpenGLShaderProgram m_shaderProgram;
QOpenGLTexture *m_texture = nullptr;
std::vector<Mesh> m_meshes;
glm::mat4 m_globalInverseTransform;
float m_animationDuration = 0.0f;
float m_ticksPerSecond = 0.0f;
std::map<std::string, std::map<float, glm::vec3>> m_positions;
std::map<std::string, std::map<float, glm::quat>> m_rotations;
std::map<std::string, std::map<float, glm::vec3>> m_scales;
void processNode(aiNode *node, const aiScene *scene)
{
for (unsigned int i = 0; i < node->mNumMeshes; ++i)
{
aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
m_meshes.push_back(processMesh(mesh, scene));
}
for (unsigned int i = 0; i < node->mNumChildren; ++i)
{
processNode(node->mChildren[i], scene);
}
}
Mesh processMesh(aiMesh *mesh, const aiScene *scene)
{
Mesh result;
for (unsigned int i = 0; i < mesh->mNumVertices; ++i)
{
Vertex vertex;
vertex.position = glm::vec3(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z);
vertex.normal = glm::vec3(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z);
if (mesh->mTextureCoords[0])
{
vertex.texCoord = glm::vec2(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y);
}
vertex.tangent = glm::vec3(mesh->mTangents[i].x, mesh->mTangents[i].y, mesh->mTangents[i].z);
vertex.bitangent = glm::vec3(mesh->mBitangents[i].x, mesh->mBitangents[i].y, mesh->mBitangents[i].z);
result.vertices.push_back(vertex);
}
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]);
}
}
if (mesh->HasBones())
{
std::map<std::string, BoneInfo> boneMap;
for (unsigned int i = 0; i < mesh->mNumBones; ++i)
{
aiBone *bone = mesh->mBones[i];
BoneInfo boneInfo;
boneInfo.offsetMatrix = glm::make_mat4(reinterpret_cast<const float *>(&bone->mOffsetMatrix));
for (unsigned int j = 0; j < bone->mNumWeights; ++j)
{
aiVertexWeight weight = bone->mWeights[j];
unsigned int vertexId = weight.mVertexId;
float weightValue = weight.mWeight;
result.vertices[vertexId].tangent = glm::vec3(0.0f);
result.vertices[vertexId].bitangent = glm::vec3(0.0f);
auto iter = boneMap.find(bone->mName.C_Str());
if (iter == boneMap.end())
{
boneInfo.finalTransformation = glm::mat4(1.0f);
boneMap[bone->mName.C_Str()] = boneInfo;
}
boneMap[bone->mName.C_Str()].finalTransformation += weightValue * boneInfo.offsetMatrix;
}
}
for (const auto &pair : boneMap)
{
result.bones.push_back(pair.second);
}
}
glGenVertexArrays(1, &result.vao);
glBindVertexArray(result.vao);
glGenBuffers(1, &result.vbo);
glBindBuffer(GL_ARRAY_BUFFER, result.vbo);
glBufferData(GL_ARRAY_BUFFER, result.vertices.size() * sizeof(Vertex), result.vertices.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void *>(offsetof(Vertex, position)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void *>(offsetof(Vertex, normal)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void *>(offsetof(Vertex, texCoord)));
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void *>(offsetof(Vertex, tangent)));
glEnableVertexAttribArray(4);
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void *>(offsetof(Vertex, bitangent)));
glGenBuffers(1, &result.ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, result.ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, result.indices.size() * sizeof(unsigned int), result.indices.data(), GL_STATIC_DRAW);
glBindVertexArray(0);
return result;
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
GLWidget widget;
widget.resize(800, 600);
widget.show();
return app.exec();
}
```
这个程序使用Assimp库加载一个包含骨骼动画的3D模型,并将其渲染到OpenGL窗口中。在渲染过程中,它会将骨骼转换矩阵传递给着色器程序,从而实现骨骼动画效果。
阅读全文