eeb830cc87555aad7f62132b0b151ef00105c5c4
[profile/ivi/qtdeclarative.git] / src / particles / qquickcustomparticle.cpp
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 For specifying shaders to paint particles
128     \ingroup qtquick-particles
129
130 */
131
132 QQuickCustomParticle::QQuickCustomParticle(QQuickItem* parent)
133     : QQuickParticlePainter(parent)
134     , m_dirtyUniforms(true)
135     , m_dirtyUniformValues(true)
136     , m_dirtyTextureProviders(true)
137     , m_dirtyProgram(true)
138 {
139     setFlag(QQuickItem::ItemHasContents);
140 }
141
142 void QQuickCustomParticle::sceneGraphInvalidated()
143 {
144     m_nodes.clear();
145 }
146
147 QQuickCustomParticle::~QQuickCustomParticle()
148 {
149 }
150
151 void QQuickCustomParticle::componentComplete()
152 {
153     m_common.updateShader(this, Key::FragmentShader);
154     updateVertexShader();
155     reset();
156     QQuickParticlePainter::componentComplete();
157 }
158
159
160 //Trying to keep the shader conventions the same as in qsgshadereffectitem
161 /*!
162     \qmlproperty string QtQuick.Particles2::CustomParticle::fragmentShader
163
164     This property holds the fragment shader's GLSL source code.
165     The default shader expects the texture coordinate to be passed from the
166     vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a
167     sampler2D named "source".
168 */
169
170 void QQuickCustomParticle::setFragmentShader(const QByteArray &code)
171 {
172     if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData())
173         return;
174     m_common.source.sourceCode[Key::FragmentShader] = code;
175     m_dirtyProgram = true;
176     if (isComponentComplete()) {
177         m_common.updateShader(this, Key::FragmentShader);
178         reset();
179     }
180     emit fragmentShaderChanged();
181 }
182
183 /*!
184     \qmlproperty string QtQuick.Particles2::CustomParticle::vertexShader
185
186     This property holds the vertex shader's GLSL source code.
187
188     The default shader passes the texture coordinate along to the fragment
189     shader as "varying highp vec2 qt_TexCoord0".
190
191     To aid writing a particle vertex shader, the following GLSL code is prepended
192     to your vertex shader:
193     \code
194         attribute highp vec2 qt_ParticlePos;
195         attribute highp vec2 qt_ParticleTex;
196         attribute highp vec4 qt_ParticleData; //  x = time,  y = lifeSpan, z = size,  w = endSize
197         attribute highp vec4 qt_ParticleVec; // x,y = constant speed,  z,w = acceleration
198         attribute highp float qt_ParticleR;
199         uniform highp mat4 qt_Matrix;
200         uniform highp float qt_Timestamp;
201         varying highp vec2 qt_TexCoord0;
202         void defaultMain() {
203             qt_TexCoord0 = qt_ParticleTex;
204             highp float size = qt_ParticleData.z;
205             highp float endSize = qt_ParticleData.w;
206             highp float t = (qt_Timestamp - qt_ParticleData.x) / qt_ParticleData.y;
207             highp float currentSize = mix(size, endSize, t * t);
208             if (t < 0. || t > 1.)
209                 currentSize = 0.;
210             highp vec2 pos = qt_ParticlePos
211                            - currentSize / 2. + currentSize * qt_ParticleTex   // adjust size
212                            + qt_ParticleVec.xy * t * qt_ParticleData.y         // apply speed vector..
213                            + 0.5 * qt_ParticleVec.zw * pow(t * qt_ParticleData.y, 2.);
214             gl_Position = qt_Matrix * vec4(pos.x, pos.y, 0, 1);
215         }
216     \endcode
217
218     defaultMain() is the same code as in the default shader, you can call this for basic
219     particle functions and then add additional variables for custom effects. Note that
220     the vertex shader for particles is responsible for simulating the movement of particles
221     over time, the particle data itself only has the starting position and spawn time.
222 */
223
224 void QQuickCustomParticle::setVertexShader(const QByteArray &code)
225 {
226     if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData())
227         return;
228     m_common.source.sourceCode[Key::VertexShader] = code;
229
230     m_dirtyProgram = true;
231     if (isComponentComplete()) {
232         updateVertexShader();
233         reset();
234     }
235     emit vertexShaderChanged();
236 }
237
238 void QQuickCustomParticle::updateVertexShader()
239 {
240     m_common.disconnectPropertySignals(this, Key::VertexShader);
241     qDeleteAll(m_common.signalMappers[Key::VertexShader]);
242     m_common.uniformData[Key::VertexShader].clear();
243     m_common.signalMappers[Key::VertexShader].clear();
244     m_common.attributes.clear();
245     m_common.attributes.append("qt_ParticlePos");
246     m_common.attributes.append("qt_ParticleTex");
247     m_common.attributes.append("qt_ParticleData");
248     m_common.attributes.append("qt_ParticleVec");
249     m_common.attributes.append("qt_ParticleR");
250
251     UniformData d;
252     d.name = "qt_Matrix";
253     d.specialType = UniformData::Matrix;
254     m_common.uniformData[Key::VertexShader].append(d);
255     m_common.signalMappers[Key::VertexShader].append(0);
256
257     d.name = "qt_Timestamp";
258     d.specialType = UniformData::None;
259     m_common.uniformData[Key::VertexShader].append(d);
260     m_common.signalMappers[Key::VertexShader].append(0);
261
262     const QByteArray &code = m_common.source.sourceCode[Key::VertexShader];
263     if (!code.isEmpty())
264         m_common.lookThroughShaderCode(this, Key::VertexShader, code);
265
266     m_common.connectPropertySignals(this, Key::VertexShader);
267 }
268
269 void QQuickCustomParticle::reset()
270 {
271     QQuickParticlePainter::reset();
272     update();
273 }
274
275 QSGNode *QQuickCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
276 {
277     QQuickShaderEffectNode *rootNode = static_cast<QQuickShaderEffectNode *>(oldNode);
278     if (m_pleaseReset){
279         delete rootNode;//Automatically deletes children
280         rootNode = 0;
281         m_nodes.clear();
282         m_pleaseReset = false;
283         m_dirtyProgram = true;
284     }
285
286     if (m_system && m_system->isRunning() && !m_system->isPaused()){
287         rootNode = prepareNextFrame(rootNode);
288         if (rootNode)
289             update();
290     }
291
292     return rootNode;
293 }
294
295 QQuickShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickShaderEffectNode *rootNode)
296 {
297     if (!rootNode)
298         rootNode = buildCustomNodes();
299
300     if (!rootNode)
301         return 0;
302
303     if (m_dirtyProgram) {
304         QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(rootNode->material());
305         Q_ASSERT(material);
306
307         Key s = m_common.source;
308         if (s.sourceCode[Key::FragmentShader].isEmpty())
309             s.sourceCode[Key::FragmentShader] = qt_particles_default_fragment_code;
310         if (s.sourceCode[Key::VertexShader].isEmpty())
311             s.sourceCode[Key::VertexShader] = qt_particles_default_vertex_code;
312         s.sourceCode[Key::VertexShader] = qt_particles_template_vertex_code + s.sourceCode[Key::VertexShader];
313         s.className = metaObject()->className();
314
315         material->setProgramSource(s);
316         material->attributes = m_common.attributes;
317         foreach (QQuickShaderEffectNode* node, m_nodes)
318             node->markDirty(QSGNode::DirtyMaterial);
319
320         m_dirtyProgram = false;
321         m_dirtyUniforms = true;
322     }
323
324     m_lastTime = m_system->systemSync(this) / 1000.;
325     if (true) //Currently this is how we update timestamp... potentially over expensive.
326         buildData(rootNode);
327     return rootNode;
328 }
329
330 QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes()
331 {
332 #ifdef QT_OPENGL_ES_2
333     if (m_count * 4 > 0xffff) {
334         printf("CustomParticle: Too many particles... \n");
335         return 0;
336     }
337 #endif
338
339     if (m_count <= 0) {
340         printf("CustomParticle: Too few particles... \n");
341         return 0;
342     }
343
344     if (m_groups.isEmpty())
345         return 0;
346
347     QQuickShaderEffectNode *rootNode = 0;
348     QQuickShaderEffectMaterial *material = new QQuickShaderEffectMaterial;
349     m_dirtyProgram = true;
350
351     foreach (const QString &str, m_groups){
352         int gIdx = m_system->groupIds[str];
353         int count = m_system->groupData[gIdx]->size();
354
355         QQuickShaderEffectNode* node = new QQuickShaderEffectNode();
356         m_nodes.insert(gIdx, node);
357
358         node->setMaterial(material);
359
360         //Create Particle Geometry
361         int vCount = count * 4;
362         int iCount = count * 6;
363         QSGGeometry *g = new QSGGeometry(PlainParticle_AttributeSet, vCount, iCount);
364         g->setDrawingMode(GL_TRIANGLES);
365         node->setGeometry(g);
366         node->setFlag(QSGNode::OwnsGeometry, true);
367         PlainVertex *vertices = (PlainVertex *) g->vertexData();
368         for (int p=0; p < count; ++p) {
369             commit(gIdx, p);
370             vertices[0].tx = 0;
371             vertices[0].ty = 0;
372
373             vertices[1].tx = 1;
374             vertices[1].ty = 0;
375
376             vertices[2].tx = 0;
377             vertices[2].ty = 1;
378
379             vertices[3].tx = 1;
380             vertices[3].ty = 1;
381             vertices += 4;
382         }
383         quint16 *indices = g->indexDataAsUShort();
384         for (int i=0; i < count; ++i) {
385             int o = i * 4;
386             indices[0] = o;
387             indices[1] = o + 1;
388             indices[2] = o + 2;
389             indices[3] = o + 1;
390             indices[4] = o + 3;
391             indices[5] = o + 2;
392             indices += 6;
393         }
394     }
395
396     QHash<int, QQuickShaderEffectNode*>::const_iterator it = m_nodes.begin();
397     rootNode = it.value();
398     rootNode->setFlag(QSGNode::OwnsMaterial, true);
399     for (++it; it != m_nodes.end(); ++it)
400         rootNode->appendChildNode(it.value());
401
402     return rootNode;
403 }
404
405 void QQuickCustomParticle::sourceDestroyed(QObject *object)
406 {
407     m_common.sourceDestroyed(object);
408 }
409
410 void QQuickCustomParticle::propertyChanged(int mappedId)
411 {
412     bool textureProviderChanged;
413     m_common.propertyChanged(this, mappedId, &textureProviderChanged);
414     m_dirtyTextureProviders |= textureProviderChanged;
415     m_dirtyUniformValues = true;
416     update();
417 }
418
419
420 void QQuickCustomParticle::buildData(QQuickShaderEffectNode *rootNode)
421 {
422     if (!rootNode)
423         return;
424     for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
425         for (int i = 0; i < m_common.uniformData[shaderType].size(); ++i) {
426             if (m_common.uniformData[shaderType].at(i).name == "qt_Timestamp")
427                 m_common.uniformData[shaderType][i].value = qVariantFromValue(m_lastTime);
428         }
429     }
430     m_common.updateMaterial(rootNode, static_cast<QQuickShaderEffectMaterial *>(rootNode->material()),
431                             m_dirtyUniforms, true, m_dirtyTextureProviders);
432     foreach (QQuickShaderEffectNode* node, m_nodes)
433         node->markDirty(QSGNode::DirtyMaterial);
434     m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false;
435 }
436
437 void QQuickCustomParticle::initialize(int gIdx, int pIdx)
438 {
439     QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
440     datum->r = rand()/(qreal)RAND_MAX;
441 }
442
443 void QQuickCustomParticle::commit(int gIdx, int pIdx)
444 {
445     if (m_nodes[gIdx] == 0)
446         return;
447
448     QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
449     PlainVertices *particles = (PlainVertices *) m_nodes[gIdx]->geometry()->vertexData();
450     PlainVertex *vertices = (PlainVertex *)&particles[pIdx];
451     for (int i=0; i<4; ++i) {
452         vertices[i].x = datum->x - m_systemOffset.x();
453         vertices[i].y = datum->y - m_systemOffset.y();
454         vertices[i].t = datum->t;
455         vertices[i].lifeSpan = datum->lifeSpan;
456         vertices[i].size = datum->size;
457         vertices[i].endSize = datum->endSize;
458         vertices[i].vx = datum->vx;
459         vertices[i].vy = datum->vy;
460         vertices[i].ax = datum->ax;
461         vertices[i].ay = datum->ay;
462         vertices[i].r = datum->r;
463     }
464 }
465
466 void QQuickCustomParticle::itemChange(ItemChange change, const ItemChangeData &value)
467 {
468     if (change == QQuickItem::ItemSceneChange)
469         m_common.updateCanvas(value.canvas);
470     QQuickParticlePainter::itemChange(change, value);
471 }
472
473
474 QT_END_NAMESPACE