qt opengl assimp 加载骨骼动画完整代码
时间: 2023-07-09 15:03:21 浏览: 212
骨骼动画演示程序
5星 · 资源好评率100%
以下是基于Qt和OpenGL的Assimp骨骼动画加载的完整代码示例:
```c++
#include <iostream>
#include <vector>
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QTimer>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
using namespace std;
struct Vertex {
QVector3D position;
QVector3D normal;
QVector2D texCoord;
QVector4D boneIds;
QVector4D boneWeights;
};
class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions {
public:
GLWidget(QWidget* parent = nullptr) : QOpenGLWidget(parent) {}
protected:
void initializeGL() override {
initializeOpenGLFunctions();
glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
glEnable(GL_DEPTH_TEST);
// Load shaders
m_program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shader.vert");
m_program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shader.frag");
m_program.link();
// Load model
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(":/model.dae", aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_LimitBoneWeights);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
cout << "Error: " << importer.GetErrorString() << endl;
return;
}
// Initialize bones
m_boneCount = 0;
for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
m_boneCount += scene->mMeshes[i]->mNumBones;
}
m_boneTransforms.resize(m_boneCount);
// Load meshes
for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
const aiMesh* mesh = scene->mMeshes[i];
// Load vertices
vector<Vertex> vertices(mesh->mNumVertices);
for (unsigned int j = 0; j < mesh->mNumVertices; ++j) {
Vertex& vertex = vertices[j];
vertex.position.setX(mesh->mVertices[j].x);
vertex.position.setY(mesh->mVertices[j].y);
vertex.position.setZ(mesh->mVertices[j].z);
if (mesh->HasNormals()) {
vertex.normal.setX(mesh->mNormals[j].x);
vertex.normal.setY(mesh->mNormals[j].y);
vertex.normal.setZ(mesh->mNormals[j].z);
}
if (mesh->HasTextureCoords(0)) {
vertex.texCoord.setX(mesh->mTextureCoords[0][j].x);
vertex.texCoord.setY(mesh->mTextureCoords[0][j].y);
}
vertex.boneIds = QVector4D(-1, -1, -1, -1);
vertex.boneWeights = QVector4D(0.0f, 0.0f, 0.0f, 0.0f);
}
// Load indices
vector<unsigned int> indices;
for (unsigned int j = 0; j < mesh->mNumFaces; ++j) {
const aiFace& face = mesh->mFaces[j];
for (unsigned int k = 0; k < face.mNumIndices; ++k) {
indices.push_back(face.mIndices[k]);
}
}
// Load bones
for (unsigned int j = 0; j < mesh->mNumBones; ++j) {
const aiBone* bone = mesh->mBones[j];
int boneIndex = m_boneMapping[bone->mName.C_Str()];
for (unsigned int k = 0; k < bone->mNumWeights; ++k) {
int vertexIndex = bone->mWeights[k].mVertexId;
float weight = bone->mWeights[k].mWeight;
for (int l = 0; l < 4; ++l) {
if (vertices[vertexIndex].boneWeights[l] == 0.0f) {
vertices[vertexIndex].boneIds[l] = boneIndex;
vertices[vertexIndex].boneWeights[l] = weight;
break;
}
}
}
}
// Create buffers
QOpenGLBuffer vertexBuffer(QOpenGLBuffer::VertexBuffer);
vertexBuffer.create();
vertexBuffer.bind();
vertexBuffer.allocate(vertices.data(), vertices.size() * sizeof(Vertex));
QOpenGLBuffer indexBuffer(QOpenGLBuffer::IndexBuffer);
indexBuffer.create();
indexBuffer.bind();
indexBuffer.allocate(indices.data(), indices.size() * sizeof(unsigned int));
// Create VAO
QOpenGLVertexArrayObject vao;
vao.create();
vao.bind();
vertexBuffer.bind();
m_program.enableAttributeArray("position");
m_program.setAttributeBuffer("position", GL_FLOAT, offsetof(Vertex, position), 3);
vertexBuffer.bind();
m_program.enableAttributeArray("normal");
m_program.setAttributeBuffer("normal", GL_FLOAT, offsetof(Vertex, normal), 3);
vertexBuffer.bind();
m_program.enableAttributeArray("texCoord");
m_program.setAttributeBuffer("texCoord", GL_FLOAT, offsetof(Vertex, texCoord), 2);
vertexBuffer.bind();
m_program.enableAttributeArray("boneIds");
m_program.setAttributeBuffer("boneIds", GL_FLOAT, offsetof(Vertex, boneIds), 4, sizeof(Vertex));
vertexBuffer.bind();
m_program.enableAttributeArray("boneWeights");
m_program.setAttributeBuffer("boneWeights", GL_FLOAT, offsetof(Vertex, boneWeights), 4, sizeof(Vertex));
indexBuffer.bind();
vao.release();
m_vaos.push_back(vao);
m_indexCounts.push_back(indices.size());
}
// Load bones
vector<aiNode*> boneNodes(m_boneCount);
loadBones(scene->mRootNode, boneNodes);
// Start timer
QTimer* timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(16);
}
void paintGL() override {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_program.bind();
// Set camera
m_program.setUniformValue("viewMatrix", m_camera.toMatrix());
m_program.setUniformValue("projectionMatrix", m_projectionMatrix);
// Set bones
for (unsigned int i = 0; i < m_boneCount; ++i) {
m_program.setUniformValue("boneTransforms[" + QString::number(i) + "]", m_boneTransforms[i]);
}
// Draw meshes
for (unsigned int i = 0; i < m_vaos.size(); ++i) {
m_vaos[i].bind();
glDrawElements(GL_TRIANGLES, m_indexCounts[i], GL_UNSIGNED_INT, 0);
m_vaos[i].release();
}
m_program.release();
}
void resizeGL(int w, int h) override {
glViewport(0, 0, w, h);
m_projectionMatrix.setToIdentity();
m_projectionMatrix.perspective(60.0f, float(w) / float(h), 0.1f, 100.0f);
}
void mousePressEvent(QMouseEvent* event) override {
m_lastPos = event->pos();
}
void mouseMoveEvent(QMouseEvent* event) override {
int deltaX = event->x() - m_lastPos.x();
int deltaY = event->y() - m_lastPos.y();
if (event->buttons() & Qt::LeftButton) {
m_camera.rotate(deltaX, deltaY);
} else if (event->buttons() & Qt::RightButton) {
m_camera.pan(deltaX, deltaY);
}
m_lastPos = event->pos();
}
void keyPressEvent(QKeyEvent* event) override {
if (event->key() == Qt::Key_W) {
m_camera.moveForward();
} else if (event->key() == Qt::Key_S) {
m_camera.moveBackward();
} else if (event->key() == Qt::Key_A) {
m_camera.moveLeft();
} else if (event->key() == Qt::Key_D) {
m_camera.moveRight();
}
}
private:
QOpenGLShaderProgram m_program;
vector<QOpenGLVertexArrayObject> m_vaos;
vector<unsigned int> m_indexCounts;
QVector<QMatrix4x4> m_boneTransforms;
int m_boneCount;
unordered_map<string, int> m_boneMapping;
QMatrix4x4 m_projectionMatrix;
QPoint m_lastPos;
Camera m_camera;
void loadBones(aiNode* node, vector<aiNode*>& boneNodes) {
for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
const aiMesh* mesh = node->mMeshes[i];
for (unsigned int j = 0; j < mesh->mNumBones; ++j) {
const aiBone* bone = mesh->mBones[j];
string boneName = bone->mName.C_Str();
if (m_boneMapping.find(boneName) == m_boneMapping.end()) {
int boneIndex = m_boneMapping.size();
m_boneMapping[boneName] = boneIndex;
boneNodes[boneIndex] = node;
m_boneTransforms[boneIndex] = QMatrix4x4(reinterpret_cast<const float*>(&bone->mOffsetMatrix));
}
}
}
for (unsigned int i = 0; i < node->mNumChildren; ++i) {
loadBones(node->mChildren[i], boneNodes);
}
}
};
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
GLWidget glWidget;
glWidget.show();
return app.exec();
}
```
该代码使用Assimp库加载骨骼动画模型,并通过OpenGL渲染。其中,`Vertex`结构体定义了顶点的属性,包括位置、法线、纹理坐标、骨骼ID和权重;`GLWidget`类是OpenGL窗口,使用了Qt的信号槽机制更新画面、处理键鼠事件等;`loadBones`函数递归地遍历场景的节点树,加载骨骼节点并计算骨骼变换矩阵,将其传递给着色器的uniform变量;`paintGL`函数绑定着色器、设置相机和骨骼变换矩阵、绑定VAO和IBO并绘制三角形。
阅读全文