1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtQml module of the Qt Toolkit.
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.
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.
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.
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.
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 #if !defined(Q_OS_BLACKBERRY)
64 " highp float currentSize = mix(size, endSize, t * t);\n"
66 " highp float mixWorkaround = (endSize - size) * t * t;\n"
67 " highp float currentSize = mixWorkaround + size;\n"
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"
77 static const char qt_particles_default_vertex_code[] =
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"
87 " gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity; \n"
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
98 static QSGGeometry::AttributeSet PlainParticle_AttributeSet =
100 5, // Attribute Count
101 (2 + 2 + 4 + 4 + 1) * sizeof(float),
102 PlainParticle_Attributes
121 struct PlainVertices {
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
138 QQuickCustomParticle::QQuickCustomParticle(QQuickItem* parent)
139 : QQuickParticlePainter(parent)
140 , m_dirtyUniforms(true)
141 , m_dirtyUniformValues(true)
142 , m_dirtyTextureProviders(true)
143 , m_dirtyProgram(true)
145 setFlag(QQuickItem::ItemHasContents);
148 void QQuickCustomParticle::sceneGraphInvalidated()
153 QQuickCustomParticle::~QQuickCustomParticle()
157 void QQuickCustomParticle::componentComplete()
159 m_common.updateShader(this, Key::FragmentShader);
160 updateVertexShader();
162 QQuickParticlePainter::componentComplete();
166 //Trying to keep the shader conventions the same as in qsgshadereffectitem
168 \qmlproperty string QtQuick.Particles2::CustomParticle::fragmentShader
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".
176 void QQuickCustomParticle::setFragmentShader(const QByteArray &code)
178 if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData())
180 m_common.source.sourceCode[Key::FragmentShader] = code;
181 m_dirtyProgram = true;
182 if (isComponentComplete()) {
183 m_common.updateShader(this, Key::FragmentShader);
186 emit fragmentShaderChanged();
190 \qmlproperty string QtQuick.Particles2::CustomParticle::vertexShader
192 This property holds the vertex shader's GLSL source code.
194 The default shader passes the texture coordinate along to the fragment
195 shader as "varying highp vec2 qt_TexCoord0".
197 To aid writing a particle vertex shader, the following GLSL code is prepended
198 to your vertex shader:
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;
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.)
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);
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.
230 void QQuickCustomParticle::setVertexShader(const QByteArray &code)
232 if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData())
234 m_common.source.sourceCode[Key::VertexShader] = code;
236 m_dirtyProgram = true;
237 if (isComponentComplete()) {
238 updateVertexShader();
241 emit vertexShaderChanged();
244 void QQuickCustomParticle::updateVertexShader()
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");
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);
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);
268 const QByteArray &code = m_common.source.sourceCode[Key::VertexShader];
270 m_common.lookThroughShaderCode(this, Key::VertexShader, code);
272 m_common.connectPropertySignals(this, Key::VertexShader);
275 void QQuickCustomParticle::reset()
277 QQuickParticlePainter::reset();
281 QSGNode *QQuickCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
283 QQuickShaderEffectNode *rootNode = static_cast<QQuickShaderEffectNode *>(oldNode);
285 delete rootNode;//Automatically deletes children
288 m_pleaseReset = false;
289 m_dirtyProgram = true;
292 if (m_system && m_system->isRunning() && !m_system->isPaused()){
293 rootNode = prepareNextFrame(rootNode);
301 QQuickShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickShaderEffectNode *rootNode)
304 rootNode = buildCustomNodes();
309 if (m_dirtyProgram) {
310 QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(rootNode->material());
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();
321 material->setProgramSource(s);
322 material->attributes = m_common.attributes;
323 foreach (QQuickShaderEffectNode* node, m_nodes)
324 node->markDirty(QSGNode::DirtyMaterial);
326 m_dirtyProgram = false;
327 m_dirtyUniforms = true;
330 m_lastTime = m_system->systemSync(this) / 1000.;
331 if (true) //Currently this is how we update timestamp... potentially over expensive.
336 QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes()
338 #ifdef QT_OPENGL_ES_2
339 if (m_count * 4 > 0xffff) {
340 printf("CustomParticle: Too many particles... \n");
346 printf("CustomParticle: Too few particles... \n");
350 if (m_groups.isEmpty())
353 QQuickShaderEffectNode *rootNode = 0;
354 QQuickShaderEffectMaterial *material = new QQuickShaderEffectMaterial;
355 m_dirtyProgram = true;
357 foreach (const QString &str, m_groups){
358 int gIdx = m_system->groupIds[str];
359 int count = m_system->groupData[gIdx]->size();
361 QQuickShaderEffectNode* node = new QQuickShaderEffectNode();
362 m_nodes.insert(gIdx, node);
364 node->setMaterial(material);
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) {
389 quint16 *indices = g->indexDataAsUShort();
390 for (int i=0; i < count; ++i) {
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());
411 void QQuickCustomParticle::sourceDestroyed(QObject *object)
413 m_common.sourceDestroyed(object);
416 void QQuickCustomParticle::propertyChanged(int mappedId)
418 bool textureProviderChanged;
419 m_common.propertyChanged(this, mappedId, &textureProviderChanged);
420 m_dirtyTextureProviders |= textureProviderChanged;
421 m_dirtyUniformValues = true;
426 void QQuickCustomParticle::buildData(QQuickShaderEffectNode *rootNode)
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);
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;
443 void QQuickCustomParticle::initialize(int gIdx, int pIdx)
445 QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
446 datum->r = rand()/(qreal)RAND_MAX;
449 void QQuickCustomParticle::commit(int gIdx, int pIdx)
451 if (m_nodes[gIdx] == 0)
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;
472 void QQuickCustomParticle::itemChange(ItemChange change, const ItemChangeData &value)
474 if (change == QQuickItem::ItemSceneChange)
475 m_common.updateWindow(value.window);
476 QQuickParticlePainter::itemChange(change, value);