Skip to content

Kuesa Car-Scene QML Example

Demonstrates the use of the Kuesa API to import a glTF2 file and extract assets from it.

car-scene-example.jpg

QtQuick and Qt3D integration

The car-scene 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
21
22
23
24
25
26
27
28
Item {
    id: mainRoot

    // 3D Content

    Material.theme: Material.Dark
    Material.accent: Material.Blue

    Item {
        id: baseUI
        anchors.fill: parent

        Wind {
            id: wind
            anchors.fill: parent
            visible: menu.useOpacityMask
        }

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

            // Root Scene Entity
            MainScene {
                id: sceneContent

Filename: car-scene/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
import Kuesa 2.0 as Kuesa
import Kuesa.Effects 2.0 as Effects


Kuesa.SceneEntity {
    id: scene

Filename: car-scene/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
    // Loads GLTF 2.0 asset
    Kuesa.GLTF2Importer {
        sceneEntity: scene
        source: "file:" + _assetsDir + "/models/car/DodgeViper" + _modelSuffix + ".gltf"
    }

Filename: car-scene/qml/MainScene.qml

Extracting Assets from Collections

Usually, you will want to interact with some elements of your scene. The [Asset ] element allows to retrieve an asset by name. If the asset for a given name really exist in the specified collection, the node property allows you to access the underlying asset instance.

You can use the Kuesa Studio gltfInspector to introspect a glTF2 scene files and find the names of the various assets it contains.

For instance you might want to select a Camera by name.

1
2
3
4
5
    Kuesa.Asset {
        id: sweepCam
        collection: scene.cameras
        name: "SweepCam"
    }

Filename: car-scene/qml/MainScene.qml

Once retrieved, you can create binding on some of the Camera properties.

1
2
3
4
5
    QQ2.Binding {
        target: sweepCam.node
        property: "aspectRatio"
        value: mainCamera.aspectRatio
    }

Filename: car-scene/qml/MainScene.qml

Similarly you could retrieve a Material asset

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
    // We create a baseColorFactor property. If the property exists in the node, it will be a proxy of the node property
    // When we set the qml property, the node property will be updated accordingly
    // When the node property is updated in C++, the qml property will be updated
    // As soon as the node is changed, the qml property is updated if it has a C++ equivalent
    Kuesa.Asset {
        property color baseColorFactor: scene.carBaseColorFactor
        id: carMaterial
        collection: scene.materials
        name: "Mat_CarPaint"
        onBaseColorFactorChanged: console.log("Binding 1 works. Color: " + baseColorFactor)
        onNodeChanged: scene.carBaseColorFactor = carMaterial.baseColorFactor
    }

Filename: car-scene/qml/MainScene.qml

And create a binding on its properties to control the appearance of elements in the scene that use that material.

1
2
    property var baseColorFactorProp: carMaterial.baseColorFactor
    onBaseColorFactorPropChanged: console.log("Binding 2 works. Color: " + baseColorFactorProp)

Filename: car-scene/qml/MainScene.qml

Playing Animations

The [AnimationPlayer ] allows to control the playback of animation clips that can be retrieve by name.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
    Kuesa.AnimationPlayer {
        id: hoodAnimator
        sceneEntity: scene
        clock: Clock { }
        clip: "HoodAction"
    }

    Kuesa.AnimationPlayer {
        id: leftDoorAnimator
        sceneEntity: scene
        clock: Clock { }
        clip: "DoorLAction"
    }

    Kuesa.AnimationPlayer {
        id: rightDoorAnimator
        sceneEntity: scene
        clock: Clock { }
        clip: "DoorRAction"
    }

Filename: car-scene/qml/MainScene.qml

Adding Post Processing Effects

First you need to instantiate the effects you want to use.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    Effects.OpacityMask {
        id: opacityMaskEffect
        mask: TextureLoader {
            source: "qrc:/opacity_mask.png";
            generateMipMaps: false
        }
        premultipliedAlpha: true // This is what Scene3D/QtQuick expects
    }

    Effects.BloomEffect {
        id: bloomFx
        threshold: 0.34
        blurPassCount: 2
    }

Filename: car-scene/qml/MainScene.qml

Once you have created the effects, they need to be added to the [ForwardRenderer ] FrameGraph. The order in which they are added dictates the order in which they are applied.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
        RenderSettings {
            // FrameGraph
            activeFrameGraph: Kuesa.ForwardRenderer {
                id: frameGraph
                camera: scene.animated && sweepCam.node ? sweepCam.node : mainCamera
                postProcessingEffects: {
                    var effects = []
                    if (useBloomEffect)
                        effects.push(bloomFx)
                    if (useOpacityMask)
                        effects.push(opacityMaskEffect)
                    return effects
                }
                skinning: showSkybox
                backToFrontSorting: true
                toneMappingAlgorithm: Effects.ToneMappingAndGammaCorrectionEffect.Reinhard
//                showDebugOverlay: true
            }
        },

Filename: car-scene/qml/MainScene.qml


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