CustomEmitter/Affector now affect whole lists at once
authorAlan Alpert <alan.alpert@nokia.com>
Tue, 20 Sep 2011 05:28:14 +0000 (15:28 +1000)
committerQt by Nokia <qt-info@nokia.com>
Wed, 21 Sep 2011 05:11:25 +0000 (07:11 +0200)
Better performance potential (fewer drops to JS, possibility of more
optimzed JS).

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

examples/declarative/particles/affectors/customaffector.qml
examples/declarative/particles/emitters/customemitter.qml
src/declarative/particles/qsgcustomaffector.cpp
src/declarative/particles/qsgcustomaffector_p.h
src/declarative/particles/qsgparticleaffector_p.h
src/declarative/particles/qsgparticleemitter.cpp
src/declarative/particles/qsgparticleemitter_p.h
src/declarative/particles/qsgtrailemitter.cpp
src/declarative/particles/qsgtrailemitter_p.h

index 35708b7..e9aea62 100644 (file)
@@ -71,7 +71,7 @@ Item {
         property real speed: 1.5
         width: parent.width
         height: parent.height - 100
-        onAffectParticle:{
+        onAffectParticles:{
         /*  //Linear movement
             if (particle.r == 0){
                 particle.r = Math.random() > 0.5 ? -1 : 1;
@@ -86,14 +86,17 @@ Item {
             }
         */
             //Wobbly movement
-            if (particle.r == 0.0){
-                particle.r = Math.random() + 0.01;
+            for (var i=0; i<particles.length; i++) {
+                var particle = particles[i];
+                if (particle.r == 0.0) {
+                    particle.r = Math.random() + 0.01;
+                }
+                particle.rotation += speed * particle.r * dt;
+                particle.r -= particle.rotation * coefficient;
+                if (particle.r == 0.0)
+                    particle.r -= particle.rotation * 0.000001;
+                particle.update = 1;
             }
-            particle.rotation += speed * particle.r * dt;
-            particle.r -= particle.rotation * coefficient;
-            if (particle.r == 0.0)
-                particle.r -= particle.rotation * 0.000001;
-            particle.update = 1;
         }
     }
 
@@ -104,19 +107,22 @@ Item {
         width: parent.width + 120
         height: 100
         anchors.bottom: parent.bottom
-        onAffectParticle:{
-            var pseudoRand = (Math.floor(particle.t*1327) % 10) + 1;
-            var yslow = pseudoRand * 0.01 + 1.01;
-            var xslow = pseudoRand * 0.005 + 1.0;
-            if (particle.vy < 1)
-                particle.vy = 0;
-            else
-                particle.vy = (particle.vy / yslow);
-            if (particle.vx < 1)
-                particle.vx = 0;
-            else
-                particle.vx = (particle.vx / xslow);
-            particle.update = 1;
+        onAffectParticles:{
+            for (var i=0; i<particles.length; i++) {
+                var particle = particles[i];
+                var pseudoRand = (Math.floor(particle.t*1327) % 10) + 1;
+                var yslow = dt * pseudoRand * 0.5 + 1;
+                var xslow = dt * pseudoRand * 0.05 + 1;
+                if (particle.vy < 1)
+                    particle.vy = 0;
+                else
+                    particle.vy = (particle.vy / yslow);
+                if (particle.vx < 1)
+                    particle.vx = 0;
+                else
+                    particle.vx = (particle.vx / xslow);
+                particle.update = 1;
+            }
         }
     }
     ImageParticle{
index 55c3884..39e2d06 100644 (file)
@@ -66,15 +66,18 @@ ParticleSystem{
         emitRate: 120
         size: 12
         anchors.centerIn: parent
-        onEmitParticle:{
-            particle.startSize = Math.max(02,Math.min(492,Math.tan(particle.t/2)*24));
-            var theta = Math.floor(Math.random() * 6.0) / 6.0;
-            theta *= 2.0*Math.PI;
-            theta += sys.convert(sys.petalRotation);
-            particle.initialVX = petalLength * Math.cos(theta);
-            particle.initialVY = petalLength * Math.sin(theta);
-            particle.initialAX = particle.initialVX * -0.5;
-            particle.initialAY = particle.initialVY * -0.5;
+        onEmitParticles:{
+            for (var i=0; i<particles.length; i++) {
+                var particle = particles[i];
+                particle.startSize = Math.max(02,Math.min(492,Math.tan(particle.t/2)*24));
+                var theta = Math.floor(Math.random() * 6.0) / 6.0;
+                theta *= 2.0*Math.PI;
+                theta += sys.convert(sys.petalRotation);
+                particle.initialVX = petalLength * Math.cos(theta);
+                particle.initialVY = petalLength * Math.sin(theta);
+                particle.initialAX = particle.initialVX * -0.5;
+                particle.initialAY = particle.initialVY * -0.5;
+            }
         }
     }
     ImageParticle{
index 70e1dbe..77da5c5 100644 (file)
 ****************************************************************************/
 
 #include "qsgcustomaffector_p.h"
+#include <private/qv8engine_p.h>
+#include <private/qdeclarativeengine_p.h>
+#include <QDeclarativeEngine>
 #include <QDebug>
 QT_BEGIN_NAMESPACE
 
 //TODO: Move docs (and inherit) to real base when docs can propagate
-//TODO: Document particle 'type'
 /*!
-    \qmlsignal QtQuick.Particles2::Affector::affectParticle(particle, dt)
+    \qmlsignal QtQuick.Particles2::Affector::affectParticles(Array particles, real dt)
 
-    This handler is called when particles are selected to be affected.
+    This handler is called when particles are selected to be affected. particles contains
+    an array of particle objects which can be directly manipulated.
 
     dt is the time since the last time it was affected. Use dt to normalize
     trajectory manipulations to real time.
@@ -63,18 +66,38 @@ QSGCustomAffector::QSGCustomAffector(QSGItem *parent) :
 
 bool QSGCustomAffector::isAffectConnected()
 {
-    static int idx = QObjectPrivate::get(this)->signalIndex("affectParticle(QDeclarativeV8Handle,qreal)");
+    static int idx = QObjectPrivate::get(this)->signalIndex("affectParticles(QDeclarativeV8Handle,qreal)");
     return QObjectPrivate::get(this)->isSignalConnected(idx);
 }
 
-bool QSGCustomAffector::affectParticle(QSGParticleData *d, qreal dt)
+void QSGCustomAffector::affectSystem(qreal dt)
 {
-    if (isAffectConnected()){
-        d->update = 0.0;
-        emit affectParticle(d->v8Value(), dt);
-        return d->update == 1.0;
+    if (!isAffectConnected()) {
+        QSGParticleAffector::affectSystem(dt);
+        return;
     }
-    return true;
+    if (!m_enabled)
+        return;
+    updateOffsets();
+
+    QList<QSGParticleData*> toAffect;
+    foreach (QSGParticleGroupData* gd, m_system->m_groupData)
+        if (activeGroup(m_system->m_groupData.key(gd)))
+            foreach (QSGParticleData* d, gd->data)
+                if (shouldAffect(d))
+                    toAffect << d;
+
+    v8::HandleScope handle_scope;
+    v8::Context::Scope scope(QDeclarativeEnginePrivate::getV8Engine(qmlEngine(this))->context());
+    v8::Handle<v8::Array> array = v8::Array::New(toAffect.size());
+    for (int i=0; i<toAffect.size(); i++)
+        array->Set(i, toAffect[i]->v8Value().toHandle());
+
+    emit affectParticles(QDeclarativeV8Handle::fromHandle(array), dt);
+
+    foreach (QSGParticleData* d, toAffect)
+        if (d->update == 1.0)
+            postAffect(d);
 }
 
 QT_END_NAMESPACE
index 7f39200..fc0735e 100644 (file)
@@ -59,13 +59,13 @@ class QSGCustomAffector : public QSGParticleAffector
 
 public:
     explicit QSGCustomAffector(QSGItem *parent = 0);
+    virtual void affectSystem(qreal dt);
 
 signals:
-    void affectParticle(QDeclarativeV8Handle particle, qreal dt);
+    void affectParticles(QDeclarativeV8Handle particles, qreal dt);
 public slots:
 protected:
     bool isAffectConnected();
-    virtual bool affectParticle(QSGParticleData *d, qreal dt);
 private:
 };
 
index c67912f..c46164d 100644 (file)
@@ -163,6 +163,8 @@ void setWhenCollidingWith(QStringList arg)
         emit whenCollidingWithChanged(arg);
     }
 }
+public slots:
+    void updateOffsets();
 
 protected:
     friend class QSGParticleSystem;
@@ -189,8 +191,6 @@ private:
     QStringList m_whenCollidingWith;
 
     bool isColliding(QSGParticleData* d);
-private slots:
-    void updateOffsets();
 };
 
 QT_END_NAMESPACE
index f10bf1e..e1a7ef2 100644 (file)
@@ -40,6 +40,7 @@
 ****************************************************************************/
 
 #include "qsgparticleemitter_p.h"
+#include <private/qdeclarativeengine_p.h>
 QT_BEGIN_NAMESPACE
 
 
@@ -183,12 +184,12 @@ QT_BEGIN_NAMESPACE
 
     Default value is 0.
 */
-//TODO: Document particle 'type'
+
 /*!
-    \qmlsignal QtQuick.Particles2::Emitter::onEmitParticle(Particle particle)
+    \qmlsignal QtQuick.Particles2::Emitter::onEmitParticles(Array particles)
 
-    This handler is called when a particle is emitted. You can modify particle
-    attributes from within the handler.
+    This handler is called when particles are emitted. particles is a javascript
+    array of Particle objects. You can modify particle attributes directly within the handler.
 
     Note that JS is slower to execute, so it is not recommended to use this in
     high-volume particle systems.
@@ -252,7 +253,7 @@ QSGParticleEmitter::~QSGParticleEmitter()
 
 bool QSGParticleEmitter::isEmitConnected()
 {
-    static int idx = QObjectPrivate::get(this)->signalIndex("emitParticle(QDeclarativeV8Handle)");
+    static int idx = QObjectPrivate::get(this)->signalIndex("emitParticles(QDeclarativeV8Handle)");
     return QObjectPrivate::get(this)->isSignalConnected(idx);
 }
 
@@ -396,6 +397,9 @@ void QSGParticleEmitter::emitWindow(int timeStamp)
     qreal emitter_y_offset = m_last_emitter.y() - y();
     if (!m_burstQueue.isEmpty() && !m_burstLeft && !m_enabled)//'outside time' emissions only
         pt = time;
+
+    QList<QSGParticleData*> toEmit;
+
     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_group], !m_overwrite);
@@ -459,9 +463,7 @@ void QSGParticleEmitter::emitWindow(int timeStamp)
             datum->size = size;// * float(m_emitting);
             datum->endSize = endSize;// * float(m_emitting);
 
-            if (isEmitConnected())
-                emitParticle(datum->v8Value());//A chance for arbitrary JS changes
-            m_system->emitParticle(datum);
+            toEmit << datum;
         }
         if (m_burstQueue.isEmpty()){
             pt += particleRatio;
@@ -471,6 +473,19 @@ void QSGParticleEmitter::emitWindow(int timeStamp)
                 m_burstQueue.pop_front();
         }
     }
+
+    if (isEmitConnected()) {
+        v8::HandleScope handle_scope;
+        v8::Context::Scope scope(QDeclarativeEnginePrivate::getV8Engine(qmlEngine(this))->context());
+        v8::Handle<v8::Array> array = v8::Array::New(toEmit.size());
+        for (int i=0; i<toEmit.size(); i++)
+            array->Set(i, toEmit[i]->v8Value().toHandle());
+
+        emitParticles(QDeclarativeV8Handle::fromHandle(array));//A chance for arbitrary JS changes
+    }
+    foreach (QSGParticleData* d, toEmit)
+            m_system->emitParticle(d);
+
     m_last_emission = pt;
 
     m_last_last_last_emitter = m_last_last_emitter;
index 8bd205b..11c79ec 100644 (file)
@@ -123,7 +123,7 @@ public:
     void setSpeedFromMovement(qreal s);
     virtual void componentComplete();
 signals:
-    void emitParticle(QDeclarativeV8Handle particle);
+    void emitParticles(QDeclarativeV8Handle particles);
     void particlesPerSecondChanged(qreal);
     void particleDurationChanged(int);
     void enabledChanged(bool);
index 5a19ac5..e819663 100644 (file)
@@ -40,6 +40,7 @@
 ****************************************************************************/
 
 #include "qsgtrailemitter_p.h"
+#include <private/qdeclarativeengine_p.h>
 #include <cmath>
 QT_BEGIN_NAMESPACE
 
@@ -102,18 +103,17 @@ QSGTrailEmitter::QSGTrailEmitter(QSGItem *parent) :
     \qmlproperty real QtQuick.Particles2::TrailEmitter::emitRatePerParticle
 */
 /*!
-    \qmlsignal QtQuick.Particles2::TrailEmitter::emitFollowParticle(particle, followed)
+    \qmlsignal QtQuick.Particles2::TrailEmitter::emitFollowParticles(Array particles, real followed)
 
-    This handler is called when a particle is emitted. You can modify particle
-    attributes from within the handler. followed is the particle that this is being
-    emitted off of.
+    This handler is called when particles are emitted from the \a followed particle. \a particles contains an array of particle objects which can be directly manipulated.
+
+    If you use this signal handler, emitParticles will not be emitted.
 
-    If you use this signal handler, emitParticle will not be emitted.
 */
 
 bool QSGTrailEmitter::isEmitFollowConnected()
 {
-    static int idx = QObjectPrivate::get(this)->signalIndex("emitFollowParticle(QDeclarativeV8Handle,QDeclarativeV8Handle)");
+    static int idx = QObjectPrivate::get(this)->signalIndex("emitFollowParticles(QDeclarativeV8Handle,QDeclarativeV8Handle)");
     return QObjectPrivate::get(this)->isSignalConnected(idx);
 }
 
@@ -180,6 +180,9 @@ void QSGTrailEmitter::emitWindow(int timeStamp)
             m_lastEmission[d->index] = time;//jump over this time period without emitting, because it's outside
             continue;
         }
+
+        QList<QSGParticleData*> toEmit;
+
         while (pt < time || !m_burstQueue.isEmpty()){
             QSGParticleData* datum = m_system->newDatum(gId2, !m_overwrite);
             if (datum){//else, skip this emission
@@ -235,10 +238,7 @@ void QSGTrailEmitter::emitWindow(int timeStamp)
                 datum->size = size * float(m_enabled);
                 datum->endSize = endSize * float(m_enabled);
 
-                if (isEmitFollowConnected())
-                    emitFollowParticle(datum->v8Value(), d->v8Value());//A chance for many arbitrary JS changes
-                else if (isEmitConnected())
-                    emitParticle(datum->v8Value());//A chance for arbitrary JS changes
+                toEmit << datum;
 
                 m_system->emitParticle(datum);
             }
@@ -250,6 +250,21 @@ void QSGTrailEmitter::emitWindow(int timeStamp)
                 pt += particleRatio;
             }
         }
+
+        if (isEmitConnected() || isEmitFollowConnected()) {
+            v8::HandleScope handle_scope;
+            v8::Context::Scope scope(QDeclarativeEnginePrivate::getV8Engine(qmlEngine(this))->context());
+            v8::Handle<v8::Array> array = v8::Array::New(toEmit.size());
+            for (int i=0; i<toEmit.size(); i++)
+                array->Set(i, toEmit[i]->v8Value().toHandle());
+
+            if (isEmitFollowConnected())
+                emitFollowParticles(QDeclarativeV8Handle::fromHandle(array), d->v8Value());//A chance for many arbitrary JS changes
+            else if (isEmitConnected())
+                emitParticles(QDeclarativeV8Handle::fromHandle(array));//A chance for arbitrary JS changes
+        }
+        foreach (QSGParticleData* d, toEmit)
+            m_system->emitParticle(d);
         m_lastEmission[d->index] = pt;
     }
 
index 009ccd5..e0103af 100644 (file)
@@ -94,7 +94,7 @@ public:
     }
 
 signals:
-    void emitFollowParticle(QDeclarativeV8Handle group, QDeclarativeV8Handle followed);
+    void emitFollowParticles(QDeclarativeV8Handle particles, QDeclarativeV8Handle followed);
 
     void particlesPerParticlePerSecondChanged(int arg);