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 bool extended =
false;
381 int extend = 2 * m_system->maxLife / 3;
382 while ((roundedTime(d->t) + extend) <= m_system->timeInt) {
383 d->extendLife(m_system->maxLife / 3000.0, m_system);
387 for (QQuickParticlePainter *p : std::as_const(painters))
390 dataHeap.insertTimed(d, roundedTime(d->t) + extend);
394QQuickV4ParticleData QQuickParticleData::v4Value(QQuickParticleSystem *particleSystem)
396 return QQuickV4ParticleData(
this, particleSystem);
399void QQuickParticleData::debugDump(QQuickParticleSystem* particleSystem)
const
401 qDebug() <<
"Particle" << systemIndex << groupId <<
"/" << index << stillAlive(particleSystem)
402 <<
"Pos: " << x <<
"," << y
403 <<
"Vel: " << vx <<
"," << vy
404 <<
"Acc: " << ax <<
"," << ay
405 <<
"Size: " << size <<
"," << endSize
406 <<
"Time: " << t <<
"," <<lifeSpan <<
";" << (particleSystem->timeInt / 1000.0) ;
409void QQuickParticleData::extendLife(
float time, QQuickParticleSystem* particleSystem)
411 qreal newX = curX(particleSystem);
412 qreal newY = curY(particleSystem);
413 qreal newVX = curVX(particleSystem);
414 qreal newVY = curVY(particleSystem);
419 qreal elapsed = (particleSystem->timeInt / 1000.0) - t;
420 qreal evy = newVY - elapsed*ay;
421 qreal ey = newY - elapsed*evy - 0.5 * elapsed*elapsed*ay;
422 qreal evx = newVX - elapsed*ax;
423 qreal ex = newX - elapsed*evx - 0.5 * elapsed*elapsed*ax;
431QQuickParticleSystem::QQuickParticleSystem(QQuickItem *parent) :
433 stateEngine(
nullptr),
435 m_animation(
nullptr),
440 m_componentComplete(
false),
444 m_debugMode = qmlParticlesDebug();
450 for (T *element : elements) {
452 element->setSystem(
nullptr);
456QQuickParticleSystem::~QQuickParticleSystem()
458 unsetSystem(std::exchange(m_emitters, {}));
459 unsetSystem(std::exchange(m_affectors, {}));
460 unsetSystem(std::exchange(m_painters, {}));
461 qDeleteAll(groupData);
464void QQuickParticleSystem::initGroups()
466 m_reusableIndexes.clear();
469 qDeleteAll(groupData);
474 for (
auto e : std::as_const(m_emitters)) {
475 e->reclaculateGroupId();
477 for (QQuickParticlePainter *p : std::as_const(m_painters)) {
478 p->recalculateGroupIds();
481 QQuickParticleGroupData *pd =
new QQuickParticleGroupData(QString(),
this);
482 Q_ASSERT(pd->index == 0);
486void QQuickParticleSystem::registerParticlePainter(QQuickParticlePainter* p)
489 qDebug() <<
"Registering Painter" << p <<
"to" <<
this;
491 m_painters << QPointer<QQuickParticlePainter>(p);
495void QQuickParticleSystem::registerParticleEmitter(QQuickParticleEmitter* e)
498 qDebug() <<
"Registering Emitter" << e <<
"to" <<
this;
499 m_emitters << QPointer<QQuickParticleEmitter>(e);
502void QQuickParticleSystem::finishRegisteringParticleEmitter(QQuickParticleEmitter* e)
504 if (m_componentComplete)
509void QQuickParticleSystem::registerParticleAffector(QQuickParticleAffector* a)
512 qDebug() <<
"Registering Affector" << a <<
"to" <<
this;
513 if (!m_affectors.contains(a))
514 m_affectors << QPointer<QQuickParticleAffector>(a);
517void QQuickParticleSystem::unregisterParticlePainter(QQuickParticlePainter *p)
520 qDebug() <<
"Unregistering Painter" << p <<
"from" <<
this;
521 m_painters.removeAll(p);
522 m_syncList.removeAll(p);
523 for (QQuickParticleGroupData *gd : std::as_const(groupData))
524 gd->painters.removeOne(p);
527void QQuickParticleSystem::unregisterParticleEmitter(QQuickParticleEmitter *e)
530 qDebug() <<
"Unregistering Emitter" << e <<
"from" <<
this;
531 m_emitters.removeAll(e);
534void QQuickParticleSystem::unregisterParticleAffector(QQuickParticleAffector *a)
537 qDebug() <<
"Unregistering Affector" << a <<
"from" <<
this;
538 m_affectors.removeAll(a);
541void QQuickParticleSystem::registerParticleGroup(QQuickParticleGroup* g)
544 qDebug() <<
"Registering Group" << g <<
"to" <<
this;
545 m_groups << QPointer<QQuickParticleGroup>(g);
549void QQuickParticleSystem::setRunning(
bool arg)
551 if (m_running != arg) {
553 emit runningChanged(arg);
556 m_running ? m_animation->start() : m_animation->stop();
561void QQuickParticleSystem::setPaused(
bool arg) {
562 if (m_paused != arg) {
564 if (m_animation && m_animation->state() != QAbstractAnimation::Stopped)
565 m_paused ? m_animation->pause() : m_animation->resume();
567 for (QQuickParticlePainter *p : std::as_const(m_painters)) {
573 emit pausedChanged(arg);
577void QQuickParticleSystem::statePropertyRedirect(QQmlListProperty<QObject> *prop, QObject *value)
580 QQuickParticleSystem* sys = qobject_cast<QQuickParticleSystem*>(prop->object->parent());
581 QQuickParticleGroup* group = qobject_cast<QQuickParticleGroup*>(prop->object);
582 if (!group || !sys || !value)
584 stateRedirect(group, sys, value);
587void QQuickParticleSystem::stateRedirect(QQuickParticleGroup* group, QQuickParticleSystem* sys, QObject *value)
590 list << group->name();
591 QQuickParticleAffector* a = qobject_cast<QQuickParticleAffector*>(value);
593 a->setParentItem(sys);
598 QQuickTrailEmitter* fe = qobject_cast<QQuickTrailEmitter*>(value);
600 fe->setParentItem(sys);
601 fe->setFollow(group->name());
605 QQuickParticleEmitter* e = qobject_cast<QQuickParticleEmitter*>(value);
607 e->setParentItem(sys);
608 e->setGroup(group->name());
612 QQuickParticlePainter* p = qobject_cast<QQuickParticlePainter*>(value);
614 p->setParentItem(sys);
619 qWarning() << value <<
" was placed inside a particle system state but cannot be taken into the particle system. It will be lost.";
623int QQuickParticleSystem::registerParticleGroupData(
const QString &name, QQuickParticleGroupData *pgd)
625 Q_ASSERT(!groupIds.contains(name));
627 if (nextFreeGroupId >= groupData.size()) {
628 groupData.push_back(pgd);
629 nextFreeGroupId = groupData.size();
630 id = nextFreeGroupId - 1;
632 id = nextFreeGroupId;
634 searchNextFreeGroupId();
636 groupIds.insert(name, id);
640void QQuickParticleSystem::searchNextFreeGroupId()
643 for (
int ei = groupData.size(); nextFreeGroupId != ei; ++nextFreeGroupId) {
644 if (groupData[nextFreeGroupId] ==
nullptr) {
650void QQuickParticleSystem::componentComplete()
653 QQuickItem::componentComplete();
654 m_componentComplete =
true;
655 m_animation =
new QQuickParticleSystemAnimation(
this);
659void QQuickParticleSystem::reset()
661 if (!m_componentComplete)
666 m_emitters.removeAll(
nullptr);
667 m_painters.removeAll(
nullptr);
668 m_affectors.removeAll(
nullptr);
676 for (QQuickParticleEmitter *e : std::as_const(m_emitters))
681 for (QQuickParticlePainter *p : std::as_const(m_painters)) {
689 if ((m_animation->state() == QAbstractAnimation::Running))
691 m_animation->start();
693 m_animation->pause();
700void QQuickParticleSystem::loadPainter(QQuickParticlePainter *painter)
702 if (!m_componentComplete || !painter)
705 for (QQuickParticleGroupData *sg : groupData) {
706 sg->painters.removeOne(painter);
709 int particleCount = 0;
710 if (painter->groups().isEmpty()) {
711 static QStringList def = QStringList() << QString();
712 painter->setGroups(def);
713 particleCount += groupData[0]->size();
714 groupData[0]->painters << painter;
716 for (
auto groupId : painter->groupIds()) {
717 QQuickParticleGroupData *gd = groupData[groupId];
718 particleCount += gd->size();
719 gd->painters << painter;
722 painter->setCount(particleCount);
727void QQuickParticleSystem::emittersChanged()
729 if (!m_componentComplete)
732 QList<
int> previousSizes;
734 previousSizes.reserve(groupData.size());
735 newSizes.reserve(groupData.size());
736 for (
int i = 0, ei = groupData.size(); i != ei; ++i) {
737 previousSizes << groupData[i]->size();
742 for (
int i = 0; i < m_emitters.size(); ) {
743 QQuickParticleEmitter *e = m_emitters.at(i);
745 m_emitters.removeAt(i);
749 int groupId = e->groupId();
750 if (groupId == QQuickParticleGroupData::InvalidID) {
751 groupId = (
new QQuickParticleGroupData(e->group(),
this))->index;
755 newSizes[groupId] += e->particleCount();
763 for (
int i = 0, ei = groupData.size(); i != ei; ++i) {
764 groupData[i]->setSize(qMax(newSizes[i], previousSizes[i]));
765 particleCount += groupData[i]->size();
768 postProcessEmitters();
771void QQuickParticleSystem::postProcessEmitters()
774 qDebug() <<
"Particle system emitters changed. New particle count: " << particleCount <<
"in" << groupData.size() <<
"groups.";
776 if (particleCount > bySysIdx.size())
777 bySysIdx.resize(particleCount);
779 for (QQuickParticleAffector *a : std::as_const(m_affectors)) {
781 a->m_updateIntSet =
true;
785 for (QQuickParticlePainter *p : std::as_const(m_painters))
788 if (!m_groups.isEmpty())
793void QQuickParticleSystem::emitterAdded(QQuickParticleEmitter *e)
795 if (!m_componentComplete)
799 const int groupId = e->groupId();
800 if (groupId == QQuickParticleGroupData::InvalidID) {
801 QQuickParticleGroupData *group =
new QQuickParticleGroupData(e->group(),
this);
802 group->setSize(e->particleCount());
804 QQuickParticleGroupData *group = groupData[groupId];
805 group->setSize(group->size() + e->particleCount());
811 for (
int i = 0, ei = groupData.size(); i != ei; ++i)
812 particleCount += groupData[i]->size();
814 postProcessEmitters();
817void QQuickParticleSystem::createEngine()
819 if (!m_componentComplete)
821 if (stateEngine && m_debugMode)
822 qDebug() <<
"Resetting Existing Sprite Engine...";
824 for (QQuickParticleGroup *group : std::as_const(m_groups)) {
826 for (
auto it = groupIds.keyBegin(), end = groupIds.keyEnd(); it != end; ++it) {
827 if (group->name() == *it) {
833 new QQuickParticleGroupData(group->name(),
this);
837 if (m_groups.size()) {
840 QList<QQuickParticleGroup*> newList;
841 for (
int i = 0, ei = groupData.size(); i != ei; ++i) {
843 QString name = groupData[i]->name();
844 for (QQuickParticleGroup *existing : std::as_const(m_groups)) {
845 if (existing->name() == name) {
851 newList <<
new QQuickParticleGroup(
this);
852 newList.back()->setName(name);
856 QList<QQuickStochasticState*> states;
857 states.reserve(m_groups.size());
858 for (QQuickParticleGroup *g : std::as_const(m_groups))
859 states << (QQuickStochasticState*)g;
862 stateEngine =
new QQuickStochasticEngine(
this);
863 stateEngine->setCount(particleCount);
864 stateEngine->m_states = states;
866 connect(stateEngine, &QQuickStochasticEngine::stateChanged,
867 this, &QQuickParticleSystem::particleStateChange);
872 stateEngine =
nullptr;
877void QQuickParticleSystem::particleStateChange(
int idx)
879 moveGroups(bySysIdx[idx], stateEngine->curState(idx));
882void QQuickParticleSystem::moveGroups(QQuickParticleData *d,
int newGIdx)
884 if (!d || newGIdx == d->groupId)
887 QQuickParticleData *pd = newDatum(newGIdx,
false, d->systemIndex, d);
894 groupData[d->groupId]->kill(d);
897int QQuickParticleSystem::nextSystemIndex()
899 if (!m_reusableIndexes.isEmpty()) {
900 int ret = *(m_reusableIndexes.begin());
901 m_reusableIndexes.remove(ret);
904 if (m_nextIndex >= bySysIdx.size()) {
905 bySysIdx.resize(bySysIdx.size() < 10 ? 10 : bySysIdx.size()*1.1);
907 stateEngine->setCount(bySysIdx.size());
910 return m_nextIndex++;
913QQuickParticleData *QQuickParticleSystem::newDatum(
914 int groupId,
bool respectLimits,
int sysIndex,
915 const QQuickParticleData *cloneFrom)
917 Q_ASSERT(groupId < groupData.size());
919 QQuickParticleData *ret = groupData[groupId]->newDatum(respectLimits);
926 const int retainedIndex = ret->index;
927 const int retainedGroupId = ret->groupId;
928 const int retainedSystemIndex = ret->systemIndex;
930 ret->index = retainedIndex;
931 ret->groupId = retainedGroupId;
932 ret->systemIndex = retainedSystemIndex;
935 if (sysIndex == -1) {
936 if (ret->systemIndex == -1)
937 ret->systemIndex = nextSystemIndex();
939 if (ret->systemIndex != -1) {
941 stateEngine->stop(ret->systemIndex);
942 m_reusableIndexes << ret->systemIndex;
943 bySysIdx[ret->systemIndex] = 0;
945 ret->systemIndex = sysIndex;
947 bySysIdx[ret->systemIndex] = ret;
950 stateEngine->start(ret->systemIndex, ret->groupId);
956void QQuickParticleSystem::emitParticle(QQuickParticleData* pd, QQuickParticleEmitter* particleEmitter)
960 QTransform t = particleEmitter->itemTransform(
this, &okay);
963 t.map(pd->x, pd->y, &tx, &ty);
971void QQuickParticleSystem::finishNewDatum(QQuickParticleData *pd)
974 groupData[pd->groupId]->prepareRecycler(pd);
976 for (QQuickParticleAffector *a : std::as_const(m_affectors))
977 if (a && a->m_needsReset)
979 for (QQuickParticlePainter *p : std::as_const(groupData[pd->groupId]->painters))
984void QQuickParticleSystem::updateCurrentTime(
int currentTime )
990 qreal dt = timeInt / 1000.;
991 timeInt = currentTime;
992 qreal time = timeInt / 1000.;
996 m_emitters.removeAll(
nullptr);
997 m_painters.removeAll(
nullptr);
998 m_affectors.removeAll(
nullptr);
1000 bool oldClear = m_empty;
1002 for (QQuickParticleGroupData *gd : std::as_const(groupData))
1003 m_empty = gd->recycle() && m_empty;
1006 stateEngine->updateSprites(timeInt);
1008 for (QQuickParticleEmitter *emitter : std::as_const(m_emitters))
1009 emitter->emitWindow(timeInt);
1010 for (QQuickParticleAffector *a : std::as_const(m_affectors))
1011 a->affectSystem(dt);
1012 for (QQuickParticleData *d : needsReset)
1013 for (QQuickParticlePainter *p : std::as_const(groupData[d->groupId]->painters))
1016 if (oldClear != m_empty)
1017 emptyChanged(m_empty);
1020int QQuickParticleSystem::systemSync(QQuickParticlePainter* p)
1026 p->performPendingCommits();
1033#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)