Qt quick3D与Aissmp结合使用Demo

1 篇文章 0 订阅
1 篇文章 0 订阅

开头先放GIF效果,最底下是我的项目Github仓
请添加图片描述

首先创建一个名为: AssimpQuick3d项目

在这里插入图片描述

我选择的是mingw64位平台,如果用的是msvc的话,对应库就要改成windows类型,.lib和.dll

请添加图片描述
首先创建相应文件

mesh.h

#ifndef MESH_H
#define MESH_H

#include "QObject"
#include <QVector3D>
#include <QList>

struct Vertex
{
    inline float x() const { return position.x(); }
    inline float y() const { return position.y(); }
    inline float z() const { return position.z(); }

    QVector3D position;
    QVector3D normal;
};

Q_DECLARE_METATYPE(Vertex)

struct Mesh
{
public:
    inline Mesh() {}
    Mesh(const QList<Vertex> &vertices, const QList<quint32> &indices, QVector3D min, QVector3D max)
        : vertices(vertices), indices(indices), boundsMin(min), boundsMax(max)
    { }

    QList<Vertex> vertices;
    QList<quint32> indices;
    QVector3D boundsMin;
    QVector3D boundsMax;

};

Q_DECLARE_METATYPE(Mesh)

#endif // MESH_H



mygeometry.h

#ifndef MYGEOMETRY_H
#define MYGEOMETRY_H

#include "mesh.h"
#include <QQuick3DGeometry>

class MyGeometry : public QQuick3DGeometry
{
    Q_OBJECT
    Q_PROPERTY(Mesh mesh READ mesh WRITE setMesh NOTIFY meshChanged)
    QML_NAMED_ELEMENT(MyGeometry)

public:
    explicit MyGeometry();

    inline const Mesh &mesh() const { return _mesh; }
    void setMesh(const Mesh &mesh);

signals:
    void meshChanged();

private:
    void updateData();
    void updatePro();

private:
    Mesh _mesh;
};


#endif // MYGEOMETRY_H


viewmanager.h

#ifndef VIEWMANAGER_H
#define VIEWMANAGER_H

#include <QQmlEngine>
#include <QObject>
#include <QList>
#include "mesh.h"
#include "mygeometry.h"

class aiNode;
class aiScene;
class aiMesh;

class ViewManager : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QList<Mesh> meshes READ meshes CONSTANT)
    Q_PROPERTY(QList<MyGeometry*> geometrys READ geometrys CONSTANT)
    QML_NAMED_ELEMENT(ViewManager)

public:
    explicit ViewManager(QObject *parent = nullptr);

    Q_INVOKABLE void loadFile();

    QList<Mesh> meshes() const { return _meshs; }
    QList<MyGeometry*> geometrys() const { return _geometrys; }

private:
    void processNode(aiNode *node, const aiScene *scene);
    Mesh processMesh(aiMesh *mesh);

private:
    QList<Mesh> _meshs;
    QList<MyGeometry*> _geometrys;

};

#endif // VIEWMANAGER_H


mygeometry.cpp

#include "mygeometry.h"

MyGeometry::MyGeometry()
{
//    updatePro();
}

void MyGeometry::setMesh(const Mesh &mesh)
{
    _mesh = mesh;
    updateData();
    update();
    emit meshChanged();
}

void MyGeometry::updateData()
{
    clear();

    int stride = sizeof(Vertex);

    QByteArray vertexData(_mesh.vertices.count() * stride, Qt::Initialization::Uninitialized);
    memcpy(vertexData.data(), _mesh.vertices.constData(), vertexData.size());
    setVertexData(vertexData);

    QByteArray indexData(_mesh.indices.count()*sizeof(quint32), Qt::Initialization::Uninitialized);
    memcpy(indexData.data(), _mesh.indices.constData(), indexData.size());
    setIndexData(indexData);

    setStride(stride);
    setBounds(_mesh.boundsMin, _mesh.boundsMax);

    setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
    addAttribute(Attribute::PositionSemantic, 0, Attribute::ComponentType::F32Type);
    addAttribute(Attribute::NormalSemantic, sizeof(QVector3D), Attribute::ComponentType::F32Type);
    addAttribute(Attribute::IndexSemantic, 0, Attribute::ComponentType::U32Type);
}

