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();
440QQuickParticleSystem::~QQuickParticleSystem()
442 for (QQuickParticleGroupData *gd : std::as_const(groupData))
446void QQuickParticleSystem::initGroups()
448 m_reusableIndexes.clear();
451 qDeleteAll(groupData);
456 for (
auto e : std::as_const(m_emitters)) {
457 e->reclaculateGroupId();
459 for (QQuickParticlePainter *p : std::as_const(m_painters)) {
460 p->recalculateGroupIds();
463 QQuickParticleGroupData *pd =
new QQuickParticleGroupData(QString(),
this);
464 Q_ASSERT(pd->index == 0);
468void QQuickParticleSystem::registerParticlePainter(QQuickParticlePainter* p)
471 qDebug() <<
"Registering Painter" << p <<
"to" <<
this;
473 m_painters << QPointer<QQuickParticlePainter>(p);
475 connect(p, &QQuickParticlePainter::groupsChanged,
this, [
this, p] {
this->loadPainter(p); }, Qt::QueuedConnection);
479void QQuickParticleSystem::registerParticleEmitter(QQuickParticleEmitter* e)
482 qDebug() <<
"Registering Emitter" << e <<
"to" <<
this;
483 m_emitters << QPointer<QQuickParticleEmitter>(e);
486void QQuickParticleSystem::finishRegisteringParticleEmitter(QQuickParticleEmitter* e)
488 connect(e, &QQuickParticleEmitter::particleCountChanged,
489 this, &QQuickParticleSystem::emittersChanged);
490 connect(e, &QQuickParticleEmitter::groupChanged,
491 this, &QQuickParticleSystem::emittersChanged);
492 if (m_componentComplete)
497void QQuickParticleSystem::registerParticleAffector(QQuickParticleAffector* a)
500 qDebug() <<
"Registering Affector" << a <<
"to" <<
this;
501 if (!m_affectors.contains(a))
502 m_affectors << QPointer<QQuickParticleAffector>(a);
505void QQuickParticleSystem::registerParticleGroup(QQuickParticleGroup* g)
508 qDebug() <<
"Registering Group" << g <<
"to" <<
this;
509 m_groups << QPointer<QQuickParticleGroup>(g);
513void QQuickParticleSystem::setRunning(
bool arg)
515 if (m_running != arg) {
517 emit runningChanged(arg);
520 m_running ? m_animation->start() : m_animation->stop();
525void QQuickParticleSystem::setPaused(
bool arg) {
526 if (m_paused != arg) {
528 if (m_animation && m_animation->state() != QAbstractAnimation::Stopped)
529 m_paused ? m_animation->pause() : m_animation->resume();
531 for (QQuickParticlePainter *p : std::as_const(m_painters)) {
537 emit pausedChanged(arg);
541void QQuickParticleSystem::statePropertyRedirect(QQmlListProperty<QObject> *prop, QObject *value)
544 QQuickParticleSystem* sys = qobject_cast<QQuickParticleSystem*>(prop->object->parent());
545 QQuickParticleGroup* group = qobject_cast<QQuickParticleGroup*>(prop->object);
546 if (!group || !sys || !value)
548 stateRedirect(group, sys, value);
551void QQuickParticleSystem::stateRedirect(QQuickParticleGroup* group, QQuickParticleSystem* sys, QObject *value)
554 list << group->name();
555 QQuickParticleAffector* a = qobject_cast<QQuickParticleAffector*>(value);
557 a->setParentItem(sys);
562 QQuickTrailEmitter* fe = qobject_cast<QQuickTrailEmitter*>(value);
564 fe->setParentItem(sys);
565 fe->setFollow(group->name());
569 QQuickParticleEmitter* e = qobject_cast<QQuickParticleEmitter*>(value);
571 e->setParentItem(sys);
572 e->setGroup(group->name());
576 QQuickParticlePainter* p = qobject_cast<QQuickParticlePainter*>(value);
578 p->setParentItem(sys);
583 qWarning() << value <<
" was placed inside a particle system state but cannot be taken into the particle system. It will be lost.";
587int QQuickParticleSystem::registerParticleGroupData(
const QString &name, QQuickParticleGroupData *pgd)
589 Q_ASSERT(!groupIds.contains(name));
591 if (nextFreeGroupId >= groupData.size()) {
592 groupData.push_back(pgd);
593 nextFreeGroupId = groupData.size();
594 id = nextFreeGroupId - 1;
596 id = nextFreeGroupId;
598 searchNextFreeGroupId();
600 groupIds.insert(name, id);
604void QQuickParticleSystem::searchNextFreeGroupId()
607 for (
int ei = groupData.size(); nextFreeGroupId != ei; ++nextFreeGroupId) {
608 if (groupData[nextFreeGroupId] ==
nullptr) {
614void QQuickParticleSystem::componentComplete()
617 QQuickItem::componentComplete();
618 m_componentComplete =
true;
619 m_animation =
new QQuickParticleSystemAnimation(
this);
623void QQuickParticleSystem::reset()
625 if (!m_componentComplete)
630 m_emitters.removeAll(
nullptr);
631 m_painters.removeAll(
nullptr);
632 m_affectors.removeAll(
nullptr);
640 for (QQuickParticleEmitter *e : std::as_const(m_emitters))
645 for (QQuickParticlePainter *p : std::as_const(m_painters)) {
653 if ((m_animation->state() == QAbstractAnimation::Running))
655 m_animation->start();
657 m_animation->pause();
664void QQuickParticleSystem::loadPainter(QQuickParticlePainter *painter)
666 if (!m_componentComplete || !painter)
669 for (QQuickParticleGroupData *sg : groupData) {
670 sg->painters.removeOne(painter);
673 int particleCount = 0;
674 if (painter->groups().isEmpty()) {
675 static QStringList def = QStringList() << QString();
676 painter->setGroups(def);
677 particleCount += groupData[0]->size();
678 groupData[0]->painters << painter;
680 for (
auto groupId : painter->groupIds()) {
681 QQuickParticleGroupData *gd = groupData[groupId];
682 particleCount += gd->size();
683 gd->painters << painter;
686 painter->setCount(particleCount);
691void QQuickParticleSystem::emittersChanged()
693 if (!m_componentComplete)
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();
706 for (
int i = 0; i < m_emitters.size(); ) {
707 QQuickParticleEmitter *e = m_emitters.at(i);
709 m_emitters.removeAt(i);
713 int groupId = e->groupId();
714 if (groupId == QQuickParticleGroupData::InvalidID) {
715 groupId = (
new QQuickParticleGroupData(e->group(),
this))->index;
719 newSizes[groupId] += e->particleCount();
727 for (
int i = 0, ei = groupData.size(); i != ei; ++i) {
728 groupData[i]->setSize(qMax(newSizes[i], previousSizes[i]));
729 particleCount += groupData[i]->size();
732 postProcessEmitters();
735void QQuickParticleSystem::postProcessEmitters()
738 qDebug() <<
"Particle system emitters changed. New particle count: " << particleCount <<
"in" << groupData.size() <<
"groups.";
740 if (particleCount > bySysIdx.size())
741 bySysIdx.resize(particleCount);
743 for (QQuickParticleAffector *a : std::as_const(m_affectors)) {
745 a->m_updateIntSet =
true;
749 for (QQuickParticlePainter *p : std::as_const(m_painters))
752 if (!m_groups.isEmpty())
757void QQuickParticleSystem::emitterAdded(QQuickParticleEmitter *e)
759 if (!m_componentComplete)
763 const int groupId = e->groupId();
764 if (groupId == QQuickParticleGroupData::InvalidID) {
765 QQuickParticleGroupData *group =
new QQuickParticleGroupData(e->group(),
this);
766 group->setSize(e->particleCount());
768 QQuickParticleGroupData *group = groupData[groupId];
769 group->setSize(group->size() + e->particleCount());
775 for (
int i = 0, ei = groupData.size(); i != ei; ++i)
776 particleCount += groupData[i]->size();
778 postProcessEmitters();
781void QQuickParticleSystem::createEngine()
783 if (!m_componentComplete)
785 if (stateEngine && m_debugMode)
786 qDebug() <<
"Resetting Existing Sprite Engine...";
788 for (QQuickParticleGroup *group : std::as_const(m_groups)) {
790 for (
auto it = groupIds.keyBegin(), end = groupIds.keyEnd(); it != end; ++it) {
791 if (group->name() == *it) {
797 new QQuickParticleGroupData(group->name(),
this);
801 if (m_groups.size()) {
804 QList<QQuickParticleGroup*> newList;
805 for (
int i = 0, ei = groupData.size(); i != ei; ++i) {
807 QString name = groupData[i]->name();
808 for (QQuickParticleGroup *existing : std::as_const(m_groups)) {
809 if (existing->name() == name) {
815 newList <<
new QQuickParticleGroup(
this);
816 newList.back()->setName(name);
820 QList<QQuickStochasticState*> states;
821 states.reserve(m_groups.size());
822 for (QQuickParticleGroup *g : std::as_const(m_groups))
823 states << (QQuickStochasticState*)g;
826 stateEngine =
new QQuickStochasticEngine(
this);
827 stateEngine->setCount(particleCount);
828 stateEngine->m_states = states;
830 connect(stateEngine, &QQuickStochasticEngine::stateChanged,
831 this, &QQuickParticleSystem::particleStateChange);
836 stateEngine =
nullptr;
841void QQuickParticleSystem::particleStateChange(
int idx)
843 moveGroups(bySysIdx[idx], stateEngine->curState(idx));
846void QQuickParticleSystem::moveGroups(QQuickParticleData *d,
int newGIdx)
848 if (!d || newGIdx == d->groupId)
851 QQuickParticleData *pd = newDatum(newGIdx,
false, d->systemIndex, d);
858 groupData[d->groupId]->kill(d);
861int QQuickParticleSystem::nextSystemIndex()
863 if (!m_reusableIndexes.isEmpty()) {
864 int ret = *(m_reusableIndexes.begin());
865 m_reusableIndexes.remove(ret);
868 if (m_nextIndex >= bySysIdx.size()) {
869 bySysIdx.resize(bySysIdx.size() < 10 ? 10 : bySysIdx.size()*1.1);
871 stateEngine->setCount(bySysIdx.size());
874 return m_nextIndex++;
877QQuickParticleData *QQuickParticleSystem::newDatum(
878 int groupId,
bool respectLimits,
int sysIndex,
879 const QQuickParticleData *cloneFrom)
881 Q_ASSERT(groupId < groupData.size());
883 QQuickParticleData *ret = groupData[groupId]->newDatum(respectLimits);
890 const int retainedIndex = ret->index;
891 const int retainedGroupId = ret->groupId;
892 const int retainedSystemIndex = ret->systemIndex;
894 ret->index = retainedIndex;
895 ret->groupId = retainedGroupId;
896 ret->systemIndex = retainedSystemIndex;
899 if (sysIndex == -1) {
900 if (ret->systemIndex == -1)
901 ret->systemIndex = nextSystemIndex();
903 if (ret->systemIndex != -1) {
905 stateEngine->stop(ret->systemIndex);
906 m_reusableIndexes << ret->systemIndex;
907 bySysIdx[ret->systemIndex] = 0;
909 ret->systemIndex = sysIndex;
911 bySysIdx[ret->systemIndex] = ret;
914 stateEngine->start(ret->systemIndex, ret->groupId);
920void QQuickParticleSystem::emitParticle(QQuickParticleData* pd, QQuickParticleEmitter* particleEmitter)
924 QTransform t = particleEmitter->itemTransform(
this, &okay);
927 t.map(pd->x, pd->y, &tx, &ty);
935void QQuickParticleSystem::finishNewDatum(QQuickParticleData *pd)
938 groupData[pd->groupId]->prepareRecycler(pd);
940 for (QQuickParticleAffector *a : std::as_const(m_affectors))
941 if (a && a->m_needsReset)
943 for (QQuickParticlePainter *p : std::as_const(groupData[pd->groupId]->painters))
948void QQuickParticleSystem::updateCurrentTime(
int currentTime )
954 qreal dt = timeInt / 1000.;
955 timeInt = currentTime;
956 qreal time = timeInt / 1000.;
960 m_emitters.removeAll(
nullptr);
961 m_painters.removeAll(
nullptr);
962 m_affectors.removeAll(
nullptr);
964 bool oldClear = m_empty;
966 for (QQuickParticleGroupData *gd : std::as_const(groupData))
967 m_empty = gd->recycle() && m_empty;
970 stateEngine->updateSprites(timeInt);
972 for (QQuickParticleEmitter *emitter : std::as_const(m_emitters))
973 emitter->emitWindow(timeInt);
974 for (QQuickParticleAffector *a : std::as_const(m_affectors))
976 for (QQuickParticleData *d : needsReset)
977 for (QQuickParticlePainter *p : std::as_const(groupData[d->groupId]->painters))
980 if (oldClear != m_empty)
981 emptyChanged(m_empty);
984int QQuickParticleSystem::systemSync(QQuickParticlePainter* p)
990 p->performPendingCommits();
997#include "moc_qquickparticlesystem_p.cpp"
static QT_BEGIN_NAMESPACE int roundedTime(qreal a)
A system which includes particle painter, emitter, and affector types.
DEFINE_BOOL_CONFIG_OPTION(forceDiskCache, QML_FORCE_DISK_CACHE)