1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
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) :
646 m_componentComplete(false),
649 connect(&m_painterMapper, SIGNAL(mapped(QObject*)),
650 this, SLOT(loadPainter(QObject*)));
652 m_debugMode = qmlParticlesDebug();
655 QQuickParticleSystem::~QQuickParticleSystem()
657 foreach (QQuickParticleGroupData* gd, groupData)
661 void QQuickParticleSystem::initGroups()
663 m_reusableIndexes.clear();
666 qDeleteAll(groupData);
670 QQuickParticleGroupData* gd = new QQuickParticleGroupData(0, this);//Default group
671 groupData.insert(0,gd);
672 groupIds.insert(QString(), 0);
676 void QQuickParticleSystem::registerParticlePainter(QQuickParticlePainter* p)
679 qDebug() << "Registering Painter" << p << "to" << this;
680 //TODO: a way to Unregister emitters, painters and affectors
681 m_painters << QPointer<QQuickParticlePainter>(p);//###Set or uniqueness checking?
682 connect(p, SIGNAL(groupsChanged(QStringList)),
683 &m_painterMapper, SLOT(map()));
687 void QQuickParticleSystem::registerParticleEmitter(QQuickParticleEmitter* e)
690 qDebug() << "Registering Emitter" << e << "to" << this;
691 m_emitters << QPointer<QQuickParticleEmitter>(e);//###How to get them out?
692 connect(e, SIGNAL(particleCountChanged()),
693 this, SLOT(emittersChanged()));
694 connect(e, SIGNAL(groupChanged(QString)),
695 this, SLOT(emittersChanged()));
697 e->reset();//Start, so that starttime factors appropriately
700 void QQuickParticleSystem::registerParticleAffector(QQuickParticleAffector* a)
703 qDebug() << "Registering Affector" << a << "to" << this;
704 m_affectors << QPointer<QQuickParticleAffector>(a);
707 void QQuickParticleSystem::registerParticleGroup(QQuickParticleGroup* g)
710 qDebug() << "Registering Group" << g << "to" << this;
711 m_groups << QPointer<QQuickParticleGroup>(g);
715 void QQuickParticleSystem::setRunning(bool arg)
717 if (m_running != arg) {
719 emit runningChanged(arg);
721 if (m_animation)//Not created until componentCompleted
722 m_running ? m_animation->start() : m_animation->stop();
727 void QQuickParticleSystem::setPaused(bool arg) {
728 if (m_paused != arg) {
730 if (m_animation && m_animation->state() != QAbstractAnimation::Stopped)
731 m_paused ? m_animation->pause() : m_animation->resume();
733 foreach (QQuickParticlePainter *p, m_painters)
736 emit pausedChanged(arg);
740 void QQuickParticleSystem::statePropertyRedirect(QDeclarativeListProperty<QObject> *prop, QObject *value)
742 //Hooks up automatic state-associated stuff
743 QQuickParticleSystem* sys = qobject_cast<QQuickParticleSystem*>(prop->object->parent());
744 QQuickParticleGroup* group = qobject_cast<QQuickParticleGroup*>(prop->object);
745 if (!group || !sys || !value)
747 stateRedirect(group, sys, value);
750 void QQuickParticleSystem::stateRedirect(QQuickParticleGroup* group, QQuickParticleSystem* sys, QObject *value)
753 list << group->name();
754 QQuickParticleAffector* a = qobject_cast<QQuickParticleAffector*>(value);
756 a->setParentItem(sys);
761 QQuickTrailEmitter* fe = qobject_cast<QQuickTrailEmitter*>(value);
763 fe->setParentItem(sys);
764 fe->setFollow(group->name());
768 QQuickParticleEmitter* e = qobject_cast<QQuickParticleEmitter*>(value);
770 e->setParentItem(sys);
771 e->setGroup(group->name());
775 QQuickParticlePainter* p = qobject_cast<QQuickParticlePainter*>(value);
777 p->setParentItem(sys);
782 qWarning() << value << " was placed inside a particle system state but cannot be taken into the particle system. It will be lost.";
785 void QQuickParticleSystem::componentComplete()
788 QQuickItem::componentComplete();
789 m_componentComplete = true;
790 m_animation = new QQuickParticleSystemAnimation(this);
791 reset();//restarts animation as well
794 void QQuickParticleSystem::reset()
796 if (!m_componentComplete)
800 //Clear guarded pointers which have been deleted
802 cleared += m_emitters.removeAll(0);
803 cleared += m_painters.removeAll(0);
804 cleared += m_affectors.removeAll(0);
807 initGroups();//Also clears all logical particles
812 foreach (QQuickParticleEmitter* e, m_emitters)
817 foreach (QQuickParticlePainter *p, m_painters) {
822 //### Do affectors need reset too?
823 if (m_animation) {//Animation is explicitly disabled in benchmarks
824 //reset restarts animation (if running)
825 if ((m_animation->state() == QAbstractAnimation::Running))
827 m_animation->start();
829 m_animation->pause();
836 void QQuickParticleSystem::loadPainter(QObject *p)
838 if (!m_componentComplete)
841 QQuickParticlePainter* painter = qobject_cast<QQuickParticlePainter*>(p);
842 Q_ASSERT(painter);//XXX
843 foreach (QQuickParticleGroupData* sg, groupData)
844 sg->painters.remove(painter);
845 int particleCount = 0;
846 if (painter->groups().isEmpty()) {//Uses default particle
849 painter->setGroups(def);
850 particleCount += groupData[0]->size();
851 groupData[0]->painters << painter;
853 foreach (const QString &group, painter->groups()) {
854 if (group != QLatin1String("") && !groupIds[group]) {//new group
855 int id = m_nextGroupId++;
856 QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this);
857 groupIds.insert(group, id);
858 groupData.insert(id, gd);
860 particleCount += groupData[groupIds[group]]->size();
861 groupData[groupIds[group]]->painters << painter;
864 painter->setCount(particleCount);
865 painter->update();//Initial update here
869 void QQuickParticleSystem::emittersChanged()
871 if (!m_componentComplete)
874 m_emitters.removeAll(0);
877 QList<int> previousSizes;
879 for (int i=0; i<m_nextGroupId; i++) {
880 previousSizes << groupData[i]->size();
884 foreach (QQuickParticleEmitter* e, m_emitters) {//Populate groups and set sizes.
885 if (!groupIds.contains(e->group())
886 || (!e->group().isEmpty() && !groupIds[e->group()])) {//or it was accidentally inserted by a failed lookup earlier
887 int id = m_nextGroupId++;
888 QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this);
889 groupIds.insert(e->group(), id);
890 groupData.insert(id, gd);
894 newSizes[groupIds[e->group()]] += e->particleCount();
895 //###: Cull emptied groups?
898 //TODO: Garbage collection?
900 for (int i=0; i<m_nextGroupId; i++) {
901 groupData[i]->setSize(qMax(newSizes[i], previousSizes[i]));
902 particleCount += groupData[i]->size();
906 qDebug() << "Particle system emitters changed. New particle count: " << particleCount;
908 if (particleCount > bySysIdx.size())//New datum requests haven't updated it
909 bySysIdx.resize(particleCount);
911 foreach (QQuickParticlePainter *p, m_painters)
914 if (!m_groups.isEmpty())
919 void QQuickParticleSystem::createEngine()
921 if (!m_componentComplete)
923 if (stateEngine && m_debugMode)
924 qDebug() << "Resetting Existing Sprite Engine...";
925 //### Solve the losses if size/states go down
926 foreach (QQuickParticleGroup* group, m_groups) {
928 foreach (const QString &name, groupIds.keys())
929 if (group->name() == name)
932 int id = m_nextGroupId++;
933 QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this);
934 groupIds.insert(group->name(), id);
935 groupData.insert(id, gd);
939 if (m_groups.count()) {
940 //Reorder groups List so as to have the same order as groupData
941 QList<QQuickParticleGroup*> newList;
942 for (int i=0; i<m_nextGroupId; i++) {
944 QString name = groupData[i]->name();
945 foreach (QQuickParticleGroup* existing, m_groups) {
946 if (existing->name() == name) {
952 newList << new QQuickParticleGroup(this);
953 newList.back()->setName(name);
957 QList<QQuickStochasticState*> states;
958 foreach (QQuickParticleGroup* g, m_groups)
959 states << (QQuickStochasticState*)g;
962 stateEngine = new QQuickStochasticEngine(this);
963 stateEngine->setCount(particleCount);
964 stateEngine->m_states = states;
966 connect(stateEngine, SIGNAL(stateChanged(int)),
967 this, SLOT(particleStateChange(int)));
977 void QQuickParticleSystem::particleStateChange(int idx)
979 moveGroups(bySysIdx[idx], stateEngine->curState(idx));
982 void QQuickParticleSystem::moveGroups(QQuickParticleData *d, int newGIdx)
984 if (!d || newGIdx == d->group)
987 QQuickParticleData* pd = newDatum(newGIdx, false, d->systemIndex);
995 groupData[d->group]->kill(d);
998 int QQuickParticleSystem::nextSystemIndex()
1000 if (!m_reusableIndexes.isEmpty()) {
1001 int ret = *(m_reusableIndexes.begin());
1002 m_reusableIndexes.remove(ret);
1005 if (m_nextIndex >= bySysIdx.size()) {
1006 bySysIdx.resize(bySysIdx.size() < 10 ? 10 : bySysIdx.size()*1.1);//###+1,10%,+10? Choose something non-arbitrarily
1008 stateEngine->setCount(bySysIdx.size());
1011 return m_nextIndex++;
1014 QQuickParticleData* QQuickParticleSystem::newDatum(int groupId, bool respectLimits, int sysIndex)
1016 Q_ASSERT(groupId < groupData.count());//XXX shouldn't really be an assert
1018 QQuickParticleData* ret = groupData[groupId]->newDatum(respectLimits);
1022 if (sysIndex == -1) {
1023 if (ret->systemIndex == -1)
1024 ret->systemIndex = nextSystemIndex();
1026 if (ret->systemIndex != -1) {
1028 stateEngine->stop(ret->systemIndex);
1029 m_reusableIndexes << ret->systemIndex;
1030 bySysIdx[ret->systemIndex] = 0;
1032 ret->systemIndex = sysIndex;
1034 bySysIdx[ret->systemIndex] = ret;
1037 stateEngine->start(ret->systemIndex, ret->group);
1043 void QQuickParticleSystem::emitParticle(QQuickParticleData* pd)
1044 {// called from prepareNextFrame()->emitWindow - enforce?
1045 //Account for relative emitter position
1046 QPointF offset = this->mapFromItem(pd->e, QPointF(0, 0));
1047 if (!offset.isNull()) {
1048 pd->x += offset.x();
1049 pd->y += offset.y();
1055 void QQuickParticleSystem::finishNewDatum(QQuickParticleData *pd)
1058 groupData[pd->group]->prepareRecycler(pd);
1060 foreach (QQuickParticleAffector *a, m_affectors)
1061 if (a && a->m_needsReset)
1063 foreach (QQuickParticlePainter* p, groupData[pd->group]->painters)
1068 void QQuickParticleSystem::updateCurrentTime( int currentTime )
1071 return;//error in initialization
1073 //### Elapsed time never shrinks - may cause problems if left emitting for weeks at a time.
1074 qreal dt = timeInt / 1000.;
1075 timeInt = currentTime;
1076 qreal time = timeInt / 1000.;
1080 m_emitters.removeAll(0);
1081 m_painters.removeAll(0);
1082 m_affectors.removeAll(0);
1084 bool oldClear = m_empty;
1086 foreach (QQuickParticleGroupData* gd, groupData)//Recycle all groups and see if they're out of live particles
1087 m_empty = gd->recycle() && m_empty;
1090 stateEngine->updateSprites(timeInt);
1092 foreach (QQuickParticleEmitter* emitter, m_emitters)
1093 emitter->emitWindow(timeInt);
1094 foreach (QQuickParticleAffector* a, m_affectors)
1095 a->affectSystem(dt);
1096 foreach (QQuickParticleData* d, needsReset)
1097 foreach (QQuickParticlePainter* p, groupData[d->group]->painters)
1100 if (oldClear != m_empty)
1101 emptyChanged(m_empty);
1104 int QQuickParticleSystem::systemSync(QQuickParticlePainter* p)
1109 return 0;//error in initialization
1110 p->performPendingCommits();