Skip to content

Kuesa Picking QML Example

Demonstrates the use of the Kuesa API to import a glTF2 file and perform 3D mouse picking on objects from the scene.

picking-example.png

QtQuick and Qt3D integration

The picking example relies on the regular QtQuick and Qt 3D APIs to instantiate a QtQuick based application that combines Qt 3D based content with a 2D UI overlay.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import QtQuick 2.12
import QtQuick.Scene3D 2.12
import QtQuick.Controls.Material 2.12

Item {
    id: root

    Scene3D {
        id: scene3d
        anchors.fill: parent
        focus: true
        multisample: true
        aspects: ["render", "input", "logic", "animation"]

        MainScene {
            id: sceneEntity
            screenWidth: scene3d.width
            screenHeight: scene3d.height
        }
    }

Filename: picking/qml/main.qml

SceneEntity

Kuesa provides the [SceneEntity ] element which holds collections of Qt 3D assets accessible by name.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import Qt3D.Core 2.12
import Qt3D.Render 2.12
import Qt3D.Input 2.12
import Qt3D.Extras 2.12
import QtQuick 2.12

import Kuesa 2.0 as Kuesa
import Kuesa.Effects 2.0 as KuesaFX
import Kuesa.Utils 2.0 as KuesaUtils

Kuesa.SceneEntity {
    id: root3D

Filename: picking/qml/MainScene.qml

Importing a glTF2 File

In order to load a glTF2 file, Kuesa provides the [GLTF2Importer ] element. If the sceneEntity property is set to a valid [SceneEntity ] instance, Qt 3D assets generated while parsing the file will be automatically added to the various asset collections.

1
2
3
4
5
6
7
    Kuesa.GLTF2Importer {
        id: gltf2importer
        sceneEntity: root3D
        assignNames: true
        source: "file:" + _assetsDir + "models/picking/picking-scene.gltf"
        options.generateTangents: true
    }

Filename: picking/qml/MainScene.qml

Defining the FrameGraph and Camera

We use a [ForwardRenderer ] FrameGraph to render our scene.

1
2
3
4
5
6
7
            activeFrameGraph: Kuesa.ForwardRenderer {
                id: frameGraph
                camera: cameraAsset.node
                clearColor: Qt.rgba(0.1, 0.1, 0.1, 1.0)
                toneMappingAlgorithm: KuesaFX.ToneMappingAndGammaCorrectionEffect.Filmic
                skinning: true
            }

Filename: picking/qml/MainScene.qml

The camera used to view the scene is retrieved directly from the glTF scene using a [Asset ] element.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    // Camera
    Kuesa.Asset {
        id: cameraAsset
        name: root3D.cameras.names.length ? root3D.cameras.names[0] : ""
        collection: root3D.cameras
        // Set aspectRatio on Camera Asset to match that of our view
        // Kuesa.Asset takes care of forwarding custom properties whose name and type matches
        // properties on the reference asset node
        property real aspectRatio: root3D.aspectRatio
    }

Filename: picking/qml/MainScene.qml

To move the camera around with the mouse or keyboard, we have also defined a [OrbitCameraController ] element.

1
2
3
4
5
     KuesaUtils.OrbitCameraController {
        id: controller
        camera: frameGraph.camera
        windowSize: Qt.size(root3D.screenWidth, root3D.screenHeight)
    }

Filename: picking/qml/MainScene.qml

Pick objects in the Scene

To make things easier, we create an inline component named PickableEntity that we will be able to instantiate later on.

It extends [Asset ] and will allow us to retrieve a Qt3D from a name

1
2
3
4
    // Inline Component
    component PickableEntity : Kuesa.Asset {
        id: pickableEntity
        collection: root3D.entities

Filename: picking/qml/MainScene.qml

This will allow use to define a Qt3D ObjectPicker component and add it to the Entity's components once loaded.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
        readonly property ObjectPicker picker: ObjectPicker {
            objectName: "Picker" + name
            hoverEnabled: true
            onPressed: (pick) => { console.log("Pressed " + pickableEntity.name + " at worldPosition " + pick.worldIntersection) }
            onReleased: (pick) => { console.log("Released " + pickableEntity.name) }
        }

        // Add the ObjectPicker component on the Entity
        onNodeChanged: {
            if (pickableEntity.node)
                pickableEntity.node.components = pickableEntity.picker
        }

Filename: picking/qml/MainScene.qml

Next, we retrieve the Transform asset associated with our Entity. We create a binding that updates the scale property of the Transform when the ObjectPicker pressed property changes. To animate the transition between the scale values we make use of a QML Behavior.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
        // Retrieve Transform
        Kuesa.Asset {
            id: transform
            collection: root3D.transforms
            name: pickableEntity.name

            property real _scale: picker.pressed ? 1.5 : 1
            Behavior on _scale { NumberAnimation { duration: 500 }}

            // Create a binding to update the scale when the picker is pressed
            Binding { target: transform.node; property: "scale"; value: transform._scale }
        }

Filename: picking/qml/MainScene.qml

Similarly, we retrieve the Material asset associated with our Entity. We create a binding that updates the baseColroFactor property of the Material when the ObjectPicker is being hovered by the mouse.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
        // Retrieve Material
        Kuesa.Asset {
            id: material
            collection: root3D.materials
            name: pickableEntity.name + "Material"

            // Copy the original baseColorFactor so that we have a value to return to
            property color originalBaseColorFactor
            onNodeChanged: {
                if (material.node)
                    originalBaseColorFactor = material.node.baseColorFactor
            }

            // Create a binding to update the baseColorFactor when the picker is hovered
            Binding { target: material.node; property: "baseColorFactor"; value: picker.containsMouse ? "red" : material.originalBaseColorFactor }
        }
    }

Filename: picking/qml/MainScene.qml

At last, we can create several instances of our PickableEntity component that will retrieve different objects from the loaded glTF scene that we want to interact with.

1
2
3
4
5
    // Instantiate Pickable Entities
    readonly property PickableEntity cubeEntity: PickableEntity { name: "Cube"}
    readonly property PickableEntity sphereEntity:  PickableEntity { name: "Sphere"}
    readonly property PickableEntity torusEntity: PickableEntity { name: "Torus"}
    readonly property PickableEntity cylinderEntity: PickableEntity { name: "Cylinder"}

Filename: picking/qml/MainScene.qml


Updated on 2023-07-03 at 11:02:17 +0000