Add clear property to QSGParticleSystem
authorAlan Alpert <alan.alpert@nokia.com>
Thu, 1 Sep 2011 07:59:11 +0000 (17:59 +1000)
committerQt by Nokia <qt-info@nokia.com>
Thu, 8 Sep 2011 00:03:13 +0000 (02:03 +0200)
Makes it possible to pause the system when all particles are expired.

Change-Id: Iebeb987c2e2af261bdffa4584d75f3b108dcf050
Reviewed-on: http://codereview.qt-project.org/4046
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Martin Jones <martin.jones@nokia.com>

examples/declarative/particles/trails/overburst.qml
src/declarative/particles/qsgparticlesystem.cpp
src/declarative/particles/qsgparticlesystem_p.h

index ca6bc6d..620ae46 100644 (file)
@@ -45,7 +45,10 @@ Rectangle{
     color: "black"
     width: 360
     height: 540
-    ParticleSystem{ id: sys }
+    ParticleSystem{
+        id: sys
+        onClearChanged: if (clear) sys.pause();
+    }
     ImageParticle{
         system: sys
         id: cp
@@ -69,6 +72,7 @@ Rectangle{
     }
     MouseArea{
         anchors.fill: parent
+        onPressed: sys.resume()
         id: ma
     }
     MouseArea{
index 8bd3998..0888e7b 100644 (file)
@@ -80,10 +80,25 @@ DEFINE_BOOL_CONFIG_OPTION(qmlParticlesDebug, QML_PARTICLES_DEBUG)
     paused is set to false again, the simulation will resume from the same point it was
     paused.
 
+    The simulation will automatically pause if it detects that there are no live particles
+    left, and unpause when new live particles are added.
+
     It can also be controlled with the pause() and resume() methods.
 */
 
 /*!
+    \qmlproperty bool QtQuick.Particles2::ParticleSystem::clear
+
+    clear is set to true when there are no live particles left in the system.
+
+    You can use this to pause the system, keeping it from spending any time updating,
+    but you will need to resume it in order for additional particles to be generated
+    by the system.
+
+    To kill all the particles in the system, use a Kill affector.
+*/
+
+/*!
     \qmlproperty int QtQuick.Particles2::ParticleSystem::startTime
 
     If start time is specified, then the system will simulate up to this time
@@ -267,7 +282,8 @@ void QSGParticleGroupData::initList()
     dataHeap.clear();
 }
 
-void QSGParticleGroupData::kill(QSGParticleData* d){
+void QSGParticleGroupData::kill(QSGParticleData* d)
+{
     Q_ASSERT(d->group == index);
     d->lifeSpan = 0;//Kill off
     foreach (QSGParticlePainter* p, painters)
@@ -275,21 +291,14 @@ void QSGParticleGroupData::kill(QSGParticleData* d){
     reusableIndexes << d->index;
 }
 
-QSGParticleData* QSGParticleGroupData::newDatum(bool respectsLimits){
-    while (dataHeap.top() <= m_system->m_timeInt){
-        foreach (QSGParticleData* datum, dataHeap.pop()){
-            if (!datum->stillAlive()){
-                reusableIndexes << datum->index;
-            }else{
-                prepareRecycler(datum); //ttl has been altered mid-way, put it back
-            }
-        }
-    }
+QSGParticleData* QSGParticleGroupData::newDatum(bool respectsLimits)
+{
+    //recycle();//Extra recycler round to be sure?
 
     while (!reusableIndexes.empty()){
         int idx = *(reusableIndexes.begin());
         reusableIndexes.remove(idx);
-        if (data[idx]->stillAlive()){// ### This means resurrection of dead particles. Is that allowed?
+        if (data[idx]->stillAlive()){// ### This means resurrection of 'dead' particles. Is that allowed?
             prepareRecycler(data[idx]);
             continue;
         }
@@ -304,6 +313,22 @@ QSGParticleData* QSGParticleGroupData::newDatum(bool respectsLimits){
     return data[oldSize];
 }
 
+bool QSGParticleGroupData::recycle()
+{
+    while (dataHeap.top() <= m_system->m_timeInt){
+        foreach (QSGParticleData* datum, dataHeap.pop()){
+            if (!datum->stillAlive()){
+                reusableIndexes << datum->index;
+            }else{
+                prepareRecycler(datum); //ttl has been altered mid-way, put it back
+            }
+        }
+    }
+
+    //TODO: If the data is clear, gc (consider shrinking stack size)?
+    return reusableIndexes.count() == m_size;
+}
+
 void QSGParticleGroupData::prepareRecycler(QSGParticleData* d){
     dataHeap.insert(d);
 }
@@ -870,6 +895,7 @@ QSGParticleData* QSGParticleSystem::newDatum(int groupId, bool respectLimits, in
     if (m_spriteEngine)
         m_spriteEngine->startSprite(ret->systemIndex, ret->group);
 
+    m_clear = false;
     return ret;
 }
 
@@ -899,8 +925,6 @@ void QSGParticleSystem::finishNewDatum(QSGParticleData *pd){
 
 void QSGParticleSystem::updateCurrentTime( int currentTime )
 {
-    if (!m_running)
-        return;
     if (!m_initialized)
         return;//error in initialization
 
@@ -910,6 +934,12 @@ void QSGParticleSystem::updateCurrentTime( int currentTime )
     qreal time =  m_timeInt / 1000.;
     dt = time - dt;
     m_needsReset.clear();
+
+    bool oldClear = m_clear;
+    m_clear = true;
+    foreach (QSGParticleGroupData* gd, m_groupData)//Recycle all groups and see if they're out of live particles
+        m_clear = m_clear && gd->recycle();
+
     if (m_spriteEngine)
         m_spriteEngine->updateSprites(m_timeInt);
 
@@ -923,6 +953,9 @@ void QSGParticleSystem::updateCurrentTime( int currentTime )
         foreach (QSGParticlePainter* p, m_groupData[d->group]->painters)
             if (p && d)
                 p->reload(d);
+
+    if (oldClear != m_clear)
+        clearChanged(m_clear);
 }
 
 int QSGParticleSystem::systemSync(QSGParticlePainter* p)
index f578c43..5df6afb 100644 (file)
@@ -117,6 +117,7 @@ public:
     QVector<QSGParticleData*> data;
     QSGParticleDataHeap dataHeap;
     QSet<int> reusableIndexes;
+    bool recycle(); //Force recycling round, reutrns true if all indexes are now reusable
 
     void initList();
     void kill(QSGParticleData* d);
@@ -219,6 +220,7 @@ class QSGParticleSystem : public QSGItem
     Q_OBJECT
     Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged)
     Q_PROPERTY(bool paused READ isPaused WRITE setPaused NOTIFY pausedChanged)
+    Q_PROPERTY(bool clear READ isClear NOTIFY clearChanged)
     Q_PROPERTY(int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
     Q_PROPERTY(QDeclarativeListProperty<QSGSprite> particleStates READ particleStates)
 
@@ -250,6 +252,8 @@ signals:
 
     void pausedChanged(bool arg);
 
+    void clearChanged(bool arg);
+
 public slots:
     void start(){setRunning(true);}
     void stop(){setRunning(false);}
@@ -315,6 +319,11 @@ public://###but only really for related class usage. Perhaps we should all be fr
         return m_paused;
     }
 
+    bool isClear() const
+    {
+        return m_clear;
+    }
+
 private:
     void initializeSystem();
     void initGroups();
@@ -337,6 +346,8 @@ private:
     QSGParticleSystemAnimation* m_animation;
     bool m_paused;
     bool m_debugMode;
+    bool m_allDead;
+    bool m_clear;
 };
 
 // Internally, this animation drives all the timing. Painters sync up in their updatePaintNode