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);