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)
438 QQuickParticleData::~QQuickParticleData()
443 void QQuickParticleData::clone(const QQuickParticleData& other)
448 lifeSpan = other.lifeSpan;
450 endSize = other.endSize;
459 rotation = other.rotation;
460 rotationSpeed = other.rotationSpeed;
461 autoRotate = other.autoRotate;
462 animIdx = other.animIdx;
463 frameDuration = other.frameDuration;
464 frameCount = other.frameCount;
468 animWidth = other.animWidth;
469 animHeight = other.animHeight;
470 color.r = other.color.r;
471 color.g = other.color.g;
472 color.b = other.color.b;
473 color.a = other.color.a;
475 delegate = other.delegate;
476 modelIndex = other.modelIndex;
478 colorOwner = other.colorOwner;
479 rotationOwner = other.rotationOwner;
480 deformationOwner = other.deformationOwner;
481 animationOwner = other.animationOwner;
484 QDeclarativeV8Handle QQuickParticleData::v8Value()
487 v8Datum = new QQuickV8ParticleData(QDeclarativeEnginePrivate::getV8Engine(qmlEngine(system)), this);
488 return v8Datum->v8Value();
490 //sets the x accleration without affecting the instantaneous x velocity or position
491 void QQuickParticleData::setInstantaneousAX(qreal ax)
493 qreal t = (system->timeInt / 1000.0) - this->t;
494 qreal vx = (this->vx + t*this->ax) - t*ax;
495 qreal ex = this->x + this->vx * t + 0.5 * this->ax * t * t;
496 qreal x = ex - t*vx - 0.5 * t*t*ax;
503 //sets the x velocity without affecting the instantaneous x postion
504 void QQuickParticleData::setInstantaneousVX(qreal vx)
506 qreal t = (system->timeInt / 1000.0) - this->t;
507 qreal evx = vx - t*this->ax;
508 qreal ex = this->x + this->vx * t + 0.5 * this->ax * t * t;
509 qreal x = ex - t*evx - 0.5 * t*t*this->ax;
515 //sets the instantaneous x postion
516 void QQuickParticleData::setInstantaneousX(qreal x)
518 qreal t = (system->timeInt / 1000.0) - this->t;
519 this->x = x - t*this->vx - 0.5 * t*t*this->ax;
522 //sets the y accleration without affecting the instantaneous y velocity or position
523 void QQuickParticleData::setInstantaneousAY(qreal ay)
525 qreal t = (system->timeInt / 1000.0) - this->t;
526 qreal vy = (this->vy + t*this->ay) - t*ay;
527 qreal ey = this->y + this->vy * t + 0.5 * this->ay * t * t;
528 qreal y = ey - t*vy - 0.5 * t*t*ay;
535 //sets the y velocity without affecting the instantaneous y position
536 void QQuickParticleData::setInstantaneousVY(qreal vy)
538 qreal t = (system->timeInt / 1000.0) - this->t;
539 qreal evy = vy - t*this->ay;
540 qreal ey = this->y + this->vy * t + 0.5 * this->ay * t * t;
541 qreal y = ey - t*evy - 0.5 * t*t*this->ay;
547 //sets the instantaneous Y position
548 void QQuickParticleData::setInstantaneousY(qreal y)
550 qreal t = (system->timeInt / 1000.0) - this->t;
551 this->y = y - t*this->vy - 0.5 * t*t*this->ay;
554 qreal QQuickParticleData::curX() const
556 qreal t = (system->timeInt / 1000.0) - this->t;
557 return this->x + this->vx * t + 0.5 * this->ax * t * t;
560 qreal QQuickParticleData::curVX() const
562 qreal t = (system->timeInt / 1000.0) - this->t;
563 return this->vx + t*this->ax;
566 qreal QQuickParticleData::curY() const
568 qreal t = (system->timeInt / 1000.0) - this->t;
569 return y + vy * t + 0.5 * ay * t * t;
572 qreal QQuickParticleData::curVY() const
574 qreal t = (system->timeInt / 1000.0) - this->t;
578 void QQuickParticleData::debugDump()
580 qDebug() << "Particle" << systemIndex << group << "/" << index << stillAlive()
581 << "Pos: " << x << "," << y
582 << "Vel: " << vx << "," << vy
583 << "Acc: " << ax << "," << ay
584 << "Size: " << size << "," << endSize
585 << "Time: " << t << "," <<lifeSpan << ";" << (system->timeInt / 1000.0) ;
588 bool QQuickParticleData::stillAlive()
592 return (t + lifeSpan - EPSILON) > ((qreal)system->timeInt/1000.0);
595 bool QQuickParticleData::alive()
599 qreal st = ((qreal)system->timeInt/1000.0);
600 return (t + EPSILON) < st && (t + lifeSpan - EPSILON) > st;
603 float QQuickParticleData::curSize()
605 if (!system || !lifeSpan)
607 return size + (endSize - size) * (1 - (lifeLeft() / lifeSpan));
610 float QQuickParticleData::lifeLeft()
614 return (t + lifeSpan) - (system->timeInt/1000.0);
617 void QQuickParticleData::extendLife(float time)
621 qreal newVX = curVX();
622 qreal newVY = curVY();
627 qreal elapsed = (system->timeInt / 1000.0) - t;
628 qreal evy = newVY - elapsed*ay;
629 qreal ey = newY - elapsed*evy - 0.5 * elapsed*elapsed*ay;
630 qreal evx = newVX - elapsed*ax;
631 qreal ex = newX - elapsed*evx - 0.5 * elapsed*elapsed*ax;
639 QQuickParticleSystem::QQuickParticleSystem(QQuickItem *parent) :
647 m_componentComplete(false),
651 connect(&m_painterMapper, SIGNAL(mapped(QObject*)),
652 this, SLOT(loadPainter(QObject*)));
654 m_debugMode = qmlParticlesDebug();
657 QQuickParticleSystem::~QQuickParticleSystem()
659 foreach (QQuickParticleGroupData* gd, groupData)
663 void QQuickParticleSystem::initGroups()
665 m_reusableIndexes.clear();
668 qDeleteAll(groupData);
672 QQuickParticleGroupData* gd = new QQuickParticleGroupData(0, this);//Default group
673 groupData.insert(0,gd);
674 groupIds.insert(QString(), 0);
678 void QQuickParticleSystem::registerParticlePainter(QQuickParticlePainter* p)
681 qDebug() << "Registering Painter" << p << "to" << this;
682 //TODO: a way to Unregister emitters, painters and affectors
683 m_painters << QPointer<QQuickParticlePainter>(p);//###Set or uniqueness checking?
684 connect(p, SIGNAL(groupsChanged(QStringList)),
685 &m_painterMapper, SLOT(map()));
689 void QQuickParticleSystem::registerParticleEmitter(QQuickParticleEmitter* e)
692 qDebug() << "Registering Emitter" << e << "to" << this;
693 m_emitters << QPointer<QQuickParticleEmitter>(e);//###How to get them out?
694 connect(e, SIGNAL(particleCountChanged()),
695 this, SLOT(emittersChanged()));
696 connect(e, SIGNAL(groupChanged(QString)),
697 this, SLOT(emittersChanged()));
699 e->reset();//Start, so that starttime factors appropriately
702 void QQuickParticleSystem::registerParticleAffector(QQuickParticleAffector* a)
705 qDebug() << "Registering Affector" << a << "to" << this;
706 m_affectors << QPointer<QQuickParticleAffector>(a);
709 void QQuickParticleSystem::registerParticleGroup(QQuickParticleGroup* g)
712 qDebug() << "Registering Group" << g << "to" << this;
713 m_groups << QPointer<QQuickParticleGroup>(g);
717 void QQuickParticleSystem::setRunning(bool arg)
719 if (m_running != arg) {
721 emit runningChanged(arg);
723 if (m_animation)//Not created until componentCompleted
724 m_running ? m_animation->start() : m_animation->stop();
729 void QQuickParticleSystem::setPaused(bool arg) {
730 if (m_paused != arg) {
732 if (m_animation && m_animation->state() != QAbstractAnimation::Stopped)
733 m_paused ? m_animation->pause() : m_animation->resume();
735 foreach (QQuickParticlePainter *p, m_painters)
738 emit pausedChanged(arg);
742 void QQuickParticleSystem::statePropertyRedirect(QDeclarativeListProperty<QObject> *prop, QObject *value)
744 //Hooks up automatic state-associated stuff
745 QQuickParticleSystem* sys = qobject_cast<QQuickParticleSystem*>(prop->object->parent());
746 QQuickParticleGroup* group = qobject_cast<QQuickParticleGroup*>(prop->object);
747 if (!group || !sys || !value)
749 stateRedirect(group, sys, value);
752 void QQuickParticleSystem::stateRedirect(QQuickParticleGroup* group, QQuickParticleSystem* sys, QObject *value)
755 list << group->name();
756 QQuickParticleAffector* a = qobject_cast<QQuickParticleAffector*>(value);
758 a->setParentItem(sys);
763 QQuickTrailEmitter* fe = qobject_cast<QQuickTrailEmitter*>(value);
765 fe->setParentItem(sys);
766 fe->setFollow(group->name());
770 QQuickParticleEmitter* e = qobject_cast<QQuickParticleEmitter*>(value);
772 e->setParentItem(sys);
773 e->setGroup(group->name());
777 QQuickParticlePainter* p = qobject_cast<QQuickParticlePainter*>(value);
779 p->setParentItem(sys);
784 qWarning() << value << " was placed inside a particle system state but cannot be taken into the particle system. It will be lost.";
787 void QQuickParticleSystem::componentComplete()
790 QQuickItem::componentComplete();
791 m_componentComplete = true;
792 m_animation = new QQuickParticleSystemAnimation(this);
793 reset();//restarts animation as well
796 void QQuickParticleSystem::reset()
798 if (!m_componentComplete)
802 //Clear guarded pointers which have been deleted
804 cleared += m_emitters.removeAll(0);
805 cleared += m_painters.removeAll(0);
806 cleared += m_affectors.removeAll(0);
809 initGroups();//Also clears all logical particles
814 foreach (QQuickParticleEmitter* e, m_emitters)
819 foreach (QQuickParticlePainter *p, m_painters) {
824 //### Do affectors need reset too?
825 if (m_animation) {//Animation is explicitly disabled in benchmarks
826 //reset restarts animation (if running)
827 if ((m_animation->state() == QAbstractAnimation::Running))
829 m_animation->start();
831 m_animation->pause();
838 void QQuickParticleSystem::loadPainter(QObject *p)
840 if (!m_componentComplete || !p)
843 QQuickParticlePainter* painter = qobject_cast<QQuickParticlePainter*>(p);
844 Q_ASSERT(painter);//XXX
845 foreach (QQuickParticleGroupData* sg, groupData)
846 sg->painters.remove(painter);
847 int particleCount = 0;
848 if (painter->groups().isEmpty()) {//Uses default particle
851 painter->setGroups(def);
852 particleCount += groupData[0]->size();
853 groupData[0]->painters << painter;
855 foreach (const QString &group, painter->groups()) {
856 if (group != QLatin1String("") && !groupIds[group]) {//new group
857 int id = m_nextGroupId++;
858 QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this);
859 groupIds.insert(group, id);
860 groupData.insert(id, gd);
862 particleCount += groupData[groupIds[group]]->size();
863 groupData[groupIds[group]]->painters << painter;
866 painter->setCount(particleCount);
867 painter->update();//Initial update here
871 void QQuickParticleSystem::emittersChanged()
873 if (!m_componentComplete)
876 m_emitters.removeAll(0);
879 QList<int> previousSizes;
881 for (int i=0; i<m_nextGroupId; i++) {
882 previousSizes << groupData[i]->size();
886 foreach (QQuickParticleEmitter* e, m_emitters) {//Populate groups and set sizes.
887 if (!groupIds.contains(e->group())
888 || (!e->group().isEmpty() && !groupIds[e->group()])) {//or it was accidentally inserted by a failed lookup earlier
889 int id = m_nextGroupId++;
890 QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this);
891 groupIds.insert(e->group(), id);
892 groupData.insert(id, gd);
896 newSizes[groupIds[e->group()]] += e->particleCount();
897 //###: Cull emptied groups?
900 //TODO: Garbage collection?
902 for (int i=0; i<m_nextGroupId; i++) {
903 groupData[i]->setSize(qMax(newSizes[i], previousSizes[i]));
904 particleCount += groupData[i]->size();
908 qDebug() << "Particle system emitters changed. New particle count: " << particleCount;
910 if (particleCount > bySysIdx.size())//New datum requests haven't updated it
911 bySysIdx.resize(particleCount);
913 foreach (QQuickParticleAffector *a, m_affectors)//Groups may have changed
914 a->m_updateIntSet = true;
916 foreach (QQuickParticlePainter *p, m_painters)
919 if (!m_groups.isEmpty())
924 void QQuickParticleSystem::createEngine()
926 if (!m_componentComplete)
928 if (stateEngine && m_debugMode)
929 qDebug() << "Resetting Existing Sprite Engine...";
930 //### Solve the losses if size/states go down
931 foreach (QQuickParticleGroup* group, m_groups) {
933 foreach (const QString &name, groupIds.keys())
934 if (group->name() == name)
937 int id = m_nextGroupId++;
938 QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this);
939 groupIds.insert(group->name(), id);
940 groupData.insert(id, gd);
944 if (m_groups.count()) {
945 //Reorder groups List so as to have the same order as groupData
946 QList<QQuickParticleGroup*> newList;
947 for (int i=0; i<m_nextGroupId; i++) {
949 QString name = groupData[i]->name();
950 foreach (QQuickParticleGroup* existing, m_groups) {
951 if (existing->name() == name) {
957 newList << new QQuickParticleGroup(this);
958 newList.back()->setName(name);
962 QList<QQuickStochasticState*> states;
963 foreach (QQuickParticleGroup* g, m_groups)
964 states << (QQuickStochasticState*)g;
967 stateEngine = new QQuickStochasticEngine(this);
968 stateEngine->setCount(particleCount);
969 stateEngine->m_states = states;
971 connect(stateEngine, SIGNAL(stateChanged(int)),
972 this, SLOT(particleStateChange(int)));
982 void QQuickParticleSystem::particleStateChange(int idx)
984 moveGroups(bySysIdx[idx], stateEngine->curState(idx));
987 void QQuickParticleSystem::moveGroups(QQuickParticleData *d, int newGIdx)
989 if (!d || newGIdx == d->group)
992 QQuickParticleData* pd = newDatum(newGIdx, false, d->systemIndex);
1000 groupData[d->group]->kill(d);
1003 int QQuickParticleSystem::nextSystemIndex()
1005 if (!m_reusableIndexes.isEmpty()) {
1006 int ret = *(m_reusableIndexes.begin());
1007 m_reusableIndexes.remove(ret);
1010 if (m_nextIndex >= bySysIdx.size()) {
1011 bySysIdx.resize(bySysIdx.size() < 10 ? 10 : bySysIdx.size()*1.1);//###+1,10%,+10? Choose something non-arbitrarily
1013 stateEngine->setCount(bySysIdx.size());
1016 return m_nextIndex++;
1019 QQuickParticleData* QQuickParticleSystem::newDatum(int groupId, bool respectLimits, int sysIndex)
1021 Q_ASSERT(groupId < groupData.count());//XXX shouldn't really be an assert
1023 QQuickParticleData* ret = groupData[groupId]->newDatum(respectLimits);
1027 if (sysIndex == -1) {
1028 if (ret->systemIndex == -1)
1029 ret->systemIndex = nextSystemIndex();
1031 if (ret->systemIndex != -1) {
1033 stateEngine->stop(ret->systemIndex);
1034 m_reusableIndexes << ret->systemIndex;
1035 bySysIdx[ret->systemIndex] = 0;
1037 ret->systemIndex = sysIndex;
1039 bySysIdx[ret->systemIndex] = ret;
1042 stateEngine->start(ret->systemIndex, ret->group);
1048 void QQuickParticleSystem::emitParticle(QQuickParticleData* pd)
1049 {// called from prepareNextFrame()->emitWindow - enforce?
1050 //Account for relative emitter position
1051 QPointF offset = this->mapFromItem(pd->e, QPointF(0, 0));
1052 if (!offset.isNull()) {
1053 pd->x += offset.x();
1054 pd->y += offset.y();
1060 void QQuickParticleSystem::finishNewDatum(QQuickParticleData *pd)
1063 groupData[pd->group]->prepareRecycler(pd);
1065 foreach (QQuickParticleAffector *a, m_affectors)
1066 if (a && a->m_needsReset)
1068 foreach (QQuickParticlePainter* p, groupData[pd->group]->painters)
1073 void QQuickParticleSystem::updateCurrentTime( int currentTime )
1076 return;//error in initialization
1078 //### Elapsed time never shrinks - may cause problems if left emitting for weeks at a time.
1079 qreal dt = timeInt / 1000.;
1080 timeInt = currentTime;
1081 qreal time = timeInt / 1000.;
1085 m_emitters.removeAll(0);
1086 m_painters.removeAll(0);
1087 m_affectors.removeAll(0);
1089 bool oldClear = m_empty;
1091 foreach (QQuickParticleGroupData* gd, groupData)//Recycle all groups and see if they're out of live particles
1092 m_empty = gd->recycle() && m_empty;
1095 stateEngine->updateSprites(timeInt);
1097 foreach (QQuickParticleEmitter* emitter, m_emitters)
1098 emitter->emitWindow(timeInt);
1099 foreach (QQuickParticleAffector* a, m_affectors)
1100 a->affectSystem(dt);
1101 foreach (QQuickParticleData* d, needsReset)
1102 foreach (QQuickParticlePainter* p, groupData[d->group]->painters)
1105 if (oldClear != m_empty)
1106 emptyChanged(m_empty);
1109 int QQuickParticleSystem::systemSync(QQuickParticlePainter* p)
1114 return 0;//error in initialization
1115 p->performPendingCommits();