Allow particles to have eternal life
authorAlan Alpert <alan.alpert@nokia.com>
Thu, 1 Sep 2011 10:11:42 +0000 (20:11 +1000)
committerQt by Nokia <qt-info@nokia.com>
Thu, 8 Sep 2011 01:34:41 +0000 (03:34 +0200)
Change-Id: I3b776a6e79cb064e826cb7b7721a3a57744225c1
Reviewed-on: http://codereview.qt-project.org/4064
Reviewed-by: Alan Alpert <alan.alpert@nokia.com>
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
src/declarative/particles/qsgparticleaffector.cpp
src/declarative/particles/qsgparticleemitter.cpp
src/declarative/particles/qsgparticleemitter_p.h
src/declarative/particles/qsgparticlesystem.cpp
src/declarative/particles/qsgparticlesystem_p.h

index a64b4ee..8391289 100644 (file)
@@ -166,7 +166,7 @@ void QSGParticleAffector::affectSystem(qreal dt)
                             m_system->m_needsReset << d;
                             if (m_onceOff)
                                 m_onceOffed << qMakePair(d->group, d->index);
-                            if (m_signal)
+                            if (m_signal)//###Make signal based on if connected, and then m_signal just affects AffectParticle for base?
                                 emit affected(curPos.x(), curPos.y());
                         }
                     }
index 2a76f77..6519bf5 100644 (file)
@@ -100,6 +100,12 @@ QT_BEGIN_NAMESPACE
 
     The time in milliseconds each emitted particle should last for.
 
