1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qquickcustomparticle_p.h"
43 #include <QtQuick/private/qquickshadereffectmesh_p.h>
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"
72 static const char qt_particles_default_vertex_code[] =
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"
82 " gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity; \n"
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
93 static QSGGeometry::AttributeSet PlainParticle_AttributeSet =
96 (2 + 2 + 4 + 4 + 1) * sizeof(float),
97 PlainParticle_Attributes
116 struct PlainVertices {
124 \qmlclass CustomParticle QQuickCustomParticle
125 \inqmlmodule QtQuick.Particles 2
126 \inherits ParticlePainter
127 \brief For specifying shaders to paint particles
128 \ingroup qtquick-particles
132 QQuickCustomParticle::QQuickCustomParticle(QQuickItem* parent)
133 : QQuickParticlePainter(parent)
134 , m_dirtyUniforms(true)
135 , m_dirtyUniformValues(true)
136 , m_dirtyTextureProviders(true)
137 , m_dirtyProgram(true)
139 setFlag(QQuickItem::ItemHasContents);
142 void QQuickCustomParticle::sceneGraphInvalidated()
147 QQuickCustomParticle::~QQuickCustomParticle()
151 void QQuickCustomParticle::componentComplete()
153 m_common.updateShader(this, Key::FragmentShader);
154 updateVertexShader();
156 QQuickParticlePainter::componentComplete();
160 //Trying to keep the shader conventions the same as in qsgshadereffectitem
162 \qmlproperty string QtQuick.Particles2::CustomParticle::fragmentShader
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".
170 void QQuickCustomParticle::setFragmentShader(const QByteArray &code)
172 if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData())
174 m_common.source.sourceCode[Key::FragmentShader] = code;
175 m_dirtyProgram = true;
176 if (isComponentComplete()) {
177 m_common.updateShader(this, Key::FragmentShader);
180 emit fragmentShaderChanged();
184 \qmlproperty string QtQuick.Particles2::CustomParticle::vertexShader
186 This property holds the vertex shader's GLSL source code.
188 The default shader passes the texture coordinate along to the fragment
189 shader as "varying highp vec2 qt_TexCoord0".
191 To aid writing a particle vertex shader, the following GLSL code is prepended
192 to your vertex shader:
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;
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.)
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);
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.
224 void QQuickCustomParticle::setVertexShader(const QByteArray &code)
226 if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData())
228 m_common.source.sourceCode[Key::VertexShader] = code;
230 m_dirtyProgram = true;
231 if (isComponentComplete()) {
232 updateVertexShader();
235 emit vertexShaderChanged();
238 void QQuickCustomParticle::updateVertexShader()
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");
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);
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);
262 const QByteArray &code = m_common.source.sourceCode[Key::VertexShader];
264 m_common.lookThroughShaderCode(this, Key::VertexShader, code);
266 m_common.connectPropertySignals(this, Key::VertexShader);
269 void QQuickCustomParticle::reset()
271 QQuickParticlePainter::reset();
275 QSGNode *QQuickCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
277 QQuickShaderEffectNode *rootNode = static_cast<QQuickShaderEffectNode *>(oldNode);
279 delete rootNode;//Automatically deletes children
282 m_pleaseReset = false;
283 m_dirtyProgram = true;
286 if (m_system && m_system->isRunning() && !m_system->isPaused()){
287 rootNode = prepareNextFrame(rootNode);
295 QQuickShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickShaderEffectNode *rootNode)
298 rootNode = buildCustomNodes();
303 if (m_dirtyProgram) {
304 QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(rootNode->material());
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();
315 material->setProgramSource(s);
316 material->attributes = m_common.attributes;
317 foreach (QQuickShaderEffectNode* node, m_nodes)
318 node->markDirty(QSGNode::DirtyMaterial);
320 m_dirtyProgram = false;
321 m_dirtyUniforms = true;
324 m_lastTime = m_system->systemSync(this) / 1000.;
325 if (true) //Currently this is how we update timestamp... potentially over expensive.
330 QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes()
332 #ifdef QT_OPENGL_ES_2
333 if (m_count * 4 > 0xffff) {
334 printf("CustomParticle: Too many particles... \n");
340 printf("CustomParticle: Too few particles... \n");
344 if (m_groups.isEmpty())
347 QQuickShaderEffectNode *rootNode = 0;
348 QQuickShaderEffectMaterial *material = new QQuickShaderEffectMaterial;
349 m_dirtyProgram = true;
351 foreach (const QString &str, m_groups){
352 int gIdx = m_system->groupIds[str];
353 int count = m_system->groupData[gIdx]->size();
355 QQuickShaderEffectNode* node = new QQuickShaderEffectNode();
356 m_nodes.insert(gIdx, node);
358 node->setMaterial(material);
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) {
383 quint16 *indices = g->indexDataAsUShort();
384 for (int i=0; i < count; ++i) {
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());
405 void QQuickCustomParticle::sourceDestroyed(QObject *object)
407 m_common.sourceDestroyed(object);
410 void QQuickCustomParticle::propertyChanged(int mappedId)
412 bool textureProviderChanged;
413 m_common.propertyChanged(this, mappedId, &textureProviderChanged);
414 m_dirtyTextureProviders |= textureProviderChanged;
415 m_dirtyUniformValues = true;
420 void QQuickCustomParticle::buildData(QQuickShaderEffectNode *rootNode)
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);
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;
437 void QQuickCustomParticle::initialize(int gIdx, int pIdx)
439 QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
440 datum->r = rand()/(qreal)RAND_MAX;
443 void QQuickCustomParticle::commit(int gIdx, int pIdx)
445 if (m_nodes[gIdx] == 0)
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;
466 void QQuickCustomParticle::itemChange(ItemChange change, const ItemChangeData &value)
468 if (change == QQuickItem::ItemSceneChange)
469 m_common.updateCanvas(value.canvas);
470 QQuickParticlePainter::itemChange(change, value);