Qt Quick 3D - Virtual Assistant Example

Qt Quick application that presents a 3D model of a virtual assistant with dynamic animations created using QML and timelines.

Virtual Assistant Example demonstrates how to bring a 3D model of a virtual assistant to life through timeline animations, increasing user engagement and interaction. The example can be run and edited in both QtDesignStudio and QtCreator.

Importing 3D Model

To load the model in QtDesingStudio it is enough to import the .gltf file to the project. QtDesignStudio automatically create a qml file that represents the object and it wil also generate necessary assets. Without QtDesignStudio you need to use balsam tool, and run it manually. In this example the generated qml file was modified to introduce the states, animations and some additional invicible models that allows to pick specific parts of the Virtual Assistant.

Preparing SceneEnvironment Scene Environment

Scene use HDR images to create a skybox and to provide natural lighthing.

        environment: ExtendedSceneEnvironment {
            backgroundMode: SceneEnvironment.SkyBox
            lightProbe: Texture { source: Constants.sceneName }
            antialiasingMode: SceneEnvironment.MSAA
            antialiasingQuality: SceneEnvironment.VeryHigh
            fxaaEnabled: true
            probeExposure: 0.6
            probeOrientation: Qt.vector3d(0, settingsPanel.skyboxRotation, 0)
            specularAAEnabled: true
            tonemapMode: SceneEnvironment.TonemapModeLinear
            vignetteEnabled: true
            vignetteRadius: 0.15
        }

Camera Options

Camera properties can be changed from the SettingsPanel. You can manipulate the camera FOV, skybox rotation using sliders. The checkbox enable the OrbitCameraController that allows to change camera position, rotation etc. using mouse and keyboards buttons.

Animations

The animations are created using multiple Timeline timelines and Keyframe keyframes. Each Timeline Timeline is connected with different state of the VirtualAssistant. The animations starts playing immadiately when the state change. At the end of each animation the object returns to the default state and restore the default values like position and rotation of the model nodes. The animations change the properties values of the nodes in our skeleton and also modify the weight of different morph targets to animate the face elements (eyes, mouth).

To run the animation you can use the button on ControlPanel on the left side of the scene or you can also click on specific model elements like hands, lower body and face to activate animation related to that part of the model.

Skeleton animations
  • Entry animation
  • Exit animation
  • Explore scene animation
  • Backflip animation
  • Bouncing animation
  • Right and Left hand waving animation
Morph Target animations
  • Face animations (Happy and Sad)

Sample implementation of waving animation on the model's left hand:

Timeline {
    id: leftHandWavingTimeline
    animations: [
        TimelineAnimation {
            id: leftHandWavingAnimation
            onFinished: node.restoreDefaults()
            running: false
            loops: 1
            duration: 2000
            to: 2000
            from: 0
        }
    ]
    startFrame: 0
    endFrame: 2000
    enabled: false

    KeyframeGroup {
        target: hand_l
        property: "x"
        Keyframe {
            value: 2.89
            frame: 400
        }

        Keyframe {
            value: 2.89
            frame: 1600
        }

        Keyframe {
            value: 1.89
            frame: 2000
        }
    }

    KeyframeGroup {
        target: hand_l
        property: "y"
        Keyframe {
            value: 1
            frame: 400
        }

        Keyframe {
            value: 1
            frame: 1600
        }

        Keyframe {
            value: 0.5
            frame: 2000
        }
    }

    KeyframeGroup {
        target: hand_l
        property: "z"
        Keyframe {
            value: 1
            frame: 400
        }

        Keyframe {
            value: 1
            frame: 1600
        }

        Keyframe {
            value: -0.1
            frame: 2000
        }
    }

    KeyframeGroup {
        target: hand_l
        property: "eulerRotation.x"
        Keyframe {
            value: -15
            frame: 400
        }

        Keyframe {
            value: -5
            frame: 700
        }

        Keyframe {
            value: -15
            frame: 1000
        }

        Keyframe {
            value: -5
            frame: 1300
        }

        Keyframe {
            value: -15
            frame: 1600
        }

        Keyframe {
            value: -0.18
            frame: 2000
        }
    }

    KeyframeGroup {
        target: hand_l
        property: "eulerRotation.y"
        Keyframe {
            value: -15
            frame: 400
        }

        Keyframe {
            value: -30
            frame: 1600
        }

        Keyframe {
            value: -145
            frame: 2000
        }

        Keyframe {
            value: -40
            frame: 700
        }
    }

    KeyframeGroup {
        target: hand_l
        property: "eulerRotation.z"
        Keyframe {
            value: -88
            frame: 400
        }

        Keyframe {
            value: -30
            frame: 700
        }

        Keyframe {
            value: -86.05
            frame: 1000
        }

        Keyframe {
            value: -30
            frame: 1300
        }

        Keyframe {
            value: -86.05
            frame: 1600
        }

        Keyframe {
            value: -178.92
            frame: 2000
        }
    }

    KeyframeGroup {
        target: morphTarget38
        property: "weight"
        Keyframe {
            value: 1
            frame: 0
        }

        Keyframe {
            value: 0.25
            frame: 400
        }

        Keyframe {
            value: 0.25
            frame: 1600
        }

        Keyframe {
            value: 1
            frame: 2000
        }
    }

    KeyframeGroup {
        target: morphTarget42
        property: "weight"
        Keyframe {
            value: 0
            frame: 2000
        }

        Keyframe {
            value: 0.75
            frame: 1600
        }

        Keyframe {
            value: 0.75
            frame: 400
        }

        Keyframe {
            value: 0
            frame: 0
        }
    }

    KeyframeGroup {
        target: morphTarget27
        property: "weight"
        Keyframe {
            value: 0
            frame: 0
        }

        Keyframe {
            value: 1
            frame: 400
        }

        Keyframe {
            value: 1
            frame: 1600
        }

        Keyframe {
            value: 0
            frame: 2000
        }
    }

    KeyframeGroup {
        target: morphTarget28
        property: "weight"
        Keyframe {
            value: 1
            frame: 2000
        }

        Keyframe {
            value: 0
            frame: 1600
        }

        Keyframe {
            value: 0
            frame: 400
        }

        Keyframe {
            value: 1
            frame: 0
        }
    }
}

Example project @ code.qt.io

© 2024 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.