+    If you do not want particles to automatically die after a time, for example if
+    you wish to dispose of them manually, set lifeSpan to Emitter.InfiniteLife.
+
+    lifeSpans greater than or equal to 600000 (10 minutes) will be treated as infinite.
+    Particles with lifeSpans less than or equal to 0 will start out dead.
+
     Default value is 1000 (one second).
 */
 /*!
@@ -205,7 +211,6 @@ QSGParticleEmitter::QSGParticleEmitter(QSGItem *parent) :
   , m_maxParticleCount(-1)
   , m_burstLeft(0)
   , m_speed_from_movement(0)
-  , m_particle_count(0)
   , m_reset_last(true)
   , m_last_timestamp(-1)
   , m_last_emission(0)
@@ -339,6 +344,7 @@ void QSGParticleEmitter::emitWindow(int timeStamp)
             m_last_timestamp = timeStamp/1000.;
         m_last_emission = m_last_timestamp;
         m_reset_last = false;
+        m_emitCap = particleCount();
     }
 
     if (m_burstLeft){
@@ -375,7 +381,7 @@ void QSGParticleEmitter::emitWindow(int timeStamp)
     qreal emitter_y_offset = m_last_emitter.y() - y();
     if (!m_burstQueue.isEmpty() && !m_burstLeft && !m_emitting)//'outside time' emissions only
         pt = time;
-    while (pt < time || !m_burstQueue.isEmpty()) {
+    while ((pt < time && m_emitCap) || !m_burstQueue.isEmpty()) {
         //int pos = m_last_particle % m_particle_count;
         QSGParticleData* datum = m_system->newDatum(m_system->m_groupIds[m_particle], !m_overwrite);
         if (datum){//actually emit(otherwise we've been asked to skip this one)
@@ -393,11 +399,16 @@ void QSGParticleEmitter::emitWindow(int timeStamp)
 
             // Particle timestamp
             datum->t = pt;
-            datum->lifeSpan = //TODO:Promote to base class?
+            datum->lifeSpan =
                     (m_particleDuration
                      + ((rand() % ((m_particleDurationVariation*2) + 1)) - m_particleDurationVariation))
                     / 1000.0;
 
+            if (datum->lifeSpan >= m_system->maxLife){
+                datum->lifeSpan = m_system->maxLife;
+                m_emitCap--;//emitCap keeps us from reemitting 'infinite' particles after their life. Unless you reset the emitter.
+            }
+
             // Particle position
             QRectF boundsRect;
             if (!m_burstQueue.isEmpty()){
index cd14fa5..9e1fc7a 100644 (file)
@@ -80,11 +80,16 @@ class QSGParticleEmitter : public QSGItem
     Q_PROPERTY(QSGStochasticDirection *acceleration READ acceleration WRITE setAcceleration NOTIFY accelerationChanged)
     Q_PROPERTY(qreal speedFromMovement READ speedFromMovement WRITE setSpeedFromMovement NOTIFY speedFromMovementChanged)
 
+    Q_ENUMS(Lifetime)
 public:
     explicit QSGParticleEmitter(QSGItem *parent = 0);
     virtual ~QSGParticleEmitter();
     virtual void emitWindow(int timeStamp);
 
+    enum Lifetime {
+        InfiniteLife = QSGParticleSystem::maxLife
+    };
+
     bool emitting() const
     {
         return m_emitting;
@@ -264,10 +269,10 @@ public slots:
            }
        }
 
+       virtual void reset();
 public:
        int particleCount() const;
 
-       virtual void reset();
        QSGParticleExtruder* extruder() const
        {
            return m_extruder;
@@ -340,7 +345,7 @@ protected:
        //Used in default implementation, but might be useful
        qreal m_speed_from_movement;
 
-       int m_particle_count;
+       int m_emitCap;
        bool m_reset_last;
        qreal m_last_timestamp;
        qreal m_last_emission;
index 0888e7b..534b1ca 100644 (file)
@@ -147,9 +147,13 @@ void QSGParticleDataHeap::grow() //###Consider automatic growth vs resize() call
     m_data.resize(1 << ++m_size);
 }
 
-void QSGParticleDataHeap::insert(QSGParticleData* data)//TODO: Optimize 0 lifespan (or already dead) case
+void QSGParticleDataHeap::insert(QSGParticleData* data)
 {
-    int time = roundedTime(data->t + data->lifeSpan);
+    insertTimed(data, roundedTime(data->t + data->lifeSpan));
+}
+
+void QSGParticleDataHeap::insertTimed(QSGParticleData* data, int time){
+    //TODO: Optimize 0 lifespan (or already dead) case
     if (m_lookups.contains(time)){
         m_data[m_lookups[time]].data << data;
         return;
@@ -330,7 +334,13 @@ bool QSGParticleGroupData::recycle()
 }
 
 void QSGParticleGroupData::prepareRecycler(QSGParticleData* d){
-    dataHeap.insert(d);
+    if (d->lifeSpan*1000 < m_system->maxLife){
+        dataHeap.insert(d);
+    } else {
+        while ((roundedTime(d->t) + 2*m_system->maxLife/3) <= m_system->m_timeInt)
+            d->extendLife(m_system->maxLife/3000.0);
+        dataHeap.insertTimed(d, roundedTime(d->t) + 2*m_system->maxLife/3);
+    }
 }
 
 QSGParticleData::QSGParticleData(QSGParticleSystem* sys)
@@ -529,6 +539,28 @@ float QSGParticleData::lifeLeft()
     return (t + lifeSpan) - (system->m_timeInt/1000.0);
 }
 
+void QSGParticleData::extendLife(float time)
+{
+    qreal newX = curX();
+    qreal newY = curY();
+    qreal newVX = curVX();
+    qreal newVY = curVY();
+
+    t += time;
+    animT += time;
+
+    qreal elapsed = (system->m_timeInt / 1000.0) - t;
+    qreal evy = newVY - elapsed*ay;
+    qreal ey = newY - elapsed*evy - 0.5 * elapsed*elapsed*ay;
+    qreal evx = newVX - elapsed*ax;
+    qreal ex = newX - elapsed*evx - 0.5 * elapsed*elapsed*ax;
+
+    x = ex;
+    vx = evx;
+    y = ey;
+    vy = evy;
+}
+
 QSGParticleSystem::QSGParticleSystem(QSGItem *parent) :
     QSGItem(parent), m_particle_count(0), m_running(true)
   , m_startTime(0), m_nextIndex(0), m_componentComplete(false), m_spriteEngine(0)
index 5df6afb..17b67d9 100644 (file)
@@ -80,6 +80,7 @@ class QSGParticleDataHeap {
 public:
     QSGParticleDataHeap();
     void insert(QSGParticleData* data);
+    void insertTimed(QSGParticleData* data, int time);
 
     int top();
 
@@ -211,6 +212,7 @@ public:
     float curSize();
     void clone(const QSGParticleData& other);//Not =, leaves meta-data like index
     QDeclarativeV8Handle v8Value();
+    void extendLife(float time);
 private:
     QSGV8ParticleData* v8Datum;
 };
@@ -242,6 +244,8 @@ public:
 
     int count(){ return m_particle_count; }
 
+    static const int maxLife = 600000;
+
 signals:
 
     void systemInitialized();