qt opengl assimp 加载骨骼动画代码
时间: 2023-07-09 21:03:22 浏览: 226
以下是一个简单的示例代码,展示了如何使用 Qt、OpenGL 和 Assimp 库来加载带有骨骼动画的 3D 模型:
```cpp
#include <QOpenGLWidget>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
class GLWidget : public QOpenGLWidget
{
public:
GLWidget(QWidget *parent = nullptr) : QOpenGLWidget(parent) {}
virtual ~GLWidget() {}
protected:
void initializeGL() override
{
initializeOpenGLFunctions();
// Load model
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile("model.dae", aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs | aiProcess_LimitBoneWeights);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
{
qDebug() << "Failed to load model:" << importer.GetErrorString();
return;
}
// Create bones
QVector<Bone> bones;
createBones(scene->mRootNode, scene, bones);
// Create meshes
QVector<Mesh> meshes;
createMeshes(scene, bones, meshes);
// Store data in OpenGL buffers
for (const Mesh &mesh : meshes)
{
mesh.createBuffers();
}
}
void resizeGL(int w, int h) override
{
glViewport(0, 0, w, h);
}
void paintGL() override
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Render meshes
for (const Mesh &mesh : meshes)
{
mesh.render();
}
}
private:
struct Bone
{
QString name;
QMatrix4x4 offset;
QMatrix4x4 transform;
int parentIndex;
QVector<int> childrenIndices;
};
struct Vertex
{
QVector3D position;
QVector3D normal;
QVector2D texCoord;
QVector4D boneWeights;
QVector4D boneIndices;
};
struct Mesh
{
QVector<Vertex> vertices;
QVector<GLuint> indices;
QVector<QMatrix4x4> boneTransforms;
QOpenGLBuffer vertexBuffer;
QOpenGLBuffer indexBuffer;
QOpenGLVertexArrayObject vertexArrayObject;
void createBuffers()
{
vertexBuffer.create();
vertexBuffer.bind();
vertexBuffer.allocate(vertices.constData(), vertices.size() * sizeof(Vertex));
indexBuffer.create();
indexBuffer.bind();
indexBuffer.allocate(indices.constData(), indices.size() * sizeof(GLuint));
vertexArrayObject.create();
vertexArrayObject.bind();
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(0));
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, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, boneWeights)));
glEnableVertexAttribArray(4);
glVertexAttribIPointer(4, 4, GL_UNSIGNED_INT, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, boneIndices)));
glBindVertexArray(0);
}
void render()
{
vertexArrayObject.bind();
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, nullptr);
vertexArrayObject.release();
}
};
void createBones(aiNode *node, const aiScene *scene, QVector<Bone> &bones, int parentIndex = -1)
{
Bone bone;
bone.name = QString::fromUtf8(node->mName.C_Str());
bone.offset = QMatrix4x4(reinterpret_cast<float*>(&node->mTransformation.Transpose()));
bone.parentIndex = parentIndex;
// Add bone to list
int boneIndex = bones.size();
bones.append(bone);
// Process node's children recursively
for (unsigned int i = 0; i < node->mNumChildren; ++i)
{
int childIndex = bones.size();
bones[parentIndex].childrenIndices.append(childIndex);
createBones(node->mChildren[i], scene, bones, boneIndex);
}
}
void createMeshes(const aiScene *scene, const QVector<Bone> &bones, QVector<Mesh> &meshes)
{
// Create bone transforms for each animation
QVector<QVector<QMatrix4x4>> boneTransformsList;
for (unsigned int i = 0; i < scene->mNumAnimations; ++i)
{
const aiAnimation *animation = scene->mAnimations[i];
QVector<QMatrix4x4> boneTransforms(animation->mNumChannels * animation->mDuration + 1);
for (unsigned int j = 0; j < animation->mNumChannels; ++j)
{
const aiNodeAnim *nodeAnim = animation->mChannels[j];
int boneIndex = findBoneIndex(nodeAnim->mNodeName.C_Str(), bones);
if (boneIndex >= 0)
{
// Create bone transforms for each keyframe
for (unsigned int k = 0; k < nodeAnim->mNumPositionKeys; ++k)
{
float time = nodeAnim->mPositionKeys[k].mTime;
QMatrix4x4 transform;
transform.translate(QVector3D(nodeAnim->mPositionKeys[k].mValue.x,
nodeAnim->mPositionKeys[k].mValue.y,
nodeAnim->mPositionKeys[k].mValue.z));
transform *= QMatrix4x4(reinterpret_cast<const float*>(&nodeAnim->mRotationKeys[k].mValue.GetMatrix().Transpose()));
transform.scale(QVector3D(nodeAnim->mScalingKeys[k].mValue.x,
nodeAnim->mScalingKeys[k].mValue.y,
nodeAnim->mScalingKeys[k].mValue.z));
boneTransforms[j * animation->mDuration + time] = bones[boneIndex].offset * transform * bones[boneIndex].transform;
}
}
}
boneTransformsList.append(boneTransforms);
}
// Create meshes
for (unsigned int i = 0; i < scene->mNumMeshes; ++i)
{
const aiMesh *aiMesh = scene->mMeshes[i];
Mesh mesh;
// Create vertices
for (unsigned int j = 0; j < aiMesh->mNumVertices; ++j)
{
Vertex vertex;
vertex.position = QVector3D(aiMesh->mVertices[j].x, aiMesh->mVertices[j].y, aiMesh->mVertices[j].z);
vertex.normal = QVector3D(aiMesh->mNormals[j].x, aiMesh->mNormals[j].y, aiMesh->mNormals[j].z);
if (aiMesh->HasTextureCoords(0))
{
vertex.texCoord = QVector2D(aiMesh->mTextureCoords[0][j].x, aiMesh->mTextureCoords[0][j].y);
}
else
{
vertex.texCoord = QVector2D(0.0f, 0.0f);
}
// Find bone weights and indices
vertex.boneWeights = QVector4D(0.0f, 0.0f, 0.0f, 0.0f);
vertex.boneIndices = QVector4D(0, 0, 0, 0);
if (aiMesh->HasBones())
{
for (unsigned int k = 0; k < aiMesh->mNumBones; ++k)
{
const aiBone *bone = aiMesh->mBones[k];
int boneIndex = findBoneIndex(bone->mName.C_Str(), bones);
if (boneIndex >= 0)
{
for (unsigned int l = 0; l < bone->mNumWeights; ++l)
{
if (bone->mWeights[l].mVertexId == j)
{
if (vertex.boneWeights.x() == 0.0f)
{
vertex.boneWeights.setX(bone->mWeights[l].mWeight);
vertex.boneIndices.setX(boneIndex);
}
else if (vertex.boneWeights.y() == 0.0f)
{
vertex.boneWeights.setY(bone->mWeights[l].mWeight);
vertex.boneIndices.setY(boneIndex);
}
else if (vertex.boneWeights.z() == 0.0f)
{
vertex.boneWeights.setZ(bone->mWeights[l].mWeight);
vertex.boneIndices.setZ(boneIndex);
}
else if (vertex.boneWeights.w() == 0.0f)
{
vertex.boneWeights.setW(bone->mWeights[l].mWeight);
vertex.boneIndices.setW(boneIndex);
}
}
}
}
}
}
mesh.vertices.append(vertex);
}
// Create indices
for (unsigned int j = 0; j < aiMesh->mNumFaces; ++j)
{
const aiFace &face = aiMesh->mFaces[j];
for (unsigned int k = 0; k < face.mNumIndices; ++k)
{
mesh.indices.append(face.mIndices[k]);
}
}
// Create bone transforms
mesh.boneTransforms.fill(QMatrix4x4(), boneTransformsList.size());
for (unsigned int j = 0; j < aiMesh->mNumBones; ++j)
{
const aiBone *bone = aiMesh->mBones[j];
int boneIndex = findBoneIndex(bone->mName.C_Str(), bones);
if (boneIndex >= 0)
{
mesh.boneTransforms.fill(bones[boneIndex].offset * bones[boneIndex].transform, boneTransformsList.size());
}
}
meshes.append(mesh);
}
}
int findBoneIndex(const QString &name, const QVector<Bone> &bones)
{
for (int i = 0; i < bones.size(); ++i)
{
if (bones[i].name == name)
{
return i;
}
}
return -1;
}
QVector<Mesh> meshes;
};
```
该代码使用 `QOpenGLWidget` 类来创建 OpenGL 上下文,并使用 Assimp 库加载 3D 模型。在加载模型时,它会遍历模型的骨骼层次结构,创建 `Bone` 对象并将其存储在列表中。然后,它会遍历模型的所有网格,为每个顶点计算骨骼权重和索引,并创建 `Mesh` 对象,其中包含该网格的所有顶点和索引。它还为每个动画序列创建骨骼变换矩阵,并将它们存储在列表中。最后,它将所有数据存储在 OpenGL 缓冲区中,并使用 OpenGL 渲染场景。注意,这只是一个简单的示例代码,实际的应用程序可能需要更复杂的模型和动画数据结构。
阅读全文