qt opengl assimp 加载骨骼动画完整代码
时间: 2023-07-09 12:03:21 浏览: 76
以下是使用 Qt、OpenGL 和 Assimp 加载骨骼动画的完整代码示例:
```c++
#include <QApplication>
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <iostream>
#include <vector>
struct Vertex {
QVector3D position;
QVector2D texCoord;
QVector3D normal;
QVector3D tangent;
QVector3D bitangent;
};
struct Texture {
GLuint id;
QString type;
aiString path;
};
struct Bone {
QString name;
int parentIndex;
QMatrix4x4 offset;
};
struct VertexBoneData {
QVector<int> IDs;
QVector<float> weights;
};
class Model {
public:
Model(const QString& path) {
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(path.toStdString(), aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace | aiProcess_JoinIdenticalVertices);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
std::cerr << "ERROR::ASSIMP::" << importer.GetErrorString() << std::endl;
return;
}
directory = path.left(path.lastIndexOf('/'));
processNode(scene->mRootNode, scene);
}
void draw(QOpenGLShaderProgram& shader) {
for (int i = 0; i < meshes.size(); ++i) {
meshes[i].draw(shader);
}
}
private:
void processNode(aiNode* node, const aiScene* scene) {
for (int i = 0; i < node->mNumMeshes; ++i) {
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
meshes.append(processMesh(mesh, scene));
}
for (int i = 0; i < node->mNumChildren; ++i) {
processNode(node->mChildren[i], scene);
}
}
QOpenGLTexture* loadTexture(const QString& path) {
QOpenGLTexture* texture = new QOpenGLTexture(QImage(directory + "/" + path).mirrored());
texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
texture->setMagnificationFilter(QOpenGLTexture::Linear);
texture->setWrapMode(QOpenGLTexture::Repeat);
return texture;
}
Mesh processMesh(aiMesh* mesh, const aiScene* scene) {
QVector<Vertex> vertices;
QVector<GLuint> indices;
QVector<Texture> textures;
QVector<VertexBoneData> boneData(mesh->mNumVertices);
for (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.texCoord = mesh->HasTextureCoords(0) ? QVector2D(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y) : QVector2D(0, 0);
vertex.normal = QVector3D(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z);
vertex.tangent = QVector3D(mesh->mTangents[i].x, mesh->mTangents[i].y, mesh->mTangents[i].z);
vertex.bitangent = QVector3D(mesh->mBitangents[i].x, mesh->mBitangents[i].y, mesh->mBitangents[i].z);
vertices.append(vertex);
}
for (int i = 0; i < mesh->mNumFaces; ++i) {
aiFace face = mesh->mFaces[i];
for (int j = 0; j < face.mNumIndices; ++j) {
indices.append(face.mIndices[j]);
}
}
if (mesh->mMaterialIndex >= 0) {
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
QVector<Texture> diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse");
textures.append(diffuseMaps);
QVector<Texture> specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");
textures.append(specularMaps);
QVector<Texture> normalMaps = loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal");
textures.append(normalMaps);
QVector<Texture> heightMaps = loadMaterialTextures(material, aiTextureType_AMBIENT, "texture_height");
textures.append(heightMaps);
}
QVector<Bone> bones = processBones(mesh);
return Mesh(vertices, indices, textures, bones, boneData);
}
QVector<Texture> loadMaterialTextures(aiMaterial* mat, aiTextureType type, const QString& typeName) {
QVector<Texture> textures;
for (int i = 0; i < mat->GetTextureCount(type); ++i) {
aiString str;
mat->GetTexture(type, i, &str);
bool skip = false;
for (int j = 0; j < loadedTextures.size(); ++j) {
if (std::strcmp(loadedTextures[j].path.C_Str(), str.C_Str()) == 0) {
textures.append(loadedTextures[j]);
skip = true;
break;
}
}
if (!skip) {
Texture texture;
texture.id = loadTexture(str.C_Str())->textureId();
texture.type = typeName;
texture.path = str;
textures.append(texture);
loadedTextures.append(texture);
}
}
return textures;
}
QVector<Bone> processBones(aiMesh* mesh) {
QVector<Bone> bones;
for (int i = 0; i < mesh->mNumBones; ++i) {
aiBone* bone = mesh->mBones[i];
Bone newBone;
newBone.name = bone->mName.C_Str();
newBone.parentIndex = -1;
newBone.offset = QMatrix4x4(&bone->mOffsetMatrix.a1);
for (int j = 0; j < nodes.size(); ++j) {
if (nodes[j].name == bone->mName.C_Str()) {
newBone.parentIndex = nodes[j].parentIndex;
break;
}
}
bones.append(newBone);
for (int j = 0; j < bone->mNumWeights; ++j) {
int vertexID = bone->mWeights[j].mVertexId;
float weight = bone->mWeights[j].mWeight;
for (int k = 0; k < boneData[vertexID].IDs.size(); ++k) {
if (weight > boneData[vertexID].weights[k]) {
boneData[vertexID].IDs.insert(k, i);
boneData[vertexID].weights.insert(k, weight);
break;
}
}
if (boneData[vertexID].IDs.size() < 4) {
boneData[vertexID].IDs.append(i);
boneData[vertexID].weights.append(weight);
}
}
}
return bones;
}
struct Node {
QString name;
int parentIndex;
};
class Mesh {
public:
Mesh(const QVector<Vertex>& vertices, const QVector<GLuint>& indices, const QVector<Texture>& textures, const QVector<Bone>& bones, const QVector<VertexBoneData>& boneData) {
this->textures = textures;
this->bones = bones;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), vertices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texCoord));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal));
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, tangent));
glEnableVertexAttribArray(4);
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, bitangent));
glGenBuffers(1, &boneBuffer);
glBindBuffer(GL_ARRAY_BUFFER, boneBuffer);
glBufferData(GL_ARRAY_BUFFER, boneData.size() * sizeof(VertexBoneData), boneData.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(5);
glVertexAttribIPointer(5, 4, GL_INT, sizeof(VertexBoneData), (void*)0);
glEnableVertexAttribArray(6);
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(VertexBoneData), (void*)(4 * sizeof(int)));
glBindVertexArray(0);
}
void draw(QOpenGLShaderProgram& shader) {
for (int i = 0; i < textures.size(); ++i) {
glActiveTexture(GL_TEXTURE0 + i);
shader.setUniformValue((textures[i].type + QString::number(i)).toStdString().c_str(), i);
glBindTexture(GL_TEXTURE_2D, textures[i].id);
}
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, indicesCount, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glActiveTexture(GL_TEXTURE0);
}
QVector<Texture> textures;
QVector<Bone> bones;
private:
GLuint VAO, VBO, EBO, boneBuffer;
int indicesCount;
};
QString directory;
QVector<Mesh> meshes;
QVector<Texture> loadedTextures;
QVector<Node> nodes;
};
class ViewerWidget : public QOpenGLWidget, protected QOpenGLFunctions {
public:
ViewerWidget(QWidget* parent = nullptr) : QOpenGLWidget(parent) {
setFocusPolicy(Qt::StrongFocus);
}
protected:
void initializeGL() override {
initializeOpenGLFunctions();
glEnable(GL_DEPTH_TEST);
shader.addShaderFromSourceFile(QOpenGLShader::Vertex, "vertex.glsl");
shader.addShaderFromSourceFile(QOpenGLShader::Fragment, "fragment.glsl");
shader.link();
model = new Model("path/to/model.fbx");
timer.start(16, this);
}
void resizeGL(int width, int height) override {
glViewport(0, 0, width, height);
}
void paintGL() override {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shader.bind();
QMatrix4x4 view;
view.translate(0, 0, -3);
QMatrix4x4 projection;
projection.perspective(45.0f, (float)width() / (float)height(), 0.1f, 100.0f);
shader.setUniformValue("view", view);
shader.setUniformValue("projection", projection);
model->draw(shader);
shader.release();
}
void timerEvent(QTimerEvent* event) override {
model->updateAnimation(timer.elapsed() / 1000.0f);
update();
}
private:
QOpenGLShaderProgram shader;
Model* model;
QElapsedTimer timer;
};
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
ViewerWidget viewerWidget;
viewerWidget.resize(640, 480);
viewerWidget.show();
return app.exec();
}
```
注意,这只是一个示例代码,可能需要根据具体情况进行调整和修改。
阅读全文