qt opengl assimp 加载骨骼动画完整代码
时间: 2023-07-09 09:03:21 浏览: 294
骨骼动画演示程序
5星 · 资源好评率100%
以下是一个使用 Qt、OpenGL 和 Assimp 库加载骨骼动画的完整代码:
```cpp
#include <QApplication>
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QTimer>
#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)
{
m_timer = new QTimer(this);
connect(m_timer, SIGNAL(timeout()), this, SLOT(update()));
m_timer->start(16);
}
void initializeGL() override
{
initializeOpenGLFunctions();
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glEnable(GL_DEPTH_TEST);
m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vertexShader.glsl");
m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/fragmentShader.glsl");
m_shaderProgram.link();
m_assimpImporter.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
m_assimpImporter.SetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true);
m_assimpImporter.SetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true);
m_assimpImporter.SetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true);
m_assimpImporter.SetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true);
m_assimpImporter.SetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS, 4);
m_assimpImporter.SetPropertyBool(AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION, true);
loadModel(":/model.dae");
}
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, -3.0f);
QMatrix4x4 model;
model.rotate(m_rotation, 0.0f, 1.0f, 0.0f);
QMatrix4x4 mvp = projection * view * model;
m_shaderProgram.setUniformValue("mvp", mvp);
for (int i = 0; i < m_meshes.size(); i++)
{
m_meshes[i].draw(&m_shaderProgram);
}
m_shaderProgram.release();
m_rotation += 1.0f;
}
private:
class Mesh
{
public:
Mesh(aiMesh *mesh, const aiScene *scene)
{
for (unsigned int i = 0; i < mesh->mNumVertices; i++)
{
Vertex vertex;
vertex.position = QVector3D(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z);
vertex.normal = QVector3D(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z);
if (mesh->mTextureCoords[0])
{
vertex.texCoord = QVector2D(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y);
}
else
{
vertex.texCoord = QVector2D(0.0f, 0.0f);
}
m_vertices.append(vertex);
}
for (unsigned int i = 0; i < mesh->mNumFaces; i++)
{
aiFace face = mesh->mFaces[i];
for (unsigned int j = 0; j < face.mNumIndices; j++)
{
m_indices.append(face.mIndices[j]);
}
}
if (mesh->mMaterialIndex >= 0)
{
aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex];
aiString texturePath;
if (material->GetTexture(aiTextureType_DIFFUSE, 0, &texturePath) == AI_SUCCESS)
{
m_texture = new QOpenGLTexture(QImage(QString::fromStdString(texturePath.C_Str())).mirrored());
m_texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
m_texture->setMagnificationFilter(QOpenGLTexture::Linear);
m_texture->setWrapMode(QOpenGLTexture::Repeat);
}
}
for (unsigned int i = 0; i < mesh->mNumBones; i++)
{
aiBone *bone = mesh->mBones[i];
QString boneName = QString::fromStdString(bone->mName.C_Str());
if (m_boneMapping.contains(boneName))
{
m_boneMapping[boneName].append(i);
}
else
{
m_boneMapping.insert(boneName, QList<unsigned int>() << i);
}
}
m_boneMatrices.resize(mesh->mNumBones);
m_vertexBoneData.resize(mesh->mNumVertices);
for (unsigned int i = 0; i < mesh->mNumBones; i++)
{
aiBone *bone = mesh->mBones[i];
QString boneName = QString::fromStdString(bone->mName.C_Str());
m_boneOffsets.append(QMatrix4x4(&bone->mOffsetMatrix.a1));
for (unsigned int j = 0; j < bone->mNumWeights; j++)
{
aiVertexWeight vertexWeight = bone->mWeights[j];
unsigned int vertexIndex = vertexWeight.mVertexId;
float weight = vertexWeight.mWeight;
QVector4D &vertexBoneData = m_vertexBoneData[vertexIndex];
for (int k = 0; k < 4; k++)
{
if (vertexBoneData[k] == 0.0f)
{
vertexBoneData[k] = weight;
m_vertexBoneIndices[vertexIndex][k] = m_boneMapping[boneName].indexOf(i);
break;
}
}
}
}
m_vbo.create();
m_vbo.bind();
m_vbo.allocate(m_vertices.constData(), m_vertices.size() * sizeof(Vertex));
m_ibo.create();
m_ibo.bind();
m_ibo.allocate(m_indices.constData(), m_indices.size() * sizeof(unsigned int));
}
void draw(QOpenGLShaderProgram *shaderProgram)
{
m_vbo.bind();
shaderProgram->setAttributeBuffer("position", GL_FLOAT, offsetof(Vertex, position), 3, sizeof(Vertex));
shaderProgram->setAttributeBuffer("normal", GL_FLOAT, offsetof(Vertex, normal), 3, sizeof(Vertex));
shaderProgram->setAttributeBuffer("texCoord", GL_FLOAT, offsetof(Vertex, texCoord), 2, sizeof(Vertex));
shaderProgram->enableAttributeArray("position");
shaderProgram->enableAttributeArray("normal");
shaderProgram->enableAttributeArray("texCoord");
for (unsigned int i = 0; i < m_boneMatrices.size(); i++)
{
m_boneMatrices[i] = m_boneOffsets[i];
aiNode *node = m_scene->mRootNode->FindNode(m_mesh->mBones[i]->mName);
while (node)
{
QMatrix4x4 transform(&node->mTransformation.a1);
m_boneMatrices[i] = transform * m_boneMatrices[i];
node = node->mParent;
}
shaderProgram->setUniformValueArray("boneMatrices[" + QString::number(i) + "]", m_boneMatrices.constData(), m_boneMatrices.size());
}
shaderProgram->setUniformValueArray("vertexBoneData", m_vertexBoneData.constData(), m_vertexBoneData.size());
shaderProgram->setUniformValueArray("vertexBoneIndices", m_vertexBoneIndices.constData(), m_vertexBoneIndices.size());
if (m_texture)
{
m_texture->bind();
shaderProgram->setUniformValue("hasTexture", true);
}
else
{
shaderProgram->setUniformValue("hasTexture", false);
}
m_ibo.bind();
glDrawElements(GL_TRIANGLES, m_indices.size(), GL_UNSIGNED_INT, nullptr);
shaderProgram->disableAttributeArray("position");
shaderProgram->disableAttributeArray("normal");
shaderProgram->disableAttributeArray("texCoord");
if (m_texture)
{
m_texture->release();
}
}
private:
struct Vertex
{
QVector3D position;
QVector3D normal;
QVector2D texCoord;
};
QList<Vertex> m_vertices;
QList<unsigned int> m_indices;
QOpenGLBuffer m_vbo;
QOpenGLBuffer m_ibo;
QOpenGLTexture *m_texture = nullptr;
QList<QMatrix4x4> m_boneOffsets;
QList<QMatrix4x4> m_boneMatrices;
QList<QVector4D> m_vertexBoneData;
QList<QVector4D> m_vertexBoneIndices;
QMap<QString, QList<unsigned int>> m_boneMapping;
aiMesh *m_mesh = nullptr;
const aiScene *m_scene = nullptr;
friend class GLWidget;
};
void loadModel(const QString &filePath)
{
m_meshes.clear();
const aiScene *scene = m_assimpImporter.ReadFile(filePath.toStdString(), aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_LimitBoneWeights);
if (!scene)
{
return;
}
aiNode *rootNode = scene->mRootNode;
m_globalInverseTransform = QMatrix4x4(&scene->mRootNode->mTransformation.a1).inverted();
loadBones(rootNode, scene);
for (unsigned int i = 0; i < rootNode->mNumChildren; i++)
{
aiNode *node = rootNode->mChildren[i];
loadNodes(node, scene);
}
}
void loadNodes(aiNode *node, const aiScene *scene)
{
for (unsigned int i = 0; i < node->mNumMeshes; i++)
{
aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
m_meshes.append(Mesh(mesh, scene));
}
for (unsigned int i = 0; i < node->mNumChildren; i++)
{
aiNode *childNode = node->mChildren[i];
loadNodes(childNode, scene);
}
}
void loadBones(aiNode *node, const aiScene *scene)
{
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_boneMap.contains(boneName))
{
continue;
}
unsigned int index = m_boneCount++;
m_boneMap.insert(boneName, index);
}
}
for (unsigned int i = 0; i < node->mNumChildren; i++)
{
aiNode *childNode = node->mChildren[i];
loadBones(childNode, scene);
}
}
QOpenGLShaderProgram m_shaderProgram;
QTimer *m_timer = nullptr;
Assimp::Importer m_assimpImporter;
QList<Mesh> m_meshes;
QMatrix4x4 m_globalInverseTransform;
QMap<QString, unsigned int> m_boneMap;
unsigned int m_boneCount = 0;
float m_rotation = 0.0f;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GLWidget w;
w.resize(800, 600);
w.show();
return a.exec();
}
```
其中,`loadModel` 函数用于加载模型,`loadNodes` 函数用于递归加载每个节点的网格,`loadBones` 函数用于递归加载每个节点的骨骼及其权重。每个网格都被封装为 `Mesh` 类,它包含了该网格的顶点、索引、纹理以及骨骼信息,并且实现了 `draw` 函数用于绘制该网格。在绘制时,它会计算每个骨骼的变换矩阵,然后传递给着色器进行顶点动画。可以在着色器中使用顶点着色器的骨骼动画实现。
需要注意的是,这里使用了 Qt 的类库和信号槽机制,如果不熟悉 Qt 的使用方法,可以先学习一下。另外,需要在项目文件中添加以下依赖项:
```
QT += core gui opengl
LIBS += -lassimp
```
阅读全文