Put the GIF effect at the beginning, and the bottom is my project Github warehouse
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
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