void MyGeometry::updatePro()
{
    clear();

    int stride = 3 * sizeof(float);
    QByteArray vertexData(3 * stride, Qt::Initialization::Uninitialized);
    float *p = reinterpret_cast<float *>(vertexData.data());
    *p++ = -1.0f; *p++ = -1.0f; *p++ = 0.0f;
    *p++ = 1.0f; *p++ = -1.0f; *p++ = 0.0f;

    *p++ = 0.0f; *p++ = 1.0f; *p++ = 0.0f;

    setVertexData(vertexData);
    setStride(stride);
    setBounds(QVector3D(-1.0f, -1.0f, 0.0f), QVector3D(+1.0f, +1.0f, 0.0f));

    setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
    addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0, QQuick3DGeometry::Attribute::F32Type);
}


viewmanager.cpp

#include "viewmanager.h"
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <QFileDialog>
#include <iostream>


ViewManager::ViewManager(QObject *parent)
    : QObject{parent}
{

}

void ViewManager::loadFile()
{
    Assimp::Importer import;
    std::string szOut;
    import.GetExtensionList(szOut);

    QString t_assimp=tr("ASSIMP (") +QString::fromStdString(szOut) +tr(")");
    QString all_filter;
    all_filter+=t_assimp;
    QString filename = QFileDialog::getOpenFileName(nullptr,tr("open file"),"./",all_filter);
    if(filename.isEmpty()) {
        return;
    }

    _meshs.clear();
    const aiScene *scene = import.ReadFile(filename.toStdString(), aiProcess_Triangulate | aiProcess_FlipUVs);
    if(!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
        std::cout << "ERROR::ASSIMP::" << import.GetErrorString()<<std::endl;
        return;
    }
    processNode(scene->mRootNode, scene);///开始遍历scene
}

void ViewManager::processNode(aiNode *node, const aiScene *scene)
{
    // 处理节点所有的网格(如果有的话)
    for(unsigned int i = 0; i < node->mNumMeshes; i++){
        aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];    ///一个节点有多个mesh
        auto nodeMesh = processMesh(mesh);
        _meshs.push_back(nodeMesh);

        MyGeometry *geometry = new MyGeometry;
        geometry->setMesh(nodeMesh);
        _geometrys.append(geometry);
    }
    // 接下来对它的子节点重复这一过程
    for(unsigned int i = 0; i < node->mNumChildren; i++) {    ///一个节点有多个子节点
        processNode(node->mChildren[i], scene);
    }
}

Mesh ViewManager::processMesh(aiMesh *mesh)
{
    QList<Vertex> vertices;
    QList<quint32> triangles;
    //遍历顶点,只存位置
    auto p = mesh->mVertices[0];
    QVector3D min, max;
    min = max = QVector3D(p.x, p.y, p.z);
    for(unsigned int i = 0; i < mesh->mNumVertices; i++) {
        Vertex ver;
        ver.position = QVector3D(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z);
        ver.normal = QVector3D(1, 1, 0);
        vertices.push_back(ver); ///把顶点存放的vector

        min.setX(std::min(min.x(), ver.position.x()));
        min.setY(std::min(min.y(), ver.position.y()));
        min.setZ(std::min(min.z(), ver.position.z()));
        max.setX(std::max(max.x(), ver.position.x()));
        max.setY(std::max(max.y(), ver.position.y()));
        max.setZ(std::max(max.z(), ver.position.z()));
    }
    // 处理索引
    for(unsigned int i = 0; i < mesh->mNumFaces; i++) {  ///索引数量mNumFaces
        aiFace face = mesh->mFaces[i];
        for(unsigned int j = 0; j < face.mNumIndices; j++) {
            triangles.push_back(face.mIndices[j]);
        }
    }

    //构建mesh并作为返回值
    return Mesh(vertices, triangles, min, max);
}


Shape.qml

import QtQuick
import QtQuick3D

Node {
    id: shape

    property var geometryData

    property real xRotation: Math.random() * (360 - (-360)) + -360
    property real yRotation: Math.random() * (360 - (-360)) + -360
    property real zRotation: Math.random() * (360 - (-360)) + -360
    property real hue: Math.random()

    property bool isPicked: false

    Model {
        id: model
        property bool isPicked: shape.isPicked
        scale: Qt.vector3d(100, 100, 100)
        eulerRotation.x: 90
        pickable: true
        geometry: geometryData
        onIsPickedChanged: hue = Math.random()
        SequentialAnimation on eulerRotation {
            loops: Animation.Infinite
            running: model.isPicked
            PropertyAnimation {
                duration: Math.random() * (10000 - 1000) + 1000
                to: Qt.vector3d(xRotation -  360, yRotation - 360, zRotation - 360)
                from: Qt.vector3d(xRotation, yRotation, zRotation)
            }
        }

        materials: [ DefaultMaterial { diffuseColor: model.isPicked ? Qt.hsva(hue, 1.0, 1.0, 1.0) : "red"  } ]
    }
}

最后的是main.qml

import QtQuick
import QtQuick.Controls
import QtQuick3D
import QtQuick3D.Helpers
import MyGeometryExample

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    RoundButton {
        z:1
        anchors.top: parent.top
        anchors.topMargin: 5
        anchors.horizontalCenter: parent.horizontalCenter
        text: "Import"
        font.bold: true
        palette.button: "slategrey"
        onClicked: shapeSpawner.addShape()
    }

    ViewManager {
        id: manager
    }

    color: "black"

    View3D {
        id: v3d
        anchors.fill: parent
        camera: camera

        Node {
            id: originNode
            PerspectiveCamera {
                id: camera
                position: Qt.vector3d(0, 0, 600)
            }
        }

        OrbitCameraController {
            anchors.fill: parent
            origin: originNode
            camera: camera
        }

        DirectionalLight {
            position: Qt.vector3d(-500, 500, -100)
            color: Qt.rgba(0.4, 0.2, 0.6, 1.0)
            ambientColor: Qt.rgba(0.1, 0.1, 0.1, 1.0)
        }

        PointLight {
            position: Qt.vector3d(0, 0, 100)
            color: Qt.rgba(0.1, 1.0, 0.1, 1.0)
            ambientColor: Qt.rgba(0.2, 0.2, 0.2, 1.0)
        }

        Node {
            id: shapeSpawner
            property var instances: []
            property int count

            function addShape() {
                manager.loadFile()
                for(var i = 0; i < manager.geometrys.length; i++) {
                    var geo = manager.geometrys[i]
                    var shapeComponent = Qt.createComponent("Shape.qml")
                    if(shapeComponent.status === Component.Ready) {
                        var props = {}
                        props["geometryData"] = geo
                        let instance = shapeComponent.createObject(shapeSpawner, props)
                        instances.push(instance)
                        count = instances.length
                    }
                }
            }
        }
    }

    MouseArea {
        anchors.fill: v3d
        onClicked: function(mouse) {
            var result = v3d.pick(mouse.x, mouse.y)
            if (result.objectHit) {
                var pickedObject = result.objectHit
                pickedObject.isPicked = !pickedObject.isPicked
            }
        }
    }
}

源码解析:
mesh.h文件中定义定义了模型所需要的结构体,Mesh和Vertex,用来存储模型的基础数据的,3D模型都是会转成顶点列表和索引列表来进行显示,Qt Quick3D、OpenGL、D3D等等都是如此。

然后是mygeometry.h文件,其中定义了MyGeometry类,它是继承了Qt的QQuick3DGeometry类,这个类主要作用是把这些顶点列表和索引列表、材质等等包裹起来变成一个item,前端的Model引用了它,就可以显示模型了,而Model就是相当于一个父级容器,它具有矩阵变换等等功能,这样移动旋转缩放就不用每个顶点去变换了。

最后是viewmanager.h文件,其中包含ViewManager类,这个类做的事情是把外部的文件加载进来,这里就用到了assimp加载文件数据,然后把数据封装到一个个的mesh中,然后根据这些mesh也创建了多个MyGeometry对象,并保存起来。

后端所要做的就这三件事,加载文件,读到mesh,用mesh创建MyGeometry对象。

接下来是前端源码解析:
首先看下Shape.qml,这个其实就是官方dymaiccreation的WeirdShape.qml文件,我在它的基础上进行了稍微的改动而来,主要它是读取本地的mesh文件,我改成了动态加载的MyGeometry对象,并在里面加上了官方picking示例的一个核心功能,模型拾取,如果大家想看详细示例可以到qt源码文件夹搜这两个名字的示例打开看。
最后前端就是main.qml文件,里面也很简单。
一个RoundButton按钮,用来控制导入模型
一个ViewManager对象,用于控制后端的文件
还有View3D对象,这是quick3D里面必须有的,还有几个其他必须有的对象,相机(PerspectiveCamera)和灯光(DirectionalLight、PointLight),这里我用到了OrbitCameraController,它主要作用是鼠标可以控制相机方向,主要围绕相机的父节点旋转缩放我们的相机,还有动态创建model的Node,id定义为shapeSpawner,它主要负责动态创建我们的模型,并添加到它这里。
最后的最后,加了一个MouseArea,anchors.fill了view3d这个对象,主要作用是获取鼠标左键,然后拾取模型
好了,最后我把源码都放到我的github仓库上了。

原创贴,需要请引用

一起学习可以加微信
微信: xgxhxtaly

仓库链接:原创 大家一起学习

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值