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
         }
     }
 }

Files: