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 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"
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 \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
133 QQuickCustomParticle::QQuickCustomParticle(QQuickItem* parent)
134 : QQuickParticlePainter(parent)
135 , m_dirtyUniforms(true)
136 , m_dirtyUniformValues(true)
137 , m_dirtyTextureProviders(true)
138 , m_dirtyProgram(true)
140 setFlag(QQuickItem::ItemHasContents);
143 void QQuickCustomParticle::sceneGraphInvalidated()
148 QQuickCustomParticle::~QQuickCustomParticle()
152 void QQuickCustomParticle::componentComplete()
154 m_common.updateShader(this, Key::FragmentShader);
155 updateVertexShader();
157 QQuickParticlePainter::componentComplete();
161 //Trying to keep the shader conventions the same as in qsgshadereffectitem
163 \qmlproperty string QtQuick.Particles2::CustomParticle::fragmentShader
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".
171 void QQuickCustomParticle::setFragmentShader(const QByteArray &code)
173 if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData())
175 m_common.source.sourceCode[Key::FragmentShader] = code;
176 m_dirtyProgram = true;
177 if (isComponentComplete()) {
178 m_common.updateShader(this, Key::FragmentShader);
181 emit fragmentShaderChanged();
185 \qmlproperty string QtQuick.Particles2::CustomParticle::vertexShader
187 This property holds the vertex shader's GLSL source code.
189 The default shader passes the texture coordinate along to the fragment
190 shader as "varying highp vec2 qt_TexCoord0".
192 To aid writing a particle vertex shader, the following GLSL code is prepended
193 to your vertex shader:
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;
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.)
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);
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.
225 void QQuickCustomParticle::setVertexShader(const QByteArray &code)
227 if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData())
229 m_common.source.sourceCode[Key::VertexShader] = code;
231 m_dirtyProgram = true;
232 if (isComponentComplete()) {
233 updateVertexShader();
236 emit vertexShaderChanged();
239 void QQuickCustomParticle::updateVertexShader()
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");
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);
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);
263 const QByteArray &code = m_common.source.sourceCode[Key::VertexShader];
265 m_common.lookThroughShaderCode(this, Key::VertexShader, code);
267 m_common.connectPropertySignals(this, Key::VertexShader);
270 void QQuickCustomParticle::reset()
272 QQuickParticlePainter::reset();
276 QSGNode *QQuickCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
278 QQuickShaderEffectNode *rootNode = static_cast<QQuickShaderEffectNode *>(oldNode);
280 delete rootNode;//Automatically deletes children
283 m_pleaseReset = false;
284 m_dirtyProgram = true;
287 if (m_system && m_system->isRunning() && !m_system->isPaused()){
288 rootNode = prepareNextFrame(rootNode);
296 QQuickShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickShaderEffectNode *rootNode)
299 rootNode = buildCustomNodes();
304 if (m_dirtyProgram) {
305 QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(rootNode->material());
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();
316 material->setProgramSource(s);
317 material->attributes = m_common.attributes;
318 foreach (QQuickShaderEffectNode* node, m_nodes)
319 node->markDirty(QSGNode::DirtyMaterial);
321 m_dirtyProgram = false;
322 m_dirtyUniforms = true;
325 m_lastTime = m_system->systemSync(this) / 1000.;
326 if (true) //Currently this is how we update timestamp... potentially over expensive.
331 QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes()
333 #ifdef QT_OPENGL_ES_2
334 if (m_count * 4 > 0xffff) {
335 printf("CustomParticle: Too many particles... \n");
341 printf("CustomParticle: Too few particles... \n");
345 if (m_groups.isEmpty())
348 QQuickShaderEffectNode *rootNode = 0;
349 QQuickShaderEffectMaterial *material = new QQuickShaderEffectMaterial;
350 m_dirtyProgram = true;
352 foreach (const QString &str, m_groups){
353 int gIdx = m_system->groupIds[str];
354 int count = m_system->groupData[gIdx]->size();
356 QQuickShaderEffectNode* node = new QQuickShaderEffectNode();
357 m_nodes.insert(gIdx, node);
359 node->setMaterial(material);
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) {
384 quint16 *indices = g->indexDataAsUShort();
385 for (int i=0; i < count; ++i) {
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());
406 void QQuickCustomParticle::sourceDestroyed(QObject *object)
408 m_common.sourceDestroyed(object);
411 void QQuickCustomParticle::propertyChanged(int mappedId)
413 bool textureProviderChanged;
414 m_common.propertyChanged(this, mappedId, &textureProviderChanged);
415 m_dirtyTextureProviders |= textureProviderChanged;
416 m_dirtyUniformValues = true;
421 void QQuickCustomParticle::buildData(QQuickShaderEffectNode *rootNode)
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);
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;
438 void QQuickCustomParticle::initialize(int gIdx, int pIdx)
440 QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
441 datum->r = rand()/(qreal)RAND_MAX;
444 void QQuickCustomParticle::commit(int gIdx, int pIdx)
446 if (m_nodes[gIdx] == 0)
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;
467 void QQuickCustomParticle::itemChange(ItemChange change, const ItemChangeData &value)
469 if (change == QQuickItem::ItemSceneChange)
470 m_common.updateWindow(value.window);
471 QQuickParticlePainter::itemChange(change, value);