qt opengl assimp 加载骨骼动画完整代码
时间: 2023-07-09 16:03:19 浏览: 268
以下是一个简单的 Qt OpenGL Assimp 加载骨骼动画的代码示例:
```cpp
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#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) {}
~GLWidget() {}
protected:
void initializeGL() override
{
initializeOpenGLFunctions();
// Load the model with Assimp
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)
{
qDebug() << "Failed to load model:" << importer.GetErrorString();
return;
}
// Find the first mesh in the scene with bones
const aiMesh *mesh = nullptr;
for (unsigned int i = 0; i < scene->mNumMeshes; ++i)
{
const aiMesh *m = scene->mMeshes[i];
if (m->HasBones())
{
mesh = m;
break;
}
}
if (!mesh)
{
qDebug() << "Model has no meshes with bones.";
return;
}
// Create the VBO with the vertex data
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, mesh->mNumVertices * sizeof(Vertex), mesh->mVertices, GL_STATIC_DRAW);
// Create the EBO with the indices
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh->mNumFaces * sizeof(Face), mesh->mFaces, GL_STATIC_DRAW);
// Create the VAO and configure the vertex attributes
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
// Load the bone data into the VBO
std::vector<BoneData> boneData(mesh->mNumVertices);
for (unsigned int i = 0; i < mesh->mNumBones; ++i)
{
const aiBone *bone = mesh->mBones[i];
int boneIndex = 0;
std::string boneName(bone->mName.C_Str());
if (boneMap.find(boneName) == boneMap.end())
{
// Create a new bone if it doesn't exist yet
boneIndex = numBones++;
boneMap[boneName] = boneIndex;
BoneInfo info;
boneInfo.push_back(info);
}
else
{
boneIndex = boneMap[boneName];
}
for (unsigned int j = 0; j < bone->mNumWeights; ++j)
{
unsigned int vertexIndex = bone->mWeights[j].mVertexId;
float weight = bone->mWeights[j].mWeight;
for (unsigned int k = 0; k < 4; ++k)
{
if (boneData[vertexIndex].weights[k] == 0.0)
{
boneData[vertexIndex].indices[k] = boneIndex;
boneData[vertexIndex].weights[k] = weight;
break;
}
}
}
}
glBufferSubData(GL_ARRAY_BUFFER, mesh->mNumVertices * sizeof(Vertex), mesh->mNumVertices * sizeof(BoneData), boneData.data());
glVertexAttribIPointer(1, 4, GL_INT, sizeof(BoneData), (void*)(mesh->mNumVertices * sizeof(Vertex)));
glEnableVertexAttribArray(1);
// Load the bone hierarchy data
readNodeHierarchy(scene->mRootNode, QMatrix4x4());
// Load the texture image with Qt
QImage image("texture.png");
if (image.isNull())
{
qDebug() << "Failed to load texture image.";
return;
}
texture = new QOpenGLTexture(image.mirrored());
texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
texture->setMagnificationFilter(QOpenGLTexture::Linear);
// Enable depth testing and face culling
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
// Use a fixed camera position
modelMatrix.setToIdentity();
viewMatrix.setToIdentity();
viewMatrix.translate(0.0, 0.0, -5.0);
projectionMatrix.setToIdentity();
projectionMatrix.perspective(45.0f, (float)width() / (float)height(), 0.1f, 100.0f);
}
void paintGL() override
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0.0, 0.0, 0.0, 1.0);
// Set the shader program and uniforms
shaderProgram.bind();
shaderProgram.setUniformValue("modelMatrix", modelMatrix);
shaderProgram.setUniformValue("viewMatrix", viewMatrix);
shaderProgram.setUniformValue("projectionMatrix", projectionMatrix);
shaderProgram.setUniformValue("texture", 0);
// Render the mesh
renderNode(scene->mRootNode);
shaderProgram.release();
}
private:
struct Vertex
{
QVector3D position;
QVector2D texCoord;
};
struct Face
{
unsigned int indices[3];
};
struct BoneData
{
int indices[4];
float weights[4];
};
struct BoneInfo
{
QMatrix4x4 offset;
QMatrix4x4 finalTransform;
};
void readNodeHierarchy(const aiNode *node, const QMatrix4x4 &parentTransform)
{
QMatrix4x4 nodeTransform;
memcpy(nodeTransform.data(), &node->mTransformation.a1, 16 * sizeof(float));
QMatrix4x4 globalTransform = parentTransform * nodeTransform;
if (boneMap.find(node->mName.C_Str()) != boneMap.end())
{
int boneIndex = boneMap[node->mName.C_Str()];
boneInfo[boneIndex].offset = globalTransform * boneInfo[boneIndex].offset;
}
for (unsigned int i = 0; i < node->mNumChildren; ++i)
{
readNodeHierarchy(node->mChildren[i], globalTransform);
}
}
void renderNode(const aiNode *node)
{
QMatrix4x4 nodeTransform;
memcpy(nodeTransform.data(), &node->mTransformation.a1, 16 * sizeof(float));
QMatrix4x4 globalTransform = modelMatrix * nodeTransform;
for (unsigned int i = 0; i < node->mNumMeshes; ++i)
{
const aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
if (mesh->HasBones())
{
shaderProgram.setUniformValueArray("boneTransforms", boneInfo[0].finalTransform.constData(), numBones);
}
texture->bind();
glDrawElements(GL_TRIANGLES, mesh->mNumFaces * 3, GL_UNSIGNED_INT, nullptr);
}
for (unsigned int i = 0; i < node->mNumChildren; ++i)
{
renderNode(node->mChildren[i]);
}
}
QOpenGLShaderProgram shaderProgram;
unsigned int vao, vbo, ebo;
int numBones = 0;
std::map<std::string, int> boneMap;
std::vector<BoneInfo> boneInfo;
QOpenGLTexture *texture = nullptr;
QMatrix4x4 modelMatrix, viewMatrix, projectionMatrix;
const aiScene *scene = nullptr;
};
```
注意:该代码示例仅用于说明如何加载骨骼动画,实际使用中还需要添加相应的着色器程序和一些错误处理。
阅读全文