Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquick3dparticleattractor.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5
9
11
12/*!
13 \qmltype Attractor3D
14 \inherits Affector3D
15 \inqmlmodule QtQuick3D.Particles3D
16 \brief Attracts particles towards a position or a shape.
17 \since 6.2
18
19 This element attracts particles towards a position inside the 3D view. To model
20 the gravity of a massive object whose center of gravity is far away, use \l Gravity3D.
21
22 The attraction position is defined either with the \l {Node::position}{position} and
23 \l positionVariation or with \l shape. If both are defined, \l shape is used.
24*/
25
26// Minimum duration in seconds
27const float MIN_DURATION = 0.001f;
28
29QQuick3DParticleAttractor::QQuick3DParticleAttractor(QQuick3DNode *parent)
30 : QQuick3DParticleAffector(parent)
31 , m_duration(-1)
32 , m_durationVariation(0)
33{
34}
35
36/*!
37 \qmlproperty vector3d Attractor3D::positionVariation
38
39 This property defines the variation on attract position. It can be used to not attract
40 into a single point, but randomly towards a wider area. Here is an example how to attract
41 particles into some random point inside (50, 50, 50) cube at position (100, 0, 0) within
42 2 to 4 seconds:
43
44 \qml
45 Attractor3D {
46 position: Qt.vector3d(100, 0, 0)
47 positionVariation: Qt.vector3d(50, 50, 50)
48 duration: 3000
49 durationVariation: 1000
50 }
51 \endqml
52
53 The default value is \c (0, 0, 0) (no variation).
54
55 \sa {Node::position}, shape
56*/
57QVector3D QQuick3DParticleAttractor::positionVariation() const
58{
59 return m_positionVariation;
60}
61
62void QQuick3DParticleAttractor::setPositionVariation(const QVector3D &positionVariation)
63{
64 if (m_positionVariation == positionVariation)
65 return;
66
67 m_positionVariation = positionVariation;
68 Q_EMIT positionVariationChanged();
69 Q_EMIT update();
70}
71
72/*!
73 \qmlproperty ParticleAbstractShape3D Attractor3D::shape
74
75 This property defines a \l ParticleAbstractShape3D for particles attraction.
76 Each particle will be attracted into a random position inside this shape. This is an
77 alternative for defining \l {Node::position}{position} and \l positionVariation. Here
78 is an example how to attract particles into some random point inside sphere by the end
79 of the particles \l {ParticleEmitter3D::}{lifeSpan}:
80
81 \qml
82 Attractor3D {
83 position: Qt.vector3d(100, 0, 0)
84 shape: ParticleShape3D {
85 type: ParticleShape3D.Sphere
86 fill: true
87 }
88 }
89 \endqml
90
91 \sa {Node::position}, positionVariation
92*/
93QQuick3DParticleAbstractShape *QQuick3DParticleAttractor::shape() const
94{
95 return m_shape;
96}
97
98void QQuick3DParticleAttractor::setShape(QQuick3DParticleAbstractShape *shape)
99{
100 if (m_shape == shape)
101 return;
102
103 m_shape = shape;
104 m_shapeDirty = true;
105 Q_EMIT shapeChanged();
106 Q_EMIT update();
107}
108
109/*!
110 \qmlproperty int Attractor3D::duration
111
112 This property defines the duration in milliseconds how long it takes for particles to
113 reach the attaction position. When the value is -1, particle lifeSpan is used
114 as the duration.
115
116 The default value is \c -1.
117*/
118int QQuick3DParticleAttractor::duration() const
119{
120 return m_duration;
121}
122
123void QQuick3DParticleAttractor::setDuration(int duration)
124{
125 if (m_duration == duration)
126 return;
127
128 m_duration = duration;
129 Q_EMIT durationChanged();
130 Q_EMIT update();
131}
132
133/*!
134 \qmlproperty int Attractor3D::durationVariation
135
136 This property defines the duration variation in milliseconds. The actual duration to
137 reach attractor is between \c duration - \c durationVariation and \c duration + \c durationVariation.
138
139 The default value is \c 0 (no variation).
140*/
141int QQuick3DParticleAttractor::durationVariation() const
142{
143 return m_durationVariation;
144}
145
146void QQuick3DParticleAttractor::setDurationVariation(int durationVariation)
147{
148 if (m_durationVariation == durationVariation)
149 return;
150
151 m_durationVariation = durationVariation;
152 Q_EMIT durationVariationChanged();
153 Q_EMIT update();
154}
155
156/*!
157 \qmlproperty bool Attractor3D::hideAtEnd
158
159 This property defines if the particle should disappear when it reaches the attractor.
160
161 The default value is \c false.
162*/
163bool QQuick3DParticleAttractor::hideAtEnd() const
164{
165 return m_hideAtEnd;
166}
167
168/*!
169 \qmlproperty bool Attractor3D::useCachedPositions
170
171 This property defines if the attractor caches possible positions within its shape.
172 Cached positions give less random results but are better for performance.
173
174 The default value is \c true.
175*/
176bool QQuick3DParticleAttractor::useCachedPositions() const
177{
178 return m_useCachedPositions;
179}
180
181/*!
182 \qmlproperty int Attractor3D::positionsAmount
183
184 This property defines the amount of possible positions stored within the attractor shape.
185 By default the amount equals the particle count, but a lower amount can be used for a smaller cache.
186 Higher amount can be used for additional randomization.
187*/
188int QQuick3DParticleAttractor::positionsAmount() const
189{
190 return m_positionsAmount;
191}
192
193void QQuick3DParticleAttractor::setHideAtEnd(bool hideAtEnd)
194{
195 if (m_hideAtEnd == hideAtEnd)
196 return;
197
198 m_hideAtEnd = hideAtEnd;
199 Q_EMIT hideAtEndChanged();
200 Q_EMIT update();
201}
202
203void QQuick3DParticleAttractor::setUseCachedPositions(bool useCachedPositions)
204{
205 if (m_useCachedPositions == useCachedPositions)
206 return;
207
208 m_useCachedPositions = useCachedPositions;
209 Q_EMIT useCachedPositionsChanged();
210 m_shapeDirty = true;
211}
212
213void QQuick3DParticleAttractor::setPositionsAmount(int positionsAmount)
214{
215 if (m_positionsAmount == positionsAmount)
216 return;
217
218 m_positionsAmount = positionsAmount;
219 Q_EMIT positionsAmountChanged();
220 m_shapeDirty = true;
221}
222
223void QQuick3DParticleAttractor::updateShapePositions()
224{
225 m_shapePositionList.clear();
226 if (!system() || !m_shape)
227 return;
228
229 m_shape->m_system = system();
230
231 if (m_useCachedPositions) {
232 // Get count of particles positions needed
233 int pCount = 0;
234 if (m_positionsAmount > 0) {
235 pCount = m_positionsAmount;
236 } else {
237 if (!m_particles.isEmpty()) {
238 for (auto p : m_particles) {
239 auto pp = qobject_cast<QQuick3DParticle *>(p);
240 pCount += pp->maxAmount();
241 }
242 } else {
243 pCount = system()->particleCount();
244 }
245 }
246
247 m_shapePositionList.reserve(pCount);
248 for (int i = 0; i < pCount; i++)
249 m_shapePositionList << m_shape->getPosition(i);
250 } else {
251 m_shapePositionList.clear();
252 m_shapePositionList.squeeze();
253 }
254
255 m_shapeDirty = false;
256}
257
258void QQuick3DParticleAttractor::prepareToAffect()
259{
260 if (m_shapeDirty)
261 updateShapePositions();
262 m_centerPos = position();
263 m_particleTransform = calculateParticleTransform(parentNode(), m_systemSharedParent);
264}
265
266void QQuick3DParticleAttractor::affectParticle(const QQuick3DParticleData &sd, QQuick3DParticleDataCurrent *d, float time)
267{
268 if (!system())
269 return;
270
271 auto rand = system()->rand();
272 float duration = m_duration < 0 ? sd.lifetime : (m_duration / 1000.0f);
273 float durationVariation = m_durationVariation == 0
274 ? 0.0f
275 : (m_durationVariation / 1000.0f) - 2.0f * rand->get(sd.index, QPRand::AttractorDurationV) * (m_durationVariation / 1000.0f);
276 duration = std::max(duration + durationVariation, MIN_DURATION);
277 float pEnd = std::min(1.0f, std::max(0.0f, time / duration));
278 // TODO: Should we support easing?
279 //pEnd = easeInOutQuad(pEnd);
280
281 if (m_hideAtEnd && pEnd >= 1.0f) {
282 d->color.a = 0;
283 return;
284 }
285
286 float pStart = 1.0f - pEnd;
287 QVector3D pos = m_centerPos;
288
289 if (m_shape) {
290 if (m_useCachedPositions)
291 pos += m_shapePositionList[sd.index % m_shapePositionList.size()];
292 else
293 pos += m_shape->getPosition(sd.index);
294 }
295
296 if (!m_positionVariation.isNull()) {
297 pos.setX(pos.x() + m_positionVariation.x() - 2.0f * rand->get(sd.index, QPRand::AttractorPosVX) * m_positionVariation.x());
298 pos.setY(pos.y() + m_positionVariation.y() - 2.0f * rand->get(sd.index, QPRand::AttractorPosVY) * m_positionVariation.y());
299 pos.setZ(pos.z() + m_positionVariation.z() - 2.0f * rand->get(sd.index, QPRand::AttractorPosVZ) * m_positionVariation.z());
300 }
301
302 d->position = (pStart * d->position) + (pEnd * m_particleTransform.map(pos));
303}
304
305QT_END_NAMESPACE
Combined button and popup list for selecting options.
QT_BEGIN_NAMESPACE const float MIN_DURATION
\qmltype Attractor3D \inherits Affector3D \inqmlmodule QtQuick3D.Particles3D