Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qquickparticlesystem.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include <QtQuick/qsgnode.h>
9#include <private/qquickspriteengine_p.h>
10#include <private/qquicksprite_p.h>
13
14#include "qquicktrailemitter_p.h"//###For auto-follow on states, perhaps should be in emitter?
15#include <private/qqmlengine_p.h>
16#include <private/qqmlglobal_p.h>
17#include <private/qqmlvaluetypewrapper_p.h>
18#include <cmath>
19#include <QDebug>
20
22//###Switch to define later, for now user-friendly (no compilation) debugging is worth it
23DEFINE_BOOL_CONFIG_OPTION(qmlParticlesDebug, QML_PARTICLES_DEBUG)
24
25
26/* \internal ParticleSystem internals documentation
27
28 Affectors, Painters, Emitters and Groups all register themselves on construction as a callback
29 from their setSystem (or componentComplete if they have a system from a parent).
30
31 Particle data is stored by group, They have a group index (used by the particle system almost
32 everywhere) and a global index (used by the Stochastic state engine powering stochastic group
33 transitions). Each group has a recycling list/heap that stores the particle data.
34
35 The recycling list/heap is a heap of particle data sorted by when they're expected to die. If
36 they die prematurely then they are marked as reusable (and will probably still be alive when
37 they exit the heap). If they have their life extended, then they aren't dead when expected.
38 If this happens, they go back in the heap with the new estimate. If they have died on schedule,
39 then the indexes are marked as reusable. If no indexes are reusable when new particles are
40 requested, then the list is extended. This relatively complex datastructure is because memory
41 allocation and deallocation on this scale proved to be a significant performance cost. In order
42 to reuse the indexes validly (even when particles can have their life extended or cut short
43 dynamically, or particle counts grow) this seemed to be the most efficient option for keeping
44 track of which indices could be reused.
45
46 When a new particle is emitted, the emitter gets a new datum from the group (through the
47 system), and sets properties on it. Then it's passed back to the group briefly so that it can
48 now guess when the particle will die. Then the painters get a change to initialize properties
49 as well, since particle data includes shared data from painters as well as logical particle
50 data.
51
52 Every animation advance, the simulation advances by running all emitters for the elapsed
53 duration, then running all affectors, then telling all particle painters to update changed
54 particles. The ParticlePainter superclass stores these changes, and they are implemented
55 when the painter is called to paint in the render thread.
56
57 Particle group changes move the particle from one group to another by killing the old particle
58 and then creating a new one with the same data in the new group.
59
60 Note that currently groups only grow. Given that data is stored in vectors, it is non-trivial
61 to pluck out the unused indexes when the count goes down. Given the dynamic nature of the
62 system, it is difficult to tell if those unused data instances will be used again. Still,
63 some form of garbage collection is on the long term plan.
64*/
65
66
69
168static inline int roundedTime(qreal a)
169{// in ms
170 return (int)qRound(a*1000.0);
171}
172
174 : m_data(0)
175{
176 m_data.reserve(1000);
177 clear();
178}
179
180void QQuickParticleDataHeap::grow() //###Consider automatic growth vs resize() calls from GroupData
181{
182 m_data.resize(qsizetype(1) << ++m_size);
183}
184
189
191{
192 //TODO: Optimize 0 lifespan (or already dead) case
193 if (m_lookups.contains(time)) {
194 m_data[m_lookups[time]].data << data;
195 return;
196 }
197 if (m_end == (1 << m_size))
198 grow();
199 m_data[m_end].time = time;
200 m_data[m_end].data.clear();
201 m_data[m_end].data.insert(data);
202 m_lookups.insert(time, m_end);
203 bubbleUp(m_end++);
204}
205
207{
208 Q_ASSERT(!isEmpty());
209 return m_data[0].time;
210}
211
212QSet<QQuickParticleData*> QQuickParticleDataHeap::pop()
213{
214 if (!m_end)
215 return QSet<QQuickParticleData*> ();
216 QSet<QQuickParticleData*> ret = m_data[0].data;
217 m_lookups.remove(m_data[0].time);
218 if (m_end == 1) {
219 --m_end;
220 } else {
221 m_data[0] = m_data[--m_end];
222 bubbleDown(0);
223 }
224 return ret;
225}
226
228{
229 m_size = 0;
230 m_end = 0;
231 //m_size is in powers of two. So to start at 0 we have one allocated
232 m_data.resize(1);
233 m_lookups.clear();
234}
235
237{
238 for (int i=0; i<m_end; i++)
239 if (m_data[i].data.contains(d))
240 return true;
241 return false;
242}
243
244void QQuickParticleDataHeap::swap(int a, int b)
245{
246 m_tmp = m_data[a];
247 m_data[a] = m_data[b];
248 m_data[b] = m_tmp;
249 m_lookups[m_data[a].time] = a;
250 m_lookups[m_data[b].time] = b;
251}
252
253void QQuickParticleDataHeap::bubbleUp(int idx)//tends to be called once
254{
255 if (!idx)
256 return;
257 int parent = (idx-1)/2;
258 if (m_data[idx].time < m_data[parent].time) {
259 swap(idx, parent);
260 bubbleUp(parent);
261 }
262}
263
264void QQuickParticleDataHeap::bubbleDown(int idx)//tends to be called log n times
265{
266 int left = idx*2 + 1;
267 if (left >= m_end)
268 return;
269 int lesser = left;
270 int right = idx*2 + 2;
271 if (right < m_end) {
272 if (m_data[left].time > m_data[right].time)
273 lesser = right;
274 }
275 if (m_data[idx].time > m_data[lesser].time) {
276 swap(idx, lesser);
277 bubbleDown(lesser);
278 }
279}
280
282 : index(sys->registerParticleGroupData(name, this))
283 , m_size(0)
284 , m_system(sys)
285{
286 initList();
287}
288
290{
291 for (QQuickParticleData *d : std::as_const(data))
292 delete d;
293}
294
295QString QQuickParticleGroupData::name() const//### Worth caching as well?
296{
297 return m_system->groupIds.key(index);
298}
299
301{
302 if (newSize == m_size)
303 return;
304 Q_ASSERT(newSize > m_size);//XXX allow shrinking
305 data.resize(newSize);
306 freeList.resize(newSize);
307 for (int i=m_size; i<newSize; i++) {
309 data[i]->groupId = index;
310 data[i]->index = i;
311 }
312 int delta = newSize - m_size;
313 m_size = newSize;
314 for (QQuickParticlePainter *p : std::as_const(painters))
315 p->setCount(p->count() + delta);
316}
317
322
324{
325 Q_ASSERT(d->groupId == index);
326 d->lifeSpan = 0;//Kill off
327 for (QQuickParticlePainter *p : std::as_const(painters))
328 p->reload(d);
329 freeList.free(d->index);
330}
331
333{
334 //recycle();//Extra recycler round to be sure?
335
336 while (freeList.hasUnusedEntries()) {
337 int idx = freeList.alloc();
338 if (data[idx]->stillAlive(m_system)) {// ### This means resurrection of 'dead' particles. Is that allowed?
339 prepareRecycler(data[idx]);
340 continue;
341 }
342 return data[idx];
343 }
344 if (respectsLimits)
345 return nullptr;
346
347 int oldSize = m_size;
348 setSize(oldSize + 10);//###+1,10%,+10? Choose something non-arbitrarily
349 int idx = freeList.alloc();
350 Q_ASSERT(idx == oldSize);
351 return data[idx];
352}
353
355{
356 m_latestAliveParticles.clear();
357
358 while (!dataHeap.isEmpty() && dataHeap.top() <= m_system->timeInt) {
359 for (QQuickParticleData *datum : dataHeap.pop()) {
360 if (!datum->stillAlive(m_system)) {
361 freeList.free(datum->index);
362 } else {
363 m_latestAliveParticles.push_back(datum);
364 }
365 }
366 }
367
368 for (auto particle : m_latestAliveParticles)
369 prepareRecycler(particle); //ttl has been altered mid-way, put it back
370
371 //TODO: If the data is clear, gc (consider shrinking stack size)?
372 return freeList.count() == 0;
373}
374
376{
377 if (d->lifeSpan*1000 < m_system->maxLife) {
379 } else {
380 int extend = 2 * m_system->maxLife / 3;
381 while ((roundedTime(d->t) + extend) <= m_system->timeInt)
382 d->extendLife(m_system->maxLife / 3000.0, m_system);
383 dataHeap.insertTimed(d, roundedTime(d->t) + extend);
384 }
385}
386
388{
389 return QQuickV4ParticleData(this, particleSystem);
390}
391
393{
394 qDebug() << "Particle" << systemIndex << groupId << "/" << index << stillAlive(particleSystem)
395 << "Pos: " << x << "," << y
396 << "Vel: " << vx << "," << vy
397 << "Acc: " << ax << "," << ay
398 << "Size: " << size << "," << endSize
399 << "Time: " << t << "," <<lifeSpan << ";" << (particleSystem->timeInt / 1000.0) ;
400}
401
403{
404 qreal newX = curX(particleSystem);
405 qreal newY = curY(particleSystem);
406 qreal newVX = curVX(particleSystem);
407 qreal newVY = curVY(particleSystem);
408
409 t += time;
410 animT += time;
411
412 qreal elapsed = (particleSystem->timeInt / 1000.0) - t;
413 qreal evy = newVY - elapsed*ay;
414 qreal ey = newY - elapsed*evy - 0.5 * elapsed*elapsed*ay;
415 qreal evx = newVX - elapsed*ax;
416 qreal ex = newX - elapsed*evx - 0.5 * elapsed*elapsed*ax;
417
418 x = ex;
419 vx = evx;
420 y = ey;
421 vy = evy;
422}
423
425 QQuickItem(parent),
426 stateEngine(nullptr),
427 nextFreeGroupId(0),
428 m_animation(nullptr),
429 m_running(true),
430 initialized(0),
431 particleCount(0),
432 m_nextIndex(0),
433 m_componentComplete(false),
434 m_paused(false),
435 m_empty(true)
436{
437 m_debugMode = qmlParticlesDebug();
438}
439
441{
442 for (QQuickParticleGroupData *gd : std::as_const(groupData))
443 delete gd;
444}
445
446void QQuickParticleSystem::initGroups()
447{
448 m_reusableIndexes.clear();
449 m_nextIndex = 0;
450
453 groupIds.clear();
454 nextFreeGroupId = 0;
455
456 for (auto e : std::as_const(m_emitters)) {
457 e->reclaculateGroupId();
458 }
459 for (QQuickParticlePainter *p : std::as_const(m_painters)) {
460 p->recalculateGroupIds();
461 }
462
463 QQuickParticleGroupData *pd = new QQuickParticleGroupData(QString(), this); // Default group
464 Q_ASSERT(pd->index == 0);
465 Q_UNUSED(pd);
466}
467
469{
470 if (m_debugMode)
471 qDebug() << "Registering Painter" << p << "to" << this;
472 //TODO: a way to Unregister emitters, painters and affectors
473 m_painters << QPointer<QQuickParticlePainter>(p);//###Set or uniqueness checking?
474
475 connect(p, &QQuickParticlePainter::groupsChanged, this, [this, p] { this->loadPainter(p); }, Qt::QueuedConnection);
476 loadPainter(p);
477}
478
480{
481 if (m_debugMode)
482 qDebug() << "Registering Emitter" << e << "to" << this;
483 m_emitters << QPointer<QQuickParticleEmitter>(e);//###How to get them out?
484}
485
487{
489 this, &QQuickParticleSystem::emittersChanged);
491 this, &QQuickParticleSystem::emittersChanged);
492 if (m_componentComplete)
493 emitterAdded(e);
494 e->reset();//Start, so that starttime factors appropriately
495}
496
498{
499 if (m_debugMode)
500 qDebug() << "Registering Affector" << a << "to" << this;
501 if (!m_affectors.contains(a))
502 m_affectors << QPointer<QQuickParticleAffector>(a);
503}
504
506{
507 if (m_debugMode)
508 qDebug() << "Registering Group" << g << "to" << this;
509 m_groups << QPointer<QQuickParticleGroup>(g);
510 createEngine();
511}
512
514{
515 if (m_running != arg) {
516 m_running = arg;
518 setPaused(false);
519 if (m_animation)//Not created until componentCompleted
521 reset();
522 }
523}
524
526 if (m_paused != arg) {
527 m_paused = arg;
529 m_paused ? m_animation->pause() : m_animation->resume();
530 if (!m_paused) {
531 for (QQuickParticlePainter *p : std::as_const(m_painters)) {
532 if (p) {
533 p->update();
534 }
535 }
536 }
538 }
539}
540
541void QQuickParticleSystem::statePropertyRedirect(QQmlListProperty<QObject> *prop, QObject *value)
542{
543 //Hooks up automatic state-associated stuff
544 QQuickParticleSystem* sys = qobject_cast<QQuickParticleSystem*>(prop->object->parent());
545 QQuickParticleGroup* group = qobject_cast<QQuickParticleGroup*>(prop->object);
546 if (!group || !sys || !value)
547 return;
549}
550
552{
554 list << group->name();
555 QQuickParticleAffector* a = qobject_cast<QQuickParticleAffector*>(value);
556 if (a) {
557 a->setParentItem(sys);
558 a->setGroups(list);
559 a->setSystem(sys);
560 return;
561 }
562 QQuickTrailEmitter* fe = qobject_cast<QQuickTrailEmitter*>(value);
563 if (fe) {
564 fe->setParentItem(sys);
565 fe->setFollow(group->name());
566 fe->setSystem(sys);
567 return;
568 }
569 QQuickParticleEmitter* e = qobject_cast<QQuickParticleEmitter*>(value);
570 if (e) {
571 e->setParentItem(sys);
572 e->setGroup(group->name());
573 e->setSystem(sys);
574 return;
575 }
576 QQuickParticlePainter* p = qobject_cast<QQuickParticlePainter*>(value);
577 if (p) {
578 p->setParentItem(sys);
579 p->setGroups(list);
580 p->setSystem(sys);
581 return;
582 }
583 qWarning() << value << " was placed inside a particle system state but cannot be taken into the particle system. It will be lost.";
584}
585
586
588{
590 int id;
591 if (nextFreeGroupId >= groupData.size()) {
592 groupData.push_back(pgd);
594 id = nextFreeGroupId - 1;
595 } else {
596 id = nextFreeGroupId;
597 groupData[id] = pgd;
598 searchNextFreeGroupId();
599 }
600 groupIds.insert(name, id);
601 return id;
602}
603
604void QQuickParticleSystem::searchNextFreeGroupId()
605{
607 for (int ei = groupData.size(); nextFreeGroupId != ei; ++nextFreeGroupId) {
608 if (groupData[nextFreeGroupId] == nullptr) {
609 return;
610 }
611 }
612}
613
615
616{
618 m_componentComplete = true;
620 reset();//restarts animation as well
621}
622
624{
625 if (!m_componentComplete)
626 return;
627
628 timeInt = 0;
629 //Clear guarded pointers which have been deleted
630 m_emitters.removeAll(nullptr);
631 m_painters.removeAll(nullptr);
632 m_affectors.removeAll(nullptr);
633
634 bySysIdx.resize(0);
635 initGroups();//Also clears all logical particles
636
637 if (!m_running)
638 return;
639
640 for (QQuickParticleEmitter *e : std::as_const(m_emitters))
641 e->reset();
642
643 emittersChanged();
644
645 for (QQuickParticlePainter *p : std::as_const(m_painters)) {
646 loadPainter(p);
647 p->reset();
648 }
649
650 //### Do affectors need reset too?
651 if (m_animation) {//Animation is explicitly disabled in benchmarks
652 //reset restarts animation (if running)
654 m_animation->stop();
656 if (m_paused)
658 }
659
660 initialized = true;
661}
662
663
664void QQuickParticleSystem::loadPainter(QQuickParticlePainter *painter)
665{
666 if (!m_componentComplete || !painter)
667 return;
668
670 sg->painters.removeOne(painter);
671 }
672
673 int particleCount = 0;
674 if (painter->groups().isEmpty()) {//Uses default particle
675 static QStringList def = QStringList() << QString();
676 painter->setGroups(def);
678 groupData[0]->painters << painter;
679 } else {
680 for (auto groupId : painter->groupIds()) {
681 QQuickParticleGroupData *gd = groupData[groupId];
682 particleCount += gd->size();
683 gd->painters << painter;
684 }
685 }
686 painter->setCount(particleCount);
687 painter->update();//Initial update here
688 return;
689}
690
691void QQuickParticleSystem::emittersChanged()
692{
693 if (!m_componentComplete)
694 return;
695
696 QVector<int> previousSizes;
697 QVector<int> newSizes;
698 previousSizes.reserve(groupData.size());
699 newSizes.reserve(groupData.size());
700 for (int i = 0, ei = groupData.size(); i != ei; ++i) {
701 previousSizes << groupData[i]->size();
702 newSizes << 0;
703 }
704
705 // Populate groups and set sizes.
706 for (int i = 0; i < m_emitters.size(); ) {
707 QQuickParticleEmitter *e = m_emitters.at(i);
708 if (!e) {
709 m_emitters.removeAt(i);
710 continue;
711 }
712
713 int groupId = e->groupId();
714 if (groupId == QQuickParticleGroupData::InvalidID) {
715 groupId = (new QQuickParticleGroupData(e->group(), this))->index;
716 previousSizes << 0;
717 newSizes << 0;
718 }
719 newSizes[groupId] += e->particleCount();
720 //###: Cull emptied groups?
721
722 ++i;
723 }
724
725 //TODO: Garbage collection?
726 particleCount = 0;
727 for (int i = 0, ei = groupData.size(); i != ei; ++i) {
728 groupData[i]->setSize(qMax(newSizes[i], previousSizes[i]));
730 }
731
732 postProcessEmitters();
733}
734
735void QQuickParticleSystem::postProcessEmitters()
736{
737 if (m_debugMode)
738 qDebug() << "Particle system emitters changed. New particle count: " << particleCount << "in" << groupData.size() << "groups.";
739
740 if (particleCount > bySysIdx.size())//New datum requests haven't updated it
741 bySysIdx.resize(particleCount);
742
743 for (QQuickParticleAffector *a : std::as_const(m_affectors)) {//Groups may have changed
744 if (a) {
745 a->m_updateIntSet = true;
746 }
747 }
748
749 for (QQuickParticlePainter *p : std::as_const(m_painters))
750 loadPainter(p);
751
752 if (!m_groups.isEmpty())
753 createEngine();
754
755}
756
757void QQuickParticleSystem::emitterAdded(QQuickParticleEmitter *e)
758{
759 if (!m_componentComplete)
760 return;
761
762 // Populate group and set size.
763 const int groupId = e->groupId();
764 if (groupId == QQuickParticleGroupData::InvalidID) {
766 group->setSize(e->particleCount());
767 } else {
769 group->setSize(group->size() + e->particleCount());
770 }
771
772 // groupData can have changed independently, so we still have to iterate it all
773 // to count the particles.
774 particleCount = 0;
775 for (int i = 0, ei = groupData.size(); i != ei; ++i)
777
778 postProcessEmitters();
779}
780
781void QQuickParticleSystem::createEngine()
782{
783 if (!m_componentComplete)
784 return;
786 qDebug() << "Resetting Existing Sprite Engine...";
787 //### Solve the losses if size/states go down
788 for (QQuickParticleGroup *group : std::as_const(m_groups)) {
789 bool exists = false;
790 for (auto it = groupIds.keyBegin(), end = groupIds.keyEnd(); it != end; ++it) {
791 if (group->name() == *it) {
792 exists = true;
793 break;
794 }
795 }
796 if (!exists) {
797 new QQuickParticleGroupData(group->name(), this);
798 }
799 }
800
801 if (m_groups.size()) {
802 //Reorder groups List so as to have the same order as groupData
803 // TODO: can't we just merge the two lists?
804 QList<QQuickParticleGroup*> newList;
805 for (int i = 0, ei = groupData.size(); i != ei; ++i) {
806 bool exists = false;
807 QString name = groupData[i]->name();
808 for (QQuickParticleGroup *existing : std::as_const(m_groups)) {
809 if (existing->name() == name) {
810 newList << existing;
811 exists = true;
812 }
813 }
814 if (!exists) {
815 newList << new QQuickParticleGroup(this);
816 newList.back()->setName(name);
817 }
818 }
819 m_groups = newList;
820 QList<QQuickStochasticState*> states;
821 states.reserve(m_groups.size());
822 for (QQuickParticleGroup *g : std::as_const(m_groups))
824
825 if (!stateEngine)
829
831 this, &QQuickParticleSystem::particleStateChange);
832
833 } else {
834 if (stateEngine)
835 delete stateEngine;
836 stateEngine = nullptr;
837 }
838
839}
840
841void QQuickParticleSystem::particleStateChange(int idx)
842{
844}
845
847{
848 if (!d || newGIdx == d->groupId)
849 return;
850
851 QQuickParticleData *pd = newDatum(newGIdx, false, d->systemIndex, d);
852 if (!pd)
853 return;
854
855 finishNewDatum(pd);
856
857 d->systemIndex = -1;
858 groupData[d->groupId]->kill(d);
859}
860
862{
863 if (!m_reusableIndexes.isEmpty()) {
864 int ret = *(m_reusableIndexes.begin());
865 m_reusableIndexes.remove(ret);
866 return ret;
867 }
868 if (m_nextIndex >= bySysIdx.size()) {
869 bySysIdx.resize(bySysIdx.size() < 10 ? 10 : bySysIdx.size()*1.1);//###+1,10%,+10? Choose something non-arbitrarily
870 if (stateEngine)
872
873 }
874 return m_nextIndex++;
875}
876
878 int groupId, bool respectLimits, int sysIndex,
879 const QQuickParticleData *cloneFrom)
880{
881 Q_ASSERT(groupId < groupData.size());//XXX shouldn't really be an assert
882
883 QQuickParticleData *ret = groupData[groupId]->newDatum(respectLimits);
884 if (!ret)
885 return nullptr;
886
887 if (cloneFrom) {
888 // We need to retain the "identity" information of the new particle data since it may be
889 // "recycled" and still be tracked.
890 const int retainedIndex = ret->index;
891 const int retainedGroupId = ret->groupId;
892 const int retainedSystemIndex = ret->systemIndex;
893 *ret = *cloneFrom;
894 ret->index = retainedIndex;
895 ret->groupId = retainedGroupId;
896 ret->systemIndex = retainedSystemIndex;
897 }
898
899 if (sysIndex == -1) {
900 if (ret->systemIndex == -1)
901 ret->systemIndex = nextSystemIndex();
902 } else {
903 if (ret->systemIndex != -1) {
904 if (stateEngine)
905 stateEngine->stop(ret->systemIndex);
906 m_reusableIndexes << ret->systemIndex;
907 bySysIdx[ret->systemIndex] = 0;
908 }
909 ret->systemIndex = sysIndex;
910 }
911 bySysIdx[ret->systemIndex] = ret;
912
913 if (stateEngine)
914 stateEngine->start(ret->systemIndex, ret->groupId);
915
916 m_empty = false;
917 return ret;
918}
919
921{// called from prepareNextFrame()->emitWindow - enforce?
922 //Account for relative emitter position
923 bool okay = false;
924 QTransform t = particleEmitter->itemTransform(this, &okay);
925 if (okay) {
926 qreal tx,ty;
927 t.map(pd->x, pd->y, &tx, &ty);
928 pd->x = tx;
929 pd->y = ty;
930 }
931
932 finishNewDatum(pd);
933}
934
936{
937 Q_ASSERT(pd);
938 groupData[pd->groupId]->prepareRecycler(pd);
939
940 for (QQuickParticleAffector *a : std::as_const(m_affectors))
941 if (a && a->m_needsReset)
942 a->reset(pd);
943 for (QQuickParticlePainter *p : std::as_const(groupData[pd->groupId]->painters))
944 if (p)
945 p->load(pd);
946}
947
949{
950 if (!initialized)
951 return;//error in initialization
952
953 //### Elapsed time never shrinks - may cause problems if left emitting for weeks at a time.
954 qreal dt = timeInt / 1000.;
956 qreal time = timeInt / 1000.;
957 dt = time - dt;
959
960 m_emitters.removeAll(nullptr);
961 m_painters.removeAll(nullptr);
962 m_affectors.removeAll(nullptr);
963
964 bool oldClear = m_empty;
965 m_empty = true;
966 for (QQuickParticleGroupData *gd : std::as_const(groupData))//Recycle all groups and see if they're out of live particles
967 m_empty = gd->recycle() && m_empty;
968
969 if (stateEngine)
971
972 for (QQuickParticleEmitter *emitter : std::as_const(m_emitters))
973 emitter->emitWindow(timeInt);
974 for (QQuickParticleAffector *a : std::as_const(m_affectors))
975 a->affectSystem(dt);
977 for (QQuickParticlePainter *p : std::as_const(groupData[d->groupId]->painters))
978 p->reload(d);
979
980 if (oldClear != m_empty)
981 emptyChanged(m_empty);
982}
983
985{
986 if (!m_running)
987 return 0;
988 if (!initialized)
989 return 0;//error in initialization
990 p->performPendingCommits();
991 return timeInt;
992}
993
994
996
997#include "moc_qquickparticlesystem_p.cpp"
NSData * m_data
State state
state of the animation.
void resume()
Resumes the animation after it was paused.
void stop()
Stops the animation.
void start(QAbstractAnimation::DeletionPolicy policy=KeepWhenStopped)
Starts the animation.
void pause()
Pauses the animation.
key_iterator keyEnd() const noexcept
Definition qhash.h:1221
bool remove(const Key &key)
Removes the item that has the key from the hash.
Definition qhash.h:958
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:1007
key_iterator keyBegin() const noexcept
Definition qhash.h:1220
Key key(const T &value) const noexcept
Definition qhash.h:1034
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:951
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
void removeAt(qsizetype i)
Definition qlist.h:590
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
qsizetype removeAll(const AT &t)
Definition qlist.h:592
void reserve(qsizetype size)
Definition qlist.h:753
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
void setParentItem(QQuickItem *parent)
void componentComplete() override
\reimp Derived classes should call the base class method before adding their own actions to perform a...
QSizeF size() const
QSet< QQuickParticleData * > pop()
void insertTimed(QQuickParticleData *data, int time)
void insert(QQuickParticleData *data)
bool contains(QQuickParticleData *)
QQuickParticleGroupData::ID groupId
float curY(QQuickParticleSystem *particleSystem) const
void extendLife(float time, QQuickParticleSystem *particleSystem)
float curVY(QQuickParticleSystem *particleSystem) const
bool stillAlive(QQuickParticleSystem *particleSystem) const
void debugDump(QQuickParticleSystem *particleSystem) const
float curVX(QQuickParticleSystem *particleSystem) const
QQuickV4ParticleData v4Value(QQuickParticleSystem *particleSystem)
float curX(QQuickParticleSystem *particleSystem) const
void groupChanged(const QString &arg)
void setSystem(QQuickParticleSystem *arg)
void setGroup(const QString &arg)
QQuickParticleGroupData::ID groupId() const
void prepareRecycler(QQuickParticleData *d)
QQuickParticleData * newDatum(bool respectsLimits)
QQuickParticleVarLengthArray< QQuickParticlePainter *, 4 > painters
QQuickParticleGroupData(const QString &name, QQuickParticleSystem *sys)
QQuickParticleDataHeap dataHeap
void kill(QQuickParticleData *d)
void groupsChanged(const QStringList &arg)
void registerParticleEmitter(QQuickParticleEmitter *e)
QQuickStochasticEngine * stateEngine
QQuickParticleData * newDatum(int groupId, bool respectLimits=true, int sysIdx=-1, const QQuickParticleData *cloneFrom=nullptr)
void runningChanged(bool arg)
void finishRegisteringParticleEmitter(QQuickParticleEmitter *e)
int registerParticleGroupData(const QString &name, QQuickParticleGroupData *pgd)
void registerParticlePainter(QQuickParticlePainter *p)
void emitParticle(QQuickParticleData *p, QQuickParticleEmitter *particleEmitter)
QVarLengthArray< QQuickParticleGroupData *, 32 > groupData
QQuickParticleSystem(QQuickItem *parent=nullptr)
QQuickParticleSystemAnimation * m_animation
void registerParticleGroup(QQuickParticleGroup *g)
void componentComplete() override
\reimp Derived classes should call the base class method before adding their own actions to perform a...
static void stateRedirect(QQuickParticleGroup *group, QQuickParticleSystem *sys, QObject *value)
QHash< QString, int > groupIds
void pausedChanged(bool arg)
QVector< QQuickParticleData * > bySysIdx
static void statePropertyRedirect(QQmlListProperty< QObject > *prop, QObject *value)
void finishNewDatum(QQuickParticleData *)
void registerParticleAffector(QQuickParticleAffector *a)
void updateCurrentTime(int currentTime)
void moveGroups(QQuickParticleData *d, int newGIdx)
void emptyChanged(bool arg)
int systemSync(QQuickParticlePainter *p)
QSet< QQuickParticleData * > needsReset
void stateChanged(int idx)
void start(int index=0, int state=0)
QList< QQuickStochasticState * > m_states
int curState(int index=0) const
bool remove(const T &value)
Definition qset.h:63
iterator begin()
Definition qset.h:136
bool isEmpty() const
Definition qset.h:52
void clear()
Definition qset.h:61
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
constexpr size_type size() const noexcept
void push_back(const T &t)
#define this
Definition dialogs.cpp:9
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
Combined button and popup list for selecting options.
@ QueuedConnection
QList< QString > QStringList
Constructs a string list that contains the given string, str.
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
return ret
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLboolean GLboolean GLboolean b
GLint GLint GLint GLint GLint x
[0]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLuint GLuint end
GLenum GLuint id
[7]
GLdouble GLdouble right
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint left
GLboolean GLuint group
GLboolean GLboolean g
GLuint name
GLint y
GLdouble GLdouble t
Definition qopenglext.h:243
GLfloat GLfloat p
[1]
GLbyte ty
GLuint * states
#define DEFINE_BOOL_CONFIG_OPTION(name, var)
static QT_BEGIN_NAMESPACE int roundedTime(qreal a)
A system which includes particle painter, emitter, and affector types.
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int void * arg
static double elapsed(qint64 after, qint64 before)
#define emit
#define Q_UNUSED(x)
ptrdiff_t qsizetype
Definition qtypes.h:165
double qreal
Definition qtypes.h:187
static double currentTime()
QList< int > list
[14]
QObject::connect nullptr
QPainter painter(this)
[7]
bool contains(const AT &t) const noexcept
Definition qlist.h:45