Rewrite particle system to cope with changing particle counts
authorAlan Alpert <alan.alpert@nokia.com>
Wed, 15 Jun 2011 08:52:26 +0000 (18:52 +1000)
committerAlan Alpert <alan.alpert@nokia.com>
Wed, 15 Jun 2011 08:52:26 +0000 (18:52 +1000)
And might do it again...
Caught up in this massive change were the following smaller ones:
-Some custom particle examples
-delegate property on ItemParticle and an example with it

20 files changed:
examples/declarative/particles/custom/fireworks.qml
examples/declarative/particles/custom/shader.qml [new file with mode: 0644]
examples/declarative/particles/trails/dynamicemitters.qml
src/declarative/particles/qsgcustomparticle.cpp
src/declarative/particles/qsgcustomparticle_p.h
src/declarative/particles/qsgfollowemitter.cpp
src/declarative/particles/qsgimageparticle.cpp
src/declarative/particles/qsgimageparticle_p.h
src/declarative/particles/qsgitemparticle.cpp
src/declarative/particles/qsgitemparticle_p.h
src/declarative/particles/qsgmodelparticle.cpp
src/declarative/particles/qsgmodelparticle_p.h
src/declarative/particles/qsgparticleaffector.cpp
src/declarative/particles/qsgparticleaffector_p.h
src/declarative/particles/qsgparticlepainter.cpp
src/declarative/particles/qsgparticlepainter_p.h
src/declarative/particles/qsgparticlesystem.cpp
src/declarative/particles/qsgparticlesystem_p.h
src/declarative/particles/qsgspritegoal.cpp
src/declarative/particles/qsgturbulence.cpp

index edd4acc..b73a5e2 100644 (file)
@@ -45,22 +45,6 @@ Rectangle{
     width: 360
     height: 600
     color: "black"
-    ParticleSystem{
-        id: otherSys
-        anchors.fill: parent
-        Emitter{
-            id: emitter
-            emitting: false
-            emitRate: 100
-            lifeSpan: 1000
-            emitCap: 1000
-            speed: AngledDirection{angleVariation:180; magnitudeVariation: 60}
-        }
-
-        ImageParticle{
-            source: "content/particle.png"
-        }
-    }
     Component{
         id: firework
         Item{
@@ -79,14 +63,28 @@ Rectangle{
                 repeat: false
                 onTriggered: {
                     img.visible = false;
-                    emitter.burst(100, container.x+24, container.y+24);
+                    emitter.burst(100);
                 }
             }
+            Emitter{
+                anchors.centerIn: parent
+                id: emitter
+                system: syssy
+                particle: "works"
+                emitting: false
+                emitRate: 100
+                lifeSpan: 1000
+                //speed: AngledDirection{angle: 270; angleVariation:60; magnitudeVariation: 60; magnitude: 20}
+                speed: PointDirection{y:-60; yVariation: 80; xVariation: 80}
+                acceleration: PointDirection{y:100; yVariation: 20}
+            }
         }
     }
     ParticleSystem{
         anchors.fill: parent
+        id: syssy
         Emitter{
+            particle: "fire"
             width: parent.width
             y: parent.height
             emitRate: 2
@@ -94,8 +92,13 @@ Rectangle{
             speed: PointDirection{y:-100}
         }
         ItemParticle{
+            particles: ["fire"]
             delegate: firework
         }
+        ImageParticle{
+            particles: ["works"]
+            source: "content/particle.png"
+        }
     }
 }
 
diff --git a/examples/declarative/particles/custom/shader.qml b/examples/declarative/particles/custom/shader.qml
new file mode 100644 (file)
index 0000000..d83e786
--- /dev/null
@@ -0,0 +1,84 @@
+import QtQuick 2.0
+import QtQuick.Particles 2.0
+
+ParticleSystem{
+    id: root
+    width: 1024
+    height: 768
+    Rectangle{
+        z: -1
+        anchors.fill: parent
+        color: "black"
+        Text{
+            anchors.bottom: parent.bottom
+            anchors.horizontalCenter: parent.horizontalCenter
+            font.pixelSize: 36
+            color: "white"
+            text: "It's all in the fragment shader."
+        }
+    }
+    Emitter{
+        emitRate: 400
+        lifeSpan: 8000
+        size: 24
+        sizeVariation: 16
+        speed: PointDirection{x: root.width/10; y: root.height/10;}
+        //acceleration: AngledDirection{angle:225; magnitude: root.width/36; angleVariation: 45; magnitudeVariation: 80}
+        acceleration: PointDirection{x: -root.width/40; y: -root.height/40; xVariation: -root.width/20; yVariation: -root.width/20}
+    }
+    CustomParticle{
+        //TODO: Someway that you don't have to rewrite the basics for a simple addition
+        vertexShader:"
+            attribute highp vec2 vPos;
+            attribute highp vec2 vTex;
+            attribute highp vec4 vData; //  x = time,  y = lifeSpan, z = size,  w = endSize
+            attribute highp vec4 vVec; // x,y = constant speed,  z,w = acceleration
+            attribute highp float r;
+
+            uniform highp mat4 qt_ModelViewProjectionMatrix;                              
+            uniform highp float timestamp;
+            uniform lowp float qt_Opacity;
+
+            varying highp vec2 fTex;                                
+            varying lowp float fFade;
+            varying highp vec2 fPos;
+
+            void main() {                                           
+                fTex = vTex;                                        
+                highp float size = vData.z;
+                highp float endSize = vData.w;
+
+                highp float t = (timestamp - vData.x) / vData.y;
+
+                highp float currentSize = mix(size, endSize, t * t);
+
+                if (t < 0. || t > 1.)
+                currentSize = 0.;
+
+                highp vec2 pos = vPos
+                - currentSize / 2. + currentSize * vTex          // adjust size
+                + vVec.xy * t * vData.y         // apply speed vector..
+                + 0.5 * vVec.zw * pow(t * vData.y, 2.);
+
+                gl_Position = qt_ModelViewProjectionMatrix * vec4(pos.x, pos.y, 0, 1);
+
+                highp float fadeIn = min(t * 20., 1.);
+                highp float fadeOut = 1. - max(0., min((t - 0.75) * 4., 1.));
+
+                fFade = fadeIn * fadeOut * qt_Opacity;
+                fPos = vec2(pos.x/1024., pos.y/768.);
+            }
+        "
+        fragmentShader: "
+            varying highp vec2 fPos;
+            varying lowp float fFade;
+            varying highp vec2 fTex;
+            void main() {//*2 because this generates dark colors mostly
+                highp vec2 circlePos = fTex*2.0 - vec2(1.0,1.0);
+                highp float dist = length(circlePos);
+                highp float circleFactor = max(min(1.0 - dist, 1.0), 0.0);
+                gl_FragColor = vec4(fPos.x*2.0 - fPos.y, fPos.y*2.0 - fPos.x, fPos.x*fPos.y*2.0, 0.0) * circleFactor * fFade;
+            }"
+
+    }
+}
index 588474f..f338c20 100644 (file)
@@ -81,7 +81,7 @@ Rectangle{
             }
             system: sys
             emitting: true
-            emitRate: 64
+            emitRate: 32
             lifeSpan: 600
             size: 24
             endSize: 8
@@ -107,12 +107,12 @@ Rectangle{
     MouseArea{
         anchors.fill: parent
         onClicked:{
-            for(var i=0; i<16; i++){
+            for(var i=0; i<8; i++){
                 var obj = emitterComp.createObject(root);
                 obj.x = mouse.x
                 obj.y = mouse.y
-                obj.targetX = Math.random() * 640
-                obj.targetY = Math.random() * 480
+                obj.targetX = Math.random() * 240 - 120 + obj.x
+                obj.targetY = Math.random() * 240 - 120 + obj.y
                 obj.life = Math.round(Math.random() * 2400) + 200
                 obj.go();
             }
index 808ff1c..f91307e 100644 (file)
 #include <cstdlib>
 
 QT_BEGIN_NAMESPACE
-/*
-    "uniform highp mat4 qt_ModelViewProjectionMatrix;               \n"
-    "attribute highp vec4 qt_Vertex;                                \n"
-    "attribute highp vec2 qt_MultiTexCoord0;                        \n"
-    "varying highp vec2 qt_TexCoord0;                               \n"
-    "void main() {                                                  \n"
-    "    qt_TexCoord0 = qt_MultiTexCoord0;                          \n"
-    "    gl_Position = qt_ModelViewProjectionMatrix * qt_Vertex;    \n"
-    "}";
-*/
+
+//TODO: Can we make the code such that you don't have to copy the whole vertex shader just to add one little calculation?
 //Includes comments because the code isn't self explanatory
 static const char qt_particles_default_vertex_code[] =
         "attribute highp vec2 vPos;                                                         \n"
@@ -86,23 +78,6 @@ static const char qt_particles_default_fragment_code[] =//TODO: Default frag req
         "    gl_FragColor = texture2D(source, fTex) * qt_Opacity;   \n"
         "}";
 
-/*
-static const char qt_particles_default_vertex_code[] =
-        "attribute highp vec2 vPos;                                                         \n"
-        "attribute highp vec2 vTex;                                                         \n"
-        "uniform highp mat4 qt_ModelViewProjectionMatrix;                                   \n"
-        "void main() {                                                                      \n"
-        "    highp float currentSize = 1000.0; \n"
-        "    highp vec2 pos = vec2(100.0,100.0) \n"
-        "                   - currentSize / 2. + currentSize * vTex;          // adjust size \n"
-        "    gl_Position = qt_ModelViewProjectionMatrix * vec4(pos.x, pos.y, 0, 1);         \n"
-        "}";
-static const char qt_particles_default_fragment_code[] =//TODO: Default frag requires source?
-        "void main() {                                              \n"
-        "    gl_FragColor = vec4(0,255,0,255);   \n"
-        "}";
-*/
-
 static const char qt_position_attribute_name[] = "qt_Vertex";
 static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0";
 
@@ -148,8 +123,36 @@ QSGCustomParticle::QSGCustomParticle(QSGItem* parent)
     : QSGParticlePainter(parent)
     , m_pleaseReset(true)
     , m_dirtyData(true)
+    , m_resizePending(false)
 {
     setFlag(QSGItem::ItemHasContents);
+    m_defaultVertices = new PlainVertices;
+    PlainVertex* vertices = (PlainVertex*) m_defaultVertices;
+    for (int i=0; i<4; ++i) {
+        vertices[i].x = 0;
+        vertices[i].y = 0;
+        vertices[i].t = -1;
+        vertices[i].lifeSpan = 0;
+        vertices[i].size = 0;
+        vertices[i].endSize = 0;
+        vertices[i].sx = 0;
+        vertices[i].sy = 0;
+        vertices[i].ax = 0;
+        vertices[i].ay = 0;
+        vertices[i].r = 0;
+    }
+
+    vertices[0].tx = 0;
+    vertices[0].ty = 0;
+
+    vertices[1].tx = 1;
+    vertices[1].ty = 0;
+
+    vertices[2].tx = 0;
+    vertices[2].ty = 1;
+
+    vertices[3].tx = 1;
+    vertices[3].ty = 1;
 }
 
 void QSGCustomParticle::componentComplete()
@@ -199,10 +202,17 @@ void QSGCustomParticle::setVertexShader(const QByteArray &code)
     emit vertexShaderChanged();
 }
 
-void QSGCustomParticle::setCount(int c)
+void QSGCustomParticle::resize(int oldCount, int newCount)
 {
-    QSGParticlePainter::setCount(c);
-    m_pleaseReset = true;
+    if(!m_node)
+        return;
+    if(!m_resizePending){
+        m_pendingResizeVector.resize(oldCount);
+        PlainVertices *particles = (PlainVertices *) m_node->geometry()->vertexData();
+        for(int i=0; i<oldCount; i++)
+            m_pendingResizeVector[i] = &particles[i];
+    }
+    groupShuffle(m_pendingResizeVector, m_defaultVertices);
 }
 
 void QSGCustomParticle::reset()
@@ -401,6 +411,8 @@ QSGNode *QSGCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat
         m_pleaseReset = false;
         m_dirtyData = false;
     }
+    if(m_resizePending)
+        performPendingResize();
 
     if(m_system && m_system->isRunning())
         prepareNextFrame();
@@ -425,6 +437,32 @@ void QSGCustomParticle::prepareNextFrame(){
         buildData();
 }
 
+void QSGCustomParticle::performPendingResize()
+{
+    m_resizePending = false;
+    if(!m_node)
+        return;
+    Q_ASSERT(m_pendingResizeVector.size() == m_count);//XXX
+    PlainVertices tmp[m_count];//###More vast memcpys that will decrease performance
+    for(int i=0; i<m_count; i++)
+        tmp[i] = *m_pendingResizeVector[i];
+    m_node->setFlag(QSGNode::OwnsGeometry, false);
+    m_node->geometry()->allocate(m_count*4, m_count*6);
+    memcpy(m_node->geometry()->vertexData(), tmp, sizeof(PlainVertices) * m_count);
+    quint16 *indices = m_node->geometry()->indexDataAsUShort();
+    for (int i=0; i<m_count; ++i) {
+        int o = i * 4;
+        indices[0] = o;
+        indices[1] = o + 1;
+        indices[2] = o + 2;
+        indices[3] = o + 1;
+        indices[4] = o + 3;
+        indices[5] = o + 2;
+        indices += 6;
+    }
+    m_node->setFlag(QSGNode::OwnsGeometry, true);
+}
+
 QSGShaderEffectNode* QSGCustomParticle::buildCustomNode()
 {
     if (m_count * 4 > 0xffff) {
@@ -437,6 +475,7 @@ QSGShaderEffectNode* QSGCustomParticle::buildCustomNode()
         return 0;
     }
 
+    m_resizePending = false;//reset resizes as well.
 
     //Create Particle Geometry
     int vCount = m_count * 4;
@@ -446,32 +485,9 @@ QSGShaderEffectNode* QSGCustomParticle::buildCustomNode()
     PlainVertex *vertices = (PlainVertex *) g->vertexData();
     for (int p=0; p<m_count; ++p) {
         double r = rand()/(double)RAND_MAX;//TODO: Seed?
-        for (int i=0; i<4; ++i) {
-            vertices[i].x = 0;
-            vertices[i].y = 0;
-            vertices[i].t = -1;
-            vertices[i].lifeSpan = 0;
-            vertices[i].size = 0;
-            vertices[i].endSize = 0;
-            vertices[i].sx = 0;
-            vertices[i].sy = 0;
-            vertices[i].ax = 0;
-            vertices[i].ay = 0;
+        memcpy(vertices, m_defaultVertices, sizeof(PlainVertices));
+        for(int i=0; i<4; i++)
             vertices[i].r = r;
-        }
-
-        vertices[0].tx = 0;
-        vertices[0].ty = 0;
-
-        vertices[1].tx = 1;
-        vertices[1].ty = 0;
-
-        vertices[2].tx = 0;
-        vertices[2].ty = 1;
-
-        vertices[3].tx = 1;
-        vertices[3].ty = 1;
-
         vertices += 4;
     }
     quint16 *indices = g->indexDataAsUShort();
@@ -493,23 +509,6 @@ QSGShaderEffectNode* QSGCustomParticle::buildCustomNode()
     node->setGeometry(g);
     node->setMaterial(&m_material);
 
-    /*
-    //For debugging, just use grid vertices like ShaderEffect
-    node->setGeometry(0);
-    QSGShaderEffectMesh* mesh = new QSGGridMesh();
-    node->setFlag(QSGNode::OwnsGeometry, false);
-
-    qDebug() << m_source.attributeNames;
-    QSGGeometry* geometry = node->geometry();
-    geometry = mesh->updateGeometry(geometry, m_source.attributeNames, QRectF(0,0,width(),height()));
-    if(!geometry)
-        qDebug() << "Should have written the error handling";
-    else
-        qDebug() << "Mesh Loaded";
-    node->setGeometry(geometry);
-    qDebug() << QString("INIT") << geometry << (QObject*)node;
-    node->setFlag(QSGNode::OwnsGeometry, true);
-    */
     QSGShaderEffectProgram s = m_source;
     if (s.fragmentCode.isEmpty())
         s.fragmentCode = qt_particles_default_fragment_code;
index 95144ef..863da05 100644 (file)
@@ -52,7 +52,7 @@ QT_BEGIN_NAMESPACE
 QT_MODULE(Declarative)
 
 class QSGNode;
-
+struct PlainVertices;
 //Genealogy: Hybrid of UltraParticle and ShaderEffectItem
 class QSGCustomParticle : public QSGParticlePainter
 {
@@ -64,7 +64,6 @@ public:
     explicit QSGCustomParticle(QSGItem* parent=0);
     virtual void load(QSGParticleData*);
     virtual void reload(QSGParticleData*);
-    virtual void setCount(int c);
 
     QByteArray fragmentShader() const { return m_source.fragmentCode; }
     void setFragmentShader(const QByteArray &code);
@@ -84,14 +83,17 @@ protected:
     void disconnectPropertySignals();
     void connectPropertySignals();
     void reset();
+    void resize(int oldCount, int newCount);
     void updateProperties();
     void lookThroughShaderCode(const QByteArray &code);
     virtual void componentComplete();
     QSGShaderEffectNode *buildCustomNode();
+    void performPendingResize();
 
 private:
     void buildData();
 
+
     bool m_pleaseReset;
     bool m_dirtyData;
     QSGShaderEffectProgram m_source;
@@ -105,6 +107,10 @@ private:
     QSGShaderEffectMaterial m_material;
     QSGShaderEffectNode* m_node;
     qreal m_lastTime;
+
+    bool m_resizePending;
+    QVector<PlainVertices*> m_pendingResizeVector;
+    PlainVertices* m_defaultVertices;
 };
 
 QT_END_NAMESPACE
index 442cff9..28a082f 100644 (file)
@@ -54,6 +54,7 @@ QSGFollowEmitter::QSGFollowEmitter(QSGItem *parent) :
   , m_emissionExtruder(0)
   , m_defaultEmissionExtruder(new QSGParticleExtruder(this))
 {
+    //TODO: If followed increased their size
     connect(this, SIGNAL(followChanged(QString)),
             this, SLOT(recalcParticlesPerSecond()));
     connect(this, SIGNAL(particleDurationChanged(int)),
@@ -67,7 +68,7 @@ void QSGFollowEmitter::recalcParticlesPerSecond(){
         return;
     m_followCount = m_system->m_groupData[m_system->m_groupIds[m_follow]]->size;
     if(!m_followCount){
-        setParticlesPerSecond(1000);//XXX: Fix this horrendous hack, needed so they aren't turned off from start (causes crashes - test that when gone you don't crash with 0 PPPS)
+        setParticlesPerSecond(1);//XXX: Fix this horrendous hack, needed so they aren't turned off from start (causes crashes - test that when gone you don't crash with 0 PPPS)
     }else{
         setParticlesPerSecond(m_particlesPerParticlePerSecond * m_followCount);
         m_lastEmission.resize(m_followCount);
@@ -111,16 +112,15 @@ void QSGFollowEmitter::emitWindow(int timeStamp)
 
     int gId = m_system->m_groupIds[m_follow];
     int gId2 = m_system->m_groupIds[m_particle];
-    for(int i=0; i<m_system->m_groupData[gId]->size; i++){
-        pt = m_lastEmission[i];
-        QSGParticleData* d = m_system->m_data[i + m_system->m_groupData[gId]->start];
+    foreach(QSGParticleData *d, m_system->m_groupData[gId]->data){
         if(!d || !d->stillAlive())
             continue;
+        pt = m_lastEmission[d->index];
         if(pt < d->pv.t)
             pt = d->pv.t;
 
         if(!effectiveExtruder()->contains(QRectF(offset.x(), offset.y(), width(), height()),QPointF(d->curX(), d->curY()))){
-            m_lastEmission[i] = time;//jump over this time period without emitting, because it's outside
+            m_lastEmission[d->index] = time;//jump over this time period without emitting, because it's outside
             continue;
         }
         while(pt < time || !m_burstQueue.isEmpty()){
@@ -187,7 +187,7 @@ void QSGFollowEmitter::emitWindow(int timeStamp)
                 pt += particleRatio;
             }
         }
-        m_lastEmission[i] = pt;
+        m_lastEmission[d->index] = pt;
     }
 
     m_lastTimeStamp = time;
index c9df5f4..15bc88b 100644 (file)
@@ -305,6 +305,62 @@ QSGImageParticle::QSGImageParticle(QSGItem* parent)
     , m_lastLevel(Unknown)
 {
     setFlag(ItemHasContents);
+    //TODO: Clean up defaults here and in custom
+    m_defaultUltra = new UltraVertices;
+    m_defaultSimple = new SimpleVertices;
+    UltraVertex *vertices = (UltraVertex *) m_defaultUltra;
+    SimpleVertex *vertices2 = (SimpleVertex *) m_defaultSimple;
+    for (int i=0; i<4; ++i) {
+        vertices2[i].x = vertices[i].x = 0;
+        vertices2[i].y = vertices[i].y = 0;
+        vertices2[i].t = vertices[i].t = -1;
+        vertices2[i].lifeSpan = vertices[i].lifeSpan = 0;
+        vertices2[i].size = vertices[i].size = 0;
+        vertices2[i].endSize = vertices[i].endSize = 0;
+        vertices2[i].sx = vertices[i].sx = 0;
+        vertices2[i].sy = vertices[i].sy = 0;
+        vertices2[i].ax = vertices[i].ax = 0;
+        vertices2[i].ay = vertices[i].ay = 0;
+        vertices[i].xx = 1;
+        vertices[i].xy = 0;
+        vertices[i].yx = 0;
+        vertices[i].yy = 1;
+        vertices[i].rotation = 0;
+        vertices[i].rotationSpeed = 0;
+        vertices[i].autoRotate = 0;
+        vertices[i].animIdx = -1;
+        vertices[i].frameDuration = 1;
+        vertices[i].frameCount = 0;
+        vertices[i].animT = -1;
+        vertices[i].color.r = 255;
+        vertices[i].color.g = 255;
+        vertices[i].color.b = 255;
+        vertices[i].color.a = 255;
+    }
+
+    vertices[0].tx = 0;
+    vertices[0].ty = 0;
+
+    vertices[1].tx = 1;
+    vertices[1].ty = 0;
+
+    vertices[2].tx = 0;
+    vertices[2].ty = 1;
+
+    vertices[3].tx = 1;
+    vertices[3].ty = 1;
+
+    vertices2[0].tx = 0;
+    vertices2[0].ty = 0;
+
+    vertices2[1].tx = 1;
+    vertices2[1].ty = 0;
+
+    vertices2[2].tx = 0;
+    vertices2[2].ty = 1;
+
+    vertices2[3].tx = 1;
+    vertices2[3].ty = 1;
 }
 
 QDeclarativeListProperty<QSGSprite> QSGImageParticle::sprites()
@@ -498,11 +554,6 @@ void QSGImageParticle::setBloat(bool arg)
     if(perfLevel < 9999)
         reset();
 }
-void QSGImageParticle::setCount(int c)
-{
-    QSGParticlePainter::setCount(c);
-    m_pleaseReset = true;
-}
 
 void QSGImageParticle::reset()
 {
@@ -568,35 +619,9 @@ QSGGeometryNode* QSGImageParticle::buildSimpleParticleNode()
     QSGGeometry *g = new QSGGeometry(SimpleParticle_AttributeSet, vCount, iCount);
     g->setDrawingMode(GL_TRIANGLES);
 
-    SimpleVertex *vertices = (SimpleVertex *) g->vertexData();
-    for (int p=0; p<m_count; ++p) {
-        for (int i=0; i<4; ++i) {
-            vertices[i].x = 0;
-            vertices[i].y = 0;
-            vertices[i].t = -1;
-            vertices[i].lifeSpan = 0;
-            vertices[i].size = 0;
-            vertices[i].endSize = 0;
-            vertices[i].sx = 0;
-            vertices[i].sy = 0;
-            vertices[i].ax = 0;
-            vertices[i].ay = 0;
-        }
-
-        vertices[0].tx = 0;
-        vertices[0].ty = 0;
-
-        vertices[1].tx = 1;
-        vertices[1].ty = 0;
-
-        vertices[2].tx = 0;
-        vertices[2].ty = 1;
-
-        vertices[3].tx = 1;
-        vertices[3].ty = 1;
-
-        vertices += 4;
-    }
+    SimpleVertices *vertices = (SimpleVertices *) g->vertexData();
+    for (int p=0; p<m_count; ++p)
+        memcpy(vertices++, m_defaultSimple, sizeof(SimpleVertices));
 
     quint16 *indices = g->indexDataAsUShort();
     for (int i=0; i<m_count; ++i) {
@@ -640,6 +665,7 @@ QSGGeometryNode* QSGImageParticle::buildParticleNode()
         return 0;
     }
 
+    m_resizePending = false;
     if(!m_sprites.count() && !m_bloat
             && m_colortable_name.isEmpty()
             && m_sizetable_name.isEmpty()
@@ -685,8 +711,8 @@ QSGGeometryNode* QSGImageParticle::buildParticleNode()
     if(m_lastLevel == 1)
         qDebug() << "Theta" << m_lastLevel << oldSimple[0].x << oldSimple[0].y << oldSimple[0].t;
     for (int p=0; p<m_count; ++p) {
-
-        if (m_lastLevel == 1) {//Transplant/IntermediateVertices?
+        memcpy(vertices, m_defaultUltra, sizeof(UltraVertices));
+        if (m_lastLevel == 1 && m_lastCount > p) {//Transplant/IntermediateVertices?
             for (int i=0; i<4; ++i) {
                 vertices[i].x = oldSimple[i].x;
                 vertices[i].y = oldSimple[i].y;
@@ -698,64 +724,14 @@ QSGGeometryNode* QSGImageParticle::buildParticleNode()
                 vertices[i].sy = oldSimple[i].sy;
                 vertices[i].ax = oldSimple[i].ax;
                 vertices[i].ay = oldSimple[i].ay;
-                vertices[i].xx = 1;
-                vertices[i].xy = 0;
-                vertices[i].yx = 0;
-                vertices[i].yy = 1;
-                vertices[i].rotation = 0;
-                vertices[i].rotationSpeed = 0;
-                vertices[i].autoRotate = 0;
-                vertices[i].animIdx = 0;
+            /*
                 vertices[i].frameDuration = oldSimple[i].lifeSpan;
                 vertices[i].frameCount = 1;
                 vertices[i].animT = oldSimple[i].t;
-                vertices[i].color.r = 255;
-                vertices[i].color.g = 255;
-                vertices[i].color.b = 255;
-                vertices[i].color.a = 255;
-            }
-        } else {
-            for (int i=0; i<4; ++i) {
-                vertices[i].x = 0;
-                vertices[i].y = 0;
-                vertices[i].t = -1;
-                vertices[i].lifeSpan = 0;
-                vertices[i].size = 0;
-                vertices[i].endSize = 0;
-                vertices[i].sx = 0;
-                vertices[i].sy = 0;
-                vertices[i].ax = 0;
-                vertices[i].ay = 0;
-                vertices[i].xx = 1;
-                vertices[i].xy = 0;
-                vertices[i].yx = 0;
-                vertices[i].yy = 1;
-                vertices[i].rotation = 0;
-                vertices[i].rotationSpeed = 0;
-                vertices[i].autoRotate = 0;
-                vertices[i].animIdx = -1;
-                vertices[i].frameDuration = 1;
-                vertices[i].frameCount = 0;
-                vertices[i].animT = -1;
-                vertices[i].color.r = 0;//TODO:Some things never get used uninitialized. Consider dropping them here?
-                vertices[i].color.g = 0;
-                vertices[i].color.b = 0;
-                vertices[i].color.a = 0;
+            */
             }
         }
 
-        vertices[0].tx = 0;
-        vertices[0].ty = 0;
-
-        vertices[1].tx = 1;
-        vertices[1].ty = 0;
-
-        vertices[2].tx = 0;
-        vertices[2].ty = 1;
-
-        vertices[3].tx = 1;
-        vertices[3].ty = 1;
-
         vertices += 4;
         oldSimple += 4;
     }
@@ -813,14 +789,95 @@ QSGGeometryNode* QSGImageParticle::buildParticleNode()
     return m_node;
 }
 
+void QSGImageParticle::resize(int oldCount, int newCount)
+{
+    //If perf level changes at the same time as a resize, we reset instead of doing pending resize
+    if(!m_node)
+        return;
+    switch(perfLevel){
+    default:
+    case Sprites:
+        if(m_spriteEngine)
+            reset();//TODO: Handle sprite resizeing (have to shuffle the engine too...)
+    case Coloured:
+    case Deformable:
+    case Tabled:
+        if(!m_resizePending){
+            m_resizePendingUltra.resize(oldCount);
+            UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData();
+            for(int i=0; i<oldCount; i++)
+                m_resizePendingUltra[i] = &particles[i];
+        }
+        groupShuffle(m_resizePendingUltra, m_defaultUltra);
+        break;
+    case Simple:
+        if(!m_resizePending){
+            m_resizePendingSimple.resize(oldCount);
+            SimpleVertices *particles = (SimpleVertices *) m_node->geometry()->vertexData();
+            for(int i=0; i<oldCount; i++)
+                m_resizePendingSimple[i] = &particles[i];
+        }
+        groupShuffle(m_resizePendingSimple, m_defaultSimple);
+        break;
+    }
+    m_resizePending = true;
+}
+
+void QSGImageParticle::performPendingResize()
+{
+    m_resizePending = false;
+    if(!m_node)
+        return;
+    UltraVertices tmp1[m_count];//###More vast memcpys that will decrease performance
+    SimpleVertices tmp2[m_count];//###More vast memcpys that will decrease performance
+    switch(perfLevel){
+    case Sprites:
+    case Coloured:
+    case Deformable:
+    case Tabled:
+        Q_ASSERT(m_resizePendingUltra.size() == m_count);//XXX
+        for(int i=0; i<m_count; i++){
+            Q_ASSERT(m_resizePendingUltra[i]);
+            tmp1[i] = *m_resizePendingUltra[i];
+        }
+        m_node->setFlag(QSGNode::OwnsGeometry, false);
+        m_node->geometry()->allocate(m_count*4, m_count*6);
+        memcpy(m_node->geometry()->vertexData(), tmp1, sizeof(UltraVertices) * m_count);
+        m_node->setFlag(QSGNode::OwnsGeometry, true);
+        break;
+    case Simple:
+        Q_ASSERT(m_resizePendingSimple.size() == m_count);//XXX
+        for(int i=0; i<m_count; i++)
+                tmp2[i] = *m_resizePendingSimple[i];
+        m_node->setFlag(QSGNode::OwnsGeometry, false);
+        m_node->geometry()->allocate(m_count*4, m_count*6);
+        memcpy(m_node->geometry()->vertexData(), tmp2, sizeof(SimpleVertices) * m_count);
+        m_node->setFlag(QSGNode::OwnsGeometry, true);
+        break;
+    }
+    quint16 *indices = m_node->geometry()->indexDataAsUShort();
+    for (int i=0; i<m_count; ++i) {
+        int o = i * 4;
+        indices[0] = o;
+        indices[1] = o + 1;
+        indices[2] = o + 2;
+        indices[3] = o + 1;
+        indices[4] = o + 3;
+        indices[5] = o + 2;
+        indices += 6;
+    }
+    m_node->setFlag(QSGNode::OwnsGeometry, true);
+}
+
 QSGNode *QSGImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
 {
     if(m_pleaseReset){
         if(m_node){
             if(perfLevel == 1){
                 qDebug() << "Beta";
-                m_lastData = qMalloc(m_count*sizeof(SimpleVertices));//TODO: Account for count_changed possibility
-                memcpy(m_lastData, m_node->geometry()->vertexData(), m_count * sizeof(SimpleVertices));//TODO: Multiple levels
+                m_lastCount = m_node->geometry()->vertexCount() / 4;
+                m_lastData = qMalloc(m_lastCount*sizeof(SimpleVertices));
+                memcpy(m_lastData, m_node->geometry()->vertexData(), m_lastCount * sizeof(SimpleVertices));//TODO: Multiple levels
             }
             m_lastLevel = perfLevel;
             delete m_node;
@@ -832,6 +889,8 @@ QSGNode *QSGImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
         m_material = 0;
         m_pleaseReset = false;
     }
+    if(m_resizePending)
+        performPendingResize();
 
     if(m_system && m_system->isRunning())
         prepareNextFrame();
@@ -990,4 +1049,20 @@ void QSGImageParticle::load(QSGParticleData *d)
     vertexCopy(*p->v4, d->pv);
 }
 
+/*
+void QSGImageParticle::verticesUpgrade(void *prev, void *next)
+{
+    PerformanceLevel copyLevel = qMin(perfLevel, m_lastLevel);
+    switch(perfLevel){//Intentional fall-through
+        case Sprites:
+            if(copyLevel >= Sprites)
+        case Tabled:
+        case Deformable:
+        case Coloured:
+    }
+
+}
+*/
+
+
 QT_END_NAMESPACE
index c6ec4c2..a644dd4 100644 (file)
@@ -167,7 +167,6 @@ public:
 
     virtual void load(QSGParticleData*);
     virtual void reload(QSGParticleData*);
-    virtual void setCount(int c);
 
     QDeclarativeListProperty<QSGSprite> sprites();
     QSGSpriteEngine* spriteEngine() {return m_spriteEngine;}
@@ -298,12 +297,14 @@ protected:
     void prepareNextFrame();
     QSGGeometryNode* buildParticleNode();
     QSGGeometryNode* buildSimpleParticleNode();
+    void resize(int oldCount, int newCount);
+    void performPendingResize();
 
 private slots:
     void createEngine(); //### method invoked by sprite list changing (in engine.h) - pretty nasty
 
 private:
-    //void vertexCopy(UltraVertex &b,const ParticleVertex& a);
+    //template <class T> void verticesUpgrade(IntermediateVertices* prev, T* next);//### Loses typessafety again...
     IntermediateVertices* fetchIntermediateVertices(int pos);
     bool m_do_reset;
 
@@ -345,6 +346,14 @@ private:
 
     PerformanceLevel m_lastLevel;
     void* m_lastData;
+    int m_lastCount;
+
+    //TODO: Some smart method that scales to multiple types better
+    bool m_resizePending;
+    QVector<UltraVertices*> m_resizePendingUltra;
+    QVector<SimpleVertices*> m_resizePendingSimple;
+    UltraVertices* m_defaultUltra;
+    SimpleVertices* m_defaultSimple;
 };
 
 QT_END_NAMESPACE
index 42f0062..1c6a8c4 100644 (file)
@@ -87,6 +87,7 @@ void QSGItemParticle::give(QSGItem *item)
 
 void QSGItemParticle::load(QSGParticleData* d)
 {
+    Q_ASSERT(d);
     int pos = particleTypeIndex(d);
     m_data[pos] = d;
     m_loadables << pos;
@@ -120,7 +121,7 @@ void QSGItemParticle::tick()
         }else if(m_delegate){
             m_items[pos] = qobject_cast<QSGItem*>(m_delegate->create(qmlContext(this)));
         }
-        if(m_items[pos]){
+        if(m_items[pos] && m_data[pos]){//###Data can be zero if creating an item leads to a reset - this screws things up.
             m_items[pos]->setX(m_data[pos]->curX() - m_items[pos]->width()/2);//TODO: adjust for system?
             m_items[pos]->setY(m_data[pos]->curY() - m_items[pos]->height()/2);
             QSGItemParticleAttached* mpa = qobject_cast<QSGItemParticleAttached*>(qmlAttachedPropertiesObject<QSGItemParticle>(m_items[pos]));
@@ -142,27 +143,22 @@ void QSGItemParticle::reload(QSGParticleData* d)
     //No-op unless we start copying the data.
 }
 
-void QSGItemParticle::setCount(int c)
-{
-    QSGParticlePainter::setCount(c);//###Do we need our own?
-    m_particleCount = c;
-    reset();
-}
-
-int QSGItemParticle::count()
+void QSGItemParticle::resize(int oldCount, int newCount)
 {
-    return m_particleCount;
+    if(!m_system)
+        return;
+    groupShuffle(m_items, (QSGItem*)0);
+    groupShuffle(m_data, (QSGParticleData*)0);
 }
 
 void QSGItemParticle::reset()
 {
     QSGParticlePainter::reset();
     //TODO: Cleanup items?
-    m_items.resize(m_particleCount);
-    m_data.resize(m_particleCount);
     m_items.fill(0);
     m_data.fill(0);
-    //m_pendingItems.clear();//TODO: Should this be done? If so, Emit signal?
+    m_loadables.clear();
+    //deletables?
 }
 
 
@@ -191,7 +187,7 @@ void QSGItemParticle::prepareNextFrame()
         return;
 
     //TODO: Size, better fade?
-    for(int i=0; i<m_particleCount; i++){
+    for(int i=0; i<count(); i++){
         QSGItem* item = m_items[i];
         QSGParticleData* data = m_data[i];
         if(!item || !data)
index 3b7db51..7cea63b 100644 (file)
@@ -65,8 +65,6 @@ public:
     virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
     virtual void load(QSGParticleData*);
     virtual void reload(QSGParticleData*);
-    virtual void setCount(int c);
-    virtual int count();
 
     static QSGItemParticleAttached *qmlAttachedProperties(QObject *object);
     QDeclarativeComponent* delegate() const
@@ -97,13 +95,13 @@ public slots:
 
 protected:
     virtual void reset();
+    virtual void resize(int oldCount, int newCount);
     void prepareNextFrame();
 private slots:
     void tick();
 private:
     QList<QSGItem* > m_deletables;
     QList< int > m_loadables;
-    int m_particleCount;
     bool m_fade;
 
     QList<QSGItem*> m_pendingItems;
index f87c0d3..aba68c6 100644 (file)
@@ -201,25 +201,17 @@ void QSGModelParticle::reload(QSGParticleData* d)
     //No-op unless we start copying the data.
 }
 
-void QSGModelParticle::setCount(int c)
+void QSGModelParticle::resize(int oldCount, int newCount)
 {
-    QSGParticlePainter::setCount(c);//###Do we need our own?
-    m_particleCount = c;
-    reset();
-}
-
-int QSGModelParticle::count()
-{
-    return m_particleCount;
+    groupShuffle(m_items, (QSGItem *) 0);
+    groupShuffle(m_data, (QSGParticleData*) 0);
+    groupShuffle(m_idx, -1);
 }
 
 void QSGModelParticle::reset()
 {
     QSGParticlePainter::reset();
     //TODO: Cleanup items?
-    m_items.resize(m_particleCount);
-    m_data.resize(m_particleCount);
-    m_idx.resize(m_particleCount);
     m_items.fill(0);
     m_data.fill(0);
     m_idx.fill(-1);
@@ -253,7 +245,7 @@ void QSGModelParticle::prepareNextFrame()
         return;
 
     //TODO: Size, better fade?
-    for(int i=0; i<m_particleCount; i++){
+    for(int i=0; i<count(); i++){
         QSGItem* item = m_items[i];
         QSGParticleData* data = m_data[i];
         if(!item || !data)
index 04533a7..cef2008 100644 (file)
@@ -76,8 +76,6 @@ public:
     virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
     virtual void load(QSGParticleData*);
     virtual void reload(QSGParticleData*);
-    virtual void setCount(int c);
-    virtual int count();
 
     static QSGModelParticleAttached *qmlAttachedProperties(QObject *object);
 signals:
@@ -93,6 +91,7 @@ public slots:
     void setFade(bool arg){if(arg == m_fade) return; m_fade = arg; emit fadeChanged();}
 protected:
     virtual void reset();
+    virtual void resize(int oldCount, int newCount);
     void prepareNextFrame();
 private slots:
     void updateCount();
@@ -104,7 +103,6 @@ private:
     QVariant m_dataSource;
     QList<QSGItem*> m_deletables;
     QList< int > m_requests;
-    int m_particleCount;
     bool m_fade;
 
     QList<QSGItem*> m_pendingItems;
index 96c5cfb..5b0936c 100644 (file)
@@ -79,22 +79,22 @@ void QSGParticleAffector::affectSystem(qreal dt)
             m_groups << m_system->m_groupIds[p];//###Can this occur before group ids are properly assigned?
         m_updateIntSet = false;
     }
-    //foreach(ParticleData* d, m_system->m_data){
-    for(int i=0; i<m_system->m_particle_count; i++){
-        QSGParticleData* d = m_system->m_data[i];
-        if(!d || (m_onceOff && m_onceOffed.contains(d->systemIndex)))
-            continue;
-        if(m_groups.isEmpty() || m_groups.contains(d->group)){
-            //Need to have previous location for affected. if signal || shape might be faster?
-            QPointF curPos = QPointF(d->curX(), d->curY());
-            if(width() == 0 || height() == 0
-                    || m_shape->contains(QRectF(m_offset.x(), m_offset.y(), width(), height()),curPos)){
-                if(affectParticle(d, dt)){
-                    m_system->m_needsReset << d;
-                    if(m_onceOff)
-                        m_onceOffed << d->systemIndex;
-                    if(m_signal)
-                        emit affected(curPos.x(), curPos.y());
+    foreach(QSGParticleGroupData* gd, m_system->m_groupData){
+        foreach(QSGParticleData* d, gd->data){
+            if(!d || (m_onceOff && m_onceOffed.contains(qMakePair(d->group, d->index))))
+                continue;
+            if(m_groups.isEmpty() || m_groups.contains(d->group)){
+                //Need to have previous location for affected. if signal || shape might be faster?
+                QPointF curPos = QPointF(d->curX(), d->curY());
+                if(width() == 0 || height() == 0
+                        || m_shape->contains(QRectF(m_offset.x(), m_offset.y(), width(), height()),curPos)){
+                    if(affectParticle(d, dt)){
+                        m_system->m_needsReset << d;
+                        if(m_onceOff)
+                            m_onceOffed << qMakePair(d->group, d->index);
+                        if(m_signal)
+                            emit affected(curPos.x(), curPos.y());
+                    }
                 }
             }
         }
@@ -108,10 +108,10 @@ bool QSGParticleAffector::affectParticle(QSGParticleData *d, qreal dt)
     return false;
 }
 
-void QSGParticleAffector::reset(int idx)
+void QSGParticleAffector::reset(QSGParticleData* pd)
 {//TODO: This, among other ones, should be restructured so they don't all need to remember to call the superclass
     if(m_onceOff)
-        m_onceOffed.remove(idx);
+        m_onceOffed.remove(qMakePair(pd->group, pd->index));
 }
 
 void QSGParticleAffector::updateOffsets()
index b299158..7418760 100644 (file)
@@ -65,7 +65,7 @@ class QSGParticleAffector : public QSGItem
 public:
     explicit QSGParticleAffector(QSGItem *parent = 0);
     virtual void affectSystem(qreal dt);
-    virtual void reset(int systemIdx);//As some store their own data per idx?
+    virtual void reset(QSGParticleData*);//As some store their own data per particle?
     QSGParticleSystem* system() const
     {
         return m_system;
@@ -108,7 +108,7 @@ signals:
 
     void shapeChanged(QSGParticleExtruder* arg);
 
-    void affected(qreal x, qreal y);//###Idx too?
+    void affected(qreal x, qreal y);
     void signalChanged(bool arg);
 
 public slots:
@@ -174,7 +174,7 @@ protected:
     QPointF m_offset;
 private:
     QSet<int> m_groups;
-    QSet<int> m_onceOffed;
+    QSet<QPair<int, int> > m_onceOffed;
     bool m_updateIntSet;
 
     bool m_onceOff;
index a5e8c0a..0d369fd 100644 (file)
@@ -44,7 +44,7 @@
 QT_BEGIN_NAMESPACE
 QSGParticlePainter::QSGParticlePainter(QSGItem *parent) :
     QSGItem(parent),
-    m_system(0)
+    m_system(0), m_count(0), m_lastStart(0)
 {
     connect(this, SIGNAL(xChanged()),
             this, SLOT(calcSystemOffset()));
@@ -67,7 +67,7 @@ void QSGParticlePainter::setSystem(QSGParticleSystem *arg)
     if (m_system != arg) {
         m_system = arg;
         if(m_system){
-            m_system->registerParticleType(this);
+            m_system->registerParticlePainter(this);
             connect(m_system, SIGNAL(xChanged()),
                     this, SLOT(calcSystemOffset()));
             connect(m_system, SIGNAL(yChanged()),
@@ -89,15 +89,24 @@ void QSGParticlePainter::reload(QSGParticleData*)
 void QSGParticlePainter::reset()
 {
     //Have to every time because what it's emitting may have changed and that affects particleTypeIndex
-    m_particleStarts.clear();
-    m_lastStart = 0;
+    if(m_system)
+        updateParticleStarts();
 }
 
+void QSGParticlePainter::resize(int, int)
+{
+}
+
+
 void QSGParticlePainter::setCount(int c)
 {
+    Q_ASSERT(c >= 0); //XXX
     if(c == m_count)
         return;
+    int lastCount = m_count;
     m_count = c;
+    resize(lastCount, m_count);//### is virtual needed? Or should I just use the signal?
+    updateParticleStarts();
     emit countChanged();
 }
 
@@ -107,14 +116,27 @@ int QSGParticlePainter::count()
 }
 
 
-int QSGParticlePainter::particleTypeIndex(QSGParticleData* d)
+void QSGParticlePainter::updateParticleStarts()
 {
-    if(!m_particleStarts.contains(d->group)){
-        m_particleStarts.insert(d->group, m_lastStart);
-        m_lastStart += m_system->m_groupData[d->group]->size;
+    m_particleStarts.clear();
+    m_lastStart = 0;
+    QList<int> particleList;
+    if(m_particles.isEmpty())
+        particleList << 0;
+    foreach(const QString &s, m_particles)
+        particleList << m_system->m_groupIds[s];
+    foreach(int gIdx, particleList){
+        QSGParticleGroupData *gd = m_system->m_groupData[gIdx];
+        m_particleStarts.insert(gIdx, qMakePair<int, int>(gd->size, m_lastStart));
+        m_lastStart += gd->size;
     }
-    int ret = m_particleStarts[d->group] + d->particleIndex;
-    Q_ASSERT(ret >=0 && ret < m_count);//XXX: Possibly shouldn't assert, but bugs here were hard to find in the past
+}
+
+int QSGParticlePainter::particleTypeIndex(QSGParticleData* d)
+{
+    Q_ASSERT(d && m_particleStarts.contains(d->group));//XXX
+    int ret = m_particleStarts[d->group].second + d->index;
+    Q_ASSERT(ret >=0 && ret < m_count);//XXX:shouldn't assert, but bugs here were hard to find in the past
     return ret;
 }
 
@@ -129,8 +151,8 @@ void QSGParticlePainter::calcSystemOffset()
         //Reload all particles//TODO: Necessary?
         foreach(const QString &g, m_particles){
             int gId = m_system->m_groupIds[g];
-            for(int i=0; i<m_system->m_groupData[gId]->size; i++)
-                reload(m_system->m_data[m_system->m_groupData[gId]->start + i]);
+            foreach(QSGParticleData* d, m_system->m_groupData[gId]->data)
+                reload(d);
         }
     }
 }
index 8f1e13b..6657d57 100644 (file)
@@ -63,8 +63,8 @@ public:
     explicit QSGParticlePainter(QSGItem *parent = 0);
     virtual void load(QSGParticleData*);
     virtual void reload(QSGParticleData*);
-    virtual void setCount(int c);
-    virtual int count();
+    void setCount(int c);
+    int count();
     QSGParticleSystem* system() const
     {
         return m_system;
@@ -76,7 +76,6 @@ public:
         return m_particles;
     }
 
-    int particleTypeIndex(QSGParticleData*);
 signals:
     void countChanged();
     void systemChanged(QSGParticleSystem* arg);
@@ -95,25 +94,23 @@ void setParticles(QStringList arg)
 }
 private slots:
     void calcSystemOffset();
+    void updateParticleStarts();
+
 protected:
     virtual void reset();
     virtual void componentComplete();
 
-//    virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *){
-//        qDebug() << "Shouldn't be here..." << this;
-//        return 0;
-//    }
 
     QSGParticleSystem* m_system;
     friend class QSGParticleSystem;
     int m_count;
     bool m_pleaseReset;
     QStringList m_particles;
-    QHash<int,int> m_particleStarts;
+    QHash<int,QPair<int, int> > m_particleStarts; //Group, size, idx
     int m_lastStart;
     QPointF m_systemOffset;
 
-    template <typename VertexStruct>
+    template <typename VertexStruct>//just convenience
     void vertexCopy(VertexStruct &b, const ParticleVertex& a)
     {
         b.x = a.x - m_systemOffset.x();
@@ -128,6 +125,36 @@ protected:
         b.ay = a.ay;
     }
 
+    //###Abstracted primarily for code reuse. Demote to subclasses?
+    int particleTypeIndex(QSGParticleData*);
+    virtual void resize(int oldCount, int newCount);
+    template <typename T>
+    void groupShuffle(QVector<T> &v, const T& zero)//Must be called inside resize
+    {
+        //TODO: In place shuffling because it's faster
+        QVector<T> v0(v);
+        v.clear();
+        v.resize(m_count);
+        int lastStart = 0;
+        QList<int> particleList;
+        if(m_particles.isEmpty())
+            particleList << 0;
+        foreach(const QString &s, m_particles)
+            particleList << m_system->m_groupIds[s];
+
+        foreach(int gIdx, particleList){
+            QSGParticleGroupData *gd = m_system->m_groupData[gIdx];
+            for(int i=0; i<gd->data.size(); i++){//TODO: When group didn't exist before
+                int newIdx = lastStart + i;//Have to make the same way as in updateParticleStarts
+                if(i >= m_particleStarts[gIdx].first || v0.size() <= m_particleStarts[gIdx].second + i)
+                    v[newIdx] = zero;
+                else
+                    v[newIdx] = v0[m_particleStarts[gIdx].second + i];
+            }
+            lastStart += gd->size;
+        }
+    }
+
 private:
 };
 
index 4a8f75b..a93f2a5 100644 (file)
@@ -52,8 +52,7 @@ QT_BEGIN_NAMESPACE
 QSGParticleData::QSGParticleData()
     : group(0)
     , e(0)
-    , particleIndex(0)
-    , systemIndex(0)
+    , index(0)
 {
     pv.x = 0;
     pv.y = 0;
@@ -71,174 +70,202 @@ QSGParticleSystem::QSGParticleSystem(QSGItem *parent) :
   , m_startTime(0), m_overwrite(false)
   , m_componentComplete(false)
 {
-    m_groupIds = QHash<QString, int>();
+    QSGParticleGroupData* gd = new QSGParticleGroupData;//Default group
+    m_groupData.insert(0,gd);
+    m_groupIds.insert("",0);
+    m_nextGroupId = 1;
+
+    connect(&m_painterMapper, SIGNAL(mapped(QObject*)),
+            this, SLOT(loadPainter(QObject*)));
 }
 
-void QSGParticleSystem::registerParticleType(QSGParticlePainter* p)
+void QSGParticleSystem::registerParticlePainter(QSGParticlePainter* p)
 {
-    m_particles << QPointer<QSGParticlePainter>(p);//###Set or uniqueness checking?
-    reset();
+    //TODO: a way to Unregister emitters, painters and affectors
+    m_particlePainters << QPointer<QSGParticlePainter>(p);//###Set or uniqueness checking?
+    connect(p, SIGNAL(particlesChanged(QStringList)),
+            &m_painterMapper, SLOT(map()));
+    loadPainter(p);
+    p->update();//###Initial update here?
 }
 
 void QSGParticleSystem::registerParticleEmitter(QSGParticleEmitter* e)
 {
     m_emitters << QPointer<QSGParticleEmitter>(e);//###How to get them out?
     connect(e, SIGNAL(particleCountChanged()),
-            this, SLOT(countChanged()));
+            this, SLOT(emittersChanged()));
     connect(e, SIGNAL(particleChanged(QString)),
-            this, SLOT(countChanged()));
-    reset();
+            this, SLOT(emittersChanged()));
+    emittersChanged();
+    e->reset();//Start, so that starttime factors appropriately
 }
 
 void QSGParticleSystem::registerParticleAffector(QSGParticleAffector* a)
 {
     m_affectors << QPointer<QSGParticleAffector>(a);
-    //reset();//TODO: Slim down the huge batch of resets at the start
 }
 
-void QSGParticleSystem::countChanged()
+void QSGParticleSystem::loadPainter(QObject *p)
 {
-    reset();//Need to give Particles new Count
-}
+    if(!m_componentComplete)
+        return;
 
-void QSGParticleSystem::setRunning(bool arg)
-{
-    if (m_running != arg) {
-        m_running = arg;
-        emit runningChanged(arg);
-        reset();
+    QSGParticlePainter* painter = qobject_cast<QSGParticlePainter*>(p);
+    Q_ASSERT(painter);//XXX
+    foreach(QSGParticleGroupData* sg, m_groupData)
+        sg->painters.remove(painter);
+    int particleCount = 0;
+    if(painter->particles().isEmpty()){//Uses default particle
+        particleCount += m_groupData[0]->size;
+        m_groupData[0]->painters << painter;
+    }else{
+        foreach(const QString &group, painter->particles()){
+            particleCount += m_groupData[m_groupIds[group]]->size;
+            m_groupData[m_groupIds[group]]->painters << painter;
+        }
     }
+    painter->setCount(particleCount);
+    painter->update();//###Initial update here?
+    return;
 }
 
-void QSGParticleSystem::componentComplete()
-{
-    QSGItem::componentComplete();
-    m_componentComplete = true;
-    if(!m_emitters.isEmpty() && !m_particles.isEmpty())
-        reset();
-}
-
-void QSGParticleSystem::initializeSystem()
+void QSGParticleSystem::emittersChanged()
 {
-    int oldCount = m_particle_count;
-    m_particle_count = 0;//TODO: Only when changed?
-
-    //### Reset the data too?
-    for(int i=0; i<oldCount; i++){
-        if(m_data[i]){
-            delete m_data[i];
-            m_data[i] = 0;
-        }
-    }
+    if(!m_componentComplete)
+        return;
 
-    for(QHash<int, GroupData*>::iterator iter = m_groupData.begin(); iter != m_groupData.end(); iter++)
-        delete (*iter);
-    m_groupData.clear();
-    m_groupIds.clear();
+    m_emitters.removeAll(0);
 
-    GroupData* gd = new GroupData;//Default group
-    gd->size = 0;
-    gd->start = -1;
-    gd->nextIdx = 0;
-    m_groupData.insert(0,gd);
-    m_groupIds.insert("",0);
-    m_nextGroupId = 1;
+    //Recalculate all counts, as emitter 'particle' may have changed as well
+    //### Worth tracking previous 'particle' per emitter to do partial recalculations?
+    m_particle_count = 0;
 
-    if(!m_emitters.count() || !m_particles.count())
-        return;
+    int previousGroups = m_nextGroupId;
+    QVector<int> previousSizes;
+    previousSizes.resize(previousGroups);
+    for(int i=0; i<previousGroups; i++)
+        previousSizes[i] = m_groupData[i]->size;
+    for(int i=0; i<previousGroups; i++)
+        m_groupData[i]->size = 0;
 
-    foreach(QSGParticleEmitter* e, m_emitters){
+    foreach(QSGParticleEmitter* e, m_emitters){//Populate groups and set sizes.
         if(!m_groupIds.contains(e->particle())
                 || (!e->particle().isEmpty() && !m_groupIds[e->particle()])){//or it was accidentally inserted by a failed lookup earlier
-            GroupData* gd = new GroupData;
-            gd->size = 0;
-            gd->start = -1;
-            gd->nextIdx = 0;
+            QSGParticleGroupData* gd = new QSGParticleGroupData;
             int id = m_nextGroupId++;
             m_groupIds.insert(e->particle(), id);
             m_groupData.insert(id, gd);
         }
         m_groupData[m_groupIds[e->particle()]]->size += e->particleCount();
+        m_particle_count += e->particleCount();
+        //###: Cull emptied groups?
     }
 
-    for(QHash<int, GroupData*>::iterator iter = m_groupData.begin(); iter != m_groupData.end(); iter++){
-        (*iter)->start = m_particle_count;
-        m_particle_count += (*iter)->size;
-    }
-    m_data.resize(m_particle_count);
-    for(int i=oldCount; i<m_particle_count; i++)
-        m_data[i] = 0;//setup new ones
-
-    if(m_particle_count > 16000)
-        qWarning() << "Particle system contains a vast number of particles (>16000). Expect poor performance";
-
-    foreach(QSGParticlePainter* particle, m_particles){
-        int particleCount = 0;
-        if(particle->particles().isEmpty()){//Uses default particle
-            particleCount += m_groupData[0]->size;
-            m_groupData[0]->types << particle;
-        }else{
-            foreach(const QString &group, particle->particles()){
-                particleCount += m_groupData[m_groupIds[group]]->size;
-                m_groupData[m_groupIds[group]]->types << particle;
+    foreach(QSGParticleGroupData* gd, m_groupData){//resize groups and update painters
+        int id = m_groupData.key(gd);
+
+        //TODO: Shrink back down! (but it has the problem of trying to remove the dead particles while maintaining integrity)
+        gd->size = qMax(gd->size, id < previousGroups?previousSizes[id]:0);
+
+        gd->data.resize(gd->size);
+        if(id < previousGroups){
+            for(int i=previousSizes[id]; i<gd->size; i++)
+                gd->data[i] = 0;
+            /*TODO:Consider salvaging partial updates, but have to batch changes to a single painter
+            int delta = 0;
+            delta = gd->size - previousSizes[id];
+            foreach(QSGParticlePainter* painter, gd->painters){
+                if(!painter->count() && delta){
+                    painter->reset();
+                    painter->update();
+                }
+                qDebug() << "Phi" << painter << painter->count() << delta;
+                painter->setCount(painter->count() + delta);
             }
+            */
         }
-        particle->setCount(particleCount);
-        particle->m_pleaseReset = true;
     }
+    foreach(QSGParticlePainter *p, m_particlePainters)
+        loadPainter(p);
 
-    m_timestamp.start();
-    m_initialized = true;
-    emit systemInitialized();
-    qDebug() << "System Initialized. Size:" << m_particle_count;
+    if(m_particle_count > 16000)//###Investigate if these limits are worth warning about?
+        qWarning() << "Particle system arbitarily believes it has a vast number of particles (>16000). Expect poor performance";
+}
+
+void QSGParticleSystem::setRunning(bool arg)
+{
+    if (m_running != arg) {
+        m_running = arg;
+        emit runningChanged(arg);
+        reset();
+    }
 }
 
-void QSGParticleSystem::reset()
+void QSGParticleSystem::componentComplete()
+{
+    QSGItem::componentComplete();
+    m_componentComplete = true;
+    //if(!m_emitters.isEmpty() && !m_particlePainters.isEmpty())
+    reset();
+}
+
+void QSGParticleSystem::reset()//TODO: Needed?
 {
     if(!m_componentComplete)
-        return;//Batch starting reset()s a little
+        return;
+
     //Clear guarded pointers which have been deleted
     int cleared = 0;
     cleared += m_emitters.removeAll(0);
-    cleared += m_particles.removeAll(0);
+    cleared += m_particlePainters.removeAll(0);
     cleared += m_affectors.removeAll(0);
     //qDebug() << "Reset" << m_emitters.count() << m_particles.count() << "Cleared" << cleared;
-    foreach(QSGParticlePainter* p, m_particles)
-        p->reset();
-    foreach(QSGParticleEmitter* e, m_emitters)
-        e->reset();
+
+    emittersChanged();
+
+    //TODO: Reset data
+//    foreach(QSGParticlePainter* p, m_particlePainters)
+//        p->reset();
+//    foreach(QSGParticleEmitter* e, m_emitters)
+//        e->reset();
+    //### Do affectors need reset too?
+
     if(!m_running)
         return;
-    initializeSystem();
-    foreach(QSGParticlePainter* p, m_particles)
-        p->update();
-    foreach(QSGParticleEmitter* e, m_emitters)
-        e->emitWindow(0);//Start, so that starttime factors appropriately
+
+    foreach(QSGParticlePainter *p, m_particlePainters){
+        loadPainter(p);
+        p->reset();
+    }
+
+    m_timestamp.start();//TODO: Better placement
+    m_initialized = true;
 }
 
 QSGParticleData* QSGParticleSystem::newDatum(int groupId)
 {
+
     Q_ASSERT(groupId < m_groupData.count());//XXX shouldn't really be an assert
     Q_ASSERT(m_groupData[groupId]->size);
-    int nextIdx = m_groupData[groupId]->start + m_groupData[groupId]->nextIdx++;
+
     if( m_groupData[groupId]->nextIdx >= m_groupData[groupId]->size)
         m_groupData[groupId]->nextIdx = 0;
+    int nextIdx = m_groupData[groupId]->nextIdx++;
 
-    Q_ASSERT(nextIdx < m_data.size());
+    Q_ASSERT(nextIdx < m_groupData[groupId]->size);
     QSGParticleData* ret;
-    if(m_data[nextIdx]){//Recycle, it's faster.
-        ret = m_data[nextIdx];
+    if(m_groupData[groupId]->data[nextIdx]){//Recycle, it's faster.
+        ret = m_groupData[groupId]->data[nextIdx];
         if(!m_overwrite && ret->stillAlive()){
             return 0;//Artificial longevity (or too fast emission) means this guy hasn't died. To maintain count, don't emit a new one
         }//###Reset?
     }else{
         ret = new QSGParticleData;
-        m_data[nextIdx] = ret;
+        m_groupData[groupId]->data[nextIdx] = ret;
     }
 
     ret->system = this;
-    ret->systemIndex = nextIdx;
-    ret->particleIndex = nextIdx - m_groupData[groupId]->start;
+    ret->index = nextIdx;
     ret->group = groupId;
     return ret;
 }
@@ -254,8 +281,8 @@ void QSGParticleSystem::emitParticle(QSGParticleData* pd)
 
     foreach(QSGParticleAffector *a, m_affectors)
         if(a && a->m_needsReset)
-            a->reset(pd->systemIndex);
-    foreach(QSGParticlePainter* p, m_groupData[pd->group]->types)
+            a->reset(pd);
+    foreach(QSGParticlePainter* p, m_groupData[pd->group]->painters)
         if(p)
             p->load(pd);
 }
@@ -285,7 +312,7 @@ qint64 QSGParticleSystem::systemSync(QSGParticlePainter* p)
             if(a)
                 a->affectSystem(dt);
         foreach(QSGParticleData* d, m_needsReset)
-            foreach(QSGParticlePainter* p, m_groupData[d->group]->types)
+            foreach(QSGParticlePainter* p, m_groupData[d->group]->painters)
                 if(p && d)
                     p->reload(d);
     }
index 9730ff3..4c690ff 100644 (file)
@@ -47,6 +47,7 @@
 #include <QVector>
 #include <QHash>
 #include <QPointer>
+#include <QSignalMapper>
 
 QT_BEGIN_HEADER
 
@@ -61,11 +62,14 @@ class QSGParticlePainter;
 class QSGParticleData;
 
 
-struct GroupData{
+class QSGParticleGroupData{
+public:
+    QSGParticleGroupData():size(0),nextIdx(0)
+    {}
     int size;
-    int start;
     int nextIdx;
-    QList<QSGParticlePainter*> types;
+    QSet<QSGParticlePainter*> painters;
+    QVector<QSGParticleData*> data;
 };
 
 class QSGParticleSystem : public QSGItem
@@ -131,20 +135,20 @@ protected:
     void componentComplete();
 
 private slots:
-    void countChanged();
+    void emittersChanged();
+    void loadPainter(QObject* p);
 public://but only really for related class usage. Perhaps we should all be friends?
     void emitParticle(QSGParticleData* p);
     QSGParticleData* newDatum(int groupId);
     qint64 systemSync(QSGParticlePainter* p);
     QElapsedTimer m_timestamp;
-    QVector<QSGParticleData*> m_data;
     QSet<QSGParticleData*> m_needsReset;
     QHash<QString, int> m_groupIds;
-    QHash<int, GroupData*> m_groupData;//id, size, start
+    QHash<int, QSGParticleGroupData*> m_groupData;
     qint64 m_timeInt;
     bool m_initialized;
 
-    void registerParticleType(QSGParticlePainter* p);
+    void registerParticlePainter(QSGParticlePainter* p);
     void registerParticleEmitter(QSGParticleEmitter* e);
     void registerParticleAffector(QSGParticleAffector* a);
     bool overwrite() const
@@ -158,12 +162,15 @@ private:
     bool m_running;
     QList<QPointer<QSGParticleEmitter> > m_emitters;
     QList<QPointer<QSGParticleAffector> > m_affectors;
-    QList<QPointer<QSGParticlePainter> > m_particles;
+    QList<QPointer<QSGParticlePainter> > m_particlePainters;
     QList<QPointer<QSGParticlePainter> > m_syncList;
     qint64 m_startTime;
     int m_nextGroupId;
     bool m_overwrite;
     bool m_componentComplete;
+
+    QSignalMapper m_painterMapper;
+    QSignalMapper m_emitterMapper;
 };
 
 //TODO: Clean up all this into ParticleData
@@ -211,10 +218,9 @@ public:
     qreal curSY() const;
 
     int group;
-    QSGParticleEmitter* e;
+    QSGParticleEmitter* e;//### Needed?
     QSGParticleSystem* system;
-    int particleIndex;
-    int systemIndex;
+    int index;
 
     void debugDump();
     bool stillAlive();
index 8dc98ae..c97bfd1 100644 (file)
@@ -81,7 +81,7 @@ bool QSGSpriteGoalAffector::affectParticle(QSGParticleData *d, qreal dt)
     Q_UNUSED(dt);
     //TODO: Affect all engines
     QSGSpriteEngine *engine = 0;
-    foreach(QSGParticlePainter *p, m_system->m_groupData[d->group]->types)
+    foreach(QSGParticlePainter *p, m_system->m_groupData[d->group]->painters)
         if(qobject_cast<QSGImageParticle*>(p))
             engine = qobject_cast<QSGImageParticle*>(p)->spriteEngine();
     if(!engine)
@@ -89,8 +89,8 @@ bool QSGSpriteGoalAffector::affectParticle(QSGParticleData *d, qreal dt)
 
     if(m_goalIdx == -2 || engine != m_lastEngine)
         updateStateIndex(engine);
-    if(engine->spriteState(d->particleIndex) != m_goalIdx){
-        engine->setGoal(m_goalIdx, d->particleIndex, m_jump);
+    if(engine->spriteState(d->index) != m_goalIdx){
+        engine->setGoal(m_goalIdx, d->index, m_jump);
         emit affected(QPointF(d->curX(), d->curY()));//###Expensive if unconnected? Move to Affector?
         return true; //Doesn't affect particle data, but necessary for onceOff
     }
index 476db9c..bb46b99 100644 (file)
@@ -128,30 +128,34 @@ void QSGTurbulenceAffector::affectSystem(qreal dt)
         m_lastT += period;
     }
 
-    foreach(QSGParticleData *d, m_system->m_data){
-        if(!d || !activeGroup(d->group))
+    foreach(QSGParticleGroupData *gd, m_system->m_groupData){
+        if(!activeGroup(m_system->m_groupData.key(gd)))//TODO: Surely this can be done better
             return;
-        qreal fx = 0.0;
-        qreal fy = 0.0;
-        QPointF pos = QPointF(d->curX() - x(), d->curY() - y());//TODO: Offset
-        QPointF nodePos = QPointF(pos.x() / m_spacing.x(), pos.y() / m_spacing.y());
-        QSet<QPair<int, int> > nodes;
-        nodes << qMakePair((int)ceil(nodePos.x()), (int)ceil(nodePos.y()));
-        nodes << qMakePair((int)ceil(nodePos.x()), (int)floor(nodePos.y()));
-        nodes << qMakePair((int)floor(nodePos.x()), (int)ceil(nodePos.y()));
-        nodes << qMakePair((int)floor(nodePos.x()), (int)floor(nodePos.y()));
-        typedef QPair<int, int> intPair;
-        foreach(const intPair &p, nodes){
-            if(!QRect(0,0,m_gridSize-1,m_gridSize-1).contains(QPoint(p.first, p.second)))
-                continue;
-            qreal dist = magnitude(pos.x() - p.first*m_spacing.x(), pos.y() - p.second*m_spacing.y());//TODO: Mathematically valid
-            fx += m_field[p.first][p.second].x() * ((m_magSum - dist)/m_magSum);//Proportionally weight nodes
-            fy += m_field[p.first][p.second].y() * ((m_magSum - dist)/m_magSum);
-        }
-        if(fx || fy){
-            d->setInstantaneousSX(d->curSX()+ fx * dt);
-            d->setInstantaneousSY(d->curSY()+ fy * dt);
-            m_system->m_needsReset << d;
+        foreach(QSGParticleData *d, gd->data){
+            if(!d || !activeGroup(d->group))
+                return;
+            qreal fx = 0.0;
+            qreal fy = 0.0;
+            QPointF pos = QPointF(d->curX() - x(), d->curY() - y());//TODO: Offset
+            QPointF nodePos = QPointF(pos.x() / m_spacing.x(), pos.y() / m_spacing.y());
+            QSet<QPair<int, int> > nodes;
+            nodes << qMakePair((int)ceil(nodePos.x()), (int)ceil(nodePos.y()));
+            nodes << qMakePair((int)ceil(nodePos.x()), (int)floor(nodePos.y()));
+            nodes << qMakePair((int)floor(nodePos.x()), (int)ceil(nodePos.y()));
+            nodes << qMakePair((int)floor(nodePos.x()), (int)floor(nodePos.y()));
+            typedef QPair<int, int> intPair;
+            foreach(const intPair &p, nodes){
+                if(!QRect(0,0,m_gridSize-1,m_gridSize-1).contains(QPoint(p.first, p.second)))
+                    continue;
+                qreal dist = magnitude(pos.x() - p.first*m_spacing.x(), pos.y() - p.second*m_spacing.y());//TODO: Mathematically valid
+                fx += m_field[p.first][p.second].x() * ((m_magSum - dist)/m_magSum);//Proportionally weight nodes
+                fy += m_field[p.first][p.second].y() * ((m_magSum - dist)/m_magSum);
+            }
+            if(fx || fy){
+                d->setInstantaneousSX(d->curSX()+ fx * dt);
+                d->setInstantaneousSY(d->curSY()+ fy * dt);
+                m_system->m_needsReset << d;
+            }
         }
     }
 }