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 QtQuick.Controls
import QtQuick.Timeline
import QtQuick.Layouts
Item {
id: mainWindow
property real cubeRotation: 0
anchors.fill: parent
Timeline {
id: timeline
enabled: true
startFrame: 0
endFrame: 100
animations: [
TimelineAnimation {
id: timelineAnimation
running: true
duration: (100 - timeline.currentFrame) * 100 //10000
from: timeline.currentFrame
to: 100
}
]
keyframeGroups: [
KeyframeGroup {
target: mainWindow
property: "cubeRotation"
Keyframe { frame: 0; value: 0 }
Keyframe { frame: 100; value: 360 }
},
KeyframeGroup {
target: psystem1
property: "time"
Keyframe { frame: 0; value: 3001 }
Keyframe { frame: 50; value: 0; easing.type: Easing.OutQuad }
Keyframe { frame: 55; value: 0 }
Keyframe { frame: 80; value: 3001 }
},
KeyframeGroup {
target: psystem2
property: "time"
Keyframe { frame: 50; value: 0; easing.type: Easing.InQuad }
Keyframe { frame: 80; value: 5000 }
},
KeyframeGroup {
target: qtCube
property: "opacity"
Keyframe { frame: 60; value: 0 }
Keyframe { frame: 70; value: 0.99 }
Keyframe { frame: 90; value: 0.99 }
Keyframe { frame: 100; value: 0.0 }
}
]
}
View3D {
id: view3D
anchors.fill: parent
environment: SceneEnvironment {
clearColor: "#202020"
backgroundMode: SceneEnvironment.Color
antialiasingMode: settings.antialiasingMode
antialiasingQuality: settings.antialiasingQuality
}
PerspectiveCamera {
id: camera
position.z: 600
}
PointLight {
position: Qt.vector3d(200, 400, 200)
brightness: 10
ambientColor: Qt.rgba(0.2, 0.2, 0.2, 1.0)
}
// Particle models
Component {
id: dotParticleComponent
Model {
source: "#Cube"
scale: Qt.vector3d(0.02, 0.02, 0.02)
materials: DefaultMaterial {
lighting: DefaultMaterial.NoLighting
}
}
}
Component {
id: smokeParticleComponent
Model {
source: "#Rectangle"
scale: Qt.vector3d(6, 6, 6)
materials: DefaultMaterial {
diffuseMap: Texture { source: "images/smoke.png" }
lighting: DefaultMaterial.NoLighting
}
opacity: 0.2
}
}
Component {
id: starParticleComponent
Model {
source: "#Rectangle"
scale: Qt.vector3d(0.5, 0.5, 0.5)
materials: DefaultMaterial {
diffuseMap: Texture { source: "images/star.png" }
lighting: DefaultMaterial.NoLighting
cullMode: DefaultMaterial.NoCulling
}
opacity: 0.2
}
}
Node {
eulerRotation: Qt.vector3d(20, -40 + cubeRotation, -10 + cubeRotation)
Model {
id: qtCube
source: "#Cube"
scale: Qt.vector3d(2.0, 2.0, 2.0)
opacity: 0
materials: DefaultMaterial {
diffuseMap: Texture { source: "images/qt_logo.png" }
}
}
ParticleSystem3D {
id: psystem1
// We animate this system time manually
running: false
ModelParticle3D {
id: particleWhite
delegate: dotParticleComponent
maxAmount: 2000
color: "#ffffff"
colorVariation: Qt.vector4d(0, 0, 0, 0.8)
fadeInEffect: ModelParticle3D.FadeNone
fadeOutEffect: ModelParticle3D.FadeOpacity
fadeOutDuration: 3000
}
ParticleEmitter3D {
id: emitter1
particle: particleWhite
scale: Qt.vector3d(2.0, 2.0, 2.0)
shape: ParticleShape3D {
type: ParticleShape3D.Cube
fill: false
}
velocity: TargetDirection3D {
magnitude: -0.6
magnitudeVariation: 0.4
}
lifeSpan: 3000
emitBursts: [
EmitBurst3D {
time: 0
amount: 2000
}
]
}
Wander3D {
uniqueAmount: Qt.vector3d(40.0, 40.0, 40.0)
uniquePace: Qt.vector3d(0.2, 0.2, 0.2)
uniqueAmountVariation: 0.5
uniquePaceVariation: 0.5
fadeInDuration: 1000
}
}
}
ParticleSystem3D {
id: psystem2
// We animate this system time manually
running: false
ModelParticle3D {
id: smokeParticle
delegate: smokeParticleComponent
maxAmount: 20
color: "#ffffff"
colorVariation: Qt.vector4d(0, 0, 0, 0.5)
fadeInEffect: ModelParticle3D.FadeScale
fadeOutEffect: ModelParticle3D.FadeOpacity
fadeOutDuration: 2000
}
ModelParticle3D {
id: starParticle
delegate: starParticleComponent
maxAmount: 50
color: "#ffff00"
colorVariation: Qt.vector4d(0.4, 0.6, 0, 0.1)
unifiedColorVariation: true
fadeInEffect: ModelParticle3D.FadeScale
fadeOutEffect: ModelParticle3D.FadeOpacity
fadeOutDuration: 2000
}
ParticleEmitter3D {
id: emitter2
particle: smokeParticle
scale: Qt.vector3d(0.1, 0.1, 0.1)
shape: ParticleShape3D {
type: ParticleShape3D.Sphere
}
particleRotationVariation: Qt.vector3d(20, 20, 180)
particleRotationVelocityVariation: Qt.vector3d(0, 0, 100)
velocity: TargetDirection3D {
normalized: true
magnitude: -200.0
magnitudeVariation: 0.5
}
lifeSpan: 4000
emitBursts: [
EmitBurst3D {
time: 400
amount: 20
duration: 600
}
]
}
ParticleEmitter3D {
id: emitter3
particle: starParticle
scale: Qt.vector3d(0.1, 0.1, 0.1)
shape: ParticleShape3D {
type: ParticleShape3D.Sphere
}
particleScale: 2.0
particleScaleVariation: 1.0
particleEndScale: 5.0
particleEndScaleVariation: 3.0
particleRotationVariation: Qt.vector3d(0, 0, 180)
particleRotationVelocityVariation: Qt.vector3d(0, 0, 200)
velocity: TargetDirection3D {
normalized: true
magnitudeVariation: 150
}
lifeSpan: 2500
emitBursts: [
EmitBurst3D {
time: 1
amount: 50
}
]
}
}
}
Frame {
id: toolbar
anchors.left: parent.left
anchors.leftMargin: 20
anchors.right: parent.right
anchors.rightMargin: 20
anchors.bottom: parent.bottom
anchors.bottomMargin: 20
height: 60
padding: 0
background: Rectangle {
color: "#ffffff"
radius: 4
opacity: 0.2
}
RowLayout {
anchors.fill: parent
Button {
id: playButton
Layout.leftMargin: 14
Layout.minimumHeight: toolbar.height - 10
Layout.minimumWidth: Layout.minimumHeight
background: Rectangle {
color : "transparent"
}
icon.source: timelineAnimation.running ? "qrc:/qml/images/icon_pause.png" : "qrc:/qml/images/icon_play.png"
icon.width: Layout.minimumWidth
icon.height: Layout.minimumHeight
icon.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 {
anchors.bottom: toolbar.top
anchors.bottomMargin: 8
particleSystems: [psystem1, psystem2]
}
}