已经有model,mesh类qt opengl assimp 绘制骨骼动画的openglwidget的全部代码
时间: 2023-10-27 21:07:01 浏览: 241
基于canvas的骨骼动画的示例代码
以下是一个使用 Qt、OpenGL、Assimp 绘制骨骼动画的 OpenGLWidget 类的示例代码:
```cpp
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <QMatrix4x4>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
class OpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
explicit OpenGLWidget(QWidget *parent = nullptr);
~OpenGLWidget();
protected:
void initializeGL() override;
void paintGL() override;
private:
QOpenGLShaderProgram m_shaderProgram;
QOpenGLBuffer m_vertexBuffer, m_indexBuffer;
int m_numIndices = 0;
// 动画相关
QMatrix4x4 m_boneMatrices[100];
int m_numBones = 0;
float m_animationTime = 0.0f;
aiAnimation *m_animation = nullptr;
aiNode *m_rootNode = nullptr;
aiMatrix4x4 m_globalInverseTransform;
// 加载模型和动画
void loadModel(const QString &fileName);
void loadAnimation(const QString &fileName);
void processNode(aiNode *node, const aiScene *scene);
void processMesh(aiMesh *mesh, const aiScene *scene);
void updateBoneMatrices(float time, aiNode *node, const aiMatrix4x4 &parentTransform);
void updateAnimation(float deltaTime);
};
OpenGLWidget::OpenGLWidget(QWidget *parent)
: QOpenGLWidget(parent)
{
}
OpenGLWidget::~OpenGLWidget()
{
}
void OpenGLWidget::initializeGL()
{
initializeOpenGLFunctions();
// 设置背景颜色
glClearColor(0.2f, 0.3f, 0.4f, 1.0f);
// 编译着色器
m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vertexShader.glsl");
m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/fragmentShader.glsl");
m_shaderProgram.link();
// 加载模型和动画
loadModel(":/models/model.dae");
loadAnimation(":/animations/animation.dae");
// 启用深度测试
glEnable(GL_DEPTH_TEST);
}
void OpenGLWidget::paintGL()
{
// 清空颜色缓存和深度缓存
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 使用着色器
m_shaderProgram.bind();
// 设置投影矩阵
QMatrix4x4 projectionMatrix;
projectionMatrix.perspective(45.0f, float(width()) / float(height()), 0.1f, 100.0f);
m_shaderProgram.setUniformValue("projectionMatrix", projectionMatrix);
// 设置视图矩阵
QMatrix4x4 viewMatrix;
viewMatrix.lookAt(QVector3D(0.0f, 0.0f, 5.0f), QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0f, 1.0f, 0.0f));
m_shaderProgram.setUniformValue("viewMatrix", viewMatrix);
// 更新动画
updateAnimation(1.0f / 60.0f);
// 设置骨骼变换矩阵
m_shaderProgram.setUniformValueArray("boneMatrices", m_boneMatrices, m_numBones);
// 绘制模型
m_vertexBuffer.bind();
m_shaderProgram.setAttributeBuffer("position", GL_FLOAT, 0, 3, sizeof(float) * 8);
m_shaderProgram.enableAttributeArray("position");
m_shaderProgram.setAttributeBuffer("normal", GL_FLOAT, sizeof(float) * 3, 3, sizeof(float) * 8);
m_shaderProgram.enableAttributeArray("normal");
m_shaderProgram.setAttributeBuffer("texCoord", GL_FLOAT, sizeof(float) * 6, 2, sizeof(float) * 8);
m_shaderProgram.enableAttributeArray("texCoord");
m_indexBuffer.bind();
glDrawElements(GL_TRIANGLES, m_numIndices, GL_UNSIGNED_INT, nullptr);
}
void OpenGLWidget::loadModel(const QString &fileName)
{
// 导入模型
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(fileName.toStdString(), aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_FlipUVs);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
qWarning() << "Failed to load model:" << importer.GetErrorString();
return;
}
// 处理场景中的每个节点
m_rootNode = scene->mRootNode;
processNode(m_rootNode, scene);
// 初始化骨骼变换矩阵
for (int i = 0; i < 100; ++i) {
m_boneMatrices[i].setToIdentity();
}
}
void OpenGLWidget::loadAnimation(const QString &fileName)
{
// 导入动画
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(fileName.toStdString(), aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_FlipUVs);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mAnimations) {
qWarning() << "Failed to load animation:" << importer.GetErrorString();
return;
}
// 获取第一个动画
m_animation = scene->mAnimations[0];
// 获取动画中的骨骼数量和骨骼变换矩阵
m_numBones = m_animation->mNumChannels;
for (int i = 0; i < m_numBones; ++i) {
aiNodeAnim *nodeAnim = m_animation->mChannels[i];
QString nodeName = QString::fromStdString(nodeAnim->mNodeName.C_Str());
// 在场景中查找骨骼节点
aiNode *node = m_rootNode->FindNode(nodeAnim->mNodeName);
if (!node) {
qWarning() << "Failed to find bone node:" << nodeName;
continue;
}
// 计算骨骼变换矩阵
aiMatrix4x4 boneTransform = nodeAnim->mTransformation;
while (node->mParent) {
node = node->mParent;
boneTransform = node->mTransformation * boneTransform;
}
// 将骨骼变换矩阵转换为 QMatrix4x4
QMatrix4x4 boneMatrix;
boneMatrix.setColumn(0, QVector4D(boneTransform.a1, boneTransform.a2, boneTransform.a3, boneTransform.a4));
boneMatrix.setColumn(1, QVector4D(boneTransform.b1, boneTransform.b2, boneTransform.b3, boneTransform.b4));
boneMatrix.setColumn(2, QVector4D(boneTransform.c1, boneTransform.c2, boneTransform.c3, boneTransform.c4));
boneMatrix.setColumn(3, QVector4D(boneTransform.d1, boneTransform.d2, boneTransform.d3, boneTransform.d4));
m_boneMatrices[i] = boneMatrix;
}
// 获取全局逆变换矩阵
m_globalInverseTransform = scene->mRootNode->mTransformation;
m_globalInverseTransform.Inverse();
}
void OpenGLWidget::processNode(aiNode *node, const aiScene *scene)
{
// 处理节点中的每个网格
for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
processMesh(mesh, scene);
}
// 处理节点的子节点
for (unsigned int i = 0; i < node->mNumChildren; ++i) {
processNode(node->mChildren[i], scene);
}
}
void OpenGLWidget::processMesh(aiMesh *mesh, const aiScene *scene)
{
// 提取顶点、法线和纹理坐标
QVector<float> vertices;
QVector<unsigned int> indices;
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
vertices.append(mesh->mVertices[i].x);
vertices.append(mesh->mVertices[i].y);
vertices.append(mesh->mVertices[i].z);
vertices.append(mesh->mNormals[i].x);
vertices.append(mesh->mNormals[i].y);
vertices.append(mesh->mNormals[i].z);
if (mesh->mTextureCoords[0]) {
vertices.append(mesh->mTextureCoords[0][i].x);
vertices.append(mesh->mTextureCoords[0][i].y);
} else {
vertices.append(0.0f);
vertices.append(0.0f);
}
}
// 提取索引
for (unsigned int i = 0; i < mesh->mNumFaces; ++i) {
aiFace face = mesh->mFaces[i];
for (unsigned int j = 0; j < face.mNumIndices; ++j) {
indices.append(face.mIndices[j]);
}
}
// 创建顶点缓冲和索引缓冲
m_vertexBuffer.create();
m_vertexBuffer.bind();
m_vertexBuffer.allocate(vertices.constData(), vertices.size() * sizeof(float));
m_vertexBuffer.release();
m_indexBuffer.create();
m_indexBuffer.bind();
m_indexBuffer.allocate(indices.constData(), indices.size() * sizeof(unsigned int));
m_indexBuffer.release();
m_numIndices = indices.size();
}
void OpenGLWidget::updateBoneMatrices(float time, aiNode *node, const aiMatrix4x4 &parentTransform)
{
aiMatrix4x4 nodeTransform = node->mTransformation;
aiAnimation *animation = m_animation;
aiNodeAnim *nodeAnim = animation->mChannels[node->mUserData];
if (nodeAnim) {
// 计算骨骼变换矩阵
aiVector3D scaling;
aiQuaternion rotation;
aiVector3D position;
nodeAnim->mScalingKeys[0].mValue.Decompose(scaling, rotation, position);
aiMatrix4x4 scalingMatrix;
aiMatrix4x4::Scaling(scaling, scalingMatrix);
aiMatrix4x4 rotationMatrix = aiMatrix4x4(rotation.GetMatrix());
aiMatrix4x4 translationMatrix;
aiMatrix4x4::Translation(position, translationMatrix);
nodeTransform = translationMatrix * rotationMatrix * scalingMatrix;
}
aiMatrix4x4 globalTransform = parentTransform * nodeTransform;
if (node->mNumChildren > 0) {
for (unsigned int i = 0; i < node->mNumChildren; ++i) {
updateBoneMatrices(time, node->mChildren[i], globalTransform);
}
} else {
m_boneMatrices[node->mUserData] = QMatrix4x4(
globalTransform.a1, globalTransform.b1, globalTransform.c1, globalTransform.d1,
globalTransform.a2, globalTransform.b2, globalTransform.c2, globalTransform.d2,
globalTransform.a3, globalTransform.b3, globalTransform.c3, globalTransform.d3,
globalTransform.a4, globalTransform.b4, globalTransform.c4, globalTransform.d4
);
}
}
void OpenGLWidget::updateAnimation(float deltaTime)
{
m_animationTime += deltaTime;
while (m_animationTime >= m_animation->mDuration) {
m_animationTime -= m_animation->mDuration;
}
updateBoneMatrices(m_animationTime, m_rootNode, aiMatrix4x4());
}
```
其中,`loadModel()` 方法用于加载模型,`loadAnimation()` 方法用于加载动画,`processNode()` 和 `processMesh()` 方法用于处理场景中的节点和网格,`updateBoneMatrices()` 方法用于更新骨骼变换矩阵,`updateAnimation()` 方法用于更新动画。在 `paintGL()` 方法中,首先设置投影矩阵和视图矩阵,然后更新动画,最后使用骨骼变换矩阵绘制模型。其中,顶点着色器和片段着色器的代码可以根据需要自行编写,这里不再赘述。
阅读全文