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