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.
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.
| 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.
| 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.
| // 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.
| 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
| // 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.
| // 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