Qt quick3D combined with Aissmp Demo

Put the GIF effect at the beginning, and the bottom is my project Github warehouse
Please add picture description

First create a project named: AssimpQuick3d

I chose the mingw64-bit platform. If msvc is used, the corresponding library must be changed to windows type, .lib and .dll

Please add picture description
First create the corresponding file

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> & amp; vertices, const QList<quint32> & amp; 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 & amp;mesh() const {<!-- --> return _mesh; }
    void setMesh(const Mesh & amp; 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*> geometries READ geometries CONSTANT)
    QML_NAMED_ELEMENT(ViewManager)

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

    Q_INVOKABLE void loadFile();

    QList<Mesh> meshes() const {<!-- --> return _meshs; }
    QList<MyGeometry*> geometries() const {<!-- --> return _geometry; }

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

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

};

#endif // VIEWMANAGER_H


mygeometry.cpp

#include "mygeometry.h"

MyGeometry::MyGeometry()
{<!-- -->
// updatePro();
}

void MyGeometry::setMesh(const Mesh & amp;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);///Start traversing the scene
}

void ViewManager::processNode(aiNode *node, const aiScene *scene)
{<!-- -->
    // Process the node's own grid (if any)
    for(unsigned int i = 0; i < node->mNumMeshes; i ++ ){<!-- -->
        aiMesh *mesh = scene->mMeshes[node->mMeshes[i]]; ///A node has multiple meshes
        auto nodeMesh = processMesh(mesh);
        _meshs.push_back(nodeMesh);

        MyGeometry *geometry = new MyGeometry;
        geometry->setMesh(nodeMesh);
        _geometry.append(geometry);
    }
    // then repeat the process for its child nodes
    for(unsigned int i = 0; i < node->mNumChildren; i ++ ) {<!-- --> ///A node has multiple child nodes
        processNode(node->mChildren[i], scene);
    }
}

Mesh ViewManager::processMesh(aiMesh *mesh)
{<!-- -->
    QList<Vertex> vertices;
    QList<quint32> triangles;
    //Traverse the vertices, only save the position
    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 to store vertices

        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()));
    }
    // handle index
    for(unsigned int i = 0; i < mesh->mNumFaces; i ++ ) {<!-- --> ///index number mNumFaces
        aiFace face = mesh->mFaces[i];
        for(unsigned int j = 0; j < face.mNumIndices; j ++ ) {<!-- -->
            triangles.push_back(face.mIndices[j]);
        }
    }

    //Construct the mesh and use it as the return value
    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" } ]
    }
}

The last one is 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.geometry.length; i ++ ) {<!-- -->
                    var geo = manager. geometries[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
            }
        }
    }
}

Source code analysis:
The mesh.h file defines the structure required by the model, Mesh and Vertex, which are used to store the basic data of the model. The 3D model will be converted into a vertex list and an index list for display. Qt Quick3D, OpenGL, D3D And so on.

Then there is the mygeometry.h file, which defines the MyGeometry class, which inherits Qt’s QQuick3DGeometry class. The main function of this class is to wrap these vertex lists, index lists, materials, etc. into an item. The front-end Model references It can display the model, and the Model is equivalent to a parent container, which has functions such as matrix transformation, so that moving, rotating and scaling does not need to transform each vertex.

The last is the viewmanager.h file, which contains the ViewManager class. What this class does is to load external files. Here, assimp is used to load file data, and then encapsulate the data into meshes one by one, and then use these meshes Multiple MyGeometry objects are created and saved.

All the backend needs to do are these three things, load the file, read the mesh, and create a MyGeometry object with the mesh.

Next is the front-end source code analysis:
First look at Shape.qml, this is actually the WeirdShape.qml file of the official dymaiccreation, I made a slight change based on it, mainly to read the local mesh file, I changed it to the dynamically loaded MyGeometry object, and added a core function of the official picking example, model picking, if you want to see detailed examples, you can go to the qt source code folder to search for examples with these two names and open them.
The last front end is the main.qml file, which is also very simple.
A RoundButton button to control the imported model
A ViewManager object for controlling the backend files
There is also the View3D object, which is a must in quick3D, and there are several other must-have objects, cameras (PerspectiveCamera) and lights (DirectionalLight, PointLight), here I use OrbitCameraController, its main function is that the mouse can control the direction of the camera , mainly rotates and scales our camera around the parent node of the camera, and dynamically creates the Node of the model, whose id is defined as shapeSpawner, which is mainly responsible for dynamically creating our model and adding it to it.
Finally, add a MouseArea, anchors.fill the view3d object, the main function is to get the left mouse button, and then pick up the model
Ok, finally I put all the source code on my github repository.

Original post, please quote if necessary

You can add WeChat to study together
WeChat: xgxhxtaly

Warehouse link: original, let’s learn together