5#include <QtQuick/qsgnode.h>
9#include <private/qquickspriteengine_p.h>
10#include <private/qquicksprite_p.h>
15#include <private/qqmlengine_p.h>
16#include <private/qqmlglobal_p.h>
17#include <private/qqmlvaluetypewrapper_p.h>
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
67
68
69
70
71
72
73
76
77
78
79
80
81
82
86
87
88
89
90
91
92
93
94
95
96
99
100
101
102
103
104
105
106
107
108
111
112
113
114
115
116
117
118
119
120
123
124
125
126
127
128
131
132
133
134
135
136
139
140
141
142
143
144
147
148
149
150
151
152
155
156
157
158
159
160
162
163
164
165
166
170 return (
int)qRound(a*1000.0);
173QQuickParticleDataHeap::QQuickParticleDataHeap()
176 m_data.reserve(1000);
180void QQuickParticleDataHeap::grow()
182 m_data.resize(qsizetype(1) << ++m_size);
185void QQuickParticleDataHeap::insert(QQuickParticleData* data)
187 insertTimed(data, roundedTime(data->t + data->lifeSpan));
190void QQuickParticleDataHeap::insertTimed(QQuickParticleData* data,
int time)
193 if (m_lookups.contains(time)) {
194 m_data[m_lookups[time]].data << data;
197 if (m_end == (1 << m_size))
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);
206int QQuickParticleDataHeap::top()
208 Q_ASSERT(!isEmpty());
209 return m_data[0].time;
212QSet<QQuickParticleData*> QQuickParticleDataHeap::pop()
215 return QSet<QQuickParticleData*> ();
216 QSet<QQuickParticleData*> ret = m_data[0].data;
217 m_lookups.remove(m_data[0].time);
221 m_data[0] = m_data[--m_end];
227void QQuickParticleDataHeap::clear()
236bool QQuickParticleDataHeap::contains(QQuickParticleData* d)
238 for (
int i=0; i<m_end; i++)
239 if (m_data[i].data.contains(d))
244void QQuickParticleDataHeap::swap(
int a,
int b)
247 m_data[a] = m_data[b];
249 m_lookups[m_data[a].time] = a;
250 m_lookups[m_data[b].time] = b;
253void QQuickParticleDataHeap::bubbleUp(
int idx)
257 int parent = (idx-1)/2;
258 if (m_data[idx].time < m_data[parent].time) {
264void QQuickParticleDataHeap::bubbleDown(
int idx)
266 int left = idx*2 + 1;
270 int right = idx*2 + 2;
272 if (m_data[left].time > m_data[right].time)
275 if (m_data[idx].time > m_data[lesser].time) {
281QQuickParticleGroupData::QQuickParticleGroupData(
const QString &name, QQuickParticleSystem* sys)
282 : index(sys->registerParticleGroupData(name,
this))
289QQuickParticleGroupData::~QQuickParticleGroupData()
291 for (QQuickParticleData *d : std::as_const(data))
295QString QQuickParticleGroupData::name()
const
297 return m_system->groupIds.key(index);
300void QQuickParticleGroupData::setSize(
int newSize)
302 if (newSize == m_size)
304 Q_ASSERT(newSize > m_size);
305 data.resize(newSize);
306 freeList.resize(newSize);
307 for (
int i=m_size; i<newSize; i++) {
308 data[i] =
new QQuickParticleData;
309 data[i]->groupId = index;
312 int delta = newSize - m_size;
314 for (QQuickParticlePainter *p : std::as_const(painters))
315 p->setCount(p->count() + delta);
318void QQuickParticleGroupData::initList()
323void QQuickParticleGroupData::kill(QQuickParticleData* d)
325 Q_ASSERT(d->groupId == index);
327 for (QQuickParticlePainter *p : std::as_const(painters))
329 freeList.free(d->index);
332QQuickParticleData* QQuickParticleGroupData::newDatum(
bool respectsLimits)
336 while (freeList.hasUnusedEntries()) {
337 int idx = freeList.alloc();
338 if (data[idx]->stillAlive(m_system)) {
339 prepareRecycler(data[idx]);
347 int oldSize = m_size;
348 setSize(oldSize + 10);
349 int idx = freeList.alloc();
350 Q_ASSERT(idx == oldSize);
354bool QQuickParticleGroupData::recycle()
356 m_latestAliveParticles.clear();
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);
363 m_latestAliveParticles.push_back(datum);
368 for (
auto particle : m_latestAliveParticles)
369 prepareRecycler(particle);
372 return freeList.count() == 0;
375void QQuickParticleGroupData::prepareRecycler(QQuickParticleData* d)
377 if (d->lifeSpan*1000 < m_system->maxLife) {
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);
387QQuickV4ParticleData QQuickParticleData::v4Value(QQuickParticleSystem *particleSystem)
389 return QQuickV4ParticleData(
this, particleSystem);
392void QQuickParticleData::debugDump(QQuickParticleSystem* particleSystem)
const
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) ;
402void QQuickParticleData::extendLife(
float time, QQuickParticleSystem* particleSystem)
404 qreal newX = curX(particleSystem);
405 qreal newY = curY(particleSystem);
406 qreal newVX = curVX(particleSystem);
407 qreal newVY = curVY(particleSystem);
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;
424QQuickParticleSystem::QQuickParticleSystem(QQuickItem *parent) :
426 stateEngine(
nullptr),
428 m_animation(
nullptr),
433 m_componentComplete(
false),
437 m_debugMode = qmlParticlesDebug();
443 for (T *element : elements) {
445 element->setSystem(
nullptr);
449QQuickParticleSystem::~QQuickParticleSystem()
451 unsetSystem(std::exchange(m_emitters, {}));
452 unsetSystem(std::exchange(m_affectors, {}));
453 unsetSystem(std::exchange(m_painters, {}));
454 qDeleteAll(groupData);
457void QQuickParticleSystem::initGroups()
459 m_reusableIndexes.clear();
462 qDeleteAll(groupData);
467 for (
auto e : std::as_const(m_emitters)) {
468 e->reclaculateGroupId();
470 for (QQuickParticlePainter *p : std::as_const(m_painters)) {
471 p->recalculateGroupIds();
474 QQuickParticleGroupData *pd =
new QQuickParticleGroupData(QString(),
this);
475 Q_ASSERT(pd->index == 0);
479void QQuickParticleSystem::registerParticlePainter(QQuickParticlePainter* p)
482 qDebug() <<
"Registering Painter" << p <<
"to" <<
this;
484 m_painters << QPointer<QQuickParticlePainter>(p);
488void QQuickParticleSystem::registerParticleEmitter(QQuickParticleEmitter* e)
491 qDebug() <<
"Registering Emitter" << e <<
"to" <<
this;
492 m_emitters << QPointer<QQuickParticleEmitter>(e);
495void QQuickParticleSystem::finishRegisteringParticleEmitter(QQuickParticleEmitter* e)
497 if (m_componentComplete)
502void QQuickParticleSystem::registerParticleAffector(QQuickParticleAffector* a)
505 qDebug() <<
"Registering Affector" << a <<
"to" <<
this;
506 if (!m_affectors.contains(a))
507 m_affectors << QPointer<QQuickParticleAffector>(a);
510void QQuickParticleSystem::unregisterParticlePainter(QQuickParticlePainter *p)
513 qDebug() <<
"Unregistering Painter" << p <<
"from" <<
this;
514 m_painters.removeAll(p);
515 m_syncList.removeAll(p);
516 for (QQuickParticleGroupData *gd : std::as_const(groupData))
517 gd->painters.removeOne(p);
520void QQuickParticleSystem::unregisterParticleEmitter(QQuickParticleEmitter *e)
523 qDebug() <<
"Unregistering Emitter" << e <<
"from" <<
this;
524 m_emitters.removeAll(e);
527void QQuickParticleSystem::unregisterParticleAffector(QQuickParticleAffector *a)
530 qDebug() <<
"Unregistering Affector" << a <<
"from" <<
this;
531 m_affectors.removeAll(a);
534void QQuickParticleSystem::registerParticleGroup(QQuickParticleGroup* g)
537 qDebug() <<
"Registering Group" << g <<
"to" <<
this;
538 m_groups << QPointer<QQuickParticleGroup>(g);
542void QQuickParticleSystem::setRunning(
bool arg)
544 if (m_running != arg) {
546 emit runningChanged(arg);
549 m_running ? m_animation->start() : m_animation->stop();
554void QQuickParticleSystem::setPaused(
bool arg) {
555 if (m_paused != arg) {
557 if (m_animation && m_animation->state() != QAbstractAnimation::Stopped)
558 m_paused ? m_animation->pause() : m_animation->resume();
560 for (QQuickParticlePainter *p : std::as_const(m_painters)) {
566 emit pausedChanged(arg);
570void QQuickParticleSystem::statePropertyRedirect(QQmlListProperty<QObject> *prop, QObject *value)
573 QQuickParticleSystem* sys = qobject_cast<QQuickParticleSystem*>(prop->object->parent());
574 QQuickParticleGroup* group = qobject_cast<QQuickParticleGroup*>(prop->object);
575 if (!group || !sys || !value)
577 stateRedirect(group, sys, value);
580void QQuickParticleSystem::stateRedirect(QQuickParticleGroup* group, QQuickParticleSystem* sys, QObject *value)
583 list << group->name();
584 QQuickParticleAffector* a = qobject_cast<QQuickParticleAffector*>(value);
586 a->setParentItem(sys);
591 QQuickTrailEmitter* fe = qobject_cast<QQuickTrailEmitter*>(value);
593 fe->setParentItem(sys);
594 fe->setFollow(group->name());
598 QQuickParticleEmitter* e = qobject_cast<QQuickParticleEmitter*>(value);
600 e->setParentItem(sys);
601 e->setGroup(group->name());
605 QQuickParticlePainter* p = qobject_cast<QQuickParticlePainter*>(value);
607 p->setParentItem(sys);
612 qWarning() << value <<
" was placed inside a particle system state but cannot be taken into the particle system. It will be lost.";
616int QQuickParticleSystem::registerParticleGroupData(
const QString &name, QQuickParticleGroupData *pgd)
618 Q_ASSERT(!groupIds.contains(name));
620 if (nextFreeGroupId >= groupData.size()) {
621 groupData.push_back(pgd);
622 nextFreeGroupId = groupData.size();
623 id = nextFreeGroupId - 1;
625 id = nextFreeGroupId;
627 searchNextFreeGroupId();
629 groupIds.insert(name, id);
633void QQuickParticleSystem::searchNextFreeGroupId()
636 for (
int ei = groupData.size(); nextFreeGroupId != ei; ++nextFreeGroupId) {
637 if (groupData[nextFreeGroupId] ==
nullptr) {
643void QQuickParticleSystem::componentComplete()
646 QQuickItem::componentComplete();
647 m_componentComplete =
true;
648 m_animation =
new QQuickParticleSystemAnimation(
this);
652void QQuickParticleSystem::reset()
654 if (!m_componentComplete)
659 m_emitters.removeAll(
nullptr);
660 m_painters.removeAll(
nullptr);
661 m_affectors.removeAll(
nullptr);
669 for (QQuickParticleEmitter *e : std::as_const(m_emitters))
674 for (QQuickParticlePainter *p : std::as_const(m_painters)) {
682 if ((m_animation->state() == QAbstractAnimation::Running))
684 m_animation->start();
686 m_animation->pause();
693void QQuickParticleSystem::loadPainter(QQuickParticlePainter *painter)
695 if (!m_componentComplete || !painter)
698 for (QQuickParticleGroupData *sg : groupData) {
699 sg->painters.removeOne(painter);
702 int particleCount = 0;
703 if (painter->groups().isEmpty()) {
704 static QStringList def = QStringList() << QString();
705 painter->setGroups(def);
706 particleCount += groupData[0]->size();
707 groupData[0]->painters << painter;
709 for (
auto groupId : painter->groupIds()) {
710 QQuickParticleGroupData *gd = groupData[groupId];
711 particleCount += gd->size();
712 gd->painters << painter;
715 painter->setCount(particleCount);
720void QQuickParticleSystem::emittersChanged()
722 if (!m_componentComplete)
725 QList<
int> previousSizes;
727 previousSizes.reserve(groupData.size());
728 newSizes.reserve(groupData.size());
729 for (
int i = 0, ei = groupData.size(); i != ei; ++i) {
730 previousSizes << groupData[i]->size();
735 for (
int i = 0; i < m_emitters.size(); ) {
736 QQuickParticleEmitter *e = m_emitters.at(i);
738 m_emitters.removeAt(i);
742 int groupId = e->groupId();
743 if (groupId == QQuickParticleGroupData::InvalidID) {
744 groupId = (
new QQuickParticleGroupData(e->group(),
this))->index;
748 newSizes[groupId] += e->particleCount();
756 for (
int i = 0, ei = groupData.size(); i != ei; ++i) {
757 groupData[i]->setSize(qMax(newSizes[i], previousSizes[i]));
758 particleCount += groupData[i]->size();
761 postProcessEmitters();
764void QQuickParticleSystem::postProcessEmitters()
767 qDebug() <<
"Particle system emitters changed. New particle count: " << particleCount <<
"in" << groupData.size() <<
"groups.";
769 if (particleCount > bySysIdx.size())
770 bySysIdx.resize(particleCount);
772 for (QQuickParticleAffector *a : std::as_const(m_affectors)) {
774 a->m_updateIntSet =
true;
778 for (QQuickParticlePainter *p : std::as_const(m_painters))
781 if (!m_groups.isEmpty())
786void QQuickParticleSystem::emitterAdded(QQuickParticleEmitter *e)
788 if (!m_componentComplete)
792 const int groupId = e->groupId();
793 if (groupId == QQuickParticleGroupData::InvalidID) {
794 QQuickParticleGroupData *group =
new QQuickParticleGroupData(e->group(),
this);
795 group->setSize(e->particleCount());
797 QQuickParticleGroupData *group = groupData[groupId];
798 group->setSize(group->size() + e->particleCount());
804 for (
int i = 0, ei = groupData.size(); i != ei; ++i)
805 particleCount += groupData[i]->size();
807 postProcessEmitters();
810void QQuickParticleSystem::createEngine()
812 if (!m_componentComplete)
814 if (stateEngine && m_debugMode)
815 qDebug() <<
"Resetting Existing Sprite Engine...";
817 for (QQuickParticleGroup *group : std::as_const(m_groups)) {
819 for (
auto it = groupIds.keyBegin(), end = groupIds.keyEnd(); it != end; ++it) {
820 if (group->name() == *it) {
826 new QQuickParticleGroupData(group->name(),
this);
830 if (m_groups.size()) {
833 QList<QQuickParticleGroup*> newList;
834 for (
int i = 0, ei = groupData.size(); i != ei; ++i) {
836 QString name = groupData[i]->name();
837 for (QQuickParticleGroup *existing : std::as_const(m_groups)) {
838 if (existing->name() == name) {
844 newList <<
new QQuickParticleGroup(
this);
845 newList.back()->setName(name);
849 QList<QQuickStochasticState*> states;
850 states.reserve(m_groups.size());
851 for (QQuickParticleGroup *g : std::as_const(m_groups))
852 states << (QQuickStochasticState*)g;
855 stateEngine =
new QQuickStochasticEngine(
this);
856 stateEngine->setCount(particleCount);
857 stateEngine->m_states = states;
859 connect(stateEngine, &QQuickStochasticEngine::stateChanged,
860 this, &QQuickParticleSystem::particleStateChange);
865 stateEngine =
nullptr;
870void QQuickParticleSystem::particleStateChange(
int idx)
872 moveGroups(bySysIdx[idx], stateEngine->curState(idx));
875void QQuickParticleSystem::moveGroups(QQuickParticleData *d,
int newGIdx)
877 if (!d || newGIdx == d->groupId)
880 QQuickParticleData *pd = newDatum(newGIdx,
false, d->systemIndex, d);
887 groupData[d->groupId]->kill(d);
890int QQuickParticleSystem::nextSystemIndex()
892 if (!m_reusableIndexes.isEmpty()) {
893 int ret = *(m_reusableIndexes.begin());
894 m_reusableIndexes.remove(ret);
897 if (m_nextIndex >= bySysIdx.size()) {
898 bySysIdx.resize(bySysIdx.size() < 10 ? 10 : bySysIdx.size()*1.1);
900 stateEngine->setCount(bySysIdx.size());
903 return m_nextIndex++;
906QQuickParticleData *QQuickParticleSystem::newDatum(
907 int groupId,
bool respectLimits,
int sysIndex,
908 const QQuickParticleData *cloneFrom)
910 Q_ASSERT(groupId < groupData.size());
912 QQuickParticleData *ret = groupData[groupId]->newDatum(respectLimits);
919 const int retainedIndex = ret->index;
920 const int retainedGroupId = ret->groupId;
921 const int retainedSystemIndex = ret->systemIndex;
923 ret->index = retainedIndex;
924 ret->groupId = retainedGroupId;
925 ret->systemIndex = retainedSystemIndex;
928 if (sysIndex == -1) {
929 if (ret->systemIndex == -1)
930 ret->systemIndex = nextSystemIndex();
932 if (ret->systemIndex != -1) {
934 stateEngine->stop(ret->systemIndex);
935 m_reusableIndexes << ret->systemIndex;
936 bySysIdx[ret->systemIndex] = 0;
938 ret->systemIndex = sysIndex;
940 bySysIdx[ret->systemIndex] = ret;
943 stateEngine->start(ret->systemIndex, ret->groupId);
949void QQuickParticleSystem::emitParticle(QQuickParticleData* pd, QQuickParticleEmitter* particleEmitter)
953 QTransform t = particleEmitter->itemTransform(
this, &okay);
956 t.map(pd->x, pd->y, &tx, &ty);
964void QQuickParticleSystem::finishNewDatum(QQuickParticleData *pd)
967 groupData[pd->groupId]->prepareRecycler(pd);
969 for (QQuickParticleAffector *a : std::as_const(m_affectors))
970 if (a && a->m_needsReset)
972 for (QQuickParticlePainter *p : std::as_const(groupData[pd->groupId]->painters))
977void QQuickParticleSystem::updateCurrentTime(
int currentTime )
983 qreal dt = timeInt / 1000.;
984 timeInt = currentTime;
985 qreal time = timeInt / 1000.;
989 m_emitters.removeAll(
nullptr);
990 m_painters.removeAll(
nullptr);
991 m_affectors.removeAll(
nullptr);
993 bool oldClear = m_empty;
995 for (QQuickParticleGroupData *gd : std::as_const(groupData))
996 m_empty = gd->recycle() && m_empty;
999 stateEngine->updateSprites(timeInt);
1001 for (QQuickParticleEmitter *emitter : std::as_const(m_emitters))
1002 emitter->emitWindow(timeInt);
1003 for (QQuickParticleAffector *a : std::as_const(m_affectors))
1004 a->affectSystem(dt);
1005 for (QQuickParticleData *d : needsReset)
1006 for (QQuickParticlePainter *p : std::as_const(groupData[d->groupId]->painters))
1009 if (oldClear != m_empty)
1010 emptyChanged(m_empty);
1013int QQuickParticleSystem::systemSync(QQuickParticlePainter* p)
1019 p->performPendingCommits();
1026#include "moc_qquickparticlesystem_p.cpp"
static QT_BEGIN_NAMESPACE int roundedTime(qreal a)
A system which includes particle painter, emitter, and affector types.
void unsetSystem(const QList< QPointer< T > > &elements)
DEFINE_BOOL_CONFIG_OPTION(forceDiskCache, QML_FORCE_DISK_CACHE)