1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <QtCore/qmath.h>
5#include "qquick3dparticlewander_p.h"
6#include "qquick3dparticlerandomizer_p.h"
7#include "qquick3dparticleutils_p.h"
8
9QT_BEGIN_NAMESPACE
10
11/*!
12 \qmltype Wander3D
13 \inherits Affector3D
14 \inqmlmodule QtQuick3D.Particles3D
15 \brief Applies random wave curves to particles.
16 \since 6.2
17
18 This element applies random wave curves to particles. Curves can combine
19 global values which are the same for all particles and unique values which
20 differ randomly.
21*/
22
23QQuick3DParticleWander::QQuick3DParticleWander(QQuick3DNode *parent)
24 : QQuick3DParticleAffector(parent)
25{
26}
27
28/*!
29 \qmlproperty vector3d Wander3D::globalAmount
30
31 This property defines how long distance each particle moves at the ends of curves.
32 So if the value is for example (100, 10, 0), all particles wander between (100, 10, 0)
33 and (-100, -10, 0).
34
35 The default value is \c (0.0, 0.0, 0.0).
36*/
37const QVector3D &QQuick3DParticleWander::globalAmount() const
38{
39 return m_globalAmount;
40}
41
42/*!
43 \qmlproperty vector3d Wander3D::globalPace
44
45 This property defines the pace (frequency) each particle wanders in curves per second.
46
47 The default value is \c (0.0, 0.0, 0.0).
48*/
49const QVector3D &QQuick3DParticleWander::globalPace() const
50{
51 return m_globalPace;
52}
53
54/*!
55 \qmlproperty vector3d Wander3D::globalPaceStart
56
57 This property defines the starting point for the pace (frequency). The meaningful range
58 is between 0 .. 2 * PI. For example, to animate the x-coordinate of the pace start:
59
60 \qml
61 PropertyAnimation on globalPaceStart {
62 loops: Animation.Infinite
63 duration: 2000
64 from: Qt.vector3d(0, 0, 0)
65 to: Qt.vector3d(Math.PI * 2, 0, 0)
66 }
67 \endqml
68
69 The default value is \c (0.0, 0.0, 0.0).
70*/
71const QVector3D &QQuick3DParticleWander::globalPaceStart() const
72{
73 return m_globalPaceStart;
74}
75
76/*!
77 \qmlproperty vector3d Wander3D::uniqueAmount
78
79 This property defines how long distance each particle moves at the ends of curves at maximum.
80
81 The default value is \c (0.0, 0.0, 0.0).
82*/
83const QVector3D &QQuick3DParticleWander::uniqueAmount() const
84{
85 return m_uniqueAmount;
86}
87
88/*!
89 \qmlproperty vector3d Wander3D::uniquePace
90
91 This property defines the unique pace (frequency) each particle wanders in curves per second.
92
93 The default value is \c (0.0, 0.0, 0.0).
94*/
95const QVector3D &QQuick3DParticleWander::uniquePace() const
96{
97 return m_uniquePace;
98}
99
100/*!
101 \qmlproperty real Wander3D::uniqueAmountVariation
102
103 This property defines variation for \l uniqueAmount between 0.0 and 1.0.
104 When the amount variation is 0.0, every particle reaches maximum amount. When it's 0.5,
105 every particle reaches between 0.5 - 1.5 of the amount.
106 For example if \l uniqueAmount is (100, 50, 20) and \l uniqueAmountVariation is 0.1,
107 the particles maximum wave distances are something random between (110, 55, 22)
108 and (90, 45, 18).
109
110 The default value is \c 0.0.
111*/
112float QQuick3DParticleWander::uniqueAmountVariation() const
113{
114 return m_uniqueAmountVariation;
115}
116
117/*!
118 \qmlproperty real Wander3D::uniquePaceVariation
119
120 This property defines the unique pace (frequency) variation for each particle
121 between 0.0 and 1.0. When the variation is 0.0, every particle wander at the same
122 frequency. For example if \l uniquePace is (1.0, 2.0, 4.0) and \l uniquePaceVariation
123 is 0.5, the particles wave paces are something random between (2.0, 4.0, 8.0)
124 and (0.5, 1.0, 2.0).
125
126 The default value is \c 0.0.
127*/
128float QQuick3DParticleWander::uniquePaceVariation() const
129{
130 return m_uniquePaceVariation;
131}
132
133/*!
134 \qmlproperty int Wander3D::fadeInDuration
135
136 This property defines the duration in milliseconds for fading in the affector.
137 After this duration, the wandering will be in full effect.
138 Setting this can be useful to emit from a specific position or shape,
139 otherwise wander will affect position also at the beginning.
140
141 The default value is \c 0.
142*/
143int QQuick3DParticleWander::fadeInDuration() const
144{
145 return m_fadeInDuration;
146}
147
148/*!
149 \qmlproperty int Wander3D::fadeOutDuration
150
151 This property defines the duration in milliseconds for fading out the affector.
152 Setting this can be useful to reduce the wander when the particle life
153 time ends, for example when combined with \l Attractor3D so end positions will
154 match the \l{Attractor3D::shape}{shape}.
155
156 The default value is \c 0.
157*/
158int QQuick3DParticleWander::fadeOutDuration() const
159{
160 return m_fadeOutDuration;
161}
162
163void QQuick3DParticleWander::setGlobalAmount(const QVector3D &globalAmount)
164{
165 if (m_globalAmount == globalAmount)
166 return;
167
168 m_globalAmount = globalAmount;
169 Q_EMIT globalAmountChanged();
170 Q_EMIT update();
171}
172
173void QQuick3DParticleWander::setGlobalPace(const QVector3D &globalPace)
174{
175 if (m_globalPace == globalPace)
176 return;
177
178 m_globalPace = globalPace;
179 Q_EMIT globalPaceChanged();
180 Q_EMIT update();
181}
182
183void QQuick3DParticleWander::setGlobalPaceStart(const QVector3D &globalPaceStart)
184{
185 if (m_globalPaceStart == globalPaceStart)
186 return;
187
188 m_globalPaceStart = globalPaceStart;
189 Q_EMIT globalPaceStartChanged();
190 Q_EMIT update();
191}
192
193void QQuick3DParticleWander::setUniqueAmount(const QVector3D &uniqueAmount)
194{
195 if (m_uniqueAmount == uniqueAmount)
196 return;
197
198 m_uniqueAmount = uniqueAmount;
199 Q_EMIT uniqueAmountChanged();
200 Q_EMIT update();
201}
202
203void QQuick3DParticleWander::setUniquePace(const QVector3D &uniquePace)
204{
205 if (m_uniquePace == uniquePace)
206 return;
207
208 m_uniquePace = uniquePace;
209 Q_EMIT uniquePaceChanged();
210 Q_EMIT update();
211}
212
213void QQuick3DParticleWander::setUniqueAmountVariation(float uniqueAmountVariation)
214{
215 if (qFuzzyCompare(p1: m_uniqueAmountVariation, p2: uniqueAmountVariation))
216 return;
217
218 uniqueAmountVariation = std::max(a: 0.0f, b: std::min(a: 1.0f, b: uniqueAmountVariation));
219 m_uniqueAmountVariation = uniqueAmountVariation;
220 Q_EMIT uniqueAmountVariationChanged();
221 Q_EMIT update();
222}
223
224void QQuick3DParticleWander::setUniquePaceVariation(float uniquePaceVariation)
225{
226 if (qFuzzyCompare(p1: m_uniquePaceVariation, p2: uniquePaceVariation))
227 return;
228
229 uniquePaceVariation = std::max(a: 0.0f, b: std::min(a: 1.0f, b: uniquePaceVariation));
230 m_uniquePaceVariation = uniquePaceVariation;
231 Q_EMIT uniquePaceVariationChanged();
232 Q_EMIT update();
233}
234
235void QQuick3DParticleWander::setFadeInDuration(int fadeInDuration)
236{
237 if (m_fadeInDuration == fadeInDuration)
238 return;
239
240 m_fadeInDuration = std::max(a: 0, b: fadeInDuration);
241 Q_EMIT fadeInDurationChanged();
242 Q_EMIT update();
243}
244
245void QQuick3DParticleWander::setFadeOutDuration(int fadeOutDuration)
246{
247 if (m_fadeOutDuration == fadeOutDuration)
248 return;
249
250 m_fadeOutDuration = std::max(a: 0, b: fadeOutDuration);
251 Q_EMIT fadeOutDurationChanged();
252 Q_EMIT update();
253}
254
255void QQuick3DParticleWander::affectParticle(const QQuick3DParticleData &sd, QQuick3DParticleDataCurrent *d, float time)
256{
257 if (!system())
258 return;
259 auto rand = system()->rand();
260
261 // Optionally smoothen the beginning & end of wander
262 float smooth = 1.0f;
263 if (m_fadeInDuration > 0) {
264 smooth = time / (float(m_fadeInDuration) / 1000.0f);
265 smooth = std::min(a: 1.0f, b: smooth);
266 }
267 if (m_fadeOutDuration > 0) {
268 float timeLeft = (sd.lifetime - time);
269 float smoothOut = timeLeft / (float(m_fadeOutDuration) / 1000.0f);
270 // When fading both in & out, select smaller (which is always max 1.0)
271 smooth = std::min(a: smoothOut, b: smooth);
272 }
273
274 const float pi2 = float(M_PI * 2);
275 // Global
276 if (!qFuzzyIsNull(f: m_globalAmount.x()) && !qFuzzyIsNull(f: m_globalPace.x()))
277 d->position.setX(d->position.x() + smooth * QPSIN(x: m_globalPaceStart.x() + time * pi2 * m_globalPace.x()) * m_globalAmount.x());
278 if (!qFuzzyIsNull(f: m_globalAmount.y()) && !qFuzzyIsNull(f: m_globalPace.y()))
279 d->position.setY(d->position.y() + smooth * QPSIN(x: m_globalPaceStart.y() + time * pi2 * m_globalPace.y()) * m_globalAmount.y());
280 if (!qFuzzyIsNull(f: m_globalAmount.z()) && !qFuzzyIsNull(f: m_globalPace.z()))
281 d->position.setZ(d->position.z() + smooth * QPSIN(x: m_globalPaceStart.z() + time * pi2 * m_globalPace.z()) * m_globalAmount.z());
282
283 // Unique
284 // Rather simple to only use a single sin operation per direction
285 if (!qFuzzyIsNull(f: m_uniqueAmount.x()) && !qFuzzyIsNull(f: m_uniquePace.x())) {
286 // Values between 1.0 +/- variation
287 float paceVariation = 1.0f + m_uniquePaceVariation - 2.0f * rand->get(particleIndex: sd.index, user: QPRand::WanderXPV) * m_uniquePaceVariation;
288 float amountVariation = 1.0f + m_uniqueAmountVariation - 2.0f * rand->get(particleIndex: sd.index, user: QPRand::WanderXAV) * m_uniqueAmountVariation;
289 float startPace = rand->get(particleIndex: sd.index, user: QPRand::WanderXPS) * pi2;
290 float pace = startPace + paceVariation * time * pi2 * m_uniquePace.x();
291 float amount = amountVariation * m_uniqueAmount.x();
292 d->position.setX(d->position.x() + smooth * QPSIN(x: pace) * amount);
293 }
294 if (!qFuzzyIsNull(f: m_uniqueAmount.y()) && !qFuzzyIsNull(f: m_uniquePace.y())) {
295 // Values between 1.0 +/- variation
296 float paceVariation = 1.0f + m_uniquePaceVariation - 2.0f * rand->get(particleIndex: sd.index, user: QPRand::WanderYPV) * m_uniquePaceVariation;
297 float amountVariation = 1.0f + m_uniqueAmountVariation - 2.0f * rand->get(particleIndex: sd.index, user: QPRand::WanderYAV) * m_uniqueAmountVariation;
298 float startPace = rand->get(particleIndex: sd.index, user: QPRand::WanderYPS) * pi2;
299 float pace = startPace + paceVariation * time * pi2 * m_uniquePace.y();
300 float amount = amountVariation * m_uniqueAmount.y();
301 d->position.setY(d->position.y() + smooth * QPSIN(x: pace) * amount);
302 }
303 if (!qFuzzyIsNull(f: m_uniqueAmount.z()) && !qFuzzyIsNull(f: m_uniquePace.z())) {
304 // Values between 1.0 +/- variation
305 float paceVariation = 1.0f + m_uniquePaceVariation - 2.0f * rand->get(particleIndex: sd.index, user: QPRand::WanderZPV) * m_uniquePaceVariation;
306 float amountVariation = 1.0f + m_uniqueAmountVariation - 2.0f * rand->get(particleIndex: sd.index, user: QPRand::WanderZAV) * m_uniqueAmountVariation;
307 float startPace = rand->get(particleIndex: sd.index, user: QPRand::WanderZPS) * pi2;
308 float pace = startPace + paceVariation * time * pi2 * m_uniquePace.z();
309 float amount = amountVariation * m_uniqueAmount.z();
310 d->position.setZ(d->position.z() + smooth * QPSIN(x: pace) * amount);
311 }
312}
313
314QT_END_NAMESPACE
315

source code of qtquick3d/src/quick3dparticles/qquick3dparticlewander.cpp