1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#ifndef PARTICLESYSTEM_H
41#define PARTICLESYSTEM_H
42
43//
44// W A R N I N G
45// -------------
46//
47// This file is not part of the Qt API. It exists purely as an
48// implementation detail. This header file may change from version to
49// version without notice, or even be removed.
50//
51// We mean it.
52//
53
54#include <QtQuick/QQuickItem>
55#include <QElapsedTimer>
56#include <QVector>
57#include <QHash>
58#include <QPointer>
59#include <private/qquicksprite_p.h>
60#include <QAbstractAnimation>
61#include <QtQml/qqml.h>
62#include <private/qv4util_p.h>
63#include <private/qv4global_p.h>
64#include <private/qv4staticvalue_p.h>
65#include "qtquickparticlesglobal_p.h"
66#include "qquickparticleflatset_p.h"
67
68QT_BEGIN_NAMESPACE
69
70template<class T, int Prealloc>
71class QQuickParticleVarLengthArray: public QVarLengthArray<T, Prealloc>
72{
73public:
74 void insert(const T &element)
75 {
76 if (!this->contains(element)) {
77 this->append(element);
78 }
79 }
80
81 bool removeOne(const T &element)
82 {
83 for (int i = 0; i < this->size(); ++i) {
84 if (this->at(i) == element) {
85 this->remove(i);
86 return true;
87 }
88 }
89
90 return false;
91 }
92};
93
94class QQuickParticleSystem;
95class QQuickParticleAffector;
96class QQuickParticleEmitter;
97class QQuickParticlePainter;
98class QQuickParticleData;
99class QQuickParticleSystemAnimation;
100class QQuickStochasticEngine;
101class QQuickSprite;
102class QQuickV4ParticleData;
103class QQuickParticleGroup;
104class QQuickImageParticle;
105
106struct QQuickParticleDataHeapNode{
107 int time;//in ms
108 QSet<QQuickParticleData*> data;//Set ptrs instead?
109};
110
111class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticleDataHeap {
112 //Idea is to do a binary heap, but which also stores a set of int,Node* so that if the int already exists, you can
113 //add it to the data* list. Pops return the whole list at once.
114public:
115 QQuickParticleDataHeap();
116 void insert(QQuickParticleData* data);
117 void insertTimed(QQuickParticleData* data, int time);
118
119 int top();
120
121 QSet<QQuickParticleData*> pop();
122
123 void clear();
124
125 bool contains(QQuickParticleData*);//O(n), for debugging purposes only
126private:
127 void grow();
128 void swap(int, int);
129 void bubbleUp(int);
130 void bubbleDown(int);
131 int m_size;
132 int m_end;
133 QQuickParticleDataHeapNode m_tmp;
134 QVector<QQuickParticleDataHeapNode> m_data;
135 QHash<int,int> m_lookups;
136};
137
138class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticleGroupData {
139 class FreeList
140 {
141 public:
142 FreeList() {}
143
144 void resize(int newSize)
145 {
146 Q_ASSERT(newSize >= 0);
147 int oldSize = isUnused.size();
148 isUnused.resize(newSize, newValue: true);
149 if (newSize > oldSize) {
150 if (firstUnused == UINT_MAX) {
151 firstUnused = oldSize;
152 } else {
153 firstUnused = std::min(a: firstUnused, b: unsigned(oldSize));
154 }
155 } else if (firstUnused >= unsigned(newSize)) {
156 firstUnused = UINT_MAX;
157 }
158 }
159
160 void free(int index)
161 {
162 isUnused.setBit(index);
163 firstUnused = std::min(a: firstUnused, b: unsigned(index));
164 --allocated;
165 }
166
167 int count() const
168 { return allocated; }
169
170 bool hasUnusedEntries() const
171 { return firstUnused != UINT_MAX; }
172
173 int alloc()
174 {
175 if (hasUnusedEntries()) {
176 int nextFree = firstUnused;
177 isUnused.clearBit(idx: firstUnused);
178 firstUnused = isUnused.findNext(start: firstUnused, value: true, wrapAround: false);
179 if (firstUnused >= unsigned(isUnused.size())) {
180 firstUnused = UINT_MAX;
181 }
182 ++allocated;
183 return nextFree;
184 } else {
185 return -1;
186 }
187 }
188
189 private:
190 QV4::BitVector isUnused;
191 unsigned firstUnused = UINT_MAX;
192 int allocated = 0;
193 };
194
195public: // types
196 typedef int ID;
197 enum { InvalidID = -1, DefaultGroupID = 0 };
198
199public:
200 QQuickParticleGroupData(const QString &name, QQuickParticleSystem* sys);
201 ~QQuickParticleGroupData();
202
203 int size()
204 { return m_size; }
205
206 QString name();
207
208 bool isActive() { return freeList.count() > 0; }
209
210 void setSize(int newSize);
211
212 const ID index;
213 QQuickParticleVarLengthArray<QQuickParticlePainter*, 4> painters;//TODO: What if they are dynamically removed?
214
215 //TODO: Refactor particle data list out into a separate class
216 QVector<QQuickParticleData*> data;
217 FreeList freeList;
218 QQuickParticleDataHeap dataHeap;
219 bool recycle(); //Force recycling round, returns true if all indexes are now reusable
220
221 void initList();
222 void kill(QQuickParticleData* d);
223
224 //After calling this, initialize, then call prepareRecycler(d)
225 QQuickParticleData* newDatum(bool respectsLimits);
226
227 //TODO: Find and clean up those that don't get added to the recycler (currently they get lost)
228 void prepareRecycler(QQuickParticleData* d);
229
230private:
231 int m_size;
232 QQuickParticleSystem* m_system;
233 // Only used in recycle() for tracking of alive particles after latest recycling round
234 QVector<QQuickParticleData*> m_latestAliveParticles;
235};
236
237struct Color4ub {
238 uchar r;
239 uchar g;
240 uchar b;
241 uchar a;
242};
243
244class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticleData {
245public:
246 //TODO: QObject like memory management (without the cost, just attached to system)
247 QQuickParticleData();
248 ~QQuickParticleData();
249
250 QQuickParticleData(const QQuickParticleData &other);
251 QQuickParticleData &operator=(const QQuickParticleData &other);
252
253 //Convenience functions for working backwards, because parameters are from the start of particle life
254 //If setting multiple parameters at once, doing the conversion yourself will be faster.
255
256 //sets the x accleration without affecting the instantaneous x velocity or position
257 void setInstantaneousAX(float ax, QQuickParticleSystem *particleSystem);
258 //sets the x velocity without affecting the instantaneous x postion
259 void setInstantaneousVX(float vx, QQuickParticleSystem *particleSystem);
260 //sets the instantaneous x postion
261 void setInstantaneousX(float x, QQuickParticleSystem *particleSystem);
262 //sets the y accleration without affecting the instantaneous y velocity or position
263 void setInstantaneousAY(float ay, QQuickParticleSystem *particleSystem);
264 //sets the y velocity without affecting the instantaneous y postion
265 void setInstantaneousVY(float vy, QQuickParticleSystem *particleSystem);
266 //sets the instantaneous Y postion
267 void setInstantaneousY(float y, QQuickParticleSystem *particleSystem);
268
269 //TODO: Slight caching?
270 float curX(QQuickParticleSystem *particleSystem) const;
271 float curVX(QQuickParticleSystem *particleSystem) const;
272 float curAX() const { return ax; }
273 float curAX(QQuickParticleSystem *) const { return ax; } // used by the macros in qquickv4particledata.cpp
274 float curY(QQuickParticleSystem *particleSystem) const;
275 float curVY(QQuickParticleSystem *particleSystem) const;
276 float curAY() const { return ay; }
277 float curAY(QQuickParticleSystem *) const { return ay; } // used by the macros in qquickv4particledata.cpp
278
279 int index;
280 int systemIndex;
281
282 //General Position Stuff
283 float x;
284 float y;
285 float t;
286 float lifeSpan;
287 float size;
288 float endSize;
289 float vx;
290 float vy;
291 float ax;
292 float ay;
293
294 //Painter-specific stuff, now universally shared
295 //Used by ImageParticle color mode
296 Color4ub color;
297 //Used by ImageParticle deform mode
298 float xx;
299 float xy;
300 float yx;
301 float yy;
302 float rotation;
303 float rotationVelocity;
304 float autoRotate;//Assume that GPUs prefer floats to bools
305 //Used by ImageParticle Sprite mode
306 float animIdx;
307 float frameDuration;
308 float frameAt;//Used for duration -1
309 float frameCount;
310 float animT;
311 float animX;
312 float animY;
313 float animWidth;
314 float animHeight;
315
316 QQuickParticleGroupData::ID groupId;
317
318 //Used by ImageParticle data shadowing
319 QQuickImageParticle* colorOwner;
320 QQuickImageParticle* rotationOwner;
321 QQuickImageParticle* deformationOwner;
322 QQuickImageParticle* animationOwner;
323
324 //Used by ItemParticle
325 QQuickItem* delegate;
326 int modelIndex;
327 //Used by custom affectors
328 float update;
329 //Used by CustomParticle
330 float r;
331
332 // 4 bytes wasted
333
334
335 void debugDump(QQuickParticleSystem *particleSystem) const;
336 bool stillAlive(QQuickParticleSystem *particleSystem) const; //Only checks end, because usually that's all you need and it's a little faster.
337 bool alive(QQuickParticleSystem *particleSystem) const;
338 float lifeLeft(QQuickParticleSystem *particleSystem) const;
339
340 float curSize(QQuickParticleSystem *particleSystem) const;
341 void clone(const QQuickParticleData& other);//Not =, leaves meta-data like index
342 QV4::ReturnedValue v4Value(QQuickParticleSystem *particleSystem);
343 void extendLife(float time, QQuickParticleSystem *particleSystem);
344
345 static inline Q_DECL_CONSTEXPR float EPSILON() Q_DECL_NOTHROW { return 0.001f; }
346
347private:
348 QQuickV4ParticleData* v8Datum;
349};
350
351class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticleSystem : public QQuickItem
352{
353 Q_OBJECT
354 Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged)
355 Q_PROPERTY(bool paused READ isPaused WRITE setPaused NOTIFY pausedChanged)
356 Q_PROPERTY(bool empty READ isEmpty NOTIFY emptyChanged)
357 QML_NAMED_ELEMENT(ParticleSystem)
358
359public:
360 explicit QQuickParticleSystem(QQuickItem *parent = nullptr);
361 ~QQuickParticleSystem();
362
363 bool isRunning() const
364 {
365 return m_running;
366 }
367
368 int count(){ return particleCount; }
369
370 static const int maxLife = 600000;
371
372Q_SIGNALS:
373
374 void systemInitialized();
375 void runningChanged(bool arg);
376 void pausedChanged(bool arg);
377 void emptyChanged(bool arg);
378
379public Q_SLOTS:
380 void start(){setRunning(true);}
381 void stop(){setRunning(false);}
382 void restart(){setRunning(false);setRunning(true);}
383 void pause(){setPaused(true);}
384 void resume(){setPaused(false);}
385
386 void reset();
387 void setRunning(bool arg);
388 void setPaused(bool arg);
389
390 virtual int duration() const { return -1; }
391
392
393protected:
394 //This one only once per frame (effectively)
395 void componentComplete() override;
396
397private Q_SLOTS:
398 void emittersChanged();
399 void loadPainter(QQuickParticlePainter *p);
400 void createEngine(); //Not invoked by sprite engine, unlike Sprite uses
401 void particleStateChange(int idx);
402
403public:
404 //These can be called multiple times per frame, performance critical
405 void emitParticle(QQuickParticleData* p, QQuickParticleEmitter *particleEmitter);
406 QQuickParticleData* newDatum(int groupId, bool respectLimits = true, int sysIdx = -1);
407 void finishNewDatum(QQuickParticleData*);
408 void moveGroups(QQuickParticleData *d, int newGIdx);
409 int nextSystemIndex();
410
411 //This one only once per painter per frame
412 int systemSync(QQuickParticlePainter* p);
413
414 //Data members here for ease of related class and auto-test usage. Not "public" API. TODO: d_ptrize
415 QtQuickParticlesPrivate::QFlatSet<QQuickParticleData*> needsReset;
416 QVector<QQuickParticleData*> bySysIdx; //Another reference to the data (data owned by group), but by sysIdx
417 QQuickStochasticEngine* stateEngine;
418
419 QHash<QString, int> groupIds;
420 QVarLengthArray<QQuickParticleGroupData*, 32> groupData;
421 int nextFreeGroupId;
422 int registerParticleGroupData(const QString &name, QQuickParticleGroupData *pgd);
423
424 //Also only here for auto-test usage
425 void updateCurrentTime( int currentTime );
426 QQuickParticleSystemAnimation* m_animation;
427 bool m_running;
428 bool m_debugMode;
429
430 int timeInt;
431 bool initialized;
432 int particleCount;
433
434 void registerParticlePainter(QQuickParticlePainter* p);
435 void registerParticleEmitter(QQuickParticleEmitter* e);
436 void finishRegisteringParticleEmitter(QQuickParticleEmitter *e);
437 void registerParticleAffector(QQuickParticleAffector* a);
438 void registerParticleGroup(QQuickParticleGroup* g);
439
440 static void statePropertyRedirect(QQmlListProperty<QObject> *prop, QObject *value);
441 static void stateRedirect(QQuickParticleGroup* group, QQuickParticleSystem* sys, QObject *value);
442 bool isPaused() const
443 {
444 return m_paused;
445 }
446
447 bool isEmpty() const
448 {
449 return m_empty;
450 }
451
452private:
453 void searchNextFreeGroupId();
454
455private:
456 void initializeSystem();
457 void initGroups();
458 QList<QPointer<QQuickParticleEmitter> > m_emitters;
459 QList<QPointer<QQuickParticleAffector> > m_affectors;
460 QList<QPointer<QQuickParticlePainter> > m_painters;
461 QList<QPointer<QQuickParticlePainter> > m_syncList;
462 QList<QQuickParticleGroup*> m_groups;
463 int m_nextIndex;
464 QSet<int> m_reusableIndexes;
465 bool m_componentComplete;
466
467 bool m_paused;
468 bool m_allDead;
469 bool m_empty;
470};
471
472// Internally, this animation drives all the timing. Painters sync up in their updatePaintNode
473class QQuickParticleSystemAnimation : public QAbstractAnimation
474{
475 Q_OBJECT
476public:
477 QQuickParticleSystemAnimation(QQuickParticleSystem* system)
478 : QAbstractAnimation(static_cast<QObject*>(system)), m_system(system)
479 { }
480protected:
481 void updateCurrentTime(int t) override
482 {
483 m_system->updateCurrentTime(currentTime: t);
484 }
485
486 int duration() const override
487 {
488 return -1;
489 }
490
491private:
492 QQuickParticleSystem* m_system;
493};
494
495inline void QQuickParticleData::setInstantaneousAX(float ax, QQuickParticleSystem* particleSystem)
496{
497 float t = (particleSystem->timeInt / 1000.0f) - this->t;
498 float t_sq = t * t;
499 float vx = (this->vx + t * this->ax) - t * ax;
500 float ex = this->x + this->vx * t + 0.5f * this->ax * t_sq;
501 float x = ex - t * vx - 0.5f * t_sq * ax;
502
503 this->ax = ax;
504 this->vx = vx;
505 this->x = x;
506}
507
508inline void QQuickParticleData::setInstantaneousVX(float vx, QQuickParticleSystem* particleSystem)
509{
510 float t = (particleSystem->timeInt / 1000.0f) - this->t;
511 float t_sq = t * t;
512 float evx = vx - t * this->ax;
513 float ex = this->x + this->vx * t + 0.5f * this->ax * t_sq;
514 float x = ex - t * evx - 0.5f * t_sq * this->ax;
515
516 this->vx = evx;
517 this->x = x;
518}
519
520inline void QQuickParticleData::setInstantaneousX(float x, QQuickParticleSystem* particleSystem)
521{
522 float t = (particleSystem->timeInt / 1000.0f) - this->t;
523 float t_sq = t * t;
524 this->x = x - t * this->vx - 0.5f * t_sq * this->ax;
525}
526
527inline void QQuickParticleData::setInstantaneousAY(float ay, QQuickParticleSystem* particleSystem)
528{
529 float t = (particleSystem->timeInt / 1000.0f) - this->t;
530 float t_sq = t * t;
531 float vy = (this->vy + t * this->ay) - t * ay;
532 float ey = this->y + this->vy * t + 0.5f * this->ay * t_sq;
533 float y = ey - t * vy - 0.5f * t_sq * ay;
534
535 this->ay = ay;
536 this->vy = vy;
537 this->y = y;
538}
539
540inline void QQuickParticleData::setInstantaneousVY(float vy, QQuickParticleSystem* particleSystem)
541{
542 float t = (particleSystem->timeInt / 1000.0f) - this->t;
543 float t_sq = t * t;
544 float evy = vy - t * this->ay;
545 float ey = this->y + this->vy * t + 0.5f * this->ay * t_sq;
546 float y = ey - t*evy - 0.5f * t_sq * this->ay;
547
548 this->vy = evy;
549 this->y = y;
550}
551
552inline void QQuickParticleData::setInstantaneousY(float y, QQuickParticleSystem *particleSystem)
553{
554 float t = (particleSystem->timeInt / 1000.0f) - this->t;
555 float t_sq = t * t;
556 this->y = y - t * this->vy - 0.5f * t_sq * this->ay;
557}
558
559inline float QQuickParticleData::curX(QQuickParticleSystem *particleSystem) const
560{
561 float t = (particleSystem->timeInt / 1000.0f) - this->t;
562 float t_sq = t * t;
563 return this->x + this->vx * t + 0.5f * this->ax * t_sq;
564}
565
566inline float QQuickParticleData::curVX(QQuickParticleSystem *particleSystem) const
567{
568 float t = (particleSystem->timeInt / 1000.0f) - this->t;
569 return this->vx + t * this->ax;
570}
571
572inline float QQuickParticleData::curY(QQuickParticleSystem *particleSystem) const
573{
574 float t = (particleSystem->timeInt / 1000.0f) - this->t;
575 float t_sq = t * t;
576 return y + vy * t + 0.5f * ay * t_sq;
577}
578
579inline float QQuickParticleData::curVY(QQuickParticleSystem *particleSystem) const
580{
581 float t = (particleSystem->timeInt / 1000.0f) - this->t;
582 return vy + t*ay;
583}
584
585inline bool QQuickParticleData::stillAlive(QQuickParticleSystem* system) const
586{
587 if (!system)
588 return false;
589 return (t + lifeSpan - EPSILON()) > (system->timeInt / 1000.0f);
590}
591
592inline bool QQuickParticleData::alive(QQuickParticleSystem* system) const
593{
594 if (!system)
595 return false;
596 float st = (system->timeInt / 1000.0f);
597 return (t + EPSILON()) < st && (t + lifeSpan - EPSILON()) > st;
598}
599
600inline float QQuickParticleData::lifeLeft(QQuickParticleSystem *particleSystem) const
601{
602 if (!particleSystem)
603 return 0.0f;
604 return (t + lifeSpan) - (particleSystem->timeInt / 1000.0f);
605}
606
607inline float QQuickParticleData::curSize(QQuickParticleSystem *particleSystem) const
608{
609 if (!particleSystem || lifeSpan == 0.0f)
610 return 0.0f;
611 return size + (endSize - size) * (1 - (lifeLeft(particleSystem) / lifeSpan));
612}
613
614QT_END_NAMESPACE
615
616#endif // PARTICLESYSTEM_H
617
618
619

source code of qtdeclarative/src/particles/qquickparticlesystem_p.h