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 The CustomParticle element allows you to specify your own shader to paint particles.
131 QQuickCustomParticle::QQuickCustomParticle(QQuickItem* parent)
132 : QQuickParticlePainter(parent)
133 , m_dirtyUniforms(true)
134 , m_dirtyUniformValues(true)
135 , m_dirtyTextureProviders(true)
136 , m_dirtyProgram(true)
138 setFlag(QQuickItem::ItemHasContents);
141 void QQuickCustomParticle::sceneGraphInvalidated()
146 QQuickCustomParticle::~QQuickCustomParticle()
150 void QQuickCustomParticle::componentComplete()
152 m_common.updateShader(this, Key::FragmentShader);
153 updateVertexShader();
155 QQuickParticlePainter::componentComplete();
159 //Trying to keep the shader conventions the same as in qsgshadereffectitem
161 \qmlproperty string QtQuick.Particles2::CustomParticle::fragmentShader
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".
169 void QQuickCustomParticle::setFragmentShader(const QByteArray &code)
171 if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData())
173 m_common.source.sourceCode[Key::FragmentShader] = code;
174 m_dirtyProgram = true;
175 if (isComponentComplete()) {
176 m_common.updateShader(this, Key::FragmentShader);
179 emit fragmentShaderChanged();
183 \qmlproperty string QtQuick.Particles2::CustomParticle::vertexShader
185 This property holds the vertex shader's GLSL source code.
187 The default shader passes the texture coordinate along to the fragment
188 shader as "varying highp vec2 qt_TexCoord0".
190 To aid writing a particle vertex shader, the following GLSL code is prepended
191 to your vertex shader:
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;
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.)
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);
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.
223 void QQuickCustomParticle::setVertexShader(const QByteArray &code)
225 if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData())
227 m_common.source.sourceCode[Key::VertexShader] = code;
229 m_dirtyProgram = true;
230 if (isComponentComplete()) {
231 updateVertexShader();
234 emit vertexShaderChanged();
237 void QQuickCustomParticle::updateVertexShader()
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");
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);
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);
261 const QByteArray &code = m_common.source.sourceCode[Key::VertexShader];
263 m_common.lookThroughShaderCode(this, Key::VertexShader, code);
265 m_common.connectPropertySignals(this, Key::VertexShader);
268 void QQuickCustomParticle::reset()
270 QQuickParticlePainter::reset();
274 QSGNode *QQuickCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
276 QQuickShaderEffectNode *rootNode = static_cast<QQuickShaderEffectNode *>(oldNode);
278 delete rootNode;//Automatically deletes children
281 m_pleaseReset = false;
282 m_dirtyProgram = true;
285 if (m_system && m_system->isRunning() && !m_system->isPaused()){
286 rootNode = prepareNextFrame(rootNode);
294 QQuickShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickShaderEffectNode *rootNode)
297 rootNode = buildCustomNodes();
302 if (m_dirtyProgram) {
303 QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(rootNode->material());
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();
314 material->setProgramSource(s);
315 material->attributes = m_common.attributes;
316 foreach (QQuickShaderEffectNode* node, m_nodes)
317 node->markDirty(QSGNode::DirtyMaterial);
319 m_dirtyProgram = false;
320 m_dirtyUniforms = true;
323 m_lastTime = m_system->systemSync(this) / 1000.;
324 if (true) //Currently this is how we update timestamp... potentially over expensive.
329 QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes()
331 #ifdef QT_OPENGL_ES_2
332 if (m_count * 4 > 0xffff) {
333 printf("CustomParticle: Too many particles... \n");
339 printf("CustomParticle: Too few particles... \n");
343 if (m_groups.isEmpty())
346 QQuickShaderEffectNode *rootNode = 0;
347 QQuickShaderEffectMaterial *material = new QQuickShaderEffectMaterial;
348 m_dirtyProgram = true;
350 foreach (const QString &str, m_groups){
351 int gIdx = m_system->groupIds[str];
352 int count = m_system->groupData[gIdx]->size();
354 QQuickShaderEffectNode* node = new QQuickShaderEffectNode();
355 m_nodes.insert(gIdx, node);
357 node->setMaterial(material);
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) {
382 quint16 *indices = g->indexDataAsUShort();
383 for (int i=0; i < count; ++i) {
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());
404 void QQuickCustomParticle::sourceDestroyed(QObject *object)
406 m_common.sourceDestroyed(object);
409 void QQuickCustomParticle::propertyChanged(int mappedId)
411 bool textureProviderChanged;
412 m_common.propertyChanged(this, mappedId, &textureProviderChanged);
413 m_dirtyTextureProviders |= textureProviderChanged;
414 m_dirtyUniformValues = true;
419 void QQuickCustomParticle::buildData(QQuickShaderEffectNode *rootNode)
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);
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;
436 void QQuickCustomParticle::initialize(int gIdx, int pIdx)
438 QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
439 datum->r = rand()/(qreal)RAND_MAX;
442 void QQuickCustomParticle::commit(int gIdx, int pIdx)
444 if (m_nodes[gIdx] == 0)
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;
465 void QQuickCustomParticle::itemChange(ItemChange change, const ItemChangeData &value)
467 if (change == QQuickItem::ItemSceneChange)
468 m_common.updateCanvas(value.canvas);
469 QQuickParticlePainter::itemChange(change, value);