~loic.molinari/+junk/qtdeclarative-shadereffectsource-changes

« back to all changes in this revision

Viewing changes to src/particles/qquickcustomparticle.cpp

  • Committer: Loïc Molinari
  • Date: 2012-04-21 17:59:51 UTC
  • Revision ID: loic.molinari@canonical.com-20120421175951-bqx68caaf5zrp76l
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
 
4
** Contact: http://www.qt-project.org/
 
5
**
 
6
** This file is part of the QtQml module of the Qt Toolkit.
 
7
**
 
8
** $QT_BEGIN_LICENSE:LGPL$
 
9
** GNU Lesser General Public License Usage
 
10
** This file may be used under the terms of the GNU Lesser General Public
 
11
** License version 2.1 as published by the Free Software Foundation and
 
12
** appearing in the file LICENSE.LGPL included in the packaging of this
 
13
** file. Please review the following information to ensure the GNU Lesser
 
14
** General Public License version 2.1 requirements will be met:
 
15
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 
16
**
 
17
** In addition, as a special exception, Nokia gives you certain additional
 
18
** rights. These rights are described in the Nokia Qt LGPL Exception
 
19
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 
20
**
 
21
** GNU General Public License Usage
 
22
** Alternatively, this file may be used under the terms of the GNU General
 
23
** Public License version 3.0 as published by the Free Software Foundation
 
24
** and appearing in the file LICENSE.GPL included in the packaging of this
 
25
** file. Please review the following information to ensure the GNU General
 
26
** Public License version 3.0 requirements will be met:
 
27
** http://www.gnu.org/copyleft/gpl.html.
 
28
**
 
29
** Other Usage
 
30
** Alternatively, this file may be used in accordance with the terms and
 
31
** conditions contained in a signed written agreement between you and Nokia.
 
32
**
 
33
**
 
34
**
 
35
**
 
36
**
 
37
**
 
38
** $QT_END_LICENSE$
 
39
**
 
40
****************************************************************************/
 
41
 
 
42
#include "qquickcustomparticle_p.h"
 
43
#include <QtQuick/private/qquickshadereffectmesh_p.h>
 
44
#include <cstdlib>
 
45
 
 
46
QT_BEGIN_NAMESPACE
 
47
 
 
48
//Includes comments because the code isn't self explanatory
 
49
static const char qt_particles_template_vertex_code[] =
 
50
        "attribute highp vec2 qt_ParticlePos;\n"
 
51
        "attribute highp vec2 qt_ParticleTex;\n"
 
52
        "attribute highp vec4 qt_ParticleData; //  x = time,  y = lifeSpan, z = size,  w = endSize\n"
 
53
        "attribute highp vec4 qt_ParticleVec; // x,y = constant speed,  z,w = acceleration\n"
 
54
        "attribute highp float qt_ParticleR;\n"
 
55
        "uniform highp mat4 qt_Matrix;\n"
 
56
        "uniform highp float qt_Timestamp;\n"
 
57
        "varying highp vec2 qt_TexCoord0;\n"
 
58
        "void defaultMain() {\n"
 
59
        "    qt_TexCoord0 = qt_ParticleTex;\n"
 
60
        "    highp float size = qt_ParticleData.z;\n"
 
61
        "    highp float endSize = qt_ParticleData.w;\n"
 
62
        "    highp float t = (qt_Timestamp - qt_ParticleData.x) / qt_ParticleData.y;\n"
 
63
        "    highp float currentSize = mix(size, endSize, t * t);\n"
 
64
        "    if (t < 0. || t > 1.)\n"
 
65
        "        currentSize = 0.;\n"
 
66
        "    highp vec2 pos = qt_ParticlePos\n"
 
67
        "                   - currentSize / 2. + currentSize * qt_ParticleTex   // adjust size\n"
 
68
        "                   + qt_ParticleVec.xy * t * qt_ParticleData.y         // apply speed vector..\n"
 
69
        "                   + 0.5 * qt_ParticleVec.zw * pow(t * qt_ParticleData.y, 2.);\n"
 
70
        "    gl_Position = qt_Matrix * vec4(pos.x, pos.y, 0, 1);\n"
 
71
        "}";
 
72
static const char qt_particles_default_vertex_code[] =
 
73
        "void main() {        \n"
 
74
        "    defaultMain();   \n"
 
75
        "}";
 
76
 
 
77
static const char qt_particles_default_fragment_code[] =
 
78
        "uniform sampler2D source;                                  \n"
 
79
        "varying highp vec2 qt_TexCoord0;                           \n"
 
80
        "uniform lowp float qt_Opacity;                             \n"
 
81
        "void main() {                                              \n"
 
82
        "    gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity;   \n"
 
83
        "}";
 
84
 
 
85
static QSGGeometry::Attribute PlainParticle_Attributes[] = {
 
86
    QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),       // Position
 
87
    QSGGeometry::Attribute::create(1, 2, GL_FLOAT),             // TexCoord
 
88
    QSGGeometry::Attribute::create(2, 4, GL_FLOAT),             // Data
 
89
    QSGGeometry::Attribute::create(3, 4, GL_FLOAT),             // Vectors
 
90
    QSGGeometry::Attribute::create(4, 1, GL_FLOAT)              // r
 
91
};
 
92
 
 
93
static QSGGeometry::AttributeSet PlainParticle_AttributeSet =
 
94
{
 
95
    5, // Attribute Count
 
96
    (2 + 2 + 4 + 4 + 1) * sizeof(float),
 
97
    PlainParticle_Attributes
 
98
};
 
99
 
 
100
struct PlainVertex {
 
101
    float x;
 
102
    float y;
 
103
    float tx;
 
104
    float ty;
 
105
    float t;
 
106
    float lifeSpan;
 
107
    float size;
 
108
    float endSize;
 
109
    float vx;
 
110
    float vy;
 
111
    float ax;
 
112
    float ay;
 
113
    float r;
 
114
};
 
115
 
 
116
struct PlainVertices {
 
117
    PlainVertex v1;
 
118
    PlainVertex v2;
 
119
    PlainVertex v3;
 
120
    PlainVertex v4;
 
121
};
 
122
 
 
123
/*!
 
124
    \qmlclass CustomParticle QQuickCustomParticle
 
125
    \inqmlmodule QtQuick.Particles 2
 
126
    \inherits ParticlePainter
 
127
    \brief The CustomParticle element allows you to specify your own shader to paint particles.
 
128
 
 
129
*/
 
130
 
 
131
QQuickCustomParticle::QQuickCustomParticle(QQuickItem* parent)
 
132
    : QQuickParticlePainter(parent)
 
133
    , m_dirtyUniforms(true)
 
134
    , m_dirtyUniformValues(true)
 
135
    , m_dirtyTextureProviders(true)
 
136
    , m_dirtyProgram(true)
 
137
{
 
138
    setFlag(QQuickItem::ItemHasContents);
 
139
}
 
140
 
 
141
void QQuickCustomParticle::sceneGraphInvalidated()
 
142
{
 
143
    m_nodes.clear();
 
144
}
 
145
 
 
146
QQuickCustomParticle::~QQuickCustomParticle()
 
147
{
 
148
}
 
149
 
 
150
void QQuickCustomParticle::componentComplete()
 
151
{
 
152
    m_common.updateShader(this, Key::FragmentShader);
 
153
    updateVertexShader();
 
154
    reset();
 
155
    QQuickParticlePainter::componentComplete();
 
156
}
 
157
 
 
158
 
 
159
//Trying to keep the shader conventions the same as in qsgshadereffectitem
 
160
/*!
 
161
    \qmlproperty string QtQuick.Particles2::CustomParticle::fragmentShader
 
162
 
 
163
    This property holds the fragment shader's GLSL source code.
 
164
    The default shader expects the texture coordinate to be passed from the
 
165
    vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a
 
166
    sampler2D named "source".
 
167
*/
 
168
 
 
169
void QQuickCustomParticle::setFragmentShader(const QByteArray &code)
 
170
{
 
171
    if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData())
 
172
        return;
 
173
    m_common.source.sourceCode[Key::FragmentShader] = code;
 
174
    m_dirtyProgram = true;
 
175
    if (isComponentComplete()) {
 
176
        m_common.updateShader(this, Key::FragmentShader);
 
177
        reset();
 
178
    }
 
179
    emit fragmentShaderChanged();
 
180
}
 
181
 
 
182
/*!
 
183
    \qmlproperty string QtQuick.Particles2::CustomParticle::vertexShader
 
184
 
 
185
    This property holds the vertex shader's GLSL source code.
 
186
 
 
187
    The default shader passes the texture coordinate along to the fragment
 
188
    shader as "varying highp vec2 qt_TexCoord0".
 
189
 
 
190
    To aid writing a particle vertex shader, the following GLSL code is prepended
 
191
    to your vertex shader:
 
192
    \code
 
193
        attribute highp vec2 qt_ParticlePos;
 
194
        attribute highp vec2 qt_ParticleTex;
 
195
        attribute highp vec4 qt_ParticleData; //  x = time,  y = lifeSpan, z = size,  w = endSize
 
196
        attribute highp vec4 qt_ParticleVec; // x,y = constant speed,  z,w = acceleration
 
197
        attribute highp float qt_ParticleR;
 
198
        uniform highp mat4 qt_Matrix;
 
199
        uniform highp float qt_Timestamp;
 
200
        varying highp vec2 qt_TexCoord0;
 
201
        void defaultMain() {
 
202
            qt_TexCoord0 = qt_ParticleTex;
 
203
            highp float size = qt_ParticleData.z;
 
204
            highp float endSize = qt_ParticleData.w;
 
205
            highp float t = (qt_Timestamp - qt_ParticleData.x) / qt_ParticleData.y;
 
206
            highp float currentSize = mix(size, endSize, t * t);
 
207
            if (t < 0. || t > 1.)
 
208
                currentSize = 0.;
 
209
            highp vec2 pos = qt_ParticlePos
 
210
                           - currentSize / 2. + currentSize * qt_ParticleTex   // adjust size
 
211
                           + qt_ParticleVec.xy * t * qt_ParticleData.y         // apply speed vector..
 
212
                           + 0.5 * qt_ParticleVec.zw * pow(t * qt_ParticleData.y, 2.);
 
213
            gl_Position = qt_Matrix * vec4(pos.x, pos.y, 0, 1);
 
214
        }
 
215
    \endcode
 
216
 
 
217
    defaultMain() is the same code as in the default shader, you can call this for basic
 
218
    particle functions and then add additional variables for custom effects. Note that
 
219
    the vertex shader for particles is responsible for simulating the movement of particles
 
220
    over time, the particle data itself only has the starting position and spawn time.
 
221
*/
 
222
 
 
223
void QQuickCustomParticle::setVertexShader(const QByteArray &code)
 
224
{
 
225
    if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData())
 
226
        return;
 
227
    m_common.source.sourceCode[Key::VertexShader] = code;
 
228
 
 
229
    m_dirtyProgram = true;
 
230
    if (isComponentComplete()) {
 
231
        updateVertexShader();
 
232
        reset();
 
233
    }
 
234
    emit vertexShaderChanged();
 
235
}
 
236
 
 
237
void QQuickCustomParticle::updateVertexShader()
 
238
{
 
239
    m_common.disconnectPropertySignals(this, Key::VertexShader);
 
240
    qDeleteAll(m_common.signalMappers[Key::VertexShader]);
 
241
    m_common.uniformData[Key::VertexShader].clear();
 
242
    m_common.signalMappers[Key::VertexShader].clear();
 
243
    m_common.attributes.clear();
 
244
    m_common.attributes.append("qt_ParticlePos");
 
245
    m_common.attributes.append("qt_ParticleTex");
 
246
    m_common.attributes.append("qt_ParticleData");
 
247
    m_common.attributes.append("qt_ParticleVec");
 
248
    m_common.attributes.append("qt_ParticleR");
 
249
 
 
250
    UniformData d;
 
251
    d.name = "qt_Matrix";
 
252
    d.specialType = UniformData::Matrix;
 
253
    m_common.uniformData[Key::VertexShader].append(d);
 
254
    m_common.signalMappers[Key::VertexShader].append(0);
 
255
 
 
256
    d.name = "qt_Timestamp";
 
257
    d.specialType = UniformData::None;
 
258
    m_common.uniformData[Key::VertexShader].append(d);
 
259
    m_common.signalMappers[Key::VertexShader].append(0);
 
260
 
 
261
    const QByteArray &code = m_common.source.sourceCode[Key::VertexShader];
 
262
    if (!code.isEmpty())
 
263
        m_common.lookThroughShaderCode(this, Key::VertexShader, code);
 
264
 
 
265
    m_common.connectPropertySignals(this, Key::VertexShader);
 
266
}
 
267
 
 
268
void QQuickCustomParticle::reset()
 
269
{
 
270
    QQuickParticlePainter::reset();
 
271
    update();
 
272
}
 
273
 
 
274
QSGNode *QQuickCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
 
275
{
 
276
    QQuickShaderEffectNode *rootNode = static_cast<QQuickShaderEffectNode *>(oldNode);
 
277
    if (m_pleaseReset){
 
278
        delete rootNode;//Automatically deletes children
 
279
        rootNode = 0;
 
280
        m_nodes.clear();
 
281
        m_pleaseReset = false;
 
282
        m_dirtyProgram = true;
 
283
    }
 
284
 
 
285
    if (m_system && m_system->isRunning() && !m_system->isPaused()){
 
286
        rootNode = prepareNextFrame(rootNode);
 
287
        if (rootNode)
 
288
            update();
 
289
    }
 
290
 
 
291
    return rootNode;
 
292
}
 
293
 
 
294
QQuickShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickShaderEffectNode *rootNode)
 
295
{
 
296
    if (!rootNode)
 
297
        rootNode = buildCustomNodes();
 
298
 
 
299
    if (!rootNode)
 
300
        return 0;
 
301
 
 
302
    if (m_dirtyProgram) {
 
303
        QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(rootNode->material());
 
304
        Q_ASSERT(material);
 
305
 
 
306
        Key s = m_common.source;
 
307
        if (s.sourceCode[Key::FragmentShader].isEmpty())
 
308
            s.sourceCode[Key::FragmentShader] = qt_particles_default_fragment_code;
 
309
        if (s.sourceCode[Key::VertexShader].isEmpty())
 
310
            s.sourceCode[Key::VertexShader] = qt_particles_default_vertex_code;
 
311
        s.sourceCode[Key::VertexShader] = qt_particles_template_vertex_code + s.sourceCode[Key::VertexShader];
 
312
        s.className = metaObject()->className();
 
313
 
 
314
        material->setProgramSource(s);
 
315
        material->attributes = m_common.attributes;
 
316
        foreach (QQuickShaderEffectNode* node, m_nodes)
 
317
            node->markDirty(QSGNode::DirtyMaterial);
 
318
 
 
319
        m_dirtyProgram = false;
 
320
        m_dirtyUniforms = true;
 
321
    }
 
322
 
 
323
    m_lastTime = m_system->systemSync(this) / 1000.;
 
324
    if (true) //Currently this is how we update timestamp... potentially over expensive.
 
325
        buildData(rootNode);
 
326
    return rootNode;
 
327
}
 
328
 
 
329
QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes()
 
330
{
 
331
#ifdef QT_OPENGL_ES_2
 
332
    if (m_count * 4 > 0xffff) {
 
333
        printf("CustomParticle: Too many particles... \n");
 
334
        return 0;
 
335
    }
 
336
#endif
 
337
 
 
338
    if (m_count <= 0) {
 
339
        printf("CustomParticle: Too few particles... \n");
 
340
        return 0;
 
341
    }
 
342
 
 
343
    if (m_groups.isEmpty())
 
344
        return 0;
 
345
 
 
346
    QQuickShaderEffectNode *rootNode = 0;
 
347
    QQuickShaderEffectMaterial *material = new QQuickShaderEffectMaterial;
 
348
    m_dirtyProgram = true;
 
349
 
 
350
    foreach (const QString &str, m_groups){
 
351
        int gIdx = m_system->groupIds[str];
 
352
        int count = m_system->groupData[gIdx]->size();
 
353
 
 
354
        QQuickShaderEffectNode* node = new QQuickShaderEffectNode();
 
355
        m_nodes.insert(gIdx, node);
 
356
 
 
357
        node->setMaterial(material);
 
358
 
 
359
        //Create Particle Geometry
 
360
        int vCount = count * 4;
 
361
        int iCount = count * 6;
 
362
        QSGGeometry *g = new QSGGeometry(PlainParticle_AttributeSet, vCount, iCount);
 
363
        g->setDrawingMode(GL_TRIANGLES);
 
364
        node->setGeometry(g);
 
365
        node->setFlag(QSGNode::OwnsGeometry, true);
 
366
        PlainVertex *vertices = (PlainVertex *) g->vertexData();
 
367
        for (int p=0; p < count; ++p) {
 
368
            commit(gIdx, p);
 
369
            vertices[0].tx = 0;
 
370
            vertices[0].ty = 0;
 
371
 
 
372
            vertices[1].tx = 1;
 
373
            vertices[1].ty = 0;
 
374
 
 
375
            vertices[2].tx = 0;
 
376
            vertices[2].ty = 1;
 
377
 
 
378
            vertices[3].tx = 1;
 
379
            vertices[3].ty = 1;
 
380
            vertices += 4;
 
381
        }
 
382
        quint16 *indices = g->indexDataAsUShort();
 
383
        for (int i=0; i < count; ++i) {
 
384
            int o = i * 4;
 
385
            indices[0] = o;
 
386
            indices[1] = o + 1;
 
387
            indices[2] = o + 2;
 
388
            indices[3] = o + 1;
 
389
            indices[4] = o + 3;
 
390
            indices[5] = o + 2;
 
391
            indices += 6;
 
392
        }
 
393
    }
 
394
 
 
395
    QHash<int, QQuickShaderEffectNode*>::const_iterator it = m_nodes.begin();
 
396
    rootNode = it.value();
 
397
    rootNode->setFlag(QSGNode::OwnsMaterial, true);
 
398
    for (++it; it != m_nodes.end(); ++it)
 
399
        rootNode->appendChildNode(it.value());
 
400
 
 
401
    return rootNode;
 
402
}
 
403
 
 
404
void QQuickCustomParticle::sourceDestroyed(QObject *object)
 
405
{
 
406
    m_common.sourceDestroyed(object);
 
407
}
 
408
 
 
409
void QQuickCustomParticle::propertyChanged(int mappedId)
 
410
{
 
411
    bool textureProviderChanged;
 
412
    m_common.propertyChanged(this, mappedId, &textureProviderChanged);
 
413
    m_dirtyTextureProviders |= textureProviderChanged;
 
414
    m_dirtyUniformValues = true;
 
415
    update();
 
416
}
 
417
 
 
418
 
 
419
void QQuickCustomParticle::buildData(QQuickShaderEffectNode *rootNode)
 
420
{
 
421
    if (!rootNode)
 
422
        return;
 
423
    for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
 
424
        for (int i = 0; i < m_common.uniformData[shaderType].size(); ++i) {
 
425
            if (m_common.uniformData[shaderType].at(i).name == "qt_Timestamp")
 
426
                m_common.uniformData[shaderType][i].value = qVariantFromValue(m_lastTime);
 
427
        }
 
428
    }
 
429
    m_common.updateMaterial(rootNode, static_cast<QQuickShaderEffectMaterial *>(rootNode->material()),
 
430
                            m_dirtyUniforms, true, m_dirtyTextureProviders);
 
431
    foreach (QQuickShaderEffectNode* node, m_nodes)
 
432
        node->markDirty(QSGNode::DirtyMaterial);
 
433
    m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false;
 
434
}
 
435
 
 
436
void QQuickCustomParticle::initialize(int gIdx, int pIdx)
 
437
{
 
438
    QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
 
439
    datum->r = rand()/(qreal)RAND_MAX;
 
440
}
 
441
 
 
442
void QQuickCustomParticle::commit(int gIdx, int pIdx)
 
443
{
 
444
    if (m_nodes[gIdx] == 0)
 
445
        return;
 
446
 
 
447
    QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
 
448
    PlainVertices *particles = (PlainVertices *) m_nodes[gIdx]->geometry()->vertexData();
 
449
    PlainVertex *vertices = (PlainVertex *)&particles[pIdx];
 
450
    for (int i=0; i<4; ++i) {
 
451
        vertices[i].x = datum->x - m_systemOffset.x();
 
452
        vertices[i].y = datum->y - m_systemOffset.y();
 
453
        vertices[i].t = datum->t;
 
454
        vertices[i].lifeSpan = datum->lifeSpan;
 
455
        vertices[i].size = datum->size;
 
456
        vertices[i].endSize = datum->endSize;
 
457
        vertices[i].vx = datum->vx;
 
458
        vertices[i].vy = datum->vy;
 
459
        vertices[i].ax = datum->ax;
 
460
        vertices[i].ay = datum->ay;
 
461
        vertices[i].r = datum->r;
 
462
    }
 
463
}
 
464
 
 
465
void QQuickCustomParticle::itemChange(ItemChange change, const ItemChangeData &value)
 
466
{
 
467
    if (change == QQuickItem::ItemSceneChange)
 
468
        m_common.updateCanvas(value.canvas);
 
469
    QQuickParticlePainter::itemChange(change, value);
 
470
}
 
471
 
 
472
 
 
473
QT_END_NAMESPACE