Add a deformable level shader for QSGImageParticle
authorAlan Alpert <alan.alpert@nokia.com>
Thu, 18 Aug 2011 04:31:35 +0000 (14:31 +1000)
committerQt by Nokia <qt-info@nokia.com>
Mon, 22 Aug 2011 09:02:08 +0000 (11:02 +0200)
This shader also implements sizeTable in a GL ES 2 compliant way.

Change-Id: If31ee01a521c1fe13f59f7d6376185bafcefedfc
Reviewed-on: http://codereview.qt.nokia.com/3132
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Alan Alpert <alan.alpert@nokia.com>
src/declarative/particles/defaultshaders/tabledfragment.shader [new file with mode: 0644]
src/declarative/particles/defaultshaders/tabledvertex.shader [new file with mode: 0644]
src/declarative/particles/particles.qrc
src/declarative/particles/qsgimageparticle.cpp
src/declarative/particles/qsgimageparticle_p.h

diff --git a/src/declarative/particles/defaultshaders/tabledfragment.shader b/src/declarative/particles/defaultshaders/tabledfragment.shader
new file mode 100644 (file)
index 0000000..e92d805
--- /dev/null
@@ -0,0 +1,23 @@
+uniform sampler2D texture;
+uniform sampler2D colortable;
+uniform sampler2D opacitytable;
+uniform sampler2D sizetable;
+uniform lowp float qt_Opacity;
+
+varying highp vec2 fTex;
+varying lowp vec4 fColor;
+varying lowp float tt;
+
+void main() {
+    highp vec2 tex = (((fTex - 0.5) / texture2D(sizetable, vec2(tt, 0.5)).w) + 0.5);
+    lowp vec4 color;
+    if(tex.x < 1.0 && tex.x > 0.0 && tex.y < 1.0 && tex.y > 0.0){//No CLAMP_TO_BORDER in ES2, so have to do it ourselves
+        color = texture2D(texture, tex);
+    }else{
+        color = vec4(0.,0.,0.,0.);
+    }
+    gl_FragColor = color
+            * fColor
+            * texture2D(colortable, vec2(tt, 0.5))
+            * (texture2D(opacitytable, vec2(tt, 0.5)).w * qt_Opacity);
+}
diff --git a/src/declarative/particles/defaultshaders/tabledvertex.shader b/src/declarative/particles/defaultshaders/tabledvertex.shader
new file mode 100644 (file)
index 0000000..d09abbd
--- /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
+attribute highp vec4 vAnimData;// idx, duration, frameCount (this anim), timestamp (this anim)
+
+uniform highp mat4 qt_Matrix;
+uniform highp float timestamp;
+
+varying lowp float tt;
+varying highp vec2 fTex;
+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;
+
+    fTex = vTex;
+    highp float currentSize = mix(size, endSize, t * t);
+    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 = qt_Matrix * vec4(pos.x, pos.y, 0, 1);
+
+    fColor = vColor;
+    tt = t;
+
+}
index 85931ec..0f2325f 100644 (file)
@@ -2,10 +2,6 @@
     <qresource prefix="/">
         <file>defaultshaders/spritefragment.shader</file>
         <file>defaultshaders/spritevertex.shader</file>
-        <file>defaultshaders/ctfragment.shader</file>
-        <file>defaultshaders/ctvertex.shader</file>
-        <file>defaultshaders/trailsfragment.shader</file>
-        <file>defaultshaders/trailsvertex.shader</file>
         <file>defaultshaders/spriteimagefragment.shader</file>
         <file>defaultshaders/spriteimagevertex.shader</file>
         <file>defaultshaders/identitytable.png</file>
@@ -14,9 +10,9 @@
         <file>defaultshaders/deformablevertex.shader</file>
         <file>defaultshaders/ultravertex.shader</file>
         <file>defaultshaders/ultrafragment.shader</file>
-        <file>defaultshaders/supervertex.shader</file>
-        <file>defaultshaders/superfragment.shader</file>
         <file>defaultshaders/simplevertex.shader</file>
         <file>defaultshaders/simplefragment.shader</file>
+        <file>defaultshaders/tabledvertex.shader</file>
+        <file>defaultshaders/tabledfragment.shader</file>
     </qresource>
 </RCC>
index 80d66db..6827d2d 100644 (file)
 QT_BEGIN_NAMESPACE
 
 const float CONV = 0.017453292519943295;
+class TabledMaterialData
+{
+    public:
+    TabledMaterialData()
+        : texture(0), colortable(0), sizetable(0), opacitytable(0)
+    {}
+
+    ~TabledMaterialData(){
+        delete texture;
+        delete colortable;
+        delete sizetable;
+        delete opacitytable;
+    }
+
+    QSGTexture *texture;
+    QSGTexture *colortable;
+    QSGTexture *sizetable;
+    QSGTexture *opacitytable;
+
+    qreal timestamp;
+};
+
+class TabledMaterial : public QSGSimpleMaterialShader<TabledMaterialData>
+{
+    QSG_DECLARE_SIMPLE_SHADER(TabledMaterial, TabledMaterialData)
+
+public:
+    TabledMaterial()
+    {
+        QFile vf(":defaultshaders/tabledvertex.shader");
+        vf.open(QFile::ReadOnly);
+        m_vertex_code = vf.readAll();
+
+        QFile ff(":defaultshaders/tabledfragment.shader");
+        ff.open(QFile::ReadOnly);
+        m_fragment_code = ff.readAll();
+
+        Q_ASSERT(!m_vertex_code.isNull());
+        Q_ASSERT(!m_fragment_code.isNull());
+    }
+
+    const char *vertexShader() const { return m_vertex_code.constData(); }
+    const char *fragmentShader() const { return m_fragment_code.constData(); }
+
+    QList<QByteArray> attributes() const {
+        return QList<QByteArray>() << "vPos" << "vTex" << "vData" << "vVec"
+            << "vColor" << "vDeformVec" << "vRotation" << "vAnimData";
+    };
+
+    void initialize() {
+        QSGSimpleMaterialShader<TabledMaterialData>::initialize();
+        program()->bind();
+        program()->setUniformValue("texture", 0);
+        program()->setUniformValue("colortable", 1);
+        program()->setUniformValue("sizetable", 2);
+        program()->setUniformValue("opacitytable", 3);
+        glFuncs = QGLContext::currentContext()->functions();
+        m_timestamp_id = program()->uniformLocation("timestamp");
+    }
+
+    void updateState(const TabledMaterialData* d, const TabledMaterialData*) {
+        glFuncs->glActiveTexture(GL_TEXTURE1);
+        d->colortable->bind();
+
+        glFuncs->glActiveTexture(GL_TEXTURE2);
+        d->sizetable->bind();
+
+        glFuncs->glActiveTexture(GL_TEXTURE3);
+        d->opacitytable->bind();
+
+        // make sure we end by setting GL_TEXTURE0 as active texture
+        glFuncs->glActiveTexture(GL_TEXTURE0);
+        d->texture->bind();
+
+        program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
+        program()->setUniformValue("framecount", (float) 1);
+        program()->setUniformValue("animcount", (float) 1);
+    }
+
+    int m_timestamp_id;
+    QByteArray m_vertex_code;
+    QByteArray m_fragment_code;
+    QGLFunctions* glFuncs;
+};
+
 class UltraMaterial : public QSGMaterial
 {
 public:
@@ -95,6 +180,7 @@ public:
     int framecount;
     int animcount;
     bool usesSprites;
+
 };
 class UltraMaterialData : public QSGMaterialShader
 {
@@ -302,6 +388,8 @@ QSGMaterialShader *SimpleMaterial::createShader() const {
 */
 /*!
     \qmlproperty url QtQuick.Particles2::ImageParticle::sizeTable
+
+    Note that currently sizeTable is ignored for sprite particles.
 */
 /*!
     \qmlproperty url QtQuick.Particles2::ImageParticle::opacityTable
@@ -359,6 +447,7 @@ QSGImageParticle::QSGImageParticle(QSGItem* parent)
     , m_color_variation(0.0)
     , m_rootNode(0)
     , m_material(0)
+    , m_tabledMaterial(0)
     , m_alphaVariation(0.0)
     , m_alpha(1.0)
     , m_redVariation(0.0)
@@ -379,6 +468,12 @@ QSGImageParticle::QSGImageParticle(QSGItem* parent)
     setFlag(ItemHasContents);
 }
 
+QSGImageParticle::~QSGImageParticle()
+{
+    delete m_material;
+    delete m_tabledMaterial;
+}
+
 QDeclarativeListProperty<QSGSprite> QSGImageParticle::sprites()
 {
     return QDeclarativeListProperty<QSGSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
@@ -695,6 +790,104 @@ QSGGeometryNode* QSGImageParticle::buildSimpleParticleNodes()
     return *(m_nodes.begin());
 }
 
+QSGGeometryNode* QSGImageParticle::buildTabledParticleNodes()//TODO: TabledParticle so as to not have the unused anim attributes
+{
+    perfLevel = Tabled;//TODO: More Intermediate levels
+    if (!m_color.isValid())//But we're in colored level (or higher)
+        m_color = QColor(Qt::white);
+    QImage image = QImage(m_image_name.toLocalFile());
+    if (image.isNull()) {
+        printf("ImageParticle: loading image failed... '%s'\n", qPrintable(m_image_name.toLocalFile()));
+        return 0;
+    }
+
+    if (m_material) {
+        delete m_material;
+        m_material = 0;
+    }
+
+    m_tabledMaterial = TabledMaterial::createMaterial();
+    m_tabledMaterial->setFlag(QSGMaterial::Blending, true);
+    QImage colortable(m_colortable_name.toLocalFile());
+    QImage sizetable(m_sizetable_name.toLocalFile());
+    QImage opacitytable(m_opacitytable_name.toLocalFile());
+    if (colortable.isNull())
+        colortable = QImage(":defaultshaders/identitytable.png");
+    if (sizetable.isNull())
+        sizetable = QImage(":defaultshaders/identitytable.png");
+    if (opacitytable.isNull())
+        opacitytable = QImage(":defaultshaders/defaultFadeInOut.png");
+    Q_ASSERT(!colortable.isNull());
+    Q_ASSERT(!sizetable.isNull());
+    Q_ASSERT(!opacitytable.isNull());
+    m_tabledMaterial->state()->colortable = sceneGraphEngine()->createTextureFromImage(colortable);
+    m_tabledMaterial->state()->sizetable = sceneGraphEngine()->createTextureFromImage(sizetable);
+    m_tabledMaterial->state()->opacitytable = sceneGraphEngine()->createTextureFromImage(opacitytable);
+    m_tabledMaterial->state()->texture = sceneGraphEngine()->createTextureFromImage(image);
+    m_tabledMaterial->state()->texture->setFiltering(QSGTexture::Linear);
+
+    foreach (const QString &str, m_particles){
+        int gIdx = m_system->m_groupIds[str];
+        int count = m_system->m_groupData[gIdx]->size();
+        QSGGeometryNode* node = new QSGGeometryNode();
+        node->setMaterial(m_tabledMaterial);
+
+        m_nodes.insert(gIdx, node);
+        m_idxStarts.insert(gIdx, m_lastIdxStart);
+        m_lastIdxStart += count;
+
+        //Create Particle Geometry
+        int vCount = count * 4;
+        int iCount = count * 6;
+
+        QSGGeometry *g = new QSGGeometry(UltraParticle_AttributeSet, vCount, iCount);
+        node->setGeometry(g);
+        g->setDrawingMode(GL_TRIANGLES);
+
+        UltraVertex *vertices = (UltraVertex *) g->vertexData();
+        for (int p=0; p < count; ++p) {
+            commit(gIdx, p);//commit sets geometry for the node
+
+            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 < 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;
+        }
+
+    }
+
+
+
+    foreach (QSGGeometryNode* node, m_nodes){
+        if (node == *(m_nodes.begin()))
+                continue;
+        (*(m_nodes.begin()))->appendChildNode(node);
+    }
+
+    return *(m_nodes.begin());
+}
+
 QSGGeometryNode* QSGImageParticle::buildParticleNodes()
 {
     if (m_count * 4 > 0xffff) {
@@ -705,8 +898,8 @@ QSGGeometryNode* QSGImageParticle::buildParticleNodes()
     if (count() <= 0)
         return 0;
 
-    if (!m_sprites.count() && !m_bloat
-            && m_colortable_name.isEmpty()
+    if (!m_sprites.count() && !m_bloat) {
+        if (m_colortable_name.isEmpty()
             && m_sizetable_name.isEmpty()
             && m_opacitytable_name.isEmpty()
             && !m_autoRotation
@@ -716,7 +909,15 @@ QSGGeometryNode* QSGImageParticle::buildParticleNodes()
             && !m_redVariation && !m_blueVariation && !m_greenVariation
             && !m_color.isValid()
             )
-        return buildSimpleParticleNodes();
+            return buildSimpleParticleNodes();
+        else
+            return buildTabledParticleNodes();
+    }
+    if (m_tabledMaterial) {
+        delete m_tabledMaterial;
+        m_tabledMaterial = 0;
+    }
+
     perfLevel = Sprites;//TODO: intermediate levels
     if (!m_color.isValid())//But we're in colored level (or higher)
         m_color = QColor(Qt::white);
@@ -871,33 +1072,46 @@ void QSGImageParticle::prepareNextFrame()
     qint64 timeStamp = m_system->systemSync(this);
 
     qreal time = timeStamp / 1000.;
-    m_material->timestamp = time;
-
-    //Advance State
-    if (m_spriteEngine){//perfLevel == Sprites?//TODO: use signals?
-
-        m_material->animcount = m_spriteEngine->spriteCount();
-        m_spriteEngine->updateSprites(timeStamp);
-        foreach (const QString &str, m_particles){
-            int gIdx = m_system->m_groupIds[str];
-            int count = m_system->m_groupData[gIdx]->size();
-
-            UltraVertices *particles = (UltraVertices *) m_nodes[gIdx]->geometry()->vertexData();
-            for (int i=0; i < count; i++){
-                int spriteIdx = m_idxStarts[gIdx] + i;
-                UltraVertices &p = particles[i];
-                int curIdx = m_spriteEngine->spriteState(spriteIdx);
-                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(spriteIdx)/1000.0;
-                    p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = m_spriteEngine->spriteFrames(spriteIdx);
-                    p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->spriteDuration(spriteIdx);
+
+    switch (perfLevel){//Fall-through intended... eventually //TODO: solve m_material/m_namedMaterial!
+    case Sprites:
+        m_material->timestamp = time;
+        //Advance State
+        if (m_spriteEngine){//perfLevel == Sprites?//TODO: use signals?
+
+            m_material->animcount = m_spriteEngine->spriteCount();
+            m_spriteEngine->updateSprites(timeStamp);
+            foreach (const QString &str, m_particles){
+                int gIdx = m_system->m_groupIds[str];
+                int count = m_system->m_groupData[gIdx]->size();
+
+                UltraVertices *particles = (UltraVertices *) m_nodes[gIdx]->geometry()->vertexData();
+                for (int i=0; i < count; i++){
+                    int spriteIdx = m_idxStarts[gIdx] + i;
+                    UltraVertices &p = particles[i];
+                    int curIdx = m_spriteEngine->spriteState(spriteIdx);
+                    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(spriteIdx)/1000.0;
+                        p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = m_spriteEngine->spriteFrames(spriteIdx);
+                        p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->spriteDuration(spriteIdx);
+                    }
                 }
             }
+        }else{
+            m_material->animcount = 1;
         }
-    }else{
-        m_material->animcount = 1;
+        break;
+    case Tabled:
+    case Deformable:
+    case Colored:
+        m_tabledMaterial->state()->timestamp = time; break;
+    case Simple:
+        m_material->timestamp = time; break;
+    default:
+        break;
     }
+
 }
 
 void QSGImageParticle::reloadColor(const Color4ub &c, QSGParticleData* d)
@@ -1001,8 +1215,33 @@ void QSGImageParticle::commit(int gIdx, int pIdx)
             ultraVertices[i].color.a = datum->color.a;
         }
         break;
-    case Tabled://TODO: Us
-    case Deformable:
+    case Tabled:
+        ultraVertices += pIdx*4;
+        for (int i=0; i<4; i++){
+            ultraVertices[i].x = datum->x  - m_systemOffset.x();
+            ultraVertices[i].y = datum->y  - m_systemOffset.y();
+            ultraVertices[i].t = datum->t;
+            ultraVertices[i].lifeSpan = datum->lifeSpan;
+            ultraVertices[i].size = datum->size;
+            ultraVertices[i].endSize = datum->endSize;
+            ultraVertices[i].vx = datum->vx;
+            ultraVertices[i].vy = datum->vy;
+            ultraVertices[i].ax = datum->ax;
+            ultraVertices[i].ay = datum->ay;
+            ultraVertices[i].xx = datum->xx;
+            ultraVertices[i].xy = datum->xy;
+            ultraVertices[i].yx = datum->yx;
+            ultraVertices[i].yy = datum->yy;
+            ultraVertices[i].rotation = datum->rotation;
+            ultraVertices[i].rotationSpeed = datum->rotationSpeed;
+            ultraVertices[i].autoRotate = datum->autoRotate;
+            ultraVertices[i].color.r = datum->color.r;
+            ultraVertices[i].color.g = datum->color.g;
+            ultraVertices[i].color.b = datum->color.b;
+            ultraVertices[i].color.a = datum->color.a;
+        }
+        break;
+    case Deformable: //TODO: Us
     case Colored:
     case Simple:
         simpleVertices += pIdx*4;
index 1ac840c..fe799f5 100644 (file)
@@ -44,6 +44,7 @@
 #include "qsgparticlepainter_p.h"
 #include "qsgstochasticdirection_p.h"
 #include <QDeclarativeListProperty>
+#include <qsgsimplematerial.h>
 
 QT_BEGIN_HEADER
 
@@ -52,6 +53,7 @@ QT_BEGIN_NAMESPACE
 QT_MODULE(Declarative)
 
 class UltraMaterial;
+class TabledMaterialData;
 class QSGGeometryNode;
 
 class QSGSprite;
@@ -156,7 +158,7 @@ class QSGImageParticle : public QSGParticlePainter
     Q_PROPERTY(bool bloat READ bloat WRITE setBloat NOTIFY bloatChanged)//Just a debugging property to bypass optimizations
 public:
     explicit QSGImageParticle(QSGItem *parent = 0);
-    virtual ~QSGImageParticle(){}
+    virtual ~QSGImageParticle();
 
 
     QDeclarativeListProperty<QSGSprite> sprites();
@@ -291,6 +293,7 @@ protected:
     void prepareNextFrame();
     QSGGeometryNode* buildParticleNodes();
     QSGGeometryNode* buildSimpleParticleNodes();
+    QSGGeometryNode* buildTabledParticleNodes();
 
 private slots:
     void createEngine(); //### method invoked by sprite list changing (in engine.h) - pretty nasty
@@ -313,6 +316,7 @@ private:
     QHash<int, int> m_idxStarts;//TODO: Proper resizing will lead to needing a spriteEngine per particle - do this after sprite engine gains transparent sharing?
     int m_lastIdxStart;
     UltraMaterial *m_material;
+    QSGSimpleMaterial<TabledMaterialData> *m_tabledMaterial;
 
     // derived values...