- Qt 6.5
- Qt Quick 3D
- Qt Quick 3D - Particles 3D Testbed Example
- Qt Quick 3D | Commercial or GPLv3

# Qt Quick 3D - Particles 3D Testbed Example

     // Copyright (C) 2021 The Qt Company Ltd.
     // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

     import QtQuick
     import QtQuick3D
     import QtQuick3D.Particles3D
     import QtQuick3D.Helpers
     import QtQuick.Timeline
     import QtQuick.Controls
     import QtQuick.Layouts

     Item {
         id: mainWindow

         anchors.fill: parent

         Timeline {
             id: timeline
             enabled: true
             startFrame: 0
             endFrame: 100
             animations: [
                 TimelineAnimation {
                     id: timelineAnimation
                     running: true
                     duration: (100 - timeline.currentFrame) * 100
                     from: timeline.currentFrame
                     to: 100
                 }
             ]
             keyframeGroups: [
                 KeyframeGroup {
                     target: psystem
                     property: "time"
                     Keyframe { frame: 0; value: 0 }
                     Keyframe { frame: 100; value: 15000 }
                 },
                 KeyframeGroup {
                     target: cameraRotation
                     property: "rot"
                     Keyframe { frame: 0; value: 0 }
                     Keyframe { frame: 100; value: 180 }
                 },
                 KeyframeGroup {
                     target: actNode
                     property: "y"
                     Keyframe { frame: 0; value: 50 }
                     Keyframe { frame: 100; value: -100 }
                 }
             ]
         }

         View3D {
             anchors.fill: parent
             camera: camera

             environment: SceneEnvironment {
                 clearColor: "#404040"
                 backgroundMode: SceneEnvironment.Color
                 antialiasingMode: settings.antialiasingMode
                 antialiasingQuality: settings.antialiasingQuality
             }

             Node {
                 id: cameraRotation
                 property real rot: 0.0
                 eulerRotation: Qt.vector3d(0, rot, 0)
                 PerspectiveCamera {
                     id: camera
                     position: Qt.vector3d(0, 0, 150)
                     clipFar: 2000
                 }
             }

             DirectionalLight {
                 brightness: 50
                 eulerRotation: Qt.vector3d(-90, -90, 0)
                 castsShadow: true
                 shadowFactor: 25
                 shadowMapQuality: Light.ShadowMapQualityHigh
             }
             DirectionalLight {
                 eulerRotation: Qt.vector3d(180, 0, 0)
                 brightness: 25
             }
             DirectionalLight {
                 brightness: 25
             }

             Model {
                 source: "#Rectangle"
                 eulerRotation: Qt.vector3d(-90, 0, 0)
                 y: -30
                 scale: Qt.vector3d(3, 3, 1)
                 receivesShadows: true
                 materials: [
                     DefaultMaterial {
                         diffuseColor: "#0c100c"
                     }
                 ]
             }

             ParticleSystem3D {
                 id: psystem
                 running: false
                 x: -100

                 Node {
                     id: actNode
                     eulerRotation: Qt.vector3d(-90, 0, 0)

                     Model {
                         visible: particle.emitMode === ModelBlendParticle3D.Activation
                         source: "#Rectangle"
                         scale: Qt.vector3d(0.5, 0.5, 1)
                         receivesShadows: false
                         materials: [
                             DefaultMaterial {
                                 lighting: DefaultMaterial.NoLighting
                                 cullMode: Material.NoCulling
                                 opacity: 0.25
                             }
                         ]
                     }
                 }

                 Component {
                     id: modelComponent
                     Model {
                         source: "meshes/oldqtlogo.mesh"
                         scale: Qt.vector3d(10, 10, 10)
                         receivesShadows: false
                         materials: [
                             PrincipledMaterial {
                                 baseColor: "#41cd52"
                                 metalness: 0.8
                                 roughness: 0.1
                                 specularAmount: 1
                                 cullMode: cullingModelBox.checked ? Material.BackFaceCulling : Material.NoCulling
                             }
                         ]
                     }
                 }

                 Node {
                     id: translateNode
                     x: 150
                 }

                 ModelBlendParticle3D {
                     id: particle
                     delegate: modelComponent
                     endNode: translateNode
                     modelBlendMode: blendModeSelectionBox.selection
                     endTime: 1500
                     activationNode: actNode
                     emitMode: emitModeSelectionBox.selection
                 }

                 ParticleEmitter3D {
                     id: emitter
                     system: psystem
                     particle: particle
                     lifeSpan: particle.modelBlendMode === ModelBlendParticle3D.Explode ? 40000 : 4000
                     emitRate: particle.maxAmount / 10
                     velocity: VectorDirection3D {
                         direction: Qt.vector3d(50, 10, 0)
                         directionVariation: Qt.vector3d(0, 10, 10)
                     }
                     particleRotation: Qt.vector3d(20, 0, 3)
                     particleRotationVariation: Qt.vector3d(4, 0, 1)
                     particleScale: 1.0
                     particleEndScale: 1.0
                 }
             }
         }

         SettingsView {
             id: settingsView
             CustomCheckBox {
                 id: cullingModelBox
                 text: "Enable culling"
                 checked: false
             }
             CustomSelectionBox {
                 id: blendModeSelectionBox
                 text: "Blend Mode"
                 values: ["Explode", "Construct", "Transfer"]
                 parentWidth: settingsView.width
                 onSelectionChanged: {
                     timeline.currentFrame = 0
                     psystem.reset()
                 }
             }
             CustomSelectionBox {
                 id: emitModeSelectionBox
                 text: "Emit Mode"
                 values: ["Sequential", "Random", "Activation"]
                 parentWidth: settingsView.width
                 onSelectionChanged: {
                     timeline.currentFrame = 0
                     psystem.reset()
                 }
             }
         }

         Frame {
             id: toolbar
             anchors.left: parent.left
             anchors.leftMargin: 20
             anchors.right: parent.right
             anchors.rightMargin: 20
             anchors.bottom: parent.bottom
             anchors.bottomMargin: 80
             height: 60
             padding: 0
             background: Rectangle {
                 color: "#ffffff"
                 radius: 4
                 opacity: 0.2
             }

             RowLayout {
                 anchors.fill: parent
                 Button {
                     id: playButton
                     Layout.leftMargin: 14
                     icon.source: timelineAnimation.running ? "qrc:/qml/images/icon_pause.png" : "qrc:/qml/images/icon_play.png"
                     icon.width: toolbar.height - 10
                     icon.height: toolbar.height - 10
                     icon.color: "transparent"
                     background: Rectangle {
                         color: "transparent"
                     }
                     onClicked: {
                         // If we are close to end, start from the beginning
                         if (timeline.currentFrame >= timeline.endFrame - 1.0)
                             timeline.currentFrame = 0;

                         timelineAnimation.running = !timelineAnimation.running;
                     }
                 }

                 CustomSlider {
                     id: sliderTimelineTime
                     Layout.fillWidth: true
                     sliderValue: timeline.currentFrame
                     sliderEnabled: !timelineAnimation.running || timelineAnimation.paused
                     fromValue: 0.0
                     toValue: 100.0
                     onSliderValueChanged: timeline.currentFrame = sliderValue;
                 }
             }
         }

         LoggingView {
             id: loggingView
             anchors.bottom: parent.bottom
             particleSystems: [psystem]
         }
     }