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