Add UltraParticle
authorAlan Alpert <alan.alpert@nokia.com>
Wed, 18 May 2011 11:10:00 +0000 (21:10 +1000)
committerAlan Alpert <alan.alpert@nokia.com>
Wed, 18 May 2011 11:10:00 +0000 (21:10 +1000)
They're not as cool as they sound. Includes example, and the now
pointless SuperParticle (for possible performance comparisions).

15 files changed:
examples/declarative/particles/allsmiles/content/squarefacewhite.png [new file with mode: 0644]
examples/declarative/particles/allsmiles/content/squarefacewhiteX.png [new file with mode: 0644]
examples/declarative/particles/allsmiles/content/squarefacewhiteXX.png [new file with mode: 0644]
examples/declarative/particles/allsmiles/ultraparticles.qml [new file with mode: 0644]
src/imports/particles/main.cpp
src/imports/particles/particles.pro
src/imports/particles/resources/superfragment.shader [new file with mode: 0644]
src/imports/particles/resources/supervertex.shader [new file with mode: 0644]
src/imports/particles/resources/ultrafragment.shader [new file with mode: 0644]
src/imports/particles/resources/ultravertex.shader [new file with mode: 0644]
src/imports/particles/spriteparticles.qrc
src/imports/particles/superparticle.cpp [new file with mode: 0644]
src/imports/particles/superparticle.h [new file with mode: 0644]
src/imports/particles/ultraparticle.cpp [new file with mode: 0644]
src/imports/particles/ultraparticle.h [new file with mode: 0644]

diff --git a/examples/declarative/particles/allsmiles/content/squarefacewhite.png b/examples/declarative/particles/allsmiles/content/squarefacewhite.png
new file mode 100644 (file)
index 0000000..02259c5
Binary files /dev/null and b/examples/declarative/particles/allsmiles/content/squarefacewhite.png differ
diff --git a/examples/declarative/particles/allsmiles/content/squarefacewhiteX.png b/examples/declarative/particles/allsmiles/content/squarefacewhiteX.png
new file mode 100644 (file)
index 0000000..59af205
Binary files /dev/null and b/examples/declarative/particles/allsmiles/content/squarefacewhiteX.png differ
diff --git a/examples/declarative/particles/allsmiles/content/squarefacewhiteXX.png b/examples/declarative/particles/allsmiles/content/squarefacewhiteXX.png
new file mode 100644 (file)
index 0000000..b0f15c6
Binary files /dev/null and b/examples/declarative/particles/allsmiles/content/squarefacewhiteXX.png differ
diff --git a/examples/declarative/particles/allsmiles/ultraparticles.qml b/examples/declarative/particles/allsmiles/ultraparticles.qml
new file mode 100644 (file)
index 0000000..85bbdba
--- /dev/null
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+**     the names of its contributors may be used to endorse or promote
+**     products derived from this software without specific prior written
+**     permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import Qt.labs.particles 2.0
+
+Rectangle{
+    color: "white"
+    width: 640
+    height: 480
+    ParticleSystem{ 
+        id: sys 
+    }
+    UltraParticle{
+        sprites: [
+            Sprite{
+                name: "licking"
+                source: "content/squarefacewhite.png"
+                frames: 6
+                duration: 120
+                to: {"dying":1, "licking":5}
+            },
+            Sprite{
+                name: "dying"
+                source: "content/squarefacewhiteX.png"
+                frames: 4
+                duration: 120
+                to: {"dead":1}
+            },
+            Sprite{
+                name: "dead"
+                source: "content/squarefacewhiteXX.png"
+                frames: 1
+                duration: 120
+            }
+        ]
+        colorVariation: 0.5
+        rotationSpeedVariation: 360
+        system: sys
+        colorTable: "../trails/content/colortable.png"
+    }
+    Friction{
+        factor: 0.1
+        system: sys
+    }
+    TrailEmitter{
+        system: sys
+        anchors.centerIn: parent
+        id: particles
+        particlesPerSecond: 200
+        particleDuration: 6000
+        emitting: true
+        speed: AngleVector{angleVariation: 360; magnitude: 80; magnitudeVariation: 40}
+        particleSize: 40
+        particleEndSize: 80
+    }
+    Text{
+        x: 16
+        y: 16
+        text: "QML..."
+        style: Text.Outline; styleColor: "#AAAAAA"
+        font.pixelSize: 32
+    }
+    Text{
+        anchors.bottom: parent.bottom
+        anchors.right: parent.right
+        anchors.margins: 16
+        text: "... can you be trusted with the power?"
+        style: Text.Outline; styleColor: "#AAAAAA"
+        font.pixelSize: 32
+    }
+}
index b2d5c27..9382f48 100644 (file)
@@ -70,6 +70,8 @@
 #include "coloredparticle.h"
 #include "spriteparticle.h"
 #include "modelparticle.h"
+#include "superparticle.h"
+#include "ultraparticle.h"
 //#include "pairedparticle.h"
 #include "spriteimage.h"
 #include "followemitter.h"
@@ -108,6 +110,8 @@ void ParticlesPlugin::registerTypes(const char *uri)
     qmlRegisterType<ModelParticle>(uri, 2, 0, "ModelParticle");
     //qmlRegisterType<PairedParticle>(uri, 2, 0, "PairedParticle");
     qmlRegisterType<DeformableParticle>(uri, 2, 0, "DeformableParticle");
+    qmlRegisterType<SuperParticle>(uri, 2, 0, "SuperParticle");
+    qmlRegisterType<UltraParticle>(uri, 2, 0, "UltraParticle");
 
     qmlRegisterType<ParticleEmitter>(uri, 2, 0, "ParticleEmitter");
     qmlRegisterType<TrailsEmitter>(uri, 2, 0, "TrailEmitter");
index 56474b4..74820e1 100644 (file)
@@ -46,9 +46,9 @@ HEADERS += \
     lineextruder.h \
     resetaffector.h \
     deformableparticle.h \
-    pictureaffector.h
-
-QT += core-private gui-private declarative-private script-private
+    pictureaffector.h \
+    superparticle.h \
+    ultraparticle.h
 
 SOURCES += \
     V1/qdeclarativeparticles.cpp \
@@ -94,9 +94,13 @@ SOURCES += \
     lineextruder.cpp \
     resetaffector.cpp \
     deformableparticle.cpp \
-    pictureaffector.cpp
+    pictureaffector.cpp \
+    superparticle.cpp \
+    ultraparticle.cpp
 
 QT += declarative opengl
+#Because we use QDeclarativePixmapCache once...
+QT += core-private gui-private declarative-private script-private 
 
 
 OTHER_FILES += \
diff --git a/src/imports/particles/resources/superfragment.shader b/src/imports/particles/resources/superfragment.shader
new file mode 100644 (file)
index 0000000..a17f584
--- /dev/null
@@ -0,0 +1,11 @@
+uniform sampler2D texture;
+uniform sampler2D colortable;
+uniform sampler2D opacitytable;
+
+varying highp vec2 fTex;
+varying lowp vec4 fColor;
+varying lowp float tt;
+
+void main() {
+    gl_FragColor = (texture2D(texture, fTex).w) * fColor * texture2D(colortable, vec2(tt, 0.5)) *( texture2D(opacitytable, vec2(tt, 0.5)).w);
+}
diff --git a/src/imports/particles/resources/supervertex.shader b/src/imports/particles/resources/supervertex.shader
new file mode 100644 (file)
index 0000000..432a23c
--- /dev/null
@@ -0,0 +1,57 @@
+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 lowp vec4 vColor;
+attribute highp vec4 vDeformVec; //x,y x unit vector; z,w = y unit vector
+attribute highp vec3 vRotation; //x = radians of rotation, y=rotation speed, z= bool autoRotate
+
+uniform highp mat4 matrix;
+uniform highp float timestamp;
+uniform sampler2D sizetable;
+uniform sampler2D opacitytable;
+
+varying highp vec2 fTex;
+varying lowp vec4 fColor;
+varying lowp float tt;
+
+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) * texture2D(sizetable, vec2(t,0.5)).w;
+
+    if (t < 0. || t > 1.)
+        currentSize = 0.;
+
+    highp vec2 pos;
+    highp float rotation = vRotation.x + vRotation.y * t * vData.y;
+    if(vRotation.z == 1.0){
+        highp vec2 curVel = vVec.zw * t * vData.y + vVec.xy;
+        rotation += atan(curVel.y, curVel.x);
+    }
+    highp vec2 trigCalcs = vec2(cos(rotation), sin(rotation));
+    highp vec2 xDeform = vDeformVec.xy * currentSize * (vTex.x-0.5);
+    highp vec2 yDeform = vDeformVec.zw * currentSize * (vTex.y-0.5);
+    highp vec2 xRotatedDeform;
+    xRotatedDeform.x = trigCalcs.x*xDeform.x - trigCalcs.y*xDeform.y;
+    xRotatedDeform.y = trigCalcs.y*xDeform.x + trigCalcs.x*xDeform.y;
+    highp vec2 yRotatedDeform;
+    yRotatedDeform.x = trigCalcs.x*yDeform.x - trigCalcs.y*yDeform.y;
+    yRotatedDeform.y = trigCalcs.y*yDeform.x + trigCalcs.x*yDeform.y;
+    pos = vPos
+          + xRotatedDeform
+          + yRotatedDeform
+          //- vec2(1,1) * currentSize * 0.5 // 'center'
+          + vVec.xy * t * vData.y         // apply speed
+          + 0.5 * vVec.zw * pow(t * vData.y, 2.); // apply acceleration
+
+    gl_Position = matrix * vec4(pos.x, pos.y, 0, 1);
+
+    fColor = vColor;
+    tt = t;
+
+}
diff --git a/src/imports/particles/resources/ultrafragment.shader b/src/imports/particles/resources/ultrafragment.shader
new file mode 100644 (file)
index 0000000..0627d0f
--- /dev/null
@@ -0,0 +1,16 @@
+uniform sampler2D texture;
+uniform sampler2D colortable;
+uniform sampler2D opacitytable;
+
+varying highp vec2 fTexA;
+varying highp vec2 fTexB;
+varying lowp float progress;
+varying lowp vec4 fColor;
+varying lowp float tt;
+
+void main() {
+    gl_FragColor = mix(texture2D(texture, fTexA), texture2D(texture, fTexB), progress)
+            * fColor
+            * texture2D(colortable, vec2(tt, 0.5))
+            *( texture2D(opacitytable, vec2(tt, 0.5)).w);
+}
diff --git a/src/imports/particles/resources/ultravertex.shader b/src/imports/particles/resources/ultravertex.shader
new file mode 100644 (file)
index 0000000..65a1a30
--- /dev/null
@@ -0,0 +1,94 @@
+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 lowp vec4 vColor;
+attribute highp vec4 vDeformVec; //x,y x unit vector; z,w = y unit vector
+attribute highp vec3 vRotation; //x = radians of rotation, y=rotation speed, z= bool autoRotate
+attribute highp vec4 vAnimData;// idx, duration, frameCount (this anim), timestamp (this anim)
+
+uniform highp mat4 matrix;
+uniform highp float timestamp;
+uniform highp float framecount; //maximum of all anims
+uniform highp float animcount;
+uniform sampler2D sizetable;
+
+varying lowp float tt;
+varying highp vec2 fTexA;
+varying highp vec2 fTexB;
+varying lowp float progress;
+varying lowp vec4 fColor;
+
+
+void main() {
+    highp float size = vData.z;
+    highp float endSize = vData.w;
+
+    highp float t = (timestamp - vData.x) / vData.y;
+
+    //Calculate frame location in texture
+    highp float frameIndex = mod((((timestamp - vAnimData.w)*1000.)/vAnimData.y),vAnimData.z);
+    progress = mod((timestamp - vAnimData.w)*1000., vAnimData.y) / vAnimData.y;
+
+    frameIndex = floor(frameIndex);
+    highp vec2 frameTex = vTex;
+    if(vTex.x == 0.)
+        frameTex.x = (frameIndex/framecount);
+    else
+        frameTex.x = 1. * ((frameIndex + 1.)/framecount);
+
+    if(vTex.y == 0.)
+        frameTex.y = (vAnimData.x/animcount);
+    else
+        frameTex.y = 1. * ((vAnimData.x + 1.)/animcount);
+
+    fTexA = frameTex;
+    //Next frame is also passed, for interpolation
+    //### Should the next anim be precalculated to allow for interpolation there?
+    if(frameIndex != vAnimData.z - 1.)//Can't do it for the last frame though, this anim may not loop
+        frameIndex = mod(frameIndex+1., vAnimData.z);
+
+    if(vTex.x == 0.)
+        frameTex.x = (frameIndex/framecount);
+    else
+        frameTex.x = 1. * ((frameIndex + 1.)/framecount);
+
+    if(vTex.y == 0.)
+        frameTex.y = (vAnimData.x/animcount);
+    else
+        frameTex.y = 1. * ((vAnimData.x + 1.)/animcount);
+    fTexB = frameTex;
+
+    highp float currentSize = mix(size, endSize, t * t) * texture2D(sizetable, vec2(t,0.5)).w;
+
+    if (t < 0. || t > 1.)
+        currentSize = 0.;
+
+    highp vec2 pos;
+    highp float rotation = vRotation.x + vRotation.y * t * vData.y;
+    if(vRotation.z == 1.0){
+        highp vec2 curVel = vVec.zw * t * vData.y + vVec.xy;
+        rotation += atan(curVel.y, curVel.x);
+    }
+    highp vec2 trigCalcs = vec2(cos(rotation), sin(rotation));
+    highp vec2 xDeform = vDeformVec.xy * currentSize * (vTex.x-0.5);
+    highp vec2 yDeform = vDeformVec.zw * currentSize * (vTex.y-0.5);
+    highp vec2 xRotatedDeform;
+    xRotatedDeform.x = trigCalcs.x*xDeform.x - trigCalcs.y*xDeform.y;
+    xRotatedDeform.y = trigCalcs.y*xDeform.x + trigCalcs.x*xDeform.y;
+    highp vec2 yRotatedDeform;
+    yRotatedDeform.x = trigCalcs.x*yDeform.x - trigCalcs.y*yDeform.y;
+    yRotatedDeform.y = trigCalcs.y*yDeform.x + trigCalcs.x*yDeform.y;
+    pos = vPos
+          + xRotatedDeform
+          + yRotatedDeform
+          //- vec2(1,1) * currentSize * 0.5 // 'center'
+          + vVec.xy * t * vData.y         // apply speed
+          + 0.5 * vVec.zw * pow(t * vData.y, 2.); // apply acceleration
+
+    gl_Position = matrix * vec4(pos.x, pos.y, 0, 1);
+
+    fColor = vColor;
+    tt = t;
+
+}
index c0c7a52..b1ebc27 100644 (file)
@@ -12,5 +12,9 @@
         <file>resources/defaultFadeInOut.png</file>
         <file>resources/deformablefragment.shader</file>
         <file>resources/deformablevertex.shader</file>
+        <file>resources/ultravertex.shader</file>
+        <file>resources/ultrafragment.shader</file>
+        <file>resources/supervertex.shader</file>
+        <file>resources/superfragment.shader</file>
     </qresource>
 </RCC>
diff --git a/src/imports/particles/superparticle.cpp b/src/imports/particles/superparticle.cpp
new file mode 100644 (file)
index 0000000..811b6a4
--- /dev/null
@@ -0,0 +1,511 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qsgcontext_p.h>
+#include <private/qsgadaptationlayer_p.h>
+#include <qsgnode.h>
+#include <qsgtexturematerial.h>
+#include <qsgtexture.h>
+#include <QFile>
+#include "superparticle.h"
+#include "particleemitter.h"
+#include <QGLFunctions>
+#include <qsgengine.h>
+
+QT_BEGIN_NAMESPACE
+
+const float CONV = 0.017453292519943295;
+class SuperMaterial : public QSGMaterial
+{
+public:
+    SuperMaterial()
+        : timestamp(0)
+    {
+        setFlag(Blending, true);
+    }
+
+    ~SuperMaterial()
+    {
+        delete texture;
+        delete colortable;
+        delete sizetable;
+        delete opacitytable;
+    }
+
+    virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; }
+    virtual QSGMaterialShader *createShader() const;
+    virtual int compare(const QSGMaterial *other) const
+    {
+        return this - static_cast<const SuperMaterial *>(other);
+    }
+
+    QSGTexture *texture;
+    QSGTexture *colortable;
+    QSGTexture *sizetable;
+    QSGTexture *opacitytable;
+
+    qreal timestamp;
+};
+
+
+class SuperMaterialData : public QSGMaterialShader
+{
+public:
+    SuperMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0)
+    {
+        QFile vf(vertexFile ? vertexFile : ":resources/supervertex.shader");
+        vf.open(QFile::ReadOnly);
+        m_vertex_code = vf.readAll();
+
+        QFile ff(fragmentFile ? fragmentFile : ":resources/superfragment.shader");
+        ff.open(QFile::ReadOnly);
+        m_fragment_code = ff.readAll();
+
+        Q_ASSERT(!m_vertex_code.isNull());
+        Q_ASSERT(!m_fragment_code.isNull());
+    }
+
+    void deactivate() {
+        QSGMaterialShader::deactivate();
+
+        for (int i=0; i<8; ++i) {
+            program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0);
+        }
+    }
+
+    virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *)
+    {
+        SuperMaterial *m = static_cast<SuperMaterial *>(newEffect);
+        state.context()->functions()->glActiveTexture(GL_TEXTURE0);
+        m->texture->bind();
+
+        state.context()->functions()->glActiveTexture(GL_TEXTURE1);
+        m->colortable->bind();
+        program()->setUniformValue(m_colortable_id, 1);
+
+        state.context()->functions()->glActiveTexture(GL_TEXTURE2);
+        m->sizetable->bind();
+        program()->setUniformValue(m_sizetable_id, 2);
+
+        state.context()->functions()->glActiveTexture(GL_TEXTURE3);
+        m->opacitytable->bind();
+        program()->setUniformValue(m_opacitytable_id, 3);
+
+        program()->setUniformValue(m_opacity_id, state.opacity());
+        program()->setUniformValue(m_timestamp_id, (float) m->timestamp);
+
+        if (state.isMatrixDirty())
+            program()->setUniformValue(m_matrix_id, state.combinedMatrix());
+    }
+
+    virtual void initialize() {
+        m_colortable_id = program()->uniformLocation("colortable");
+        m_sizetable_id = program()->uniformLocation("sizetable");
+        m_opacitytable_id = program()->uniformLocation("opacitytable");
+        m_matrix_id = program()->uniformLocation("matrix");
+        m_opacity_id = program()->uniformLocation("opacity");
+        m_timestamp_id = program()->uniformLocation("timestamp");
+    }
+
+    virtual const char *vertexShader() const { return m_vertex_code.constData(); }
+    virtual const char *fragmentShader() const { return m_fragment_code.constData(); }
+
+    virtual char const *const *attributeNames() const {
+        static const char *attr[] = {
+            "vPos",
+            "vTex",
+            "vData",
+            "vVec",
+            "vColor",
+            "vDeformVec",
+            "vRotation",
+            0
+        };
+        return attr;
+    }
+
+    virtual bool isColorTable() const { return false; }
+
+    int m_matrix_id;
+    int m_opacity_id;
+    int m_timestamp_id;
+    int m_colortable_id;
+    int m_sizetable_id;
+    int m_opacitytable_id;
+
+    QByteArray m_vertex_code;
+    QByteArray m_fragment_code;
+
+    static float chunkOfBytes[1024];
+};
+float SuperMaterialData::chunkOfBytes[1024];
+
+
+QSGMaterialShader *SuperMaterial::createShader() const
+{
+    return new SuperMaterialData;
+}
+
+SuperParticle::SuperParticle(QSGItem* parent)
+    : ParticleType(parent)
+    , m_do_reset(false)
+    , m_color(Qt::white)
+    , m_color_variation(0.5)
+    , m_node(0)
+    , m_material(0)
+    , m_alphaVariation(0.0)
+    , m_alpha(1.0)
+    , m_redVariation(0.0)
+    , m_greenVariation(0.0)
+    , m_blueVariation(0.0)
+{
+    setFlag(ItemHasContents);
+}
+
+void SuperParticle::setImage(const QUrl &image)
+{
+    if (image == m_image_name)
+        return;
+    m_image_name = image;
+    emit imageChanged();
+    reset();
+}
+
+
+void SuperParticle::setColortable(const QUrl &table)
+{
+    if (table == m_colortable_name)
+        return;
+    m_colortable_name = table;
+    emit colortableChanged();
+    reset();
+}
+
+void SuperParticle::setSizetable(const QUrl &table)
+{
+    if (table == m_sizetable_name)
+        return;
+    m_sizetable_name = table;
+    emit sizetableChanged();
+    reset();
+}
+
+void SuperParticle::setOpacitytable(const QUrl &table)
+{
+    if (table == m_opacitytable_name)
+        return;
+    m_opacitytable_name = table;
+    emit opacitytableChanged();
+    reset();
+}
+
+void SuperParticle::setColor(const QColor &color)
+{
+    if (color == m_color)
+        return;
+    m_color = color;
+    emit colorChanged();
+    //m_system->pleaseReset();//XXX
+}
+
+void SuperParticle::setColorVariation(qreal var)
+{
+    if (var == m_color_variation)
+        return;
+    m_color_variation = var;
+    emit colorVariationChanged();
+    //m_system->pleaseReset();//XXX
+}
+
+void SuperParticle::setCount(int c)
+{
+    ParticleType::setCount(c);
+    m_pleaseReset = true;
+}
+
+void SuperParticle::reset()
+{
+    ParticleType::reset();
+     m_pleaseReset = true;
+}
+
+static QSGGeometry::Attribute SuperParticle_Attributes[] = {
+    { 0, 2, GL_FLOAT },             // Position
+    { 1, 2, GL_FLOAT },             // TexCoord
+    { 2, 4, GL_FLOAT },             // Data
+    { 3, 4, GL_FLOAT },             // Vectors
+    { 4, 4, GL_UNSIGNED_BYTE },     // Colors
+    { 5, 4, GL_FLOAT },             // DeformationVectors
+    { 6, 3, GL_FLOAT }              // Rotation
+};
+
+static QSGGeometry::AttributeSet SuperParticle_AttributeSet =
+{
+    7, // Attribute Count
+    (2 + 2 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar),
+    SuperParticle_Attributes
+};
+
+QSGGeometryNode* SuperParticle::buildParticleNode()
+{
+    if (m_count * 4 > 0xffff) {
+        printf("SuperParticle: Too many particles... \n");
+        return 0;
+    }
+
+    if(m_count <= 0) {
+        printf("SuperParticle: Too few particles... \n");
+        return 0;
+    }
+
+    QImage image(m_image_name.toLocalFile());
+    if (image.isNull()) {
+        printf("SuperParticle: loading image failed... '%s'\n", qPrintable(m_image_name.toLocalFile()));
+        return 0;
+    }
+
+    int vCount = m_count * 4;
+    int iCount = m_count * 6;
+
+    QSGGeometry *g = new QSGGeometry(SuperParticle_AttributeSet, vCount, iCount);
+    g->setDrawingMode(GL_TRIANGLES);
+
+    SuperVertex *vertices = (SuperVertex *) 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[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[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();
+    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;
+    }
+
+    if (m_material) {
+        delete m_material;
+        m_material = 0;
+    }
+
+    QImage colortable(m_colortable_name.toLocalFile());
+    QImage sizetable(m_sizetable_name.toLocalFile());
+    QImage opacitytable(m_opacitytable_name.toLocalFile());
+    m_material = new SuperMaterial();
+    if(colortable.isNull())
+        colortable = QImage(":resources/identitytable.png");
+    if(sizetable.isNull())
+        sizetable = QImage(":resources/identitytable.png");
+    if(opacitytable.isNull())
+        opacitytable = QImage(":resources/defaultFadeInOut.png");
+    Q_ASSERT(!colortable.isNull());
+    Q_ASSERT(!sizetable.isNull());
+    Q_ASSERT(!opacitytable.isNull());
+    m_material->colortable = sceneGraphEngine()->createTextureFromImage(colortable);
+    m_material->sizetable = sceneGraphEngine()->createTextureFromImage(sizetable);
+    m_material->opacitytable = sceneGraphEngine()->createTextureFromImage(opacitytable);
+
+    m_material->texture = sceneGraphEngine()->createTextureFromImage(image);
+    m_material->texture->setFiltering(QSGTexture::Linear);
+
+    m_node = new QSGGeometryNode();
+    m_node->setGeometry(g);
+    m_node->setMaterial(m_material);
+
+    m_last_particle = 0;
+
+    return m_node;
+}
+
+QSGNode *SuperParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
+{
+    if(m_pleaseReset){
+        if(m_node)
+            delete m_node;
+        if(m_material)
+            delete m_material;
+
+        m_node = 0;
+        m_material = 0;
+        m_pleaseReset = false;
+    }
+
+    if(m_system && m_system->isRunning())
+        prepareNextFrame();
+    if (m_node){
+        update();
+        m_node->markDirty(QSGNode::DirtyMaterial);
+    }
+
+    return m_node;
+}
+
+void SuperParticle::prepareNextFrame()
+{
+    if (m_node == 0){    //TODO: Staggered loading (as emitted)
+        m_node = buildParticleNode();
+        if(m_node == 0)
+            return;
+    }
+    qint64 timeStamp = m_system->systemSync(this);
+
+    qreal time = timeStamp / 1000.;
+    m_material->timestamp = time;
+}
+
+void SuperParticle::reloadColor(const Color4ub &c, ParticleData* d)
+{
+    SuperVertices *particles = (SuperVertices *) m_node->geometry()->vertexData();
+    int pos = particleTypeIndex(d);
+    SuperVertices &p = particles[pos];
+    p.v1.color = p.v2.color = p.v3.color = p.v4.color = c;
+}
+
+void SuperParticle::vertexCopy(SuperVertex &b,const ParticleVertex& a)
+{
+    b.x = a.x - m_systemOffset.x();
+    b.y = a.y - m_systemOffset.y();
+    b.t = a.t;
+    b.lifeSpan = a.lifeSpan;
+    b.size = a.size;
+    b.endSize = a.endSize;
+    b.sx = a.sx;
+    b.sy = a.sy;
+    b.ax = a.ax;
+    b.ay = a.ay;
+}
+
+void SuperParticle::reload(ParticleData *d)
+{
+    if (m_node == 0)
+        return;
+
+    SuperVertices *particles = (SuperVertices *) m_node->geometry()->vertexData();
+
+    int pos = particleTypeIndex(d);
+
+    SuperVertices &p = particles[pos];
+
+    //Perhaps we could be more efficient?
+    vertexCopy(p.v1, d->pv);
+    vertexCopy(p.v2, d->pv);
+    vertexCopy(p.v3, d->pv);
+    vertexCopy(p.v4, d->pv);
+}
+
+void SuperParticle::load(ParticleData *d)
+{
+    if (m_node == 0)
+        return;
+
+    //Color initialization
+    // Particle color
+    Color4ub color;
+    qreal redVariation = m_color_variation + m_redVariation;
+    qreal greenVariation = m_color_variation + m_greenVariation;
+    qreal blueVariation = m_color_variation + m_blueVariation;
+    color.r = m_color.red() * (1 - redVariation) + rand() % 256 * redVariation;
+    color.g = m_color.green() * (1 - greenVariation) + rand() % 256 * greenVariation;
+    color.b = m_color.blue() * (1 - blueVariation) + rand() % 256 * blueVariation;
+    color.a = m_alpha * m_color.alpha() * (1 - m_alphaVariation) + rand() % 256 * m_alphaVariation;
+    SuperVertices *particles = (SuperVertices *) m_node->geometry()->vertexData();
+    SuperVertices &p = particles[particleTypeIndex(d)];
+    p.v1.color = p.v2.color = p.v3.color = p.v4.color = color;
+    if(m_xVector){
+        const QPointF &ret = m_xVector->sample(QPointF(d->pv.x, d->pv.y));
+        p.v1.xx = p.v2.xx = p.v3.xx = p.v4.xx = ret.x();
+        p.v1.xy = p.v2.xy = p.v3.xy = p.v4.xy = ret.y();
+    }
+    if(m_yVector){
+        const QPointF &ret = m_yVector->sample(QPointF(d->pv.x, d->pv.y));
+        p.v1.yx = p.v2.yx = p.v3.yx = p.v4.yx = ret.x();
+        p.v1.yy = p.v2.yy = p.v3.yy = p.v4.yy = ret.y();
+    }
+    p.v1.rotation = p.v2.rotation = p.v3.rotation = p.v4.rotation =
+            (m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV;
+    p.v1.rotationSpeed = p.v2.rotationSpeed = p.v3.rotationSpeed = p.v4.rotationSpeed =
+            (m_rotationSpeed + (m_rotationSpeedVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationSpeedVariation) ) * CONV;
+    p.v1.autoRotate = p.v2.autoRotate = p.v3.autoRotate = p.v4.autoRotate = m_autoRotation?1.0:0.0;
+
+    vertexCopy(p.v1, d->pv);
+    vertexCopy(p.v2, d->pv);
+    vertexCopy(p.v3, d->pv);
+    vertexCopy(p.v4, d->pv);
+}
+
+QT_END_NAMESPACE
diff --git a/src/imports/particles/superparticle.h b/src/imports/particles/superparticle.h
new file mode 100644 (file)
index 0000000..ac2f986
--- /dev/null
@@ -0,0 +1,389 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SUPERPARTICLE_H
+#define SUPERPARTICLE_H
+#include "particle.h"
+#include "varyingvector.h"
+
+#include "coloredparticle.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class SuperMaterial;
+class QSGGeometryNode;
+
+/*struct Color4ub {//in coloredparticle
+    uchar r;
+    uchar g;
+    uchar b;
+    uchar a;
+};*/
+
+struct SuperVertex {
+    float x;
+    float y;
+    float tx;
+    float ty;
+    float t;
+    float lifeSpan;
+    float size;
+    float endSize;
+    float sx;
+    float sy;
+    float ax;
+    float ay;
+    Color4ub color;
+    float xx;
+    float xy;
+    float yx;
+    float yy;
+    float rotation;
+    float rotationSpeed;
+    float autoRotate;//Assume that GPUs prefer floats to bools
+};
+
+struct SuperVertices {
+    SuperVertex v1;
+    SuperVertex v2;
+    SuperVertex v3;
+    SuperVertex v4;
+};
+
+class SuperParticle : public ParticleType
+{
+    Q_OBJECT
+    Q_PROPERTY(QUrl image READ image WRITE setImage NOTIFY imageChanged)
+    Q_PROPERTY(QUrl colorTable READ colortable WRITE setColortable NOTIFY colortableChanged)
+    Q_PROPERTY(QUrl sizeTable READ sizetable WRITE setSizetable NOTIFY sizetableChanged)
+    Q_PROPERTY(QUrl opacityTable READ opacitytable WRITE setOpacitytable NOTIFY opacitytableChanged)
+
+    Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
+    //Stacks (added) with individual colorVariations
+    Q_PROPERTY(qreal colorVariation READ colorVariation WRITE setColorVariation NOTIFY colorVariationChanged)
+    Q_PROPERTY(qreal redVariation READ redVariation WRITE setRedVariation NOTIFY redVariationChanged)
+    Q_PROPERTY(qreal greenVariation READ greenVariation WRITE setGreenVariation NOTIFY greenVariationChanged)
+    Q_PROPERTY(qreal blueVariation READ blueVariation WRITE setBlueVariation NOTIFY blueVariationChanged)
+    //Stacks (multiplies) with the Alpha in the color, mostly here so you can use svg color names (which have full alpha)
+    Q_PROPERTY(qreal alpha READ alpha WRITE setAlpha NOTIFY alphaChanged)
+    Q_PROPERTY(qreal alphaVariation READ alphaVariation WRITE setAlphaVariation NOTIFY alphaVariationChanged)
+
+    Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged)
+    Q_PROPERTY(qreal rotationVariation READ rotationVariation WRITE setRotationVariation NOTIFY rotationVariationChanged)
+    Q_PROPERTY(qreal rotationSpeed READ rotationSpeed WRITE setRotationSpeed NOTIFY rotationSpeedChanged)
+    Q_PROPERTY(qreal rotationSpeedVariation READ rotationSpeedVariation WRITE setRotationSpeedVariation NOTIFY rotationSpeedVariationChanged)
+    //If true, then will face the direction of motion. Stacks with rotation, e.g. setting rotation
+    //to 180 will lead to facing away from the direction of motion
+    Q_PROPERTY(bool autoRotation READ autoRotation WRITE autoRotation NOTIFY autoRotationChanged)
+
+    //###Call i/j? Makes more sense to those with vector calculus experience, and I could even add the cirumflex in QML?
+    //xVector is the vector from the top-left point to the top-right point, and is multiplied by current size
+    Q_PROPERTY(VaryingVector* xVector READ xVector WRITE setXVector NOTIFY xVectorChanged)
+    //yVector is the same, but top-left to bottom-left. The particle is always a parallelogram.
+    Q_PROPERTY(VaryingVector* yVector READ yVector WRITE setYVector NOTIFY yVectorChanged)
+public:
+    explicit SuperParticle(QSGItem *parent = 0);
+    virtual ~SuperParticle(){}
+
+    virtual void load(ParticleData*);
+    virtual void reload(ParticleData*);
+    virtual void setCount(int c);
+
+    QUrl image() const { return m_image_name; }
+    void setImage(const QUrl &image);
+
+    QUrl colortable() const { return m_colortable_name; }
+    void setColortable(const QUrl &table);
+
+    QUrl sizetable() const { return m_sizetable_name; }
+    void setSizetable (const QUrl &table);
+
+    QUrl opacitytable() const { return m_opacitytable_name; }
+    void setOpacitytable(const QUrl &table);
+
+    QColor color() const { return m_color; }
+    void setColor(const QColor &color);
+
+    qreal colorVariation() const { return m_color_variation; }
+    void setColorVariation(qreal var);
+
+    qreal renderOpacity() const { return m_render_opacity; }
+
+    qreal alphaVariation() const
+    {
+        return m_alphaVariation;
+    }
+
+    qreal alpha() const
+    {
+        return m_alpha;
+    }
+
+    qreal redVariation() const
+    {
+        return m_redVariation;
+    }
+
+    qreal greenVariation() const
+    {
+        return m_greenVariation;
+    }
+
+    qreal blueVariation() const
+    {
+        return m_blueVariation;
+    }
+
+    qreal rotation() const
+    {
+        return m_rotation;
+    }
+
+    qreal rotationVariation() const
+    {
+        return m_rotationVariation;
+    }
+
+    qreal rotationSpeed() const
+    {
+        return m_rotationSpeed;
+    }
+
+    qreal rotationSpeedVariation() const
+    {
+        return m_rotationSpeedVariation;
+    }
+
+    bool autoRotation() const
+    {
+        return m_autoRotation;
+    }
+
+    VaryingVector* xVector() const
+    {
+        return m_xVector;
+    }
+
+    VaryingVector* yVector() const
+    {
+        return m_yVector;
+    }
+
+signals:
+
+    void imageChanged();
+    void colortableChanged();
+    void sizetableChanged();
+    void opacitytableChanged();
+
+    void colorChanged();
+    void colorVariationChanged();
+
+    void particleDurationChanged();
+    void alphaVariationChanged(qreal arg);
+
+    void alphaChanged(qreal arg);
+
+    void redVariationChanged(qreal arg);
+
+    void greenVariationChanged(qreal arg);
+
+    void blueVariationChanged(qreal arg);
+
+    void rotationChanged(qreal arg);
+
+    void rotationVariationChanged(qreal arg);
+
+    void rotationSpeedChanged(qreal arg);
+
+    void rotationSpeedVariationChanged(qreal arg);
+
+    void autoRotationChanged(bool arg);
+
+    void xVectorChanged(VaryingVector* arg);
+
+    void yVectorChanged(VaryingVector* arg);
+
+public slots:
+    void setAlphaVariation(qreal arg)
+    {
+        if (m_alphaVariation != arg) {
+            m_alphaVariation = arg;
+            emit alphaVariationChanged(arg);
+        }
+    }
+
+    void setAlpha(qreal arg)
+    {
+        if (m_alpha != arg) {
+            m_alpha = arg;
+            emit alphaChanged(arg);
+        }
+    }
+
+    void setRedVariation(qreal arg)
+    {
+        if (m_redVariation != arg) {
+            m_redVariation = arg;
+            emit redVariationChanged(arg);
+        }
+    }
+
+    void setGreenVariation(qreal arg)
+    {
+        if (m_greenVariation != arg) {
+            m_greenVariation = arg;
+            emit greenVariationChanged(arg);
+        }
+    }
+
+    void setBlueVariation(qreal arg)
+    {
+        if (m_blueVariation != arg) {
+            m_blueVariation = arg;
+            emit blueVariationChanged(arg);
+        }
+    }
+
+    void reloadColor(const Color4ub &c, ParticleData* d);
+    void setRotation(qreal arg)
+    {
+        if (m_rotation != arg) {
+            m_rotation = arg;
+            emit rotationChanged(arg);
+        }
+    }
+
+    void setRotationVariation(qreal arg)
+    {
+        if (m_rotationVariation != arg) {
+            m_rotationVariation = arg;
+            emit rotationVariationChanged(arg);
+        }
+    }
+
+    void setRotationSpeed(qreal arg)
+    {
+        if (m_rotationSpeed != arg) {
+            m_rotationSpeed = arg;
+            emit rotationSpeedChanged(arg);
+        }
+    }
+
+    void setRotationSpeedVariation(qreal arg)
+    {
+        if (m_rotationSpeedVariation != arg) {
+            m_rotationSpeedVariation = arg;
+            emit rotationSpeedVariationChanged(arg);
+        }
+    }
+
+    void autoRotation(bool arg)
+    {
+        if (m_autoRotation != arg) {
+            m_autoRotation = arg;
+            emit autoRotationChanged(arg);
+        }
+    }
+
+    void setXVector(VaryingVector* arg)
+    {
+        if (m_xVector != arg) {
+            m_xVector = arg;
+            emit xVectorChanged(arg);
+        }
+    }
+
+    void setYVector(VaryingVector* arg)
+    {
+        if (m_yVector != arg) {
+            m_yVector = arg;
+            emit yVectorChanged(arg);
+        }
+    }
+
+protected:
+    QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+    void reset();
+    void prepareNextFrame();
+    QSGGeometryNode* buildParticleNode();
+private:
+    void vertexCopy(SuperVertex &b,const ParticleVertex& a);
+    bool m_do_reset;
+
+    QUrl m_image_name;
+    QUrl m_colortable_name;
+    QUrl m_sizetable_name;
+    QUrl m_opacitytable_name;
+
+
+    QColor m_color;
+    qreal m_color_variation;
+    qreal m_particleDuration;
+
+    QSGGeometryNode *m_node;
+    SuperMaterial *m_material;
+
+    // derived values...
+    int m_last_particle;
+
+    qreal m_render_opacity;
+    qreal m_alphaVariation;
+    qreal m_alpha;
+    qreal m_redVariation;
+    qreal m_greenVariation;
+    qreal m_blueVariation;
+    qreal m_rotation;
+    qreal m_rotationVariation;
+    qreal m_rotationSpeed;
+    qreal m_rotationSpeedVariation;
+    bool m_autoRotation;
+    VaryingVector* m_xVector;
+    VaryingVector* m_yVector;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // SUPERPARTICLE_H
diff --git a/src/imports/particles/ultraparticle.cpp b/src/imports/particles/ultraparticle.cpp
new file mode 100644 (file)
index 0000000..6dd05aa
--- /dev/null
@@ -0,0 +1,603 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qsgcontext_p.h>
+#include <private/qsgadaptationlayer_p.h>
+#include <qsgnode.h>
+#include <qsgtexturematerial.h>
+#include <qsgtexture.h>
+#include <QFile>
+#include "ultraparticle.h"
+#include "particleemitter.h"
+#include "spritestate.h"
+#include "spriteengine.h"
+#include <QGLFunctions>
+#include <qsgengine.h>
+
+QT_BEGIN_NAMESPACE
+
+const float CONV = 0.017453292519943295;
+class UltraMaterial : public QSGMaterial
+{
+public:
+    UltraMaterial()
+        : timestamp(0)
+        , framecount(1)
+        , animcount(1)
+    {
+        setFlag(Blending, true);
+    }
+
+    ~UltraMaterial()
+    {
+        delete texture;
+        delete colortable;
+        delete sizetable;
+        delete opacitytable;
+    }
+
+    virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; }
+    virtual QSGMaterialShader *createShader() const;
+    virtual int compare(const QSGMaterial *other) const
+    {
+        return this - static_cast<const UltraMaterial *>(other);
+    }
+
+    QSGTexture *texture;
+    QSGTexture *colortable;
+    QSGTexture *sizetable;
+    QSGTexture *opacitytable;
+
+    qreal timestamp;
+    int framecount;
+    int animcount;
+};
+
+
+class UltraMaterialData : public QSGMaterialShader
+{
+public:
+    UltraMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0)
+    {
+        QFile vf(vertexFile ? vertexFile : ":resources/ultravertex.shader");
+        vf.open(QFile::ReadOnly);
+        m_vertex_code = vf.readAll();
+
+        QFile ff(fragmentFile ? fragmentFile : ":resources/ultrafragment.shader");
+        ff.open(QFile::ReadOnly);
+        m_fragment_code = ff.readAll();
+
+        Q_ASSERT(!m_vertex_code.isNull());
+        Q_ASSERT(!m_fragment_code.isNull());
+    }
+
+    void deactivate() {
+        QSGMaterialShader::deactivate();
+
+        for (int i=0; i<8; ++i) {
+            program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0);
+        }
+    }
+
+    virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *)
+    {
+        UltraMaterial *m = static_cast<UltraMaterial *>(newEffect);
+        state.context()->functions()->glActiveTexture(GL_TEXTURE1);
+        m->colortable->bind();
+        program()->setUniformValue(m_colortable_id, 1);
+
+        state.context()->functions()->glActiveTexture(GL_TEXTURE2);
+        m->sizetable->bind();
+        program()->setUniformValue(m_sizetable_id, 2);
+
+        state.context()->functions()->glActiveTexture(GL_TEXTURE3);
+        m->opacitytable->bind();
+        program()->setUniformValue(m_opacitytable_id, 3);
+
+        state.context()->functions()->glActiveTexture(GL_TEXTURE0);//Investigate why this screws up Text{} if placed before 1
+        m->texture->bind();
+
+        program()->setUniformValue(m_opacity_id, state.opacity());
+        program()->setUniformValue(m_timestamp_id, (float) m->timestamp);
+        program()->setUniformValue(m_framecount_id, (float) m->framecount);
+        program()->setUniformValue(m_animcount_id, (float) m->animcount);
+
+        if (state.isMatrixDirty())
+            program()->setUniformValue(m_matrix_id, state.combinedMatrix());
+    }
+
+    virtual void initialize() {
+        m_colortable_id = program()->uniformLocation("colortable");
+        m_sizetable_id = program()->uniformLocation("sizetable");
+        m_opacitytable_id = program()->uniformLocation("opacitytable");
+        m_matrix_id = program()->uniformLocation("matrix");
+        m_opacity_id = program()->uniformLocation("opacity");
+        m_timestamp_id = program()->uniformLocation("timestamp");
+        m_framecount_id = program()->uniformLocation("framecount");
+        m_animcount_id = program()->uniformLocation("animcount");
+    }
+
+    virtual const char *vertexShader() const { return m_vertex_code.constData(); }
+    virtual const char *fragmentShader() const { return m_fragment_code.constData(); }
+
+    virtual char const *const *attributeNames() const {
+        static const char *attr[] = {
+            "vPos",
+            "vTex",
+            "vData",
+            "vVec",
+            "vColor",
+            "vDeformVec",
+            "vRotation",
+            "vAnimData",
+            0
+        };
+        return attr;
+    }
+
+    virtual bool isColorTable() const { return false; }
+
+    int m_matrix_id;
+    int m_opacity_id;
+    int m_timestamp_id;
+    int m_colortable_id;
+    int m_sizetable_id;
+    int m_opacitytable_id;
+    int m_framecount_id;
+    int m_animcount_id;
+
+    QByteArray m_vertex_code;
+    QByteArray m_fragment_code;
+
+    static float chunkOfBytes[1024];
+};
+float UltraMaterialData::chunkOfBytes[1024];
+
+
+QSGMaterialShader *UltraMaterial::createShader() const
+{
+    return new UltraMaterialData;
+}
+
+UltraParticle::UltraParticle(QSGItem* parent)
+    : ParticleType(parent)
+    , m_do_reset(false)
+    , m_color(Qt::white)
+    , m_color_variation(0.5)
+    , m_node(0)
+    , m_material(0)
+    , m_alphaVariation(0.0)
+    , m_alpha(1.0)
+    , m_redVariation(0.0)
+    , m_greenVariation(0.0)
+    , m_blueVariation(0.0)
+    , m_rotation(0)
+    , m_autoRotation(false)
+    , m_xVector(0)
+    , m_yVector(0)
+    , m_rotationVariation(0)
+    , m_rotationSpeed(0)
+    , m_rotationSpeedVariation(0)
+    , m_spriteEngine(0)
+{
+    setFlag(ItemHasContents);
+}
+
+QDeclarativeListProperty<SpriteState> UltraParticle::sprites()
+{
+    return QDeclarativeListProperty<SpriteState>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
+}
+
+void UltraParticle::setImage(const QUrl &image)
+{
+    if (image == m_image_name)
+        return;
+    m_image_name = image;
+    emit imageChanged();
+    reset();
+}
+
+
+void UltraParticle::setColortable(const QUrl &table)
+{
+    if (table == m_colortable_name)
+        return;
+    m_colortable_name = table;
+    emit colortableChanged();
+    reset();
+}
+
+void UltraParticle::setSizetable(const QUrl &table)
+{
+    if (table == m_sizetable_name)
+        return;
+    m_sizetable_name = table;
+    emit sizetableChanged();
+    reset();
+}
+
+void UltraParticle::setOpacitytable(const QUrl &table)
+{
+    if (table == m_opacitytable_name)
+        return;
+    m_opacitytable_name = table;
+    emit opacitytableChanged();
+    reset();
+}
+
+void UltraParticle::setColor(const QColor &color)
+{
+    if (color == m_color)
+        return;
+    m_color = color;
+    emit colorChanged();
+    //m_system->pleaseReset();//XXX
+}
+
+void UltraParticle::setColorVariation(qreal var)
+{
+    if (var == m_color_variation)
+        return;
+    m_color_variation = var;
+    emit colorVariationChanged();
+    //m_system->pleaseReset();//XXX
+}
+
+void UltraParticle::setCount(int c)
+{
+    ParticleType::setCount(c);
+    m_pleaseReset = true;
+}
+
+void UltraParticle::reset()
+{
+    ParticleType::reset();
+     m_pleaseReset = true;
+}
+
+void UltraParticle::createEngine()
+{
+    if(m_spriteEngine)
+        delete m_spriteEngine;
+    if(m_sprites.count())
+        m_spriteEngine = new SpriteEngine(m_sprites, this);
+    else
+        m_spriteEngine = 0;
+    reset();//###this is probably out of updatePaintNode and shouldn't be
+}
+
+static QSGGeometry::Attribute UltraParticle_Attributes[] = {
+    { 0, 2, GL_FLOAT },             // Position
+    { 1, 2, GL_FLOAT },             // TexCoord
+    { 2, 4, GL_FLOAT },             // Data
+    { 3, 4, GL_FLOAT },             // Vectors
+    { 4, 4, GL_UNSIGNED_BYTE },     // Colors
+    { 5, 4, GL_FLOAT },             // DeformationVectors
+    { 6, 3, GL_FLOAT },             // Rotation
+    { 7, 4, GL_FLOAT }              // Anim Data
+};
+
+static QSGGeometry::AttributeSet UltraParticle_AttributeSet =
+{
+    8, // Attribute Count
+    (2 + 2 + 4 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar),
+    UltraParticle_Attributes
+};
+
+QSGGeometryNode* UltraParticle::buildParticleNode()
+{
+    if (m_count * 4 > 0xffff) {
+        printf("UltraParticle: Too many particles... \n");
+        return 0;
+    }
+
+    if(m_count <= 0) {
+        printf("UltraParticle: Too few particles... \n");
+        return 0;
+    }
+
+    QImage image;
+    if(m_sprites.count()){
+        if (!m_spriteEngine) {
+            qWarning() << "UltraParticle: No sprite engine...";
+            return 0;
+        }
+        image = m_spriteEngine->assembledImage();
+        if(image.isNull())//Warning is printed in engine
+            return 0;
+    }else{
+        image = QImage(m_image_name.toLocalFile());
+        if (image.isNull()) {
+            printf("UltraParticle: loading image failed... '%s'\n", qPrintable(m_image_name.toLocalFile()));
+            return 0;
+        }
+    }
+
+    int vCount = m_count * 4;
+    int iCount = m_count * 6;
+
+    QSGGeometry *g = new QSGGeometry(UltraParticle_AttributeSet, vCount, iCount);
+    g->setDrawingMode(GL_TRIANGLES);
+
+    UltraVertex *vertices = (UltraVertex *) 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[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 = 1;
+            vertices[i].frameCount = 1;
+            vertices[i].animT = -1;
+        }
+
+        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();
+    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;
+    }
+
+    if (m_material) {
+        delete m_material;
+        m_material = 0;
+    }
+
+    QImage colortable(m_colortable_name.toLocalFile());
+    QImage sizetable(m_sizetable_name.toLocalFile());
+    QImage opacitytable(m_opacitytable_name.toLocalFile());
+    m_material = new UltraMaterial();
+    if(colortable.isNull())
+        colortable = QImage(":resources/identitytable.png");
+    if(sizetable.isNull())
+        sizetable = QImage(":resources/identitytable.png");
+    if(opacitytable.isNull())
+        opacitytable = QImage(":resources/defaultFadeInOut.png");
+    Q_ASSERT(!colortable.isNull());
+    Q_ASSERT(!sizetable.isNull());
+    Q_ASSERT(!opacitytable.isNull());
+    m_material->colortable = sceneGraphEngine()->createTextureFromImage(colortable);
+    m_material->sizetable = sceneGraphEngine()->createTextureFromImage(sizetable);
+    m_material->opacitytable = sceneGraphEngine()->createTextureFromImage(opacitytable);
+
+    m_material->texture = sceneGraphEngine()->createTextureFromImage(image);
+    m_material->texture->setFiltering(QSGTexture::Linear);
+
+    m_material->framecount = 1;
+    if(m_spriteEngine){
+        m_material->framecount = m_spriteEngine->maxFrames();
+        m_spriteEngine->setCount(m_count);
+    }
+
+    m_node = new QSGGeometryNode();
+    m_node->setGeometry(g);
+    m_node->setMaterial(m_material);
+
+    m_last_particle = 0;
+
+    return m_node;
+}
+
+QSGNode *UltraParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
+{
+    if(m_pleaseReset){
+        if(m_node)
+            delete m_node;
+        if(m_material)
+            delete m_material;
+
+        m_node = 0;
+        m_material = 0;
+        m_pleaseReset = false;
+    }
+
+    if(m_system && m_system->isRunning())
+        prepareNextFrame();
+    if (m_node){
+        update();
+        m_node->markDirty(QSGNode::DirtyMaterial);
+    }
+
+    return m_node;
+}
+
+void UltraParticle::prepareNextFrame()
+{
+    if (m_node == 0){    //TODO: Staggered loading (as emitted)
+        m_node = buildParticleNode();
+        if(m_node == 0)
+            return;
+    }
+    qint64 timeStamp = m_system->systemSync(this);
+
+    qreal time = timeStamp / 1000.;
+    m_material->timestamp = time;
+
+    //Advance State
+    if(m_spriteEngine){
+        m_material->animcount = m_spriteEngine->stateCount();
+        UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData();
+        m_spriteEngine->updateSprites(timeStamp);
+        for(int i=0; i<m_count; i++){
+            UltraVertices &p = particles[i];
+            int curIdx = m_spriteEngine->spriteState(i);
+            if(curIdx != p.v1.animIdx){
+                p.v1.animIdx = p.v2.animIdx = p.v3.animIdx = p.v4.animIdx = curIdx;
+                p.v1.animT = p.v2.animT = p.v3.animT = p.v4.animT = m_spriteEngine->spriteStart(i)/1000.0;
+                p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = m_spriteEngine->state(curIdx)->frames();
+                p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->state(curIdx)->duration();
+            }
+        }
+    }else{
+        m_material->animcount = 1;
+    }
+}
+
+void UltraParticle::reloadColor(const Color4ub &c, ParticleData* d)
+{
+    UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData();
+    int pos = particleTypeIndex(d);
+    UltraVertices &p = particles[pos];
+    p.v1.color = p.v2.color = p.v3.color = p.v4.color = c;
+}
+
+void UltraParticle::vertexCopy(UltraVertex &b,const ParticleVertex& a)
+{
+    b.x = a.x - m_systemOffset.x();
+    b.y = a.y - m_systemOffset.y();
+    b.t = a.t;
+    b.lifeSpan = a.lifeSpan;
+    b.size = a.size;
+    b.endSize = a.endSize;
+    b.sx = a.sx;
+    b.sy = a.sy;
+    b.ax = a.ax;
+    b.ay = a.ay;
+}
+
+void UltraParticle::reload(ParticleData *d)
+{
+    if (m_node == 0)
+        return;
+
+    UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData();
+
+    int pos = particleTypeIndex(d);
+
+    UltraVertices &p = particles[pos];
+
+    //Perhaps we could be more efficient?
+    vertexCopy(p.v1, d->pv);
+    vertexCopy(p.v2, d->pv);
+    vertexCopy(p.v3, d->pv);
+    vertexCopy(p.v4, d->pv);
+}
+
+void UltraParticle::load(ParticleData *d)
+{
+    if (m_node == 0)
+        return;
+
+    //Color initialization
+    // Particle color
+    Color4ub color;
+    qreal redVariation = m_color_variation + m_redVariation;
+    qreal greenVariation = m_color_variation + m_greenVariation;
+    qreal blueVariation = m_color_variation + m_blueVariation;
+    color.r = m_color.red() * (1 - redVariation) + rand() % 256 * redVariation;
+    color.g = m_color.green() * (1 - greenVariation) + rand() % 256 * greenVariation;
+    color.b = m_color.blue() * (1 - blueVariation) + rand() % 256 * blueVariation;
+    color.a = m_alpha * m_color.alpha() * (1 - m_alphaVariation) + rand() % 256 * m_alphaVariation;
+    UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData();
+    UltraVertices &p = particles[particleTypeIndex(d)];
+    p.v1.color = p.v2.color = p.v3.color = p.v4.color = color;
+    //Initial Rotation
+    if(m_xVector){
+        const QPointF &ret = m_xVector->sample(QPointF(d->pv.x, d->pv.y));
+        p.v1.xx = p.v2.xx = p.v3.xx = p.v4.xx = ret.x();
+        p.v1.xy = p.v2.xy = p.v3.xy = p.v4.xy = ret.y();
+    }
+    if(m_yVector){
+        const QPointF &ret = m_yVector->sample(QPointF(d->pv.x, d->pv.y));
+        p.v1.yx = p.v2.yx = p.v3.yx = p.v4.yx = ret.x();
+        p.v1.yy = p.v2.yy = p.v3.yy = p.v4.yy = ret.y();
+    }
+    p.v1.rotation = p.v2.rotation = p.v3.rotation = p.v4.rotation =
+            (m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV;
+    p.v1.rotationSpeed = p.v2.rotationSpeed = p.v3.rotationSpeed = p.v4.rotationSpeed =
+            (m_rotationSpeed + (m_rotationSpeedVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationSpeedVariation) ) * CONV;
+    p.v1.autoRotate = p.v2.autoRotate = p.v3.autoRotate = p.v4.autoRotate = m_autoRotation?1.0:0.0;
+    // Initial Sprite State
+    p.v1.animT = p.v2.animT = p.v3.animT = p.v4.animT = p.v1.t;
+    p.v1.animIdx = p.v2.animIdx = p.v3.animIdx = p.v4.animIdx = 0;
+    if(m_spriteEngine){
+        SpriteState* state = m_spriteEngine->state(0);
+        p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = state->frames();
+        p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = state->duration();
+        m_spriteEngine->startSprite(particleTypeIndex(d));
+    }else{
+        p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = 1;
+        p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = 9999;
+    }
+
+
+    vertexCopy(p.v1, d->pv);
+    vertexCopy(p.v2, d->pv);
+    vertexCopy(p.v3, d->pv);
+    vertexCopy(p.v4, d->pv);
+}
+
+QT_END_NAMESPACE
diff --git a/src/imports/particles/ultraparticle.h b/src/imports/particles/ultraparticle.h
new file mode 100644 (file)
index 0000000..2001f09
--- /dev/null
@@ -0,0 +1,409 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Declarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ULTRAPARTICLE_H
+#define ULTRAPARTICLE_H
+#include "particle.h"
+#include "varyingvector.h"
+#include <QDeclarativeListProperty>
+
+#include "coloredparticle.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class UltraMaterial;
+class QSGGeometryNode;
+
+class SpriteState;
+class SpriteEngine;
+
+/*struct Color4ub {//in coloredparticle
+    uchar r;
+    uchar g;
+    uchar b;
+    uchar a;
+};*/
+
+struct UltraVertex {
+    float x;
+    float y;
+    float tx;
+    float ty;
+    float t;
+    float lifeSpan;
+    float size;
+    float endSize;
+    float sx;
+    float sy;
+    float ax;
+    float ay;
+    Color4ub color;
+    float xx;
+    float xy;
+    float yx;
+    float yy;
+    float rotation;
+    float rotationSpeed;
+    float autoRotate;//Assume that GPUs prefer floats to bools
+    float animIdx;
+    float frameDuration;
+    float frameCount;
+    float animT;
+};
+
+struct UltraVertices {
+    UltraVertex v1;
+    UltraVertex v2;
+    UltraVertex v3;
+    UltraVertex v4;
+};
+
+class UltraParticle : public ParticleType
+{
+    Q_OBJECT
+    Q_PROPERTY(QUrl image READ image WRITE setImage NOTIFY imageChanged)
+    Q_PROPERTY(QUrl colorTable READ colortable WRITE setColortable NOTIFY colortableChanged)
+    Q_PROPERTY(QUrl sizeTable READ sizetable WRITE setSizetable NOTIFY sizetableChanged)
+    Q_PROPERTY(QUrl opacityTable READ opacitytable WRITE setOpacitytable NOTIFY opacitytableChanged)
+
+    Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
+    //Stacks (added) with individual colorVariations
+    Q_PROPERTY(qreal colorVariation READ colorVariation WRITE setColorVariation NOTIFY colorVariationChanged)
+    Q_PROPERTY(qreal redVariation READ redVariation WRITE setRedVariation NOTIFY redVariationChanged)
+    Q_PROPERTY(qreal greenVariation READ greenVariation WRITE setGreenVariation NOTIFY greenVariationChanged)
+    Q_PROPERTY(qreal blueVariation READ blueVariation WRITE setBlueVariation NOTIFY blueVariationChanged)
+    //Stacks (multiplies) with the Alpha in the color, mostly here so you can use svg color names (which have full alpha)
+    Q_PROPERTY(qreal alpha READ alpha WRITE setAlpha NOTIFY alphaChanged)
+    Q_PROPERTY(qreal alphaVariation READ alphaVariation WRITE setAlphaVariation NOTIFY alphaVariationChanged)
+
+    Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged)
+    Q_PROPERTY(qreal rotationVariation READ rotationVariation WRITE setRotationVariation NOTIFY rotationVariationChanged)
+    Q_PROPERTY(qreal rotationSpeed READ rotationSpeed WRITE setRotationSpeed NOTIFY rotationSpeedChanged)
+    Q_PROPERTY(qreal rotationSpeedVariation READ rotationSpeedVariation WRITE setRotationSpeedVariation NOTIFY rotationSpeedVariationChanged)
+    //If true, then will face the direction of motion. Stacks with rotation, e.g. setting rotation
+    //to 180 will lead to facing away from the direction of motion
+    Q_PROPERTY(bool autoRotation READ autoRotation WRITE autoRotation NOTIFY autoRotationChanged)
+
+    //###Call i/j? Makes more sense to those with vector calculus experience, and I could even add the cirumflex in QML?
+    //xVector is the vector from the top-left point to the top-right point, and is multiplied by current size
+    Q_PROPERTY(VaryingVector* xVector READ xVector WRITE setXVector NOTIFY xVectorChanged)
+    //yVector is the same, but top-left to bottom-left. The particle is always a parallelogram.
+    Q_PROPERTY(VaryingVector* yVector READ yVector WRITE setYVector NOTIFY yVectorChanged)
+    Q_PROPERTY(QDeclarativeListProperty<SpriteState> sprites READ sprites)
+public:
+    explicit UltraParticle(QSGItem *parent = 0);
+    virtual ~UltraParticle(){}
+
+    virtual void load(ParticleData*);
+    virtual void reload(ParticleData*);
+    virtual void setCount(int c);
+
+    QDeclarativeListProperty<SpriteState> sprites();
+    SpriteEngine* spriteEngine() {return m_spriteEngine;}
+
+    QUrl image() const { return m_image_name; }
+    void setImage(const QUrl &image);
+
+    QUrl colortable() const { return m_colortable_name; }
+    void setColortable(const QUrl &table);
+
+    QUrl sizetable() const { return m_sizetable_name; }
+    void setSizetable (const QUrl &table);
+
+    QUrl opacitytable() const { return m_opacitytable_name; }
+    void setOpacitytable(const QUrl &table);
+
+    QColor color() const { return m_color; }
+    void setColor(const QColor &color);
+
+    qreal colorVariation() const { return m_color_variation; }
+    void setColorVariation(qreal var);
+
+    qreal renderOpacity() const { return m_render_opacity; }
+
+    qreal alphaVariation() const
+    {
+        return m_alphaVariation;
+    }
+
+    qreal alpha() const
+    {
+        return m_alpha;
+    }
+
+    qreal redVariation() const
+    {
+        return m_redVariation;
+    }
+
+    qreal greenVariation() const
+    {
+        return m_greenVariation;
+    }
+
+    qreal blueVariation() const
+    {
+        return m_blueVariation;
+    }
+
+    qreal rotation() const
+    {
+        return m_rotation;
+    }
+
+    qreal rotationVariation() const
+    {
+        return m_rotationVariation;
+    }
+
+    qreal rotationSpeed() const
+    {
+        return m_rotationSpeed;
+    }
+
+    qreal rotationSpeedVariation() const
+    {
+        return m_rotationSpeedVariation;
+    }
+
+    bool autoRotation() const
+    {
+        return m_autoRotation;
+    }
+
+    VaryingVector* xVector() const
+    {
+        return m_xVector;
+    }
+
+    VaryingVector* yVector() const
+    {
+        return m_yVector;
+    }
+
+signals:
+
+    void imageChanged();
+    void colortableChanged();
+    void sizetableChanged();
+    void opacitytableChanged();
+
+    void colorChanged();
+    void colorVariationChanged();
+
+    void particleDurationChanged();
+    void alphaVariationChanged(qreal arg);
+
+    void alphaChanged(qreal arg);
+
+    void redVariationChanged(qreal arg);
+
+    void greenVariationChanged(qreal arg);
+
+    void blueVariationChanged(qreal arg);
+
+    void rotationChanged(qreal arg);
+
+    void rotationVariationChanged(qreal arg);
+
+    void rotationSpeedChanged(qreal arg);
+
+    void rotationSpeedVariationChanged(qreal arg);
+
+    void autoRotationChanged(bool arg);
+
+    void xVectorChanged(VaryingVector* arg);
+
+    void yVectorChanged(VaryingVector* arg);
+
+public slots:
+    void setAlphaVariation(qreal arg)
+    {
+        if (m_alphaVariation != arg) {
+            m_alphaVariation = arg;
+            emit alphaVariationChanged(arg);
+        }
+    }
+
+    void setAlpha(qreal arg)
+    {
+        if (m_alpha != arg) {
+            m_alpha = arg;
+            emit alphaChanged(arg);
+        }
+    }
+
+    void setRedVariation(qreal arg)
+    {
+        if (m_redVariation != arg) {
+            m_redVariation = arg;
+            emit redVariationChanged(arg);
+        }
+    }
+
+    void setGreenVariation(qreal arg)
+    {
+        if (m_greenVariation != arg) {
+            m_greenVariation = arg;
+            emit greenVariationChanged(arg);
+        }
+    }
+
+    void setBlueVariation(qreal arg)
+    {
+        if (m_blueVariation != arg) {
+            m_blueVariation = arg;
+            emit blueVariationChanged(arg);
+        }
+    }
+
+    void reloadColor(const Color4ub &c, ParticleData* d);
+    void setRotation(qreal arg)
+    {
+        if (m_rotation != arg) {
+            m_rotation = arg;
+            emit rotationChanged(arg);
+        }
+    }
+
+    void setRotationVariation(qreal arg)
+    {
+        if (m_rotationVariation != arg) {
+            m_rotationVariation = arg;
+            emit rotationVariationChanged(arg);
+        }
+    }
+
+    void setRotationSpeed(qreal arg)
+    {
+        if (m_rotationSpeed != arg) {
+            m_rotationSpeed = arg;
+            emit rotationSpeedChanged(arg);
+        }
+    }
+
+    void setRotationSpeedVariation(qreal arg)
+    {
+        if (m_rotationSpeedVariation != arg) {
+            m_rotationSpeedVariation = arg;
+            emit rotationSpeedVariationChanged(arg);
+        }
+    }
+
+    void autoRotation(bool arg)
+    {
+        if (m_autoRotation != arg) {
+            m_autoRotation = arg;
+            emit autoRotationChanged(arg);
+        }
+    }
+
+    void setXVector(VaryingVector* arg)
+    {
+        if (m_xVector != arg) {
+            m_xVector = arg;
+            emit xVectorChanged(arg);
+        }
+    }
+
+    void setYVector(VaryingVector* arg)
+    {
+        if (m_yVector != arg) {
+            m_yVector = arg;
+            emit yVectorChanged(arg);
+        }
+    }
+
+protected:
+    QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+    void reset();
+    void prepareNextFrame();
+    QSGGeometryNode* buildParticleNode();
+
+private slots:
+    void createEngine(); //### method invoked by sprite list changing (in engine.h) - pretty nasty
+
+private:
+    void vertexCopy(UltraVertex &b,const ParticleVertex& a);
+    bool m_do_reset;
+
+    QUrl m_image_name;
+    QUrl m_colortable_name;
+    QUrl m_sizetable_name;
+    QUrl m_opacitytable_name;
+
+
+    QColor m_color;
+    qreal m_color_variation;
+    qreal m_particleDuration;
+
+    QSGGeometryNode *m_node;
+    UltraMaterial *m_material;
+
+    // derived values...
+    int m_last_particle;
+
+    qreal m_render_opacity;
+    qreal m_alphaVariation;
+    qreal m_alpha;
+    qreal m_redVariation;
+    qreal m_greenVariation;
+    qreal m_blueVariation;
+    qreal m_rotation;
+    qreal m_rotationVariation;
+    qreal m_rotationSpeed;
+    qreal m_rotationSpeedVariation;
+    bool m_autoRotation;
+    VaryingVector* m_xVector;
+    VaryingVector* m_yVector;
+
+    QList<SpriteState*> m_sprites;
+    SpriteEngine* m_spriteEngine;
+
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif // ULTRAPARTICLE_H