qt opengl assimp 加载骨骼动画完整代码
时间: 2023-07-09 22:03:22 浏览: 104
这里是一个简单的使用Qt、OpenGL和Assimp库加载带有骨骼动画的模型的示例代码:
```cpp
#include <QOpenGLFunctions>
#include <QOpenGLWidget>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <QOpenGLTexture>
#include <QMouseEvent>
#include <QWheelEvent>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
GLWidget(QWidget *parent = nullptr) : QOpenGLWidget(parent)
{
setFocusPolicy(Qt::StrongFocus);
}
~GLWidget()
{
makeCurrent();
m_shaderProgram.release();
m_vbo.destroy();
m_ibo.destroy();
m_vao.destroy();
doneCurrent();
}
protected:
void initializeGL() override
{
initializeOpenGLFunctions();
m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vertex.glsl");
m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/fragment.glsl");
m_shaderProgram.link();
loadModel(":/models/model.dae");
glEnable(GL_DEPTH_TEST);
glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
}
void resizeGL(int width, int height) override
{
glViewport(0, 0, width, height);
}
void paintGL() override
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_shaderProgram.bind();
m_shaderProgram.setUniformValue("mvpMatrix", m_projMatrix * m_cameraMatrix * m_modelMatrix);
m_vao.bind();
glDrawElements(GL_TRIANGLES, m_indices.size(), GL_UNSIGNED_INT, nullptr);
m_vao.release();
}
void mousePressEvent(QMouseEvent *event) override
{
m_lastPos = event->pos();
}
void mouseMoveEvent(QMouseEvent *event) override
{
if (event->buttons() & Qt::LeftButton) {
int dx = event->x() - m_lastPos.x();
int dy = event->y() - m_lastPos.y();
m_cameraYaw += dx * 0.01f;
m_cameraPitch += dy * 0.01f;
if (m_cameraPitch > 1.5f)
m_cameraPitch = 1.5f;
else if (m_cameraPitch < -1.5f)
m_cameraPitch = -1.5f;
updateCameraMatrix();
m_lastPos = event->pos();
update();
}
}
void wheelEvent(QWheelEvent *event) override
{
m_cameraDistance -= event->delta() * 0.01f;
if (m_cameraDistance < 0.1f)
m_cameraDistance = 0.1f;
updateCameraMatrix();
update();
}
private:
void loadModel(const QString &filePath)
{
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(filePath.toStdString(), aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs | aiProcess_LimitBoneWeights);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
qDebug() << "Error: " << importer.GetErrorString();
return;
}
m_boneMatrices.resize(scene->mNumMeshes);
aiMatrix4x4 globalTransform = scene->mRootNode->mTransformation;
globalTransform.Inverse();
loadNode(scene->mRootNode, scene, globalTransform);
m_vao.create();
m_vao.bind();
m_vbo.create();
m_vbo.bind();
m_vbo.allocate(m_vertices.data(), m_vertices.size() * sizeof(Vertex));
m_shaderProgram.enableAttributeArray("position");
m_shaderProgram.setAttributeBuffer("position", GL_FLOAT, offsetof(Vertex, position), 3, sizeof(Vertex));
m_shaderProgram.enableAttributeArray("normal");
m_shaderProgram.setAttributeBuffer("normal", GL_FLOAT, offsetof(Vertex, normal), 3, sizeof(Vertex));
m_shaderProgram.enableAttributeArray("texcoord");
m_shaderProgram.setAttributeBuffer("texcoord", GL_FLOAT, offsetof(Vertex, texcoord), 2, sizeof(Vertex));
m_shaderProgram.enableAttributeArray("boneIndices");
m_shaderProgram.setAttributeBuffer("boneIndices", GL_FLOAT, offsetof(Vertex, boneIndices), 4, sizeof(Vertex));
m_shaderProgram.enableAttributeArray("boneWeights");
m_shaderProgram.setAttributeBuffer("boneWeights", GL_FLOAT, offsetof(Vertex, boneWeights), 4, sizeof(Vertex));
m_ibo.create();
m_ibo.bind();
m_ibo.allocate(m_indices.data(), m_indices.size() * sizeof(unsigned int));
m_vao.release();
}
void loadNode(aiNode *node, const aiScene *scene, const aiMatrix4x4 &parentTransform)
{
aiMatrix4x4 transform = parentTransform * node->mTransformation;
for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
for (unsigned int j = 0; j < mesh->mNumBones; ++j) {
aiBone *bone = mesh->mBones[j];
QString boneName = QString::fromStdString(bone->mName.C_Str());
if (m_boneMapping.find(boneName) == m_boneMapping.end()) {
int boneIndex = m_boneMapping.size();
m_boneMapping[boneName] = boneIndex;
m_boneInfo.push_back(BoneInfo{transform * bone->mOffsetMatrix, QMatrix4x4()});
}
}
for (unsigned int j = 0; j < mesh->mNumFaces; ++j) {
aiFace &face = mesh->mFaces[j];
for (unsigned int k = 0; k < face.mNumIndices; ++k)
m_indices.push_back(face.mIndices[k]);
}
for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
Vertex vertex;
vertex.position = QVector3D(mesh->mVertices[j].x, mesh->mVertices[j].y, mesh->mVertices[j].z);
vertex.normal = QVector3D(mesh->mNormals[j].x, mesh->mNormals[j].y, mesh->mNormals[j].z);
if (mesh->mTextureCoords[0]) {
vertex.texcoord = QVector2D(mesh->mTextureCoords[0][j].x, mesh->mTextureCoords[0][j].y);
} else {
vertex.texcoord = QVector2D(0.0f, 0.0f);
}
for (unsigned int k = 0; k < 4; ++k) {
vertex.boneIndices[k] = -1;
vertex.boneWeights[k] = 0.0f;
}
for (unsigned int k = 0; k < mesh->mNumBones; ++k) {
aiBone *bone = mesh->mBones[k];
QString boneName = QString::fromStdString(bone->mName.C_Str());
if (m_boneMapping.find(boneName) != m_boneMapping.end()) {
int boneIndex = m_boneMapping[boneName];
vertex.boneIndices[k] = boneIndex;
vertex.boneWeights[k] = bone->mWeights[j].mWeight;
}
}
m_vertices.push_back(vertex);
}
}
for (unsigned int i = 0; i < node->mNumChildren; ++i) {
loadNode(node->mChildren[i], scene, transform);
}
}
void updateBoneMatrices(aiNode *node, const QMatrix4x4 &parentMatrix)
{
QString nodeName = QString::fromStdString(node->mName.C_Str());
if (m_boneMapping.find(nodeName) != m_boneMapping.end()) {
int boneIndex = m_boneMapping[nodeName];
m_boneInfo[boneIndex].finalTransform = parentMatrix * m_boneInfo[boneIndex].offsetMatrix * m_boneInfo[boneIndex].boneTransform;
m_boneMatrices[boneIndex] = m_boneInfo[boneIndex].finalTransform * m_boneInfo[boneIndex].boneOffset;
}
QMatrix4x4 transform;
transform(0, 0) = node->mTransformation.a1;
transform(1, 0) = node->mTransformation.a2;
transform(2, 0) = node->mTransformation.a3;
transform(3, 0) = node->mTransformation.a4;
transform(0, 1) = node->mTransformation.b1;
transform(1, 1) = node->mTransformation.b2;
transform(2, 1) = node->mTransformation.b3;
transform(3, 1) = node->mTransformation.b4;
transform(0, 2) = node->mTransformation.c1;
transform(1, 2) = node->mTransformation.c2;
transform(2, 2) = node->mTransformation.c3;
transform(3, 2) = node->mTransformation.c4;
transform(0, 3) = node->mTransformation.d1;
transform(1, 3) = node->mTransformation.d2;
transform(2, 3) = node->mTransformation.d3;
transform(3, 3) = node->mTransformation.d4;
QMatrix4x4 combinedTransform = parentMatrix * transform;
for (unsigned int i = 0; i < node->mNumChildren; ++i) {
updateBoneMatrices(node->mChildren[i], combinedTransform);
}
}
void updateCameraMatrix()
{
m_cameraMatrix.setToIdentity();
m_cameraMatrix.translate(0.0f, 0.0f, -m_cameraDistance);
m_cameraMatrix.rotate(m_cameraPitch, QVector3D(1.0f, 0.0f, 0.0f));
m_cameraMatrix.rotate(m_cameraYaw, QVector3D(0.0f, 1.0f, 0.0f));
}
void updateAnimation(float deltaTime)
{
m_animationTime += deltaTime;
if (m_animationTime > m_animationDuration)
m_animationTime -= m_animationDuration;
aiMatrix4x4 identity;
identity.SetIdentity();
updateBoneMatrices(m_scene->mRootNode, identity);
for (int i = 0; i < m_boneMatrices.size(); ++i) {
m_shaderProgram.setUniformValue(QString("boneMatrices[%1]").arg(i), m_boneMatrices[i]);
}
}
QOpenGLShaderProgram m_shaderProgram;
QOpenGLBuffer m_vbo;
QOpenGLBuffer m_ibo;
QOpenGLVertexArrayObject m_vao;
QVector<Vertex> m_vertices;
QVector<unsigned int> m_indices;
QMap<QString, int> m_boneMapping;
QVector<BoneInfo> m_boneInfo;
QVector<QMatrix4x4> m_boneMatrices;
const aiScene *m_scene = nullptr;
float m_animationTime = 0.0f;
float m_animationDuration = 0.0f;
float m_cameraYaw = 0.0f;
float m_cameraPitch = 0.0f;
float m_cameraDistance = 5.0f;
QVector3D m_cameraPosition;
QMatrix4x4 m_cameraMatrix;
QMatrix4x4 m_modelMatrix;
QMatrix4x4 m_projMatrix;
QPoint m_lastPos;
struct Vertex {
QVector3D position;
QVector3D normal;
QVector2D texcoord;
QVector4D boneIndices;
QVector4D boneWeights;
};
struct BoneInfo {
aiMatrix4x4 offsetMatrix;
QMatrix4x4 boneTransform;
QMatrix4x4 finalTransform;
QMatrix4x4 boneOffset;
};
};
```
其中,`loadModel`函数用于加载模型,`loadNode`函数用于递归加载节点和骨骼信息,`updateBoneMatrices`函数用于更新骨骼变换矩阵,`updateAnimation`函数用于更新动画播放进度和骨骼变换矩阵,`updateCameraMatrix`函数用于更新摄像机变换矩阵。在`initializeGL`函数中调用`loadModel`函数进行模型加载,在`paintGL`函数中进行渲染。
需要注意的是,这里使用了Assimp库来加载带有骨骼动画的模型,并且使用了Qt的OpenGL相关类来进行渲染。如果要使用这段代码,请确保已经正确安装了Qt和Assimp库,并且已经将模型文件和着色器文件添加到了项目中。
阅读全文