Merge remote-tracking branch 'origin/api_changes'
[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 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