1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the Declarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickparticlesystem_p.h"
43 #include <QtQuick/qsgnode.h>
44 #include "qquickparticleemitter_p.h"
45 #include "qquickparticleaffector_p.h"
46 #include "qquickparticlepainter_p.h"
47 #include <private/qquickspriteengine_p.h>
48 #include <private/qquicksprite_p.h>
49 #include "qquickv8particledata_p.h"
50 #include "qquickparticlegroup_p.h"
52 #include "qquicktrailemitter_p.h"//###For auto-follow on states, perhaps should be in emitter?
53 #include <private/qdeclarativeengine_p.h>
58 //###Switch to define later, for now user-friendly (no compilation) debugging is worth it
59 DEFINE_BOOL_CONFIG_OPTION(qmlParticlesDebug, QML_PARTICLES_DEBUG)
61 \qmlclass ParticleSystem QQuickParticleSystem
62 \inqmlmodule QtQuick.Particles 2
63 \brief The ParticleSystem brings together ParticlePainter, Emitter and Affector elements.
68 \qmlproperty bool QtQuick.Particles2::ParticleSystem::running
70 If running is set to false, the particle system will stop the simulation. All particles
71 will be destroyed when the system is set to running again.
73 It can also be controlled with the start() and stop() methods.
78 \qmlproperty bool QtQuick.Particles2::ParticleSystem::paused
80 If paused is set to true, the particle system will not advance the simulation. When
81 paused is set to false again, the simulation will resume from the same point it was
84 The simulation will automatically pause if it detects that there are no live particles
85 left, and unpause when new live particles are added.
87 It can also be controlled with the pause() and resume() methods.
91 \qmlproperty bool QtQuick.Particles2::ParticleSystem::empty
93 empty is set to true when there are no live particles left in the system.
95 You can use this to pause the system, keeping it from spending any time updating,
96 but you will need to resume it in order for additional particles to be generated
99 To kill all the particles in the system, use a Kill affector.
103 \qmlproperty list<Sprite> QtQuick.Particles2::ParticleSystem::particleStates
105 You can define a sub-set of particle groups in this property in order to provide them
106 with stochastic state transitions.
108 Each QtQuick2::Sprite in this list is interpreted as corresponding to the particle group
109 with ths same name. Any transitions defined in these sprites will take effect on the particle
110 groups as well. Additionally TrailEmitters, Affectors and ParticlePainters definined
111 inside one of these sprites are automatically associated with the corresponding particle group.
115 \qmlmethod void QtQuick.Particles2::ParticleSystem::pause
117 Pauses the simulation if it is running.
123 \qmlmethod void QtQuick.Particles2::ParticleSystem::resume
125 Resumes the simulation if it is paused.
131 \qmlmethod void QtQuick.Particles2::ParticleSystem::start
133 Starts the simulation if it has not already running.
135 \sa stop, restart, running
139 \qmlmethod void QtQuick.Particles2::ParticleSystem::stop
141 Stops the simulation if it is running.
143 \sa start, restart, running
147 \qmlmethod void QtQuick.Particles2::ParticleSystem::restart
149 Stops the simulation if it is running, and then starts it.
151 \sa stop, restart, running
154 \qmlmethod void QtQuick.Particles2::ParticleSystem::reset
156 Discards all currently existing particles.
159 const qreal EPSILON = 0.001;
160 //Utility functions for when within 1ms is close enough
161 bool timeEqualOrGreater(qreal a, qreal b)
163 return (a+EPSILON >= b);
166 bool timeLess(qreal a, qreal b)
168 return (a-EPSILON < b);
171 bool timeEqual(qreal a, qreal b)
173 return (a+EPSILON > b) && (a-EPSILON < b);
176 int roundedTime(qreal a)
178 return (int)qRound(a*1000.0);
181 QQuickParticleDataHeap::QQuickParticleDataHeap()
184 m_data.reserve(1000);
188 void QQuickParticleDataHeap::grow() //###Consider automatic growth vs resize() calls from GroupData
190 m_data.resize(1 << ++m_size);
193 void QQuickParticleDataHeap::insert(QQuickParticleData* data)
195 insertTimed(data, roundedTime(data->t + data->lifeSpan));
198 void QQuickParticleDataHeap::insertTimed(QQuickParticleData* data, int time)
200 //TODO: Optimize 0 lifespan (or already dead) case
201 if (m_lookups.contains(time)) {
202 m_data[m_lookups[time]].data << data;
205 if (m_end == (1 << m_size))
207 m_data[m_end].time = time;
208 m_data[m_end].data.clear();
209 m_data[m_end].data.insert(data);
210 m_lookups.insert(time, m_end);
214 int QQuickParticleDataHeap::top()
218 return m_data[0].time;
221 QSet<QQuickParticleData*> QQuickParticleDataHeap::pop()
224 return QSet<QQuickParticleData*> ();
225 QSet<QQuickParticleData*> ret = m_data[0].data;
226 m_lookups.remove(m_data[0].time);
230 m_data[0] = m_data[--m_end];
236 void QQuickParticleDataHeap::clear()
240 //m_size is in powers of two. So to start at 0 we have one allocated
245 bool QQuickParticleDataHeap::contains(QQuickParticleData* d)
247 for (int i=0; i<m_end; i++)
248 if (m_data[i].data.contains(d))
253 void QQuickParticleDataHeap::swap(int a, int b)
256 m_data[a] = m_data[b];
258 m_lookups[m_data[a].time] = a;
259 m_lookups[m_data[b].time] = b;
262 void QQuickParticleDataHeap::bubbleUp(int idx)//tends to be called once
266 int parent = (idx-1)/2;
267 if (m_data[idx].time < m_data[parent].time) {
273 void QQuickParticleDataHeap::bubbleDown(int idx)//tends to be called log n times
275 int left = idx*2 + 1;
279 int right = idx*2 + 2;
281 if (m_data[left].time > m_data[right].time)
284 if (m_data[idx].time > m_data[lesser].time) {
290 QQuickParticleGroupData::QQuickParticleGroupData(int id, QQuickParticleSystem* sys):index(id),m_size(0),m_system(sys)
295 QQuickParticleGroupData::~QQuickParticleGroupData()
297 foreach (QQuickParticleData* d, data)
301 int QQuickParticleGroupData::size()
306 QString QQuickParticleGroupData::name()//### Worth caching as well?
308 return m_system->groupIds.key(index);
311 void QQuickParticleGroupData::setSize(int newSize)
313 if (newSize == m_size)
315 Q_ASSERT(newSize > m_size);//XXX allow shrinking
316 data.resize(newSize);
317 for (int i=m_size; i<newSize; i++) {
318 data[i] = new QQuickParticleData(m_system);
319 data[i]->group = index;
321 reusableIndexes << i;
323 int delta = newSize - m_size;
325 foreach (QQuickParticlePainter* p, painters)
326 p->setCount(p->count() + delta);
329 void QQuickParticleGroupData::initList()
334 void QQuickParticleGroupData::kill(QQuickParticleData* d)
336 Q_ASSERT(d->group == index);
337 d->lifeSpan = 0;//Kill off
338 foreach (QQuickParticlePainter* p, painters)
340 reusableIndexes << d->index;
343 QQuickParticleData* QQuickParticleGroupData::newDatum(bool respectsLimits)
345 //recycle();//Extra recycler round to be sure?
347 while (!reusableIndexes.empty()) {
348 int idx = *(reusableIndexes.begin());
349 reusableIndexes.remove(idx);
350 if (data[idx]->stillAlive()) {// ### This means resurrection of 'dead' particles. Is that allowed?
351 prepareRecycler(data[idx]);
359 int oldSize = m_size;
360 setSize(oldSize + 10);//###+1,10%,+10? Choose something non-arbitrarily
361 reusableIndexes.remove(oldSize);
362 return data[oldSize];
365 bool QQuickParticleGroupData::recycle()
367 while (dataHeap.top() <= m_system->timeInt) {
368 foreach (QQuickParticleData* datum, dataHeap.pop()) {
369 if (!datum->stillAlive()) {
370 reusableIndexes << datum->index;
372 prepareRecycler(datum); //ttl has been altered mid-way, put it back
377 //TODO: If the data is clear, gc (consider shrinking stack size)?
378 return reusableIndexes.count() == m_size;
381 void QQuickParticleGroupData::prepareRecycler(QQuickParticleData* d)
383 if (d->lifeSpan*1000 < m_system->maxLife) {
386 while ((roundedTime(d->t) + 2*m_system->maxLife/3) <= m_system->timeInt)
387 d->extendLife(m_system->maxLife/3000.0);
388 dataHeap.insertTimed(d, roundedTime(d->t) + 2*m_system->maxLife/3);
392 QQuickParticleData::QQuickParticleData(QQuickParticleSystem* sys)
400 , deformationOwner(0)
439 QQuickParticleData::~QQuickParticleData()
444 void QQuickParticleData::clone(const QQuickParticleData& other)
449 lifeSpan = other.lifeSpan;
451 endSize = other.endSize;
460 rotation = other.rotation;
461 rotationSpeed = other.rotationSpeed;
462 autoRotate = other.autoRotate;
463 animIdx = other.animIdx;
464 frameDuration = other.frameDuration;
465 frameCount = other.frameCount;
469 animWidth = other.animWidth;
470 animHeight = other.animHeight;
471 color.r = other.color.r;
472 color.g = other.color.g;
473 color.b = other.color.b;
474 color.a = other.color.a;
476 delegate = other.delegate;
477 modelIndex = other.modelIndex;
479 colorOwner = other.colorOwner;
480 rotationOwner = other.rotationOwner;
481 deformationOwner = other.deformationOwner;
482 animationOwner = other.animationOwner;
485 QDeclarativeV8Handle QQuickParticleData::v8Value()
488 v8Datum = new QQuickV8ParticleData(QDeclarativeEnginePrivate::getV8Engine(qmlEngine(system)), this);
489 return v8Datum->v8Value();
491 //sets the x accleration without affecting the instantaneous x velocity or position
492 void QQuickParticleData::setInstantaneousAX(qreal ax)
494 qreal t = (system->timeInt / 1000.0) - this->t;
495 qreal vx = (this->vx + t*this->ax) - t*ax;
496 qreal ex = this->x + this->vx * t + 0.5 * this->ax * t * t;
497 qreal x = ex - t*vx - 0.5 * t*t*ax;
504 //sets the x velocity without affecting the instantaneous x postion
505 void QQuickParticleData::setInstantaneousVX(qreal vx)
507 qreal t = (system->timeInt / 1000.0) - this->t;
508 qreal evx = vx - t*this->ax;
509 qreal ex = this->x + this->vx * t + 0.5 * this->ax * t * t;
510 qreal x = ex - t*evx - 0.5 * t*t*this->ax;
516 //sets the instantaneous x postion
517 void QQuickParticleData::setInstantaneousX(qreal x)
519 qreal t = (system->timeInt / 1000.0) - this->t;
520 this->x = x - t*this->vx - 0.5 * t*t*this->ax;
523 //sets the y accleration without affecting the instantaneous y velocity or position
524 void QQuickParticleData::setInstantaneousAY(qreal ay)
526 qreal t = (system->timeInt / 1000.0) - this->t;
527 qreal vy = (this->vy + t*this->ay) - t*ay;
528 qreal ey = this->y + this->vy * t + 0.5 * this->ay * t * t;
529 qreal y = ey - t*vy - 0.5 * t*t*ay;
536 //sets the y velocity without affecting the instantaneous y position
537 void QQuickParticleData::setInstantaneousVY(qreal vy)
539 qreal t = (system->timeInt / 1000.0) - this->t;
540 qreal evy = vy - t*this->ay;
541 qreal ey = this->y + this->vy * t + 0.5 * this->ay * t * t;
542 qreal y = ey - t*evy - 0.5 * t*t*this->ay;
548 //sets the instantaneous Y position
549 void QQuickParticleData::setInstantaneousY(qreal y)
551 qreal t = (system->timeInt / 1000.0) - this->t;
552 this->y = y - t*this->vy - 0.5 * t*t*this->ay;
555 qreal QQuickParticleData::curX() const
557 qreal t = (system->timeInt / 1000.0) - this->t;
558 return this->x + this->vx * t + 0.5 * this->ax * t * t;
561 qreal QQuickParticleData::curVX() const
563 qreal t = (system->timeInt / 1000.0) - this->t;
564 return this->vx + t*this->ax;
567 qreal QQuickParticleData::curY() const
569 qreal t = (system->timeInt / 1000.0) - this->t;
570 return y + vy * t + 0.5 * ay * t * t;
573 qreal QQuickParticleData::curVY() const
575 qreal t = (system->timeInt / 1000.0) - this->t;
579 void QQuickParticleData::debugDump()
581 qDebug() << "Particle" << systemIndex << group << "/" << index << stillAlive()
582 << "Pos: " << x << "," << y
583 << "Vel: " << vx << "," << vy
584 << "Acc: " << ax << "," << ay
585 << "Size: " << size << "," << endSize
586 << "Time: " << t << "," <<lifeSpan << ";" << (system->timeInt / 1000.0) ;
589 bool QQuickParticleData::stillAlive()
593 return (t + lifeSpan - EPSILON) > ((qreal)system->timeInt/1000.0);
596 bool QQuickParticleData::alive()
600 qreal st = ((qreal)system->timeInt/1000.0);
601 return (t + EPSILON) < st && (t + lifeSpan - EPSILON) > st;
604 float QQuickParticleData::curSize()
606 if (!system || !lifeSpan)
608 return size + (endSize - size) * (1 - (lifeLeft() / lifeSpan));
611 float QQuickParticleData::lifeLeft()
615 return (t + lifeSpan) - (system->timeInt/1000.0);
618 void QQuickParticleData::extendLife(float time)
622 qreal newVX = curVX();
623 qreal newVY = curVY();
628 qreal elapsed = (system->timeInt / 1000.0) - t;
629 qreal evy = newVY - elapsed*ay;
630 qreal ey = newY - elapsed*evy - 0.5 * elapsed*elapsed*ay;
631 qreal evx = newVX - elapsed*ax;
632 qreal ex = newX - elapsed*evx - 0.5 * elapsed*elapsed*ax;
640 QQuickParticleSystem::QQuickParticleSystem(QQuickItem *parent) :
648 m_componentComplete(false),
652 connect(&m_painterMapper, SIGNAL(mapped(QObject*)),
653 this, SLOT(loadPainter(QObject*)));
655 m_debugMode = qmlParticlesDebug();
658 QQuickParticleSystem::~QQuickParticleSystem()
660 foreach (QQuickParticleGroupData* gd, groupData)
664 void QQuickParticleSystem::initGroups()
666 m_reusableIndexes.clear();
669 qDeleteAll(groupData);
673 QQuickParticleGroupData* gd = new QQuickParticleGroupData(0, this);//Default group
674 groupData.insert(0,gd);
675 groupIds.insert(QString(), 0);
679 void QQuickParticleSystem::registerParticlePainter(QQuickParticlePainter* p)
682 qDebug() << "Registering Painter" << p << "to" << this;
683 //TODO: a way to Unregister emitters, painters and affectors
684 m_painters << QPointer<QQuickParticlePainter>(p);//###Set or uniqueness checking?
685 connect(p, SIGNAL(groupsChanged(QStringList)),
686 &m_painterMapper, SLOT(map()));
690 void QQuickParticleSystem::registerParticleEmitter(QQuickParticleEmitter* e)
693 qDebug() << "Registering Emitter" << e << "to" << this;
694 m_emitters << QPointer<QQuickParticleEmitter>(e);//###How to get them out?
695 connect(e, SIGNAL(particleCountChanged()),
696 this, SLOT(emittersChanged()));
697 connect(e, SIGNAL(groupChanged(QString)),
698 this, SLOT(emittersChanged()));
700 e->reset();//Start, so that starttime factors appropriately
703 void QQuickParticleSystem::registerParticleAffector(QQuickParticleAffector* a)
706 qDebug() << "Registering Affector" << a << "to" << this;
707 m_affectors << QPointer<QQuickParticleAffector>(a);
710 void QQuickParticleSystem::registerParticleGroup(QQuickParticleGroup* g)
713 qDebug() << "Registering Group" << g << "to" << this;
714 m_groups << QPointer<QQuickParticleGroup>(g);
718 void QQuickParticleSystem::setRunning(bool arg)
720 if (m_running != arg) {
722 emit runningChanged(arg);
724 if (m_animation)//Not created until componentCompleted
725 m_running ? m_animation->start() : m_animation->stop();
730 void QQuickParticleSystem::setPaused(bool arg) {
731 if (m_paused != arg) {
733 if (m_animation && m_animation->state() != QAbstractAnimation::Stopped)
734 m_paused ? m_animation->pause() : m_animation->resume();
736 foreach (QQuickParticlePainter *p, m_painters)
739 emit pausedChanged(arg);
743 void QQuickParticleSystem::statePropertyRedirect(QDeclarativeListProperty<QObject> *prop, QObject *value)
745 //Hooks up automatic state-associated stuff
746 QQuickParticleSystem* sys = qobject_cast<QQuickParticleSystem*>(prop->object->parent());
747 QQuickParticleGroup* group = qobject_cast<QQuickParticleGroup*>(prop->object);
748 if (!group || !sys || !value)
750 stateRedirect(group, sys, value);
753 void QQuickParticleSystem::stateRedirect(QQuickParticleGroup* group, QQuickParticleSystem* sys, QObject *value)
756 list << group->name();
757 QQuickParticleAffector* a = qobject_cast<QQuickParticleAffector*>(value);
759 a->setParentItem(sys);
764 QQuickTrailEmitter* fe = qobject_cast<QQuickTrailEmitter*>(value);
766 fe->setParentItem(sys);
767 fe->setFollow(group->name());
771 QQuickParticleEmitter* e = qobject_cast<QQuickParticleEmitter*>(value);
773 e->setParentItem(sys);
774 e->setGroup(group->name());
778 QQuickParticlePainter* p = qobject_cast<QQuickParticlePainter*>(value);
780 p->setParentItem(sys);
785 qWarning() << value << " was placed inside a particle system state but cannot be taken into the particle system. It will be lost.";
788 void QQuickParticleSystem::componentComplete()
791 QQuickItem::componentComplete();
792 m_componentComplete = true;
793 m_animation = new QQuickParticleSystemAnimation(this);
794 reset();//restarts animation as well
797 void QQuickParticleSystem::reset()
799 if (!m_componentComplete)
803 //Clear guarded pointers which have been deleted
805 cleared += m_emitters.removeAll(0);
806 cleared += m_painters.removeAll(0);
807 cleared += m_affectors.removeAll(0);
810 initGroups();//Also clears all logical particles
815 foreach (QQuickParticleEmitter* e, m_emitters)
820 foreach (QQuickParticlePainter *p, m_painters) {
825 //### Do affectors need reset too?
826 if (m_animation) {//Animation is explicitly disabled in benchmarks
827 //reset restarts animation (if running)
828 if ((m_animation->state() == QAbstractAnimation::Running))
830 m_animation->start();
832 m_animation->pause();
839 void QQuickParticleSystem::loadPainter(QObject *p)
841 if (!m_componentComplete || !p)
844 QQuickParticlePainter* painter = qobject_cast<QQuickParticlePainter*>(p);
845 Q_ASSERT(painter);//XXX
846 foreach (QQuickParticleGroupData* sg, groupData)
847 sg->painters.remove(painter);
848 int particleCount = 0;
849 if (painter->groups().isEmpty()) {//Uses default particle
852 painter->setGroups(def);
853 particleCount += groupData[0]->size();
854 groupData[0]->painters << painter;
856 foreach (const QString &group, painter->groups()) {
857 if (group != QLatin1String("") && !groupIds[group]) {//new group
858 int id = m_nextGroupId++;
859 QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this);
860 groupIds.insert(group, id);
861 groupData.insert(id, gd);
863 particleCount += groupData[groupIds[group]]->size();
864 groupData[groupIds[group]]->painters << painter;
867 painter->setCount(particleCount);
868 painter->update();//Initial update here
872 void QQuickParticleSystem::emittersChanged()
874 if (!m_componentComplete)
877 m_emitters.removeAll(0);
880 QList<int> previousSizes;
882 for (int i=0; i<m_nextGroupId; i++) {
883 previousSizes << groupData[i]->size();
887 foreach (QQuickParticleEmitter* e, m_emitters) {//Populate groups and set sizes.
888 if (!groupIds.contains(e->group())
889 || (!e->group().isEmpty() && !groupIds[e->group()])) {//or it was accidentally inserted by a failed lookup earlier
890 int id = m_nextGroupId++;
891 QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this);
892 groupIds.insert(e->group(), id);
893 groupData.insert(id, gd);
897 newSizes[groupIds[e->group()]] += e->particleCount();
898 //###: Cull emptied groups?
901 //TODO: Garbage collection?
903 for (int i=0; i<m_nextGroupId; i++) {
904 groupData[i]->setSize(qMax(newSizes[i], previousSizes[i]));
905 particleCount += groupData[i]->size();
909 qDebug() << "Particle system emitters changed. New particle count: " << particleCount;
911 if (particleCount > bySysIdx.size())//New datum requests haven't updated it
912 bySysIdx.resize(particleCount);
914 foreach (QQuickParticleAffector *a, m_affectors)//Groups may have changed
915 a->m_updateIntSet = true;
917 foreach (QQuickParticlePainter *p, m_painters)
920 if (!m_groups.isEmpty())
925 void QQuickParticleSystem::createEngine()
927 if (!m_componentComplete)
929 if (stateEngine && m_debugMode)
930 qDebug() << "Resetting Existing Sprite Engine...";
931 //### Solve the losses if size/states go down
932 foreach (QQuickParticleGroup* group, m_groups) {
934 foreach (const QString &name, groupIds.keys())
935 if (group->name() == name)
938 int id = m_nextGroupId++;
939 QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this);
940 groupIds.insert(group->name(), id);
941 groupData.insert(id, gd);
945 if (m_groups.count()) {
946 //Reorder groups List so as to have the same order as groupData
947 QList<QQuickParticleGroup*> newList;
948 for (int i=0; i<m_nextGroupId; i++) {
950 QString name = groupData[i]->name();
951 foreach (QQuickParticleGroup* existing, m_groups) {
952 if (existing->name() == name) {
958 newList << new QQuickParticleGroup(this);
959 newList.back()->setName(name);
963 QList<QQuickStochasticState*> states;
964 foreach (QQuickParticleGroup* g, m_groups)
965 states << (QQuickStochasticState*)g;
968 stateEngine = new QQuickStochasticEngine(this);
969 stateEngine->setCount(particleCount);
970 stateEngine->m_states = states;
972 connect(stateEngine, SIGNAL(stateChanged(int)),
973 this, SLOT(particleStateChange(int)));
983 void QQuickParticleSystem::particleStateChange(int idx)
985 moveGroups(bySysIdx[idx], stateEngine->curState(idx));
988 void QQuickParticleSystem::moveGroups(QQuickParticleData *d, int newGIdx)
990 if (!d || newGIdx == d->group)
993 QQuickParticleData* pd = newDatum(newGIdx, false, d->systemIndex);
1000 d->systemIndex = -1;
1001 groupData[d->group]->kill(d);
1004 int QQuickParticleSystem::nextSystemIndex()
1006 if (!m_reusableIndexes.isEmpty()) {
1007 int ret = *(m_reusableIndexes.begin());
1008 m_reusableIndexes.remove(ret);
1011 if (m_nextIndex >= bySysIdx.size()) {
1012 bySysIdx.resize(bySysIdx.size() < 10 ? 10 : bySysIdx.size()*1.1);//###+1,10%,+10? Choose something non-arbitrarily
1014 stateEngine->setCount(bySysIdx.size());
1017 return m_nextIndex++;
1020 QQuickParticleData* QQuickParticleSystem::newDatum(int groupId, bool respectLimits, int sysIndex)
1022 Q_ASSERT(groupId < groupData.count());//XXX shouldn't really be an assert
1024 QQuickParticleData* ret = groupData[groupId]->newDatum(respectLimits);
1028 if (sysIndex == -1) {
1029 if (ret->systemIndex == -1)
1030 ret->systemIndex = nextSystemIndex();
1032 if (ret->systemIndex != -1) {
1034 stateEngine->stop(ret->systemIndex);
1035 m_reusableIndexes << ret->systemIndex;
1036 bySysIdx[ret->systemIndex] = 0;
1038 ret->systemIndex = sysIndex;
1040 bySysIdx[ret->systemIndex] = ret;
1043 stateEngine->start(ret->systemIndex, ret->group);
1049 void QQuickParticleSystem::emitParticle(QQuickParticleData* pd)
1050 {// called from prepareNextFrame()->emitWindow - enforce?
1051 //Account for relative emitter position
1052 QPointF offset = this->mapFromItem(pd->e, QPointF(0, 0));
1053 if (!offset.isNull()) {
1054 pd->x += offset.x();
1055 pd->y += offset.y();
1061 void QQuickParticleSystem::finishNewDatum(QQuickParticleData *pd)
1064 groupData[pd->group]->prepareRecycler(pd);
1066 foreach (QQuickParticleAffector *a, m_affectors)
1067 if (a && a->m_needsReset)
1069 foreach (QQuickParticlePainter* p, groupData[pd->group]->painters)
1074 void QQuickParticleSystem::updateCurrentTime( int currentTime )
1077 return;//error in initialization
1079 //### Elapsed time never shrinks - may cause problems if left emitting for weeks at a time.
1080 qreal dt = timeInt / 1000.;
1081 timeInt = currentTime;
1082 qreal time = timeInt / 1000.;
1086 m_emitters.removeAll(0);
1087 m_painters.removeAll(0);
1088 m_affectors.removeAll(0);
1090 bool oldClear = m_empty;
1092 foreach (QQuickParticleGroupData* gd, groupData)//Recycle all groups and see if they're out of live particles
1093 m_empty = gd->recycle() && m_empty;
1096 stateEngine->updateSprites(timeInt);
1098 foreach (QQuickParticleEmitter* emitter, m_emitters)
1099 emitter->emitWindow(timeInt);
1100 foreach (QQuickParticleAffector* a, m_affectors)
1101 a->affectSystem(dt);
1102 foreach (QQuickParticleData* d, needsReset)
1103 foreach (QQuickParticlePainter* p, groupData[d->group]->painters)
1106 if (oldClear != m_empty)
1107 emptyChanged(m_empty);
1110 int QQuickParticleSystem::systemSync(QQuickParticlePainter* p)
1115 return 0;//error in initialization
1116 p->performPendingCommits();