6b5fbfae0b99922d6b74b879d52c209581232ede
[profile/ivi/qtdeclarative.git] / src / particles / qquickparticlesystem.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQuick module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
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"
51
52 #include "qquicktrailemitter_p.h"//###For auto-follow on states, perhaps should be in emitter?
53 #include <private/qqmlengine_p.h>
54 #include <private/qqmlglobal_p.h>
55 #include <cmath>
56 #include <QDebug>
57
58 QT_BEGIN_NAMESPACE
59 //###Switch to define later, for now user-friendly (no compilation) debugging is worth it
60 DEFINE_BOOL_CONFIG_OPTION(qmlParticlesDebug, QML_PARTICLES_DEBUG)
61
62
63 /* \internal ParticleSystem internals documentation
64
65    Affectors, Painters, Emitters and Groups all register themselves on construction as a callback
66    from their setSystem (or componentComplete if they have a system from a parent).
67
68    Particle data is stored by group, They have a group index (used by the particle system almost
69    everywhere) and a global index (used by the Stochastic state engine powering stochastic group
70    transitions). Each group has a recycling list/heap that stores the particle data.
71
72    The recycling list/heap is a heap of particle data sorted by when they're expected to die. If
73    they die prematurely then they are marked as reusable (and will probably still be alive when
74    they exit the heap). If they have their life extended, then they aren't dead when expected.
75    If this happens, they go back in the heap with the new estimate. If they have died on schedule,
76    then the indexes are marked as reusable. If no indexes are reusable when new particles are
77    requested, then the list is extended. This relatively complex datastructure is because memory
78    allocation and deallocation on this scale proved to be a significant performance cost. In order
79    to reuse the indexes validly (even when particles can have their life extended or cut short
80    dynamically, or particle counts grow) this seemed to be the most efficient option for keeping
81    track of which indices could be reused.
82
83    When a new particle is emitted, the emitter gets a new datum from the group (through the
84    system), and sets properties on it. Then it's passed back to the group briefly so that it can
85    now guess when the particle will die. Then the painters get a change to initialize properties
86    as well, since particle data includes shared data from painters as well as logical particle
87    data.
88
89    Every animation advance, the simulation advances by running all emitters for the elapsed
90    duration, then running all affectors, then telling all particle painters to update changed
91    particles. The ParticlePainter superclass stores these changes, and they are implemented
92    when the painter is called to paint in the render thread.
93
94    Particle group changes move the particle from one group to another by killing the old particle
95    and then creating a new one with the same data in the new group.
96
97    Note that currently groups only grow. Given that data is stored in vectors, it is non-trivial
98    to pluck out the unused indexes when the count goes down. Given the dynamic nature of the
99    system, it is difficult to tell if those unused data instances will be used again. Still,
100    some form of garbage collection is on the long term plan.
101 */
102
103 /*!
104     \qmlclass ParticleSystem QQuickParticleSystem
105     \inqmlmodule QtQuick.Particles 2
106     \brief A system which includes particle painter, emitter, and affector types
107     \ingroup qtquick-particles
108
109 */
110
111 /*!
112     \qmlproperty bool QtQuick.Particles2::ParticleSystem::running
113
114     If running is set to false, the particle system will stop the simulation. All particles
115     will be destroyed when the system is set to running again.
116
117     It can also be controlled with the start() and stop() methods.
118 */
119
120
121 /*!
122     \qmlproperty bool QtQuick.Particles2::ParticleSystem::paused
123
124     If paused is set to true, the particle system will not advance the simulation. When
125     paused is set to false again, the simulation will resume from the same point it was
126     paused.
127
128     The simulation will automatically pause if it detects that there are no live particles
129     left, and unpause when new live particles are added.
130
131     It can also be controlled with the pause() and resume() methods.
132 */
133
134 /*!
135     \qmlproperty bool QtQuick.Particles2::ParticleSystem::empty
136
137     empty is set to true when there are no live particles left in the system.
138
139     You can use this to pause the system, keeping it from spending any time updating,
140     but you will need to resume it in order for additional particles to be generated
141     by the system.
142
143     To kill all the particles in the system, use a Kill affector.
144 */
145
146 /*!
147     \qmlproperty list<Sprite> QtQuick.Particles2::ParticleSystem::particleStates
148
149     You can define a sub-set of particle groups in this property in order to provide them
150     with stochastic state transitions.
151
152     Each QtQuick2::Sprite in this list is interpreted as corresponding to the particle group
153     with ths same name. Any transitions defined in these sprites will take effect on the particle
154     groups as well. Additionally TrailEmitters, Affectors and ParticlePainters definined
155     inside one of these sprites are automatically associated with the corresponding particle group.
156 */
157
158 /*!
159     \qmlmethod void QtQuick.Particles2::ParticleSystem::pause
160
161     Pauses the simulation if it is running.
162
163     \sa resume, paused
164 */
165
166 /*!
167     \qmlmethod void QtQuick.Particles2::ParticleSystem::resume
168
169     Resumes the simulation if it is paused.
170
171     \sa pause, paused
172 */
173
174 /*!
175     \qmlmethod void QtQuick.Particles2::ParticleSystem::start
176
177     Starts the simulation if it has not already running.
178
179     \sa stop, restart, running
180 */
181
182 /*!
183     \qmlmethod void QtQuick.Particles2::ParticleSystem::stop
184
185     Stops the simulation if it is running.
186
187     \sa start, restart, running
188 */
189
190 /*!
191     \qmlmethod void QtQuick.Particles2::ParticleSystem::restart
192
193     Stops the simulation if it is running, and then starts it.
194
195     \sa stop, restart, running
196 */
197 /*!
198     \qmlmethod void QtQuick.Particles2::ParticleSystem::reset
199
200     Discards all currently existing particles.
201
202 */
203 const qreal EPSILON = 0.001;
204 //Utility functions for when within 1ms is close enough
205 bool timeEqualOrGreater(qreal a, qreal b)
206 {
207     return (a+EPSILON >= b);
208 }
209
210 bool timeLess(qreal a, qreal b)
211 {
212     return (a-EPSILON < b);
213 }
214
215 bool timeEqual(qreal a, qreal b)
216 {
217     return (a+EPSILON > b) && (a-EPSILON < b);
218 }
219
220 int roundedTime(qreal a)
221 {// in ms
222     return (int)qRound(a*1000.0);
223 }
224
225 QQuickParticleDataHeap::QQuickParticleDataHeap()
226     : m_data(0)
227 {
228     m_data.reserve(1000);
229     clear();
230 }
231
232 void QQuickParticleDataHeap::grow() //###Consider automatic growth vs resize() calls from GroupData
233 {
234     m_data.resize(1 << ++m_size);
235 }
236
237 void QQuickParticleDataHeap::insert(QQuickParticleData* data)
238 {
239     insertTimed(data, roundedTime(data->t + data->lifeSpan));
240 }
241
242 void QQuickParticleDataHeap::insertTimed(QQuickParticleData* data, int time)
243 {
244     //TODO: Optimize 0 lifespan (or already dead) case
245     if (m_lookups.contains(time)) {
246         m_data[m_lookups[time]].data << data;
247         return;
248     }
249     if (m_end == (1 << m_size))
250         grow();
251     m_data[m_end].time = time;
252     m_data[m_end].data.clear();
253     m_data[m_end].data.insert(data);
254     m_lookups.insert(time, m_end);
255     bubbleUp(m_end++);
256 }
257
258 int QQuickParticleDataHeap::top()
259 {
260     if (m_end == 0)
261         return 1 << 30;
262     return m_data[0].time;
263 }
264
265 QSet<QQuickParticleData*> QQuickParticleDataHeap::pop()
266 {
267     if (!m_end)
268         return QSet<QQuickParticleData*> ();
269     QSet<QQuickParticleData*> ret = m_data[0].data;
270     m_lookups.remove(m_data[0].time);
271     if (m_end == 1) {
272         --m_end;
273     } else {
274         m_data[0] = m_data[--m_end];
275         bubbleDown(0);
276     }
277     return ret;
278 }
279
280 void QQuickParticleDataHeap::clear()
281 {
282     m_size = 0;
283     m_end = 0;
284     //m_size is in powers of two. So to start at 0 we have one allocated
285     m_data.resize(1);
286     m_lookups.clear();
287 }
288
289 bool QQuickParticleDataHeap::contains(QQuickParticleData* d)
290 {
291     for (int i=0; i<m_end; i++)
292         if (m_data[i].data.contains(d))
293             return true;
294     return false;
295 }
296
297 void QQuickParticleDataHeap::swap(int a, int b)
298 {
299     m_tmp = m_data[a];
300     m_data[a] = m_data[b];
301     m_data[b] = m_tmp;
302     m_lookups[m_data[a].time] = a;
303     m_lookups[m_data[b].time] = b;
304 }
305
306 void QQuickParticleDataHeap::bubbleUp(int idx)//tends to be called once
307 {
308     if (!idx)
309         return;
310     int parent = (idx-1)/2;
311     if (m_data[idx].time < m_data[parent].time) {
312         swap(idx, parent);
313         bubbleUp(parent);
314     }
315 }
316
317 void QQuickParticleDataHeap::bubbleDown(int idx)//tends to be called log n times
318 {
319     int left = idx*2 + 1;
320     if (left >= m_end)
321         return;
322     int lesser = left;
323     int right = idx*2 + 2;
324     if (right < m_end) {
325         if (m_data[left].time > m_data[right].time)
326             lesser = right;
327     }
328     if (m_data[idx].time > m_data[lesser].time) {
329         swap(idx, lesser);
330         bubbleDown(lesser);
331     }
332 }
333
334 QQuickParticleGroupData::QQuickParticleGroupData(int id, QQuickParticleSystem* sys):index(id),m_size(0),m_system(sys)
335 {
336     initList();
337 }
338
339 QQuickParticleGroupData::~QQuickParticleGroupData()
340 {
341     foreach (QQuickParticleData* d, data)
342         delete d;
343 }
344
345 int QQuickParticleGroupData::size()
346 {
347     return m_size;
348 }
349
350 QString QQuickParticleGroupData::name()//### Worth caching as well?
351 {
352     return m_system->groupIds.key(index);
353 }
354
355 void QQuickParticleGroupData::setSize(int newSize)
356 {
357     if (newSize == m_size)
358         return;
359     Q_ASSERT(newSize > m_size);//XXX allow shrinking
360     data.resize(newSize);
361     for (int i=m_size; i<newSize; i++) {
362         data[i] = new QQuickParticleData(m_system);
363         data[i]->group = index;
364         data[i]->index = i;
365         reusableIndexes << i;
366     }
367     int delta = newSize - m_size;
368     m_size = newSize;
369     foreach (QQuickParticlePainter* p, painters)
370         p->setCount(p->count() + delta);
371 }
372
373 void QQuickParticleGroupData::initList()
374 {
375     dataHeap.clear();
376 }
377
378 void QQuickParticleGroupData::kill(QQuickParticleData* d)
379 {
380     Q_ASSERT(d->group == index);
381     d->lifeSpan = 0;//Kill off
382     foreach (QQuickParticlePainter* p, painters)
383         p->reload(d);
384     reusableIndexes << d->index;
385 }
386
387 QQuickParticleData* QQuickParticleGroupData::newDatum(bool respectsLimits)
388 {
389     //recycle();//Extra recycler round to be sure?
390
391     while (!reusableIndexes.empty()) {
392         int idx = *(reusableIndexes.begin());
393         reusableIndexes.remove(idx);
394         if (data[idx]->stillAlive()) {// ### This means resurrection of 'dead' particles. Is that allowed?
395             prepareRecycler(data[idx]);
396             continue;
397         }
398         return data[idx];
399     }
400     if (respectsLimits)
401         return 0;
402
403     int oldSize = m_size;
404     setSize(oldSize + 10);//###+1,10%,+10? Choose something non-arbitrarily
405     reusableIndexes.remove(oldSize);
406     return data[oldSize];
407 }
408
409 bool QQuickParticleGroupData::recycle()
410 {
411     while (dataHeap.top() <= m_system->timeInt) {
412         foreach (QQuickParticleData* datum, dataHeap.pop()) {
413             if (!datum->stillAlive()) {
414                 reusableIndexes << datum->index;
415             } else {
416                 prepareRecycler(datum); //ttl has been altered mid-way, put it back
417             }
418         }
419     }
420
421     //TODO: If the data is clear, gc (consider shrinking stack size)?
422     return reusableIndexes.count() == m_size;
423 }
424
425 void QQuickParticleGroupData::prepareRecycler(QQuickParticleData* d)
426 {
427     if (d->lifeSpan*1000 < m_system->maxLife) {
428         dataHeap.insert(d);
429     } else {
430         while ((roundedTime(d->t) + 2*m_system->maxLife/3) <= m_system->timeInt)
431             d->extendLife(m_system->maxLife/3000.0);
432         dataHeap.insertTimed(d, roundedTime(d->t) + 2*m_system->maxLife/3);
433     }
434 }
435
436 QQuickParticleData::QQuickParticleData(QQuickParticleSystem* sys)
437     : group(0)
438     , e(0)
439     , system(sys)
440     , index(0)
441     , systemIndex(-1)
442     , colorOwner(0)
443     , rotationOwner(0)
444     , deformationOwner(0)
445     , animationOwner(0)
446     , v8Datum(0)
447 {
448     x = 0;
449     y = 0;
450     t = -1;
451     lifeSpan = 0;
452     size = 0;
453     endSize = 0;
454     vx = 0;
455     vy = 0;
456     ax = 0;
457     ay = 0;
458     xx = 1;
459     xy = 0;
460     yx = 0;
461     yy = 1;
462     rotation = 0;
463     rotationSpeed = 0;
464     autoRotate = 0;
465     animIdx = 0;
466     frameDuration = 1;
467     frameAt = -1;
468     frameCount = 1;
469     animT = -1;
470     animX = 0;
471     animY = 0;
472     animWidth = 1;
473     animHeight = 1;
474     color.r = 255;
475     color.g = 255;
476     color.b = 255;
477     color.a = 255;
478     r = 0;
479     delegate = 0;
480     modelIndex = -1;
481 }
482
483 QQuickParticleData::~QQuickParticleData()
484 {
485     delete v8Datum;
486 }
487
488 void QQuickParticleData::clone(const QQuickParticleData& other)
489 {
490     x = other.x;
491     y = other.y;
492     t = other.t;
493     lifeSpan = other.lifeSpan;
494     size = other.size;
495     endSize = other.endSize;
496     vx = other.vx;
497     vy = other.vy;
498     ax = other.ax;
499     ay = other.ay;
500     xx = other.xx;
501     xy = other.xy;
502     yx = other.yx;
503     yy = other.yy;
504     rotation = other.rotation;
505     rotationSpeed = other.rotationSpeed;
506     autoRotate = other.autoRotate;
507     animIdx = other.animIdx;
508     frameDuration = other.frameDuration;
509     frameCount = other.frameCount;
510     animT = other.animT;
511     animX = other.animX;
512     animY = other.animY;
513     animWidth = other.animWidth;
514     animHeight = other.animHeight;
515     color.r = other.color.r;
516     color.g = other.color.g;
517     color.b = other.color.b;
518     color.a = other.color.a;
519     r = other.r;
520     delegate = other.delegate;
521     modelIndex = other.modelIndex;
522
523     colorOwner = other.colorOwner;
524     rotationOwner = other.rotationOwner;
525     deformationOwner = other.deformationOwner;
526     animationOwner = other.animationOwner;
527 }
528
529 QQmlV8Handle QQuickParticleData::v8Value()
530 {
531     if (!v8Datum)
532         v8Datum = new QQuickV8ParticleData(QQmlEnginePrivate::getV8Engine(qmlEngine(system)), this);
533     return v8Datum->v8Value();
534 }
535 //sets the x accleration without affecting the instantaneous x velocity or position
536 void QQuickParticleData::setInstantaneousAX(qreal ax)
537 {
538     qreal t = (system->timeInt / 1000.0) - this->t;
539     qreal vx = (this->vx + t*this->ax) - t*ax;
540     qreal ex = this->x + this->vx * t + 0.5 * this->ax * t * t;
541     qreal x = ex - t*vx - 0.5 * t*t*ax;
542
543     this->ax = ax;
544     this->vx = vx;
545     this->x = x;
546 }
547
548 //sets the x velocity without affecting the instantaneous x postion
549 void QQuickParticleData::setInstantaneousVX(qreal vx)
550 {
551     qreal t = (system->timeInt / 1000.0) - this->t;
552     qreal evx = vx - t*this->ax;
553     qreal ex = this->x + this->vx * t + 0.5 * this->ax * t * t;
554     qreal x = ex - t*evx - 0.5 * t*t*this->ax;
555
556     this->vx = evx;
557     this->x = x;
558 }
559
560 //sets the instantaneous x postion
561 void QQuickParticleData::setInstantaneousX(qreal x)
562 {
563     qreal t = (system->timeInt / 1000.0) - this->t;
564     this->x = x - t*this->vx - 0.5 * t*t*this->ax;
565 }
566
567 //sets the y accleration without affecting the instantaneous y velocity or position
568 void QQuickParticleData::setInstantaneousAY(qreal ay)
569 {
570     qreal t = (system->timeInt / 1000.0) - this->t;
571     qreal vy = (this->vy + t*this->ay) - t*ay;
572     qreal ey = this->y + this->vy * t + 0.5 * this->ay * t * t;
573     qreal y = ey - t*vy - 0.5 * t*t*ay;
574
575     this->ay = ay;
576     this->vy = vy;
577     this->y = y;
578 }
579
580 //sets the y velocity without affecting the instantaneous y position
581 void QQuickParticleData::setInstantaneousVY(qreal vy)
582 {
583     qreal t = (system->timeInt / 1000.0) - this->t;
584     qreal evy = vy - t*this->ay;
585     qreal ey = this->y + this->vy * t + 0.5 * this->ay * t * t;
586     qreal y = ey - t*evy - 0.5 * t*t*this->ay;
587
588     this->vy = evy;
589     this->y = y;
590 }
591
592 //sets the instantaneous Y position
593 void QQuickParticleData::setInstantaneousY(qreal y)
594 {
595     qreal t = (system->timeInt / 1000.0) - this->t;
596     this->y = y - t*this->vy - 0.5 * t*t*this->ay;
597 }
598
599 qreal QQuickParticleData::curX() const
600 {
601     qreal t = (system->timeInt / 1000.0) - this->t;
602     return this->x + this->vx * t + 0.5 * this->ax * t * t;
603 }
604
605 qreal QQuickParticleData::curVX() const
606 {
607     qreal t = (system->timeInt / 1000.0) - this->t;
608     return this->vx + t*this->ax;
609 }
610
611 qreal QQuickParticleData::curY() const
612 {
613     qreal t = (system->timeInt / 1000.0) - this->t;
614     return y + vy * t + 0.5 * ay * t * t;
615 }
616
617 qreal QQuickParticleData::curVY() const
618 {
619     qreal t = (system->timeInt / 1000.0) - this->t;
620     return vy + t*ay;
621 }
622
623 void QQuickParticleData::debugDump()
624 {
625     qDebug() << "Particle" << systemIndex << group << "/" << index << stillAlive()
626              << "Pos: " << x << "," << y
627              << "Vel: " << vx << "," << vy
628              << "Acc: " << ax << "," << ay
629              << "Size: " << size << "," << endSize
630              << "Time: " << t << "," <<lifeSpan << ";" << (system->timeInt / 1000.0) ;
631 }
632
633 bool QQuickParticleData::stillAlive()
634 {
635     if (!system)
636         return false;
637     return (t + lifeSpan - EPSILON) > ((qreal)system->timeInt/1000.0);
638 }
639
640 bool QQuickParticleData::alive()
641 {
642     if (!system)
643         return false;
644     qreal st = ((qreal)system->timeInt/1000.0);
645     return (t + EPSILON) < st && (t + lifeSpan - EPSILON) > st;
646 }
647
648 float QQuickParticleData::curSize()
649 {
650     if (!system || !lifeSpan)
651         return 0.0f;
652     return size + (endSize - size) * (1 - (lifeLeft() / lifeSpan));
653 }
654
655 float QQuickParticleData::lifeLeft()
656 {
657     if (!system)
658         return 0.0f;
659     return (t + lifeSpan) - (system->timeInt/1000.0);
660 }
661
662 void QQuickParticleData::extendLife(float time)
663 {
664     qreal newX = curX();
665     qreal newY = curY();
666     qreal newVX = curVX();
667     qreal newVY = curVY();
668
669     t += time;
670     animT += time;
671
672     qreal elapsed = (system->timeInt / 1000.0) - t;
673     qreal evy = newVY - elapsed*ay;
674     qreal ey = newY - elapsed*evy - 0.5 * elapsed*elapsed*ay;
675     qreal evx = newVX - elapsed*ax;
676     qreal ex = newX - elapsed*evx - 0.5 * elapsed*elapsed*ax;
677
678     x = ex;
679     vx = evx;
680     y = ey;
681     vy = evy;
682 }
683
684 QQuickParticleSystem::QQuickParticleSystem(QQuickItem *parent) :
685     QQuickItem(parent),
686     stateEngine(0),
687     m_animation(0),
688     m_running(true),
689     initialized(0),
690     particleCount(0),
691     m_nextIndex(0),
692     m_componentComplete(false),
693     m_paused(false),
694     m_empty(true)
695 {
696     connect(&m_painterMapper, SIGNAL(mapped(QObject*)),
697             this, SLOT(loadPainter(QObject*)));
698
699     m_debugMode = qmlParticlesDebug();
700 }
701
702 QQuickParticleSystem::~QQuickParticleSystem()
703 {
704     foreach (QQuickParticleGroupData* gd, groupData)
705         delete gd;
706 }
707
708 void QQuickParticleSystem::initGroups()
709 {
710     m_reusableIndexes.clear();
711     m_nextIndex = 0;
712
713     qDeleteAll(groupData);
714     groupData.clear();
715     groupIds.clear();
716
717     QQuickParticleGroupData* gd = new QQuickParticleGroupData(0, this);//Default group
718     groupData.insert(0,gd);
719     groupIds.insert(QString(), 0);
720     m_nextGroupId = 1;
721 }
722
723 void QQuickParticleSystem::registerParticlePainter(QQuickParticlePainter* p)
724 {
725     if (m_debugMode)
726         qDebug() << "Registering Painter" << p << "to" << this;
727     //TODO: a way to Unregister emitters, painters and affectors
728     m_painters << QPointer<QQuickParticlePainter>(p);//###Set or uniqueness checking?
729     connect(p, SIGNAL(groupsChanged(QStringList)),
730             &m_painterMapper, SLOT(map()));
731     loadPainter(p);
732 }
733
734 void QQuickParticleSystem::registerParticleEmitter(QQuickParticleEmitter* e)
735 {
736     if (m_debugMode)
737         qDebug() << "Registering Emitter" << e << "to" << this;
738     m_emitters << QPointer<QQuickParticleEmitter>(e);//###How to get them out?
739     connect(e, SIGNAL(particleCountChanged()),
740             this, SLOT(emittersChanged()));
741     connect(e, SIGNAL(groupChanged(QString)),
742             this, SLOT(emittersChanged()));
743     emittersChanged();
744     e->reset();//Start, so that starttime factors appropriately
745 }
746
747 void QQuickParticleSystem::registerParticleAffector(QQuickParticleAffector* a)
748 {
749     if (m_debugMode)
750         qDebug() << "Registering Affector" << a << "to" << this;
751     m_affectors << QPointer<QQuickParticleAffector>(a);
752 }
753
754 void QQuickParticleSystem::registerParticleGroup(QQuickParticleGroup* g)
755 {
756     if (m_debugMode)
757         qDebug() << "Registering Group" << g << "to" << this;
758     m_groups << QPointer<QQuickParticleGroup>(g);
759     createEngine();
760 }
761
762 void QQuickParticleSystem::setRunning(bool arg)
763 {
764     if (m_running != arg) {
765         m_running = arg;
766         emit runningChanged(arg);
767         setPaused(false);
768         if (m_animation)//Not created until componentCompleted
769             m_running ? m_animation->start() : m_animation->stop();
770         reset();
771     }
772 }
773
774 void QQuickParticleSystem::setPaused(bool arg) {
775     if (m_paused != arg) {
776         m_paused = arg;
777         if (m_animation && m_animation->state() != QAbstractAnimation::Stopped)
778             m_paused ? m_animation->pause() : m_animation->resume();
779         if (!m_paused) {
780             foreach (QQuickParticlePainter *p, m_painters)
781                 p->update();
782         }
783         emit pausedChanged(arg);
784     }
785 }
786
787 void QQuickParticleSystem::statePropertyRedirect(QQmlListProperty<QObject> *prop, QObject *value)
788 {
789     //Hooks up automatic state-associated stuff
790     QQuickParticleSystem* sys = qobject_cast<QQuickParticleSystem*>(prop->object->parent());
791     QQuickParticleGroup* group = qobject_cast<QQuickParticleGroup*>(prop->object);
792     if (!group || !sys || !value)
793         return;
794     stateRedirect(group, sys, value);
795 }
796
797 void QQuickParticleSystem::stateRedirect(QQuickParticleGroup* group, QQuickParticleSystem* sys, QObject *value)
798 {
799     QStringList list;
800     list << group->name();
801     QQuickParticleAffector* a = qobject_cast<QQuickParticleAffector*>(value);
802     if (a) {
803         a->setParentItem(sys);
804         a->setGroups(list);
805         a->setSystem(sys);
806         return;
807     }
808     QQuickTrailEmitter* fe = qobject_cast<QQuickTrailEmitter*>(value);
809     if (fe) {
810         fe->setParentItem(sys);
811         fe->setFollow(group->name());
812         fe->setSystem(sys);
813         return;
814     }
815     QQuickParticleEmitter* e = qobject_cast<QQuickParticleEmitter*>(value);
816     if (e) {
817         e->setParentItem(sys);
818         e->setGroup(group->name());
819         e->setSystem(sys);
820         return;
821     }
822     QQuickParticlePainter* p = qobject_cast<QQuickParticlePainter*>(value);
823     if (p) {
824         p->setParentItem(sys);
825         p->setGroups(list);
826         p->setSystem(sys);
827         return;
828     }
829     qWarning() << value << " was placed inside a particle system state but cannot be taken into the particle system. It will be lost.";
830 }
831
832 void QQuickParticleSystem::componentComplete()
833
834 {
835     QQuickItem::componentComplete();
836     m_componentComplete = true;
837     m_animation = new QQuickParticleSystemAnimation(this);
838     reset();//restarts animation as well
839 }
840
841 void QQuickParticleSystem::reset()
842 {
843     if (!m_componentComplete)
844         return;
845
846     timeInt = 0;
847     //Clear guarded pointers which have been deleted
848     int cleared = 0;
849     cleared += m_emitters.removeAll(0);
850     cleared += m_painters.removeAll(0);
851     cleared += m_affectors.removeAll(0);
852
853     bySysIdx.resize(0);
854     initGroups();//Also clears all logical particles
855
856     if (!m_running)
857         return;
858
859     foreach (QQuickParticleEmitter* e, m_emitters)
860         e->reset();
861
862     emittersChanged();
863
864     foreach (QQuickParticlePainter *p, m_painters) {
865         loadPainter(p);
866         p->reset();
867     }
868
869     //### Do affectors need reset too?
870     if (m_animation) {//Animation is explicitly disabled in benchmarks
871         //reset restarts animation (if running)
872         if ((m_animation->state() == QAbstractAnimation::Running))
873             m_animation->stop();
874         m_animation->start();
875         if (m_paused)
876             m_animation->pause();
877     }
878
879     initialized = true;
880 }
881
882
883 void QQuickParticleSystem::loadPainter(QObject *p)
884 {
885     if (!m_componentComplete || !p)
886         return;
887
888     QQuickParticlePainter* painter = qobject_cast<QQuickParticlePainter*>(p);
889     Q_ASSERT(painter);//XXX
890     foreach (QQuickParticleGroupData* sg, groupData)
891         sg->painters.remove(painter);
892     int particleCount = 0;
893     if (painter->groups().isEmpty()) {//Uses default particle
894         QStringList def;
895         def << QString();
896         painter->setGroups(def);
897         particleCount += groupData[0]->size();
898         groupData[0]->painters << painter;
899     } else {
900         foreach (const QString &group, painter->groups()) {
901             if (group != QLatin1String("") && !groupIds[group]) {//new group
902                 int id = m_nextGroupId++;
903                 QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this);
904                 groupIds.insert(group, id);
905                 groupData.insert(id, gd);
906             }
907             particleCount += groupData[groupIds[group]]->size();
908             groupData[groupIds[group]]->painters << painter;
909         }
910     }
911     painter->setCount(particleCount);
912     painter->update();//Initial update here
913     return;
914 }
915
916 void QQuickParticleSystem::emittersChanged()
917 {
918     if (!m_componentComplete)
919         return;
920
921     m_emitters.removeAll(0);
922
923
924     QList<int> previousSizes;
925     QList<int> newSizes;
926     for (int i=0; i<m_nextGroupId; i++) {
927         previousSizes << groupData[i]->size();
928         newSizes << 0;
929     }
930
931     foreach (QQuickParticleEmitter* e, m_emitters) {//Populate groups and set sizes.
932         if (!groupIds.contains(e->group())
933                 || (!e->group().isEmpty() && !groupIds[e->group()])) {//or it was accidentally inserted by a failed lookup earlier
934             int id = m_nextGroupId++;
935             QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this);
936             groupIds.insert(e->group(), id);
937             groupData.insert(id, gd);
938             previousSizes << 0;
939             newSizes << 0;
940         }
941         newSizes[groupIds[e->group()]] += e->particleCount();
942         //###: Cull emptied groups?
943     }
944
945     //TODO: Garbage collection?
946     particleCount = 0;
947     for (int i=0; i<m_nextGroupId; i++) {
948         groupData[i]->setSize(qMax(newSizes[i], previousSizes[i]));
949         particleCount += groupData[i]->size();
950     }
951
952     if (m_debugMode)
953         qDebug() << "Particle system emitters changed. New particle count: " << particleCount;
954
955     if (particleCount > bySysIdx.size())//New datum requests haven't updated it
956         bySysIdx.resize(particleCount);
957
958     foreach (QQuickParticleAffector *a, m_affectors)//Groups may have changed
959         a->m_updateIntSet = true;
960
961     foreach (QQuickParticlePainter *p, m_painters)
962         loadPainter(p);
963
964     if (!m_groups.isEmpty())
965         createEngine();
966
967 }
968
969 void QQuickParticleSystem::createEngine()
970 {
971     if (!m_componentComplete)
972         return;
973     if (stateEngine && m_debugMode)
974         qDebug() << "Resetting Existing Sprite Engine...";
975     //### Solve the losses if size/states go down
976     foreach (QQuickParticleGroup* group, m_groups) {
977         bool exists = false;
978         foreach (const QString &name, groupIds.keys())
979             if (group->name() == name)
980                 exists = true;
981         if (!exists) {
982             int id = m_nextGroupId++;
983             QQuickParticleGroupData* gd = new QQuickParticleGroupData(id, this);
984             groupIds.insert(group->name(), id);
985             groupData.insert(id, gd);
986         }
987     }
988
989     if (m_groups.count()) {
990         //Reorder groups List so as to have the same order as groupData
991         QList<QQuickParticleGroup*> newList;
992         for (int i=0; i<m_nextGroupId; i++) {
993             bool exists = false;
994             QString name = groupData[i]->name();
995             foreach (QQuickParticleGroup* existing, m_groups) {
996                 if (existing->name() == name) {
997                     newList << existing;
998                     exists = true;
999                 }
1000             }
1001             if (!exists) {
1002                 newList << new QQuickParticleGroup(this);
1003                 newList.back()->setName(name);
1004             }
1005         }
1006         m_groups = newList;
1007         QList<QQuickStochasticState*> states;
1008         foreach (QQuickParticleGroup* g, m_groups)
1009             states << (QQuickStochasticState*)g;
1010
1011         if (!stateEngine)
1012             stateEngine = new QQuickStochasticEngine(this);
1013         stateEngine->setCount(particleCount);
1014         stateEngine->m_states = states;
1015
1016         connect(stateEngine, SIGNAL(stateChanged(int)),
1017                 this, SLOT(particleStateChange(int)));
1018
1019     } else {
1020         if (stateEngine)
1021             delete stateEngine;
1022         stateEngine = 0;
1023     }
1024
1025 }
1026
1027 void QQuickParticleSystem::particleStateChange(int idx)
1028 {
1029     moveGroups(bySysIdx[idx], stateEngine->curState(idx));
1030 }
1031
1032 void QQuickParticleSystem::moveGroups(QQuickParticleData *d, int newGIdx)
1033 {
1034     if (!d || newGIdx == d->group)
1035         return;
1036
1037     QQuickParticleData* pd = newDatum(newGIdx, false, d->systemIndex);
1038     if (!pd)
1039         return;
1040
1041     pd->clone(*d);
1042     finishNewDatum(pd);
1043
1044     d->systemIndex = -1;
1045     groupData[d->group]->kill(d);
1046 }
1047
1048 int QQuickParticleSystem::nextSystemIndex()
1049 {
1050     if (!m_reusableIndexes.isEmpty()) {
1051         int ret = *(m_reusableIndexes.begin());
1052         m_reusableIndexes.remove(ret);
1053         return ret;
1054     }
1055     if (m_nextIndex >= bySysIdx.size()) {
1056         bySysIdx.resize(bySysIdx.size() < 10 ? 10 : bySysIdx.size()*1.1);//###+1,10%,+10? Choose something non-arbitrarily
1057         if (stateEngine)
1058             stateEngine->setCount(bySysIdx.size());
1059
1060     }
1061     return m_nextIndex++;
1062 }
1063
1064 QQuickParticleData* QQuickParticleSystem::newDatum(int groupId, bool respectLimits, int sysIndex)
1065 {
1066     Q_ASSERT(groupId < groupData.count());//XXX shouldn't really be an assert
1067
1068     QQuickParticleData* ret = groupData[groupId]->newDatum(respectLimits);
1069     if (!ret) {
1070         return 0;
1071     }
1072     if (sysIndex == -1) {
1073         if (ret->systemIndex == -1)
1074             ret->systemIndex = nextSystemIndex();
1075     } else {
1076         if (ret->systemIndex != -1) {
1077             if (stateEngine)
1078                 stateEngine->stop(ret->systemIndex);
1079             m_reusableIndexes << ret->systemIndex;
1080             bySysIdx[ret->systemIndex] = 0;
1081         }
1082         ret->systemIndex = sysIndex;
1083     }
1084     bySysIdx[ret->systemIndex] = ret;
1085
1086     if (stateEngine)
1087         stateEngine->start(ret->systemIndex, ret->group);
1088
1089     m_empty = false;
1090     return ret;
1091 }
1092
1093 void QQuickParticleSystem::emitParticle(QQuickParticleData* pd)
1094 {// called from prepareNextFrame()->emitWindow - enforce?
1095     //Account for relative emitter position
1096     QPointF offset = this->mapFromItem(pd->e, QPointF(0, 0));
1097     if (!offset.isNull()) {
1098         pd->x += offset.x();
1099         pd->y += offset.y();
1100     }
1101
1102     finishNewDatum(pd);
1103 }
1104
1105 void QQuickParticleSystem::finishNewDatum(QQuickParticleData *pd)
1106 {
1107     Q_ASSERT(pd);
1108     groupData[pd->group]->prepareRecycler(pd);
1109
1110     foreach (QQuickParticleAffector *a, m_affectors)
1111         if (a && a->m_needsReset)
1112             a->reset(pd);
1113     foreach (QQuickParticlePainter* p, groupData[pd->group]->painters)
1114         if (p)
1115             p->load(pd);
1116 }
1117
1118 void QQuickParticleSystem::updateCurrentTime( int currentTime )
1119 {
1120     if (!initialized)
1121         return;//error in initialization
1122
1123     //### Elapsed time never shrinks - may cause problems if left emitting for weeks at a time.
1124     qreal dt = timeInt / 1000.;
1125     timeInt = currentTime;
1126     qreal time =  timeInt / 1000.;
1127     dt = time - dt;
1128     needsReset.clear();
1129
1130     m_emitters.removeAll(0);
1131     m_painters.removeAll(0);
1132     m_affectors.removeAll(0);
1133
1134     bool oldClear = m_empty;
1135     m_empty = true;
1136     foreach (QQuickParticleGroupData* gd, groupData)//Recycle all groups and see if they're out of live particles
1137         m_empty = gd->recycle() && m_empty;
1138
1139     if (stateEngine)
1140         stateEngine->updateSprites(timeInt);
1141
1142     foreach (QQuickParticleEmitter* emitter, m_emitters)
1143         emitter->emitWindow(timeInt);
1144     foreach (QQuickParticleAffector* a, m_affectors)
1145         a->affectSystem(dt);
1146     foreach (QQuickParticleData* d, needsReset)
1147         foreach (QQuickParticlePainter* p, groupData[d->group]->painters)
1148             p->reload(d);
1149
1150     if (oldClear != m_empty)
1151         emptyChanged(m_empty);
1152 }
1153
1154 int QQuickParticleSystem::systemSync(QQuickParticlePainter* p)
1155 {
1156     if (!m_running)
1157         return 0;
1158     if (!initialized)
1159         return 0;//error in initialization
1160     p->performPendingCommits();
1161     return timeInt;
1162 }
1163
1164
1165 QT_END_NAMESPACE