From a639eccfe1251c00010669efa56e81f9cf27e994 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Thu, 18 Aug 2011 14:31:35 +1000 Subject: [PATCH] Add a deformable level shader for QSGImageParticle 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 Reviewed-by: Alan Alpert --- .../particles/defaultshaders/tabledfragment.shader | 23 ++ .../particles/defaultshaders/tabledvertex.shader | 57 ++++ src/declarative/particles/particles.qrc | 8 +- src/declarative/particles/qsgimageparticle.cpp | 295 ++++++++++++++++++-- src/declarative/particles/qsgimageparticle_p.h | 6 +- 5 files changed, 354 insertions(+), 35 deletions(-) create mode 100644 src/declarative/particles/defaultshaders/tabledfragment.shader create mode 100644 src/declarative/particles/defaultshaders/tabledvertex.shader diff --git a/src/declarative/particles/defaultshaders/tabledfragment.shader b/src/declarative/particles/defaultshaders/tabledfragment.shader new file mode 100644 index 0000000..e92d805 --- /dev/null +++ b/src/declarative/particles/defaultshaders/tabledfragment.shader @@ -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 index 0000000..d09abbd --- /dev/null +++ b/src/declarative/particles/defaultshaders/tabledvertex.shader @@ -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; + +} diff --git a/src/declarative/particles/particles.qrc b/src/declarative/particles/particles.qrc index 85931ec..0f2325f 100644 --- a/src/declarative/particles/particles.qrc +++ b/src/declarative/particles/particles.qrc @@ -2,10 +2,6 @@ defaultshaders/spritefragment.shader defaultshaders/spritevertex.shader - defaultshaders/ctfragment.shader - defaultshaders/ctvertex.shader - defaultshaders/trailsfragment.shader - defaultshaders/trailsvertex.shader defaultshaders/spriteimagefragment.shader defaultshaders/spriteimagevertex.shader defaultshaders/identitytable.png @@ -14,9 +10,9 @@ defaultshaders/deformablevertex.shader defaultshaders/ultravertex.shader defaultshaders/ultrafragment.shader - defaultshaders/supervertex.shader - defaultshaders/superfragment.shader defaultshaders/simplevertex.shader defaultshaders/simplefragment.shader + defaultshaders/tabledvertex.shader + defaultshaders/tabledfragment.shader diff --git a/src/declarative/particles/qsgimageparticle.cpp b/src/declarative/particles/qsgimageparticle.cpp index 80d66db..6827d2d 100644 --- a/src/declarative/particles/qsgimageparticle.cpp +++ b/src/declarative/particles/qsgimageparticle.cpp @@ -55,6 +55,91 @@ 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 +{ + 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 attributes() const { + return QList() << "vPos" << "vTex" << "vData" << "vVec" + << "vColor" << "vDeformVec" << "vRotation" << "vAnimData"; + }; + + void initialize() { + QSGSimpleMaterialShader::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 QSGImageParticle::sprites() { return QDeclarativeListProperty(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; diff --git a/src/declarative/particles/qsgimageparticle_p.h b/src/declarative/particles/qsgimageparticle_p.h index 1ac840c..fe799f5 100644 --- a/src/declarative/particles/qsgimageparticle_p.h +++ b/src/declarative/particles/qsgimageparticle_p.h @@ -44,6 +44,7 @@ #include "qsgparticlepainter_p.h" #include "qsgstochasticdirection_p.h" #include +#include 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 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 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 *m_tabledMaterial; // derived values... -- 1.7.2.5