Don't accidentally use atlas images in particle nodes
[profile/ivi/qtdeclarative.git] / src / declarative / particles / qsgimageparticle.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the Declarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <private/qsgcontext_p.h>
43 #include <private/qsgadaptationlayer_p.h>
44 #include <qsgnode.h>
45 #include <qsgtexturematerial.h>
46 #include <qsgtexture.h>
47 #include <QFile>
48 #include "qsgimageparticle_p.h"
49 #include "qsgparticleemitter_p.h"
50 #include "qsgsprite_p.h"
51 #include "qsgspriteengine_p.h"
52 #include <QOpenGLFunctions>
53 #include <qsgengine.h>
54 #include <private/qsgtexture_p.h>
55
56 QT_BEGIN_NAMESPACE
57 //###Switch to define later, for now user-friendly (no compilation) debugging is worth it
58 DEFINE_BOOL_CONFIG_OPTION(qmlParticlesDebug, QML_PARTICLES_DEBUG)
59
60 #ifndef QT_OPENGL_ES_2
61 #define SHADER_DEFINES "#version 120\n"
62 #else
63 #define SHADER_DEFINES ""
64 #endif
65
66 //TODO: Make it larger on desktop? Requires fixing up shader code with the same define
67 #define UNIFORM_ARRAY_SIZE 64
68
69 const qreal CONV = 0.017453292519943295;
70 class ImageMaterialData
71 {
72     public:
73     ImageMaterialData()
74         : texture(0), colorTable(0)
75     {}
76
77     ~ImageMaterialData(){
78         delete texture;
79         delete colorTable;
80     }
81
82     QSGTexture *texture;
83     QSGTexture *colorTable;
84     float sizeTable[UNIFORM_ARRAY_SIZE];
85     float opacityTable[UNIFORM_ARRAY_SIZE];
86
87     qreal timestamp;
88     qreal entry;
89     QSizeF animSheetSize;
90 };
91
92 //TODO: Move shaders inline once they've stablilized
93 class TabledMaterialData : public ImageMaterialData {};
94 class TabledMaterial : public QSGSimpleMaterialShader<TabledMaterialData>
95 {
96     QSG_DECLARE_SIMPLE_SHADER(TabledMaterial, TabledMaterialData)
97
98 public:
99     TabledMaterial()
100     {
101         QFile vf(QStringLiteral(":defaultshaders/imagevertex.shader"));
102         vf.open(QFile::ReadOnly);
103         m_vertex_code = QByteArray(SHADER_DEFINES)
104             + QByteArray("#define TABLE\n#define DEFORM\n#define COLOR\n")
105             + vf.readAll();
106
107         QFile ff(QStringLiteral(":defaultshaders/imagefragment.shader"));
108         ff.open(QFile::ReadOnly);
109         m_fragment_code = QByteArray(SHADER_DEFINES)
110             + QByteArray("#define TABLE\n#define DEFORM\n#define COLOR\n")
111             + ff.readAll();
112
113         Q_ASSERT(!m_vertex_code.isNull());
114         Q_ASSERT(!m_fragment_code.isNull());
115     }
116
117     const char *vertexShader() const { return m_vertex_code.constData(); }
118     const char *fragmentShader() const { return m_fragment_code.constData(); }
119
120     QList<QByteArray> attributes() const {
121         return QList<QByteArray>() << "vPos" << "vTex" << "vData" << "vVec"
122             << "vColor" << "vDeformVec" << "vRotation";
123     };
124
125     void initialize() {
126         QSGSimpleMaterialShader<TabledMaterialData>::initialize();
127         program()->bind();
128         program()->setUniformValue("texture", 0);
129         program()->setUniformValue("colortable", 1);
130         glFuncs = QOpenGLContext::currentContext()->functions();
131         m_timestamp_id = program()->uniformLocation("timestamp");
132         m_entry_id = program()->uniformLocation("entry");
133         m_sizetable_id = program()->uniformLocation("sizetable");
134         m_opacitytable_id = program()->uniformLocation("opacitytable");
135     }
136
137     void updateState(const TabledMaterialData* d, const TabledMaterialData*) {
138         glFuncs->glActiveTexture(GL_TEXTURE1);
139         d->colorTable->bind();
140
141         glFuncs->glActiveTexture(GL_TEXTURE0);
142         d->texture->bind();
143
144         program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
145         program()->setUniformValue(m_entry_id, (float) d->entry);
146         program()->setUniformValueArray(m_sizetable_id, (float*) d->sizeTable, UNIFORM_ARRAY_SIZE, 1);
147         program()->setUniformValueArray(m_opacitytable_id, (float*) d->opacityTable, UNIFORM_ARRAY_SIZE, 1);
148     }
149
150     int m_entry_id;
151     int m_timestamp_id;
152     int m_sizetable_id;
153     int m_opacitytable_id;
154     QByteArray m_vertex_code;
155     QByteArray m_fragment_code;
156     QOpenGLFunctions* glFuncs;
157 };
158
159 class DeformableMaterialData : public ImageMaterialData {};
160 class DeformableMaterial : public QSGSimpleMaterialShader<DeformableMaterialData>
161 {
162     QSG_DECLARE_SIMPLE_SHADER(DeformableMaterial, DeformableMaterialData)
163
164 public:
165     DeformableMaterial()
166     {
167         QFile vf(QStringLiteral(":defaultshaders/imagevertex.shader"));
168         vf.open(QFile::ReadOnly);
169         m_vertex_code = QByteArray(SHADER_DEFINES)
170             + QByteArray("#define DEFORM\n#define COLOR\n")
171             + vf.readAll();
172
173         QFile ff(QStringLiteral(":defaultshaders/imagefragment.shader"));
174         ff.open(QFile::ReadOnly);
175         m_fragment_code = QByteArray(SHADER_DEFINES)
176             + QByteArray("#define DEFORM\n#define COLOR\n")
177             + ff.readAll();
178
179         Q_ASSERT(!m_vertex_code.isNull());
180         Q_ASSERT(!m_fragment_code.isNull());
181     }
182
183     const char *vertexShader() const { return m_vertex_code.constData(); }
184     const char *fragmentShader() const { return m_fragment_code.constData(); }
185
186     QList<QByteArray> attributes() const {
187         return QList<QByteArray>() << "vPos" << "vTex" << "vData" << "vVec"
188             << "vColor" << "vDeformVec" << "vRotation";
189     };
190
191     void initialize() {
192         QSGSimpleMaterialShader<DeformableMaterialData>::initialize();
193         program()->bind();
194         program()->setUniformValue("texture", 0);
195         glFuncs = QOpenGLContext::currentContext()->functions();
196         m_timestamp_id = program()->uniformLocation("timestamp");
197         m_entry_id = program()->uniformLocation("entry");
198     }
199
200     void updateState(const DeformableMaterialData* d, const DeformableMaterialData*) {
201         glFuncs->glActiveTexture(GL_TEXTURE0);
202         d->texture->bind();
203
204         program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
205         program()->setUniformValue(m_entry_id, (float) d->entry);
206     }
207
208     int m_entry_id;
209     int m_timestamp_id;
210     QByteArray m_vertex_code;
211     QByteArray m_fragment_code;
212     QOpenGLFunctions* glFuncs;
213 };
214
215 class SpriteMaterialData : public ImageMaterialData {};
216 class SpriteMaterial : public QSGSimpleMaterialShader<SpriteMaterialData>
217 {
218     QSG_DECLARE_SIMPLE_SHADER(SpriteMaterial, SpriteMaterialData)
219
220 public:
221     SpriteMaterial()
222     {
223         QFile vf(QStringLiteral(":defaultshaders/imagevertex.shader"));
224         vf.open(QFile::ReadOnly);
225         m_vertex_code = QByteArray(SHADER_DEFINES)
226             + QByteArray("#define SPRITE\n#define TABLE\n#define DEFORM\n#define COLOR\n")
227             + vf.readAll();
228
229         QFile ff(QStringLiteral(":defaultshaders/imagefragment.shader"));
230         ff.open(QFile::ReadOnly);
231         m_fragment_code = QByteArray(SHADER_DEFINES)
232             + QByteArray("#define SPRITE\n#define TABLE\n#define DEFORM\n#define COLOR\n")
233             + ff.readAll();
234
235         Q_ASSERT(!m_vertex_code.isNull());
236         Q_ASSERT(!m_fragment_code.isNull());
237     }
238
239     const char *vertexShader() const { return m_vertex_code.constData(); }
240     const char *fragmentShader() const { return m_fragment_code.constData(); }
241
242     QList<QByteArray> attributes() const {
243         return QList<QByteArray>() << "vPos" << "vTex" << "vData" << "vVec"
244             << "vColor" << "vDeformVec" << "vRotation" << "vAnimData" << "vAnimPos";
245     };
246
247     void initialize() {
248         QSGSimpleMaterialShader<SpriteMaterialData>::initialize();
249         program()->bind();
250         program()->setUniformValue("texture", 0);
251         program()->setUniformValue("colortable", 1);
252         glFuncs = QOpenGLContext::currentContext()->functions();
253         m_timestamp_id = program()->uniformLocation("timestamp");
254         m_animsize_id = program()->uniformLocation("animSheetSize");
255         m_entry_id = program()->uniformLocation("entry");
256         m_sizetable_id = program()->uniformLocation("sizetable");
257         m_opacitytable_id = program()->uniformLocation("opacitytable");
258     }
259
260     void updateState(const SpriteMaterialData* d, const SpriteMaterialData*) {
261         glFuncs->glActiveTexture(GL_TEXTURE1);
262         d->colorTable->bind();
263
264         // make sure we end by setting GL_TEXTURE0 as active texture
265         glFuncs->glActiveTexture(GL_TEXTURE0);
266         d->texture->bind();
267
268         program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
269         program()->setUniformValue(m_animsize_id, d->animSheetSize);
270         program()->setUniformValue(m_entry_id, (float) d->entry);
271         program()->setUniformValueArray(m_sizetable_id, (float*) d->sizeTable, 64, 1);
272         program()->setUniformValueArray(m_opacitytable_id, (float*) d->opacityTable, UNIFORM_ARRAY_SIZE, 1);
273     }
274
275     int m_timestamp_id;
276     int m_animsize_id;
277     int m_entry_id;
278     int m_sizetable_id;
279     int m_opacitytable_id;
280     QByteArray m_vertex_code;
281     QByteArray m_fragment_code;
282     QOpenGLFunctions* glFuncs;
283 };
284
285 class ColoredMaterialData : public ImageMaterialData {};
286 class ColoredMaterial : public QSGSimpleMaterialShader<ColoredMaterialData>
287 {
288     QSG_DECLARE_SIMPLE_SHADER(ColoredMaterial, ColoredMaterialData)
289
290 public:
291     ColoredMaterial()
292     {
293         QFile vf(QStringLiteral(":defaultshaders/imagevertex.shader"));
294         vf.open(QFile::ReadOnly);
295         m_vertex_code = QByteArray(SHADER_DEFINES)
296             + QByteArray("#define COLOR\n")
297             + vf.readAll();
298
299         QFile ff(QStringLiteral(":defaultshaders/imagefragment.shader"));
300         ff.open(QFile::ReadOnly);
301         m_fragment_code = QByteArray(SHADER_DEFINES)
302             + QByteArray("#define COLOR\n")
303             + ff.readAll();
304
305         Q_ASSERT(!m_vertex_code.isNull());
306         Q_ASSERT(!m_fragment_code.isNull());
307     }
308
309     const char *vertexShader() const { return m_vertex_code.constData(); }
310     const char *fragmentShader() const { return m_fragment_code.constData(); }
311
312     void activate() {
313         QSGSimpleMaterialShader<ColoredMaterialData>::activate();
314 #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
315         glEnable(GL_POINT_SPRITE);
316         glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
317 #endif
318     }
319
320     void deactivate() {
321         QSGSimpleMaterialShader<ColoredMaterialData>::deactivate();
322 #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
323         glDisable(GL_POINT_SPRITE);
324         glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
325 #endif
326     }
327
328     QList<QByteArray> attributes() const {
329         return QList<QByteArray>() << "vPos" << "vData" << "vVec" << "vColor";
330     }
331
332     void initialize() {
333         QSGSimpleMaterialShader<ColoredMaterialData>::initialize();
334         program()->bind();
335         program()->setUniformValue("texture", 0);
336         glFuncs = QOpenGLContext::currentContext()->functions();
337         m_timestamp_id = program()->uniformLocation("timestamp");
338         m_entry_id = program()->uniformLocation("entry");
339     }
340
341     void updateState(const ColoredMaterialData* d, const ColoredMaterialData*) {
342         glFuncs->glActiveTexture(GL_TEXTURE0);
343         d->texture->bind();
344
345         program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
346         program()->setUniformValue(m_entry_id, (float) d->entry);
347     }
348
349     int m_timestamp_id;
350     int m_entry_id;
351     QByteArray m_vertex_code;
352     QByteArray m_fragment_code;
353     QOpenGLFunctions* glFuncs;
354 };
355
356 class SimpleMaterialData : public ImageMaterialData {};
357 class SimpleMaterial : public QSGSimpleMaterialShader<SimpleMaterialData>
358 {
359     QSG_DECLARE_SIMPLE_SHADER(SimpleMaterial, SimpleMaterialData)
360
361 public:
362     SimpleMaterial()
363     {
364         QFile vf(QStringLiteral(":defaultshaders/imagevertex.shader"));
365         vf.open(QFile::ReadOnly);
366         m_vertex_code = QByteArray(SHADER_DEFINES)
367             + vf.readAll();
368
369         QFile ff(QStringLiteral(":defaultshaders/imagefragment.shader"));
370         ff.open(QFile::ReadOnly);
371         m_fragment_code = QByteArray(SHADER_DEFINES)
372             + ff.readAll();
373
374         Q_ASSERT(!m_vertex_code.isNull());
375         Q_ASSERT(!m_fragment_code.isNull());
376     }
377
378     const char *vertexShader() const { return m_vertex_code.constData(); }
379     const char *fragmentShader() const { return m_fragment_code.constData(); }
380
381     void activate() {
382         QSGSimpleMaterialShader<SimpleMaterialData>::activate();
383 #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
384         glEnable(GL_POINT_SPRITE);
385         glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
386 #endif
387     }
388
389     void deactivate() {
390         QSGSimpleMaterialShader<SimpleMaterialData>::deactivate();
391 #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
392         glDisable(GL_POINT_SPRITE);
393         glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
394 #endif
395     }
396
397     QList<QByteArray> attributes() const {
398         return QList<QByteArray>() << "vPos" << "vData" << "vVec";
399     }
400
401     void initialize() {
402         QSGSimpleMaterialShader<SimpleMaterialData>::initialize();
403         program()->bind();
404         program()->setUniformValue("texture", 0);
405         glFuncs = QOpenGLContext::currentContext()->functions();
406         m_timestamp_id = program()->uniformLocation("timestamp");
407         m_entry_id = program()->uniformLocation("entry");
408     }
409
410     void updateState(const SimpleMaterialData* d, const SimpleMaterialData*) {
411         glFuncs->glActiveTexture(GL_TEXTURE0);
412         d->texture->bind();
413
414         program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
415         program()->setUniformValue(m_entry_id, (float) d->entry);
416     }
417
418     int m_timestamp_id;
419     int m_entry_id;
420     QByteArray m_vertex_code;
421     QByteArray m_fragment_code;
422     QOpenGLFunctions* glFuncs;
423 };
424
425 void fillUniformArrayFromImage(float* array, const QImage& img, int size)
426 {
427     if (img.isNull()){
428         for (int i=0; i<size; i++)
429             array[i] = 1.0;
430         return;
431     }
432     QImage scaled = img.scaled(size,1);
433     for (int i=0; i<size; i++)
434         array[i] = qAlpha(scaled.pixel(i,0))/255.0;
435 }
436
437 /*!
438     \qmlclass ImageParticle QSGImageParticle
439     \inqmlmodule QtQuick.Particles 2
440     \inherits ParticlePainter
441     \brief The ImageParticle element visualizes logical particles using an image
442
443     This element renders a logical particle as an image. The image can be
444     \list
445     \o colorized
446     \o rotated
447     \o deformed
448     \o a sprite-based animation
449     \endlist
450
451     ImageParticles implictly share data on particles if multiple ImageParticles are painting
452     the same logical particle group. This is broken down along the four capabilities listed
453     above. So if one ImageParticle defines data for rendering the particles in one of those
454     capabilities, and the other does not, then both will draw the particles the same in that
455     aspect automatically. This is primarily useful when there is some random variation on
456     the particle which is supposed to stay with it when switching painters. If both ImageParticles
457     define how they should appear for that aspect, they diverge and each appears as it is defined.
458
459     This sharing of data happens behind the scenes based off of whether properties were implicitly or explicitly
460     set. One drawback of the current implementation is that it is only possible to reset the capabilities as a whole.
461     So if you explicity set an attribute affecting color, such as redVariation, and then reset it (by setting redVariation
462     to undefined), all color data will be reset and it will begin to have an implicit value of any shared color from
463     other ImageParticles.
464 */
465 /*!
466     \qmlproperty url QtQuick.Particles2::ImageParticle::source
467
468     The source image to be used.
469
470     If the image is a sprite animation, use the sprite property instead.
471 */
472 /*!
473     \qmlproperty list<Sprite> QtQuick.Particles2::ImageParticle::sprites
474
475     The sprite or sprites used to draw this particle.
476 */
477 /*!
478     \qmlproperty url QtQuick.Particles2::ImageParticle::colorTable
479
480     An image whose color will be used as a 1D texture to determine color over life. E.g. when
481     the particle is halfway through its lifetime, it will have the color specified halfway
482     across the image.
483
484     This color is blended with the color property and the color of the source image.
485 */
486 /*!
487     \qmlproperty url QtQuick.Particles2::ImageParticle::sizeTable
488
489     An image whose opacity will be used as a 1D texture to determine size over life.
490
491     This property is expected to be removed shortly, in favor of custom easing curves to determine size over life.
492 */
493 /*!
494     \qmlproperty url QtQuick.Particles2::ImageParticle::opacityTable
495
496     An image whose opacity will be used as a 1D texture to determine size over life.
497
498     This property is expected to be removed shortly, in favor of custom easing curves to determine opacity over life.
499 */
500 /*!
501     \qmlproperty color QtQuick.Particles2::ImageParticle::color
502
503     If a color is specified, the provided image will be colorized with it.
504
505     Default is white (no change).
506 */
507 /*!
508     \qmlproperty real QtQuick.Particles2::ImageParticle::colorVariation
509
510     This number represents the color variation applied to individual particles.
511     Setting colorVariation is the same as setting redVariation, greenVariation,
512     and blueVariation to the same number.
513
514     Each channel can vary between particle by up to colorVariation from its usual color.
515
516     Color is measured, per channel, from 0.0 to 1.0.
517
518     Default is 0.0
519 */
520 /*!
521     \qmlproperty real QtQuick.Particles2::ImageParticle::redVariation
522     The variation in the red color channel between particles.
523
524     Color is measured, per channel, from 0.0 to 1.0.
525
526     Default is 0.0
527 */
528 /*!
529     \qmlproperty real QtQuick.Particles2::ImageParticle::greenVariation
530     The variation in the green color channel between particles.
531
532     Color is measured, per channel, from 0.0 to 1.0.
533
534     Default is 0.0
535 */
536 /*!
537     \qmlproperty real QtQuick.Particles2::ImageParticle::blueVariation
538     The variation in the blue color channel between particles.
539
540     Color is measured, per channel, from 0.0 to 1.0.
541
542     Default is 0.0
543 */
544 /*!
545     \qmlproperty real QtQuick.Particles2::ImageParticle::alpha
546     An alpha to be applied to the image. This value is multiplied by the value in
547     the image, and the value in the color property.
548
549     Particles have additive blending, so lower alpha on single particles leads
550     to stronger effects when multiple particles overlap.
551
552     Alpha is measured from 0.0 to 1.0.
553
554     Default is 1.0
555 */
556 /*!
557     \qmlproperty real QtQuick.Particles2::ImageParticle::alphaVariation
558     The variation in the alpha channel between particles.
559
560     Alpha is measured from 0.0 to 1.0.
561
562     Default is 0.0
563 */
564 /*!
565     \qmlproperty real QtQuick.Particles2::ImageParticle::rotation
566
567     If set the image will be rotated by this many degrees before it is drawn.
568
569     The particle coordinates are not transformed.
570 */
571 /*!
572     \qmlproperty real QtQuick.Particles2::ImageParticle::rotationVariation
573
574     If set the rotation of individual particles will vary by up to this much
575     between particles.
576
577 */
578 /*!
579     \qmlproperty real QtQuick.Particles2::ImageParticle::rotationSpeed
580
581     If set particles will rotate at this speed in degrees/second.
582 */
583 /*!
584     \qmlproperty real QtQuick.Particles2::ImageParticle::rotationSpeedVariation
585
586     If set the rotationSpeed of individual particles will vary by up to this much
587     between particles.
588
589 */
590 /*!
591     \qmlproperty bool QtQuick.Particles2::ImageParticle::autoRotation
592
593     If set to true then a rotation will be applied on top of the particles rotation, so
594     that it faces the direction of travel. So to face away from the direction of travel,
595     set autoRotation to true and rotation to 180.
596
597     Default is false
598 */
599 /*!
600     \qmlproperty StochasticDirection QtQuick.Particles2::ImageParticle::xVector
601
602     Allows you to deform the particle image when drawn. The rectangular image will
603     be deformed so that the horizontal sides are in the shape of this vector instead
604     of (1,0).
605 */
606 /*!
607     \qmlproperty StochasticDirection QtQuick.Particles2::ImageParticle::yVector
608
609     Allows you to deform the particle image when drawn. The rectangular image will
610     be deformed so that the vertical sides are in the shape of this vector instead
611     of (0,1).
612 */
613 /*!
614     \qmlproperty EntryEffect QtQuick.Particles2::ImageParticle::entryEffect
615
616     This property provides basic and cheap entrance and exit effects for the particles.
617     For fine-grained control, see sizeTable and opacityTable.
618
619     Acceptable values are
620     \list
621     \o None: Particles just appear and disappear.
622     \o Fade: Particles fade in from 0 opacity at the start of their life, and fade out to 0 at the end.
623     \o Scale: Particles scale in from 0 size at the start of their life, and scale back to 0 at the end.
624     \endlist
625
626     Default value is Fade.
627 */
628 /*!
629     \qmlproperty bool QtQuick.Particles2::ImageParticle::spritesInterpolate
630
631     If set to true, sprite particles will interpolate between sprite frames each rendered frame, making
632     the sprites look smoother.
633
634     Default is true.
635 */
636
637
638 QSGImageParticle::QSGImageParticle(QSGItem* parent)
639     : QSGParticlePainter(parent)
640     , m_color_variation(0.0)
641     , m_rootNode(0)
642     , m_material(0)
643     , m_alphaVariation(0.0)
644     , m_alpha(1.0)
645     , m_redVariation(0.0)
646     , m_greenVariation(0.0)
647     , m_blueVariation(0.0)
648     , m_rotation(0)
649     , m_rotationVariation(0)
650     , m_rotationSpeed(0)
651     , m_rotationSpeedVariation(0)
652     , m_autoRotation(false)
653     , m_xVector(0)
654     , m_yVector(0)
655     , m_spriteEngine(0)
656     , m_spritesInterpolate(true)
657     , m_explicitColor(false)
658     , m_explicitRotation(false)
659     , m_explicitDeformation(false)
660     , m_explicitAnimation(false)
661     , m_bloat(false)
662     , perfLevel(Unknown)
663     , m_lastLevel(Unknown)
664     , m_debugMode(false)
665     , m_entryEffect(Fade)
666 {
667     setFlag(ItemHasContents);
668     m_debugMode = qmlParticlesDebug();
669 }
670
671 QSGImageParticle::~QSGImageParticle()
672 {
673 }
674
675 QDeclarativeListProperty<QSGSprite> QSGImageParticle::sprites()
676 {
677     return QDeclarativeListProperty<QSGSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
678 }
679
680 void QSGImageParticle::setImage(const QUrl &image)
681 {
682     if (image == m_image_name)
683         return;
684     m_image_name = image;
685     emit imageChanged();
686     reset();
687 }
688
689
690 void QSGImageParticle::setColortable(const QUrl &table)
691 {
692     if (table == m_colortable_name)
693         return;
694     m_colortable_name = table;
695     emit colortableChanged();
696     reset();
697 }
698
699 void QSGImageParticle::setSizetable(const QUrl &table)
700 {
701     if (table == m_sizetable_name)
702         return;
703     m_sizetable_name = table;
704     emit sizetableChanged();
705     reset();
706 }
707
708 void QSGImageParticle::setOpacitytable(const QUrl &table)
709 {
710     if (table == m_opacitytable_name)
711         return;
712     m_opacitytable_name = table;
713     emit opacitytableChanged();
714     reset();
715 }
716
717 void QSGImageParticle::setColor(const QColor &color)
718 {
719     if (color == m_color)
720         return;
721     m_color = color;
722     emit colorChanged();
723     m_explicitColor = true;
724     if (perfLevel < Colored)
725         reset();
726 }
727
728 void QSGImageParticle::setColorVariation(qreal var)
729 {
730     if (var == m_color_variation)
731         return;
732     m_color_variation = var;
733     emit colorVariationChanged();
734     m_explicitColor = true;
735     if (perfLevel < Colored)
736         reset();
737 }
738
739 void QSGImageParticle::setAlphaVariation(qreal arg)
740 {
741     if (m_alphaVariation != arg) {
742         m_alphaVariation = arg;
743         emit alphaVariationChanged(arg);
744     }
745     m_explicitColor = true;
746     if (perfLevel < Colored)
747         reset();
748 }
749
750 void QSGImageParticle::setAlpha(qreal arg)
751 {
752     if (m_alpha != arg) {
753         m_alpha = arg;
754         emit alphaChanged(arg);
755     }
756     m_explicitColor = true;
757     if (perfLevel < Colored)
758         reset();
759 }
760
761 void QSGImageParticle::setRedVariation(qreal arg)
762 {
763     if (m_redVariation != arg) {
764         m_redVariation = arg;
765         emit redVariationChanged(arg);
766     }
767     m_explicitColor = true;
768     if (perfLevel < Colored)
769         reset();
770 }
771
772 void QSGImageParticle::setGreenVariation(qreal arg)
773 {
774     if (m_greenVariation != arg) {
775         m_greenVariation = arg;
776         emit greenVariationChanged(arg);
777     }
778     m_explicitColor = true;
779     if (perfLevel < Colored)
780         reset();
781 }
782
783 void QSGImageParticle::setBlueVariation(qreal arg)
784 {
785     if (m_blueVariation != arg) {
786         m_blueVariation = arg;
787         emit blueVariationChanged(arg);
788     }
789     m_explicitColor = true;
790     if (perfLevel < Colored)
791         reset();
792 }
793
794 void QSGImageParticle::setRotation(qreal arg)
795 {
796     if (m_rotation != arg) {
797         m_rotation = arg;
798         emit rotationChanged(arg);
799     }
800     m_explicitRotation = true;
801     if (perfLevel < Deformable)
802         reset();
803 }
804
805 void QSGImageParticle::setRotationVariation(qreal arg)
806 {
807     if (m_rotationVariation != arg) {
808         m_rotationVariation = arg;
809         emit rotationVariationChanged(arg);
810     }
811     m_explicitRotation = true;
812     if (perfLevel < Deformable)
813         reset();
814 }
815
816 void QSGImageParticle::setRotationSpeed(qreal arg)
817 {
818     if (m_rotationSpeed != arg) {
819         m_rotationSpeed = arg;
820         emit rotationSpeedChanged(arg);
821     }
822     m_explicitRotation = true;
823     if (perfLevel < Deformable)
824         reset();
825 }
826
827 void QSGImageParticle::setRotationSpeedVariation(qreal arg)
828 {
829     if (m_rotationSpeedVariation != arg) {
830         m_rotationSpeedVariation = arg;
831         emit rotationSpeedVariationChanged(arg);
832     }
833     m_explicitRotation = true;
834     if (perfLevel < Deformable)
835         reset();
836 }
837
838 void QSGImageParticle::setAutoRotation(bool arg)
839 {
840     if (m_autoRotation != arg) {
841         m_autoRotation = arg;
842         emit autoRotationChanged(arg);
843     }
844     m_explicitRotation = true;
845     if (perfLevel < Deformable)
846         reset();
847 }
848
849 void QSGImageParticle::setXVector(QSGDirection* arg)
850 {
851     if (m_xVector != arg) {
852         m_xVector = arg;
853         emit xVectorChanged(arg);
854     }
855     m_explicitDeformation = true;
856     if (perfLevel < Deformable)
857         reset();
858 }
859
860 void QSGImageParticle::setYVector(QSGDirection* arg)
861 {
862     if (m_yVector != arg) {
863         m_yVector = arg;
864         emit yVectorChanged(arg);
865     }
866     m_explicitDeformation = true;
867     if (perfLevel < Deformable)
868         reset();
869 }
870
871 void QSGImageParticle::setSpritesInterpolate(bool arg)
872 {
873     if (m_spritesInterpolate != arg) {
874         m_spritesInterpolate = arg;
875         emit spritesInterpolateChanged(arg);
876     }
877 }
878
879 void QSGImageParticle::setBloat(bool arg)
880 {
881     if (m_bloat != arg) {
882         m_bloat = arg;
883         emit bloatChanged(arg);
884     }
885     if (perfLevel < 9999)
886         reset();
887 }
888
889 void QSGImageParticle::setEntryEffect(EntryEffect arg)
890 {
891     if (m_entryEffect != arg) {
892         m_entryEffect = arg;
893         if (m_material)
894             getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect;
895         emit entryEffectChanged(arg);
896     }
897 }
898
899 void QSGImageParticle::resetColor()
900 {
901     m_explicitColor = false;
902     foreach (const QString &str, m_groups)
903         foreach (QSGParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
904             if (d->colorOwner == this)
905                 d->colorOwner = 0;
906     m_color = QColor();
907     m_color_variation = 0.0f;
908     m_redVariation = 0.0f;
909     m_blueVariation = 0.0f;
910     m_greenVariation = 0.0f;
911     m_alpha = 1.0f;
912     m_alphaVariation = 0.0f;
913 }
914
915 void QSGImageParticle::resetRotation()
916 {
917     m_explicitRotation = false;
918     foreach (const QString &str, m_groups)
919         foreach (QSGParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
920             if (d->rotationOwner == this)
921                 d->rotationOwner = 0;
922     m_rotation = 0;
923     m_rotationVariation = 0;
924     m_rotationSpeed = 0;
925     m_rotationSpeedVariation = 0;
926     m_autoRotation = false;
927 }
928
929 void QSGImageParticle::resetDeformation()
930 {
931     m_explicitDeformation = false;
932     foreach (const QString &str, m_groups)
933         foreach (QSGParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
934             if (d->deformationOwner == this)
935                 d->deformationOwner = 0;
936     if (m_xVector)
937         delete m_xVector;
938     if (m_yVector)
939         delete m_yVector;
940     m_xVector = 0;
941     m_yVector = 0;
942 }
943
944 void QSGImageParticle::reset()
945 {
946     QSGParticlePainter::reset();
947     m_pleaseReset = true;
948     update();
949 }
950
951 void QSGImageParticle::createEngine()
952 {
953     if (m_spriteEngine)
954         delete m_spriteEngine;
955     if (m_sprites.count())
956         m_spriteEngine = new QSGSpriteEngine(m_sprites, this);
957     else
958         m_spriteEngine = 0;
959     m_explicitAnimation = true;
960     reset();
961 }
962
963 static QSGGeometry::Attribute SimpleParticle_Attributes[] = {
964     QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),             // Position
965     QSGGeometry::Attribute::create(1, 4, GL_FLOAT),             // Data
966     QSGGeometry::Attribute::create(2, 4, GL_FLOAT)             // Vectors
967 };
968
969 static QSGGeometry::AttributeSet SimpleParticle_AttributeSet =
970 {
971     3, // Attribute Count
972     ( 2 + 4 + 4 ) * sizeof(float),
973     SimpleParticle_Attributes
974 };
975
976 static QSGGeometry::Attribute ColoredParticle_Attributes[] = {
977     QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),             // Position
978     QSGGeometry::Attribute::create(1, 4, GL_FLOAT),             // Data
979     QSGGeometry::Attribute::create(2, 4, GL_FLOAT),             // Vectors
980     QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE),     // Colors
981 };
982
983 static QSGGeometry::AttributeSet ColoredParticle_AttributeSet =
984 {
985     4, // Attribute Count
986     ( 2 + 4 + 4 ) * sizeof(float) + 4 * sizeof(uchar),
987     ColoredParticle_Attributes
988 };
989
990 static QSGGeometry::Attribute DeformableParticle_Attributes[] = {
991     QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),             // Position
992     QSGGeometry::Attribute::create(1, 2, GL_FLOAT),             // TexCoord
993     QSGGeometry::Attribute::create(2, 4, GL_FLOAT),             // Data
994     QSGGeometry::Attribute::create(3, 4, GL_FLOAT),             // Vectors
995     QSGGeometry::Attribute::create(4, 4, GL_UNSIGNED_BYTE),     // Colors
996     QSGGeometry::Attribute::create(5, 4, GL_FLOAT),             // DeformationVectors
997     QSGGeometry::Attribute::create(6, 3, GL_FLOAT),             // Rotation
998 };
999
1000 static QSGGeometry::AttributeSet DeformableParticle_AttributeSet =
1001 {
1002     7, // Attribute Count
1003     (2 + 2 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar),
1004     DeformableParticle_Attributes
1005 };
1006
1007 static QSGGeometry::Attribute SpriteParticle_Attributes[] = {
1008     QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),             // Position
1009     QSGGeometry::Attribute::create(1, 2, GL_FLOAT),             // TexCoord
1010     QSGGeometry::Attribute::create(2, 4, GL_FLOAT),             // Data
1011     QSGGeometry::Attribute::create(3, 4, GL_FLOAT),             // Vectors
1012     QSGGeometry::Attribute::create(4, 4, GL_UNSIGNED_BYTE),     // Colors
1013     QSGGeometry::Attribute::create(5, 4, GL_FLOAT),             // DeformationVectors
1014     QSGGeometry::Attribute::create(6, 3, GL_FLOAT),             // Rotation
1015     QSGGeometry::Attribute::create(7, 4, GL_FLOAT),             // Anim Data
1016     QSGGeometry::Attribute::create(8, 4, GL_FLOAT)              // Anim Pos
1017 };
1018
1019 static QSGGeometry::AttributeSet SpriteParticle_AttributeSet =
1020 {
1021     9, // Attribute Count
1022     (2 + 2 + 4 + 4 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar),
1023     SpriteParticle_Attributes
1024 };
1025
1026 void QSGImageParticle::clearShadows()
1027 {
1028     m_shadowInit = false;
1029     foreach (const QVector<QSGParticleData*> data, m_shadowData)
1030         qDeleteAll(data);
1031     m_shadowData.clear();
1032 }
1033
1034 //Only call if you need to, may initialize the whole array first time
1035 QSGParticleData* QSGImageParticle::getShadowDatum(QSGParticleData* datum)
1036 {
1037     QSGParticleGroupData* gd = m_system->groupData[datum->group];
1038     if (!m_shadowData.contains(datum->group)) {
1039         QVector<QSGParticleData*> data;
1040         for (int i=0; i<gd->size(); i++){
1041             QSGParticleData* datum = new QSGParticleData(m_system);
1042             *datum = *(gd->data[i]);
1043             data << datum;
1044         }
1045         m_shadowData.insert(datum->group, data);
1046     }
1047     //### If dynamic resize is added, remember to potentially resize the shadow data on out-of-bounds access request
1048
1049     return m_shadowData[datum->group][datum->index];
1050 }
1051
1052 QSGGeometryNode* QSGImageParticle::buildParticleNodes()
1053 {
1054 #ifdef QT_OPENGL_ES_2
1055     if (m_count * 4 > 0xffff) {
1056         printf("ImageParticle: Too many particles - maximum 16,000 per ImageParticle.\n");//ES 2 vertex count limit is ushort
1057         return 0;
1058     }
1059 #endif
1060
1061     if (count() <= 0)
1062         return 0;
1063
1064     if (m_sprites.count() || m_bloat) {
1065         perfLevel = Sprites;
1066     } else if (!m_colortable_name.isEmpty() || !m_sizetable_name.isEmpty()
1067                || !m_opacitytable_name.isEmpty()) {
1068         perfLevel = Tabled;
1069     } else if (m_autoRotation || m_rotation || m_rotationVariation
1070                || m_rotationSpeed || m_rotationSpeedVariation
1071                || m_xVector || m_yVector) {
1072         perfLevel = Deformable;
1073     } else if (m_alphaVariation || m_alpha != 1.0 || m_color.isValid() || m_color_variation
1074                || m_redVariation || m_blueVariation || m_greenVariation) {
1075         perfLevel = Colored;
1076     } else {
1077         perfLevel = Simple;
1078     }
1079
1080     foreach (const QString &str, m_groups){//For sharing higher levels, need to have highest used so it renders
1081         int gIdx = m_system->groupIds[str];
1082         foreach (QSGParticlePainter* p, m_system->groupData[gIdx]->painters){
1083             QSGImageParticle* other = qobject_cast<QSGImageParticle*>(p);
1084             if (other){
1085                 if (other->perfLevel > perfLevel) {
1086                     if (other->perfLevel >= Tabled){//Deformable is the highest level needed for this, anything higher isn't shared (or requires your own sprite)
1087                         if (perfLevel < Deformable)
1088                             perfLevel = Deformable;
1089                     } else {
1090                         perfLevel = other->perfLevel;
1091                     }
1092                 } else if (other->perfLevel < perfLevel) {
1093                     other->reset();
1094                 }
1095             }
1096         }
1097     }
1098
1099     if (perfLevel >= Colored  && !m_color.isValid())
1100         m_color = QColor(Qt::white);//Hidden default, but different from unset
1101
1102     QImage image;
1103     if (perfLevel >= Sprites){
1104         if (!m_spriteEngine) {
1105             qWarning() << "ImageParticle: No sprite engine...";
1106             return 0;
1107         }
1108         image = m_spriteEngine->assembledImage();
1109         if (image.isNull())//Warning is printed in engine
1110             return 0;
1111     } else {
1112         image = QImage(m_image_name.toLocalFile());
1113         if (image.isNull()) {
1114             printf("ImageParticle: loading image failed '%s'\n", qPrintable(m_image_name.toLocalFile()));
1115             return 0;
1116         }
1117     }
1118
1119     clearShadows();
1120     if (m_material)
1121         m_material = 0;
1122
1123     //Setup material
1124     QImage colortable;
1125     QImage sizetable;
1126     QImage opacitytable;
1127     switch (perfLevel) {//Fallthrough intended
1128     case Sprites:
1129         m_material = SpriteMaterial::createMaterial();
1130         getState<ImageMaterialData>(m_material)->animSheetSize = QSizeF(image.size());
1131         m_spriteEngine->setCount(m_count);
1132     case Tabled:
1133         if (!m_material)
1134             m_material = TabledMaterial::createMaterial();
1135         colortable = QImage(m_colortable_name.toLocalFile());
1136         sizetable = QImage(m_sizetable_name.toLocalFile());
1137         opacitytable = QImage(m_opacitytable_name.toLocalFile());
1138         if (colortable.isNull())
1139             colortable = QImage(QStringLiteral(":defaultshaders/identitytable.png"));
1140         Q_ASSERT(!colortable.isNull());
1141         getState<ImageMaterialData>(m_material)->colorTable = QSGPlainTexture::fromImage(colortable);
1142         fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->sizeTable, sizetable, UNIFORM_ARRAY_SIZE);
1143         fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->opacityTable, opacitytable, UNIFORM_ARRAY_SIZE);
1144     case Deformable:
1145         if (!m_material)
1146             m_material = DeformableMaterial::createMaterial();
1147     case Colored:
1148         if (!m_material)
1149             m_material = ColoredMaterial::createMaterial();
1150     default://Also Simple
1151         if (!m_material)
1152             m_material = SimpleMaterial::createMaterial();
1153         getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(image);
1154         getState<ImageMaterialData>(m_material)->texture->setFiltering(QSGTexture::Linear);
1155         getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect;
1156         m_material->setFlag(QSGMaterial::Blending);
1157     }
1158
1159     foreach (const QString &str, m_groups){
1160         int gIdx = m_system->groupIds[str];
1161         int count = m_system->groupData[gIdx]->size();
1162         QSGGeometryNode* node = new QSGGeometryNode();
1163         node->setMaterial(m_material);
1164         node->markDirty(QSGNode::DirtyMaterial);
1165
1166         m_nodes.insert(gIdx, node);
1167         m_idxStarts.insert(gIdx, m_lastIdxStart);
1168         m_lastIdxStart += count;
1169
1170         //Create Particle Geometry
1171         int vCount = count * 4;
1172         int iCount = count * 6;
1173
1174         QSGGeometry *g;
1175         if (perfLevel == Sprites)
1176             g = new QSGGeometry(SpriteParticle_AttributeSet, vCount, iCount);
1177         else if (perfLevel == Tabled)
1178             g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
1179         else if (perfLevel == Deformable)
1180             g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
1181         else if (perfLevel == Colored)
1182             g = new QSGGeometry(ColoredParticle_AttributeSet, count, 0);
1183         else //Simple
1184             g = new QSGGeometry(SimpleParticle_AttributeSet, count, 0);
1185
1186         node->setGeometry(g);
1187         if (perfLevel <= Colored){
1188             g->setDrawingMode(GL_POINTS);
1189             if (m_debugMode){
1190                 GLfloat pointSizeRange[2];
1191                 glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
1192                 qDebug() << "Using point sprites, GL_ALIASED_POINT_SIZE_RANGE " <<pointSizeRange[0] << ":" << pointSizeRange[1];
1193             }
1194         }else
1195             g->setDrawingMode(GL_TRIANGLES);
1196
1197         for (int p=0; p < count; ++p)
1198             commit(gIdx, p);//commit sets geometry for the node, has its own perfLevel switch
1199
1200         if (perfLevel == Sprites)
1201             initTexCoords<SpriteVertex>((SpriteVertex*)g->vertexData(), vCount);
1202         else if (perfLevel == Tabled)
1203             initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
1204         else if (perfLevel == Deformable)
1205             initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
1206
1207         if (perfLevel > Colored){
1208             quint16 *indices = g->indexDataAsUShort();
1209             for (int i=0; i < count; ++i) {
1210                 int o = i * 4;
1211                 indices[0] = o;
1212                 indices[1] = o + 1;
1213                 indices[2] = o + 2;
1214                 indices[3] = o + 1;
1215                 indices[4] = o + 3;
1216                 indices[5] = o + 2;
1217                 indices += 6;
1218             }
1219         }
1220
1221     }
1222
1223     foreach (QSGGeometryNode* node, m_nodes){
1224         if (node == *(m_nodes.begin()))
1225             node->setFlag(QSGGeometryNode::OwnsMaterial);//Root node owns the material for memory management purposes
1226         else
1227             (*(m_nodes.begin()))->appendChildNode(node);
1228     }
1229
1230     return *(m_nodes.begin());
1231 }
1232
1233 QSGNode *QSGImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
1234 {
1235     if (m_pleaseReset){
1236         m_lastLevel = perfLevel;
1237
1238         delete m_rootNode;//Automatically deletes children, and SG manages material lifetime
1239         m_rootNode = 0;
1240         m_nodes.clear();
1241
1242         m_idxStarts.clear();
1243         m_lastIdxStart = 0;
1244
1245         m_material = 0;
1246
1247         m_pleaseReset = false;
1248     }
1249
1250     if (m_system && m_system->isRunning() && !m_system->isPaused()){
1251         prepareNextFrame();
1252         if (m_rootNode) {
1253             update();
1254             foreach (QSGGeometryNode* node, m_nodes)
1255                 node->markDirty(QSGNode::DirtyGeometry);
1256         }
1257     }
1258
1259     return m_rootNode;
1260 }
1261
1262 void QSGImageParticle::prepareNextFrame()
1263 {
1264     if (m_rootNode == 0){//TODO: Staggered loading (as emitted)
1265         m_rootNode = buildParticleNodes();
1266         if (m_rootNode == 0)
1267             return;
1268         if(m_debugMode){
1269             qDebug() << "QSGImageParticle Feature level: " << perfLevel;
1270             qDebug() << "QSGImageParticle Nodes: ";
1271             int count = 0;
1272             foreach(int i, m_nodes.keys()){
1273                 qDebug() << "Group " << i << " (" << m_system->groupData[i]->size() << " particles)";
1274                 count += m_system->groupData[i]->size();
1275             }
1276             qDebug() << "Total count: " << count;
1277         }
1278     }
1279     qint64 timeStamp = m_system->systemSync(this);
1280
1281     qreal time = timeStamp / 1000.;
1282
1283     switch (perfLevel){//Fall-through intended
1284     case Sprites:
1285         //Advance State
1286         m_spriteEngine->updateSprites(timeStamp);
1287         foreach (const QString &str, m_groups){
1288             int gIdx = m_system->groupIds[str];
1289             int count = m_system->groupData[gIdx]->size();
1290
1291             Vertices<SpriteVertex>* particles = (Vertices<SpriteVertex> *) m_nodes[gIdx]->geometry()->vertexData();
1292             for (int i=0; i < count; i++){
1293                 int spriteIdx = m_idxStarts[gIdx] + i;
1294                 Vertices<SpriteVertex> &p = particles[i];
1295                 int curY = m_spriteEngine->spriteY(spriteIdx);//Y is fixed per sprite row, used to distinguish rows here
1296                 if (curY != p.v1.animY){
1297                     p.v1.animT = p.v2.animT = p.v3.animT = p.v4.animT = m_spriteEngine->spriteStart(spriteIdx)/1000.0;
1298                     p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = m_spriteEngine->spriteFrames(spriteIdx);
1299                     p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->spriteDuration(spriteIdx);
1300                     p.v1.animX = p.v2.animX = p.v3.animX = p.v4.animX = m_spriteEngine->spriteX(spriteIdx);
1301                     p.v1.animY = p.v2.animY = p.v3.animY = p.v4.animY = m_spriteEngine->spriteY(spriteIdx);
1302                     p.v1.animWidth = p.v2.animWidth = p.v3.animWidth = p.v4.animWidth = m_spriteEngine->spriteWidth(spriteIdx);
1303                     p.v1.animHeight = p.v2.animHeight = p.v3.animHeight = p.v4.animHeight = m_spriteEngine->spriteHeight(spriteIdx);
1304                 }
1305             }
1306         }
1307     case Tabled:
1308     case Deformable:
1309     case Colored:
1310     case Simple:
1311     default: //Also Simple
1312         getState<ImageMaterialData>(m_material)->timestamp = time;
1313         break;
1314     }
1315
1316     foreach (QSGGeometryNode* node, m_nodes)
1317         node->markDirty(QSGNode::DirtyMaterial);
1318 }
1319
1320 void QSGImageParticle::reloadColor(const Color4ub &c, QSGParticleData* d)
1321 {
1322     d->color = c;
1323     //TODO: get index for reload - or make function take an index
1324 }
1325
1326 void QSGImageParticle::initialize(int gIdx, int pIdx)
1327 {
1328     Color4ub color;
1329     QSGParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
1330     qreal redVariation = m_color_variation + m_redVariation;
1331     qreal greenVariation = m_color_variation + m_greenVariation;
1332     qreal blueVariation = m_color_variation + m_blueVariation;
1333     int spriteIdx = m_idxStarts[gIdx] + datum->index;
1334     float rotation;
1335     float rotationSpeed;
1336     float autoRotate;
1337     switch (perfLevel){//Fall-through is intended on all of them
1338         case Sprites:
1339             // Initial Sprite State
1340             if (m_explicitAnimation){
1341                 if (!datum->animationOwner)
1342                     datum->animationOwner = this;
1343                 QSGParticleData* writeTo = (datum->animationOwner == this ? datum : getShadowDatum(datum));
1344                 writeTo->animT = writeTo->t;
1345                 //writeTo->animInterpolate = m_spritesInterpolate;
1346                 if (m_spriteEngine){
1347                     m_spriteEngine->start(spriteIdx);
1348                     writeTo->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
1349                     writeTo->frameDuration = m_spriteEngine->spriteDuration(spriteIdx);
1350                     writeTo->animX = m_spriteEngine->spriteX(spriteIdx);
1351                     writeTo->animY = m_spriteEngine->spriteY(spriteIdx);
1352                     writeTo->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
1353                     writeTo->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
1354                 }else{
1355                     writeTo->frameCount = 1;
1356                     writeTo->frameDuration = 9999;
1357                     writeTo->animX = writeTo->animY = writeTo->animWidth = writeTo->animHeight = 0;
1358                 }
1359             }
1360         case Tabled:
1361         case Deformable:
1362             //Initial Rotation
1363             if (m_explicitDeformation){
1364                 if (!datum->deformationOwner)
1365                     datum->deformationOwner = this;
1366                 if (m_xVector){
1367                     const QPointF &ret = m_xVector->sample(QPointF(datum->x, datum->y));
1368                     if (datum->deformationOwner == this) {
1369                         datum->xx = ret.x();
1370                         datum->xy = ret.y();
1371                     } else {
1372                         getShadowDatum(datum)->xx = ret.x();
1373                         getShadowDatum(datum)->xy = ret.y();
1374                     }
1375                 }
1376                 if (m_yVector){
1377                     const QPointF &ret = m_yVector->sample(QPointF(datum->x, datum->y));
1378                     if (datum->deformationOwner == this) {
1379                         datum->yx = ret.x();
1380                         datum->yy = ret.y();
1381                     } else {
1382                         getShadowDatum(datum)->yx = ret.x();
1383                         getShadowDatum(datum)->yy = ret.y();
1384                     }
1385                 }
1386             }
1387
1388             if (m_explicitRotation){
1389                 if (!datum->rotationOwner)
1390                     datum->rotationOwner = this;
1391                 rotation =
1392                         (m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV;
1393                 rotationSpeed =
1394                         (m_rotationSpeed + (m_rotationSpeedVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationSpeedVariation) ) * CONV;
1395                 autoRotate = m_autoRotation?1.0:0.0;
1396                 if (datum->rotationOwner == this) {
1397                     datum->rotation = rotation;
1398                     datum->rotationSpeed = rotationSpeed;
1399                     datum->autoRotate = autoRotate;
1400                 } else {
1401                     getShadowDatum(datum)->rotation = rotation;
1402                     getShadowDatum(datum)->rotationSpeed = rotationSpeed;
1403                     getShadowDatum(datum)->autoRotate = autoRotate;
1404                 }
1405             }
1406         case Colored:
1407             //Color initialization
1408             // Particle color
1409             if (m_explicitColor) {
1410                 if (!datum->colorOwner)
1411                     datum->colorOwner = this;
1412                 color.r = m_color.red() * (1 - redVariation) + rand() % 256 * redVariation;
1413                 color.g = m_color.green() * (1 - greenVariation) + rand() % 256 * greenVariation;
1414                 color.b = m_color.blue() * (1 - blueVariation) + rand() % 256 * blueVariation;
1415                 color.a = m_alpha * m_color.alpha() * (1 - m_alphaVariation) + rand() % 256 * m_alphaVariation;
1416                 if (datum->colorOwner == this)
1417                     datum->color = color;
1418                 else
1419                     getShadowDatum(datum)->color = color;
1420             }
1421         default:
1422             break;
1423     }
1424 }
1425
1426 void QSGImageParticle::commit(int gIdx, int pIdx)
1427 {
1428     if (m_pleaseReset)
1429         return;
1430     QSGGeometryNode *node = m_nodes[gIdx];
1431     if (!node)
1432         return;
1433     QSGParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
1434     node->setFlag(QSGNode::OwnsGeometry, false);
1435     SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
1436     DeformableVertex *deformableVertices = (DeformableVertex *) node->geometry()->vertexData();
1437     ColoredVertex *coloredVertices = (ColoredVertex *) node->geometry()->vertexData();
1438     SimpleVertex *simpleVertices = (SimpleVertex *) node->geometry()->vertexData();
1439     switch (perfLevel){//No automatic fall through intended on this one
1440     case Sprites:
1441         spriteVertices += pIdx*4;
1442         for (int i=0; i<4; i++){
1443             spriteVertices[i].x = datum->x  - m_systemOffset.x();
1444             spriteVertices[i].y = datum->y  - m_systemOffset.y();
1445             spriteVertices[i].t = datum->t;
1446             spriteVertices[i].lifeSpan = datum->lifeSpan;
1447             spriteVertices[i].size = datum->size;
1448             spriteVertices[i].endSize = datum->endSize;
1449             spriteVertices[i].vx = datum->vx;
1450             spriteVertices[i].vy = datum->vy;
1451             spriteVertices[i].ax = datum->ax;
1452             spriteVertices[i].ay = datum->ay;
1453             if (m_explicitDeformation && datum->deformationOwner != this) {
1454                 QSGParticleData* shadow = getShadowDatum(datum);
1455                 spriteVertices[i].xx = shadow->xx;
1456                 spriteVertices[i].xy = shadow->xy;
1457                 spriteVertices[i].yx = shadow->yx;
1458                 spriteVertices[i].yy = shadow->yy;
1459             } else {
1460                 spriteVertices[i].xx = datum->xx;
1461                 spriteVertices[i].xy = datum->xy;
1462                 spriteVertices[i].yx = datum->yx;
1463                 spriteVertices[i].yy = datum->yy;
1464             }
1465             if (m_explicitRotation && datum->rotationOwner != this) {
1466                 QSGParticleData* shadow = getShadowDatum(datum);
1467                 spriteVertices[i].rotation = shadow->rotation;
1468                 spriteVertices[i].rotationSpeed = shadow->rotationSpeed;
1469                 spriteVertices[i].autoRotate = shadow->autoRotate;
1470             } else {
1471                 spriteVertices[i].rotation = datum->rotation;
1472                 spriteVertices[i].rotationSpeed = datum->rotationSpeed;
1473                 spriteVertices[i].autoRotate = datum->autoRotate;
1474             }
1475             spriteVertices[i].animInterpolate = m_spritesInterpolate ? 1.0 : 0.0;//### Shadow? In particleData? Or uniform?
1476             if (m_explicitAnimation && datum->animationOwner != this) {
1477                 QSGParticleData* shadow = getShadowDatum(datum);
1478                 spriteVertices[i].frameDuration = shadow->frameDuration;
1479                 spriteVertices[i].frameCount = shadow->frameCount;
1480                 spriteVertices[i].animT = shadow->animT;
1481                 spriteVertices[i].animX = shadow->animX;
1482                 spriteVertices[i].animY = shadow->animY;
1483                 spriteVertices[i].animWidth = shadow->animWidth;
1484                 spriteVertices[i].animHeight = shadow->animHeight;
1485             } else {
1486                 spriteVertices[i].frameDuration = datum->frameDuration;
1487                 spriteVertices[i].frameCount = datum->frameCount;
1488                 spriteVertices[i].animT = datum->animT;
1489                 spriteVertices[i].animX = datum->animX;
1490                 spriteVertices[i].animY = datum->animY;
1491                 spriteVertices[i].animWidth = datum->animWidth;
1492                 spriteVertices[i].animHeight = datum->animHeight;
1493             }
1494             if (m_explicitColor && datum->colorOwner != this) {
1495                 QSGParticleData* shadow = getShadowDatum(datum);
1496                 spriteVertices[i].color.r = shadow->color.r;
1497                 spriteVertices[i].color.g = shadow->color.g;
1498                 spriteVertices[i].color.b = shadow->color.b;
1499                 spriteVertices[i].color.a = shadow->color.a;
1500             } else {
1501                 spriteVertices[i].color.r = datum->color.r;
1502                 spriteVertices[i].color.g = datum->color.g;
1503                 spriteVertices[i].color.b = datum->color.b;
1504                 spriteVertices[i].color.a = datum->color.a;
1505             }
1506         }
1507         break;
1508     case Tabled: //Fall through until it has its own vertex class
1509     case Deformable:
1510         deformableVertices += pIdx*4;
1511         for (int i=0; i<4; i++){
1512             deformableVertices[i].x = datum->x  - m_systemOffset.x();
1513             deformableVertices[i].y = datum->y  - m_systemOffset.y();
1514             deformableVertices[i].t = datum->t;
1515             deformableVertices[i].lifeSpan = datum->lifeSpan;
1516             deformableVertices[i].size = datum->size;
1517             deformableVertices[i].endSize = datum->endSize;
1518             deformableVertices[i].vx = datum->vx;
1519             deformableVertices[i].vy = datum->vy;
1520             deformableVertices[i].ax = datum->ax;
1521             deformableVertices[i].ay = datum->ay;
1522             if (m_explicitDeformation && datum->deformationOwner != this) {
1523                 QSGParticleData* shadow = getShadowDatum(datum);
1524                 deformableVertices[i].xx = shadow->xx;
1525                 deformableVertices[i].xy = shadow->xy;
1526                 deformableVertices[i].yx = shadow->yx;
1527                 deformableVertices[i].yy = shadow->yy;
1528             } else {
1529                 deformableVertices[i].xx = datum->xx;
1530                 deformableVertices[i].xy = datum->xy;
1531                 deformableVertices[i].yx = datum->yx;
1532                 deformableVertices[i].yy = datum->yy;
1533             }
1534             if (m_explicitRotation && datum->rotationOwner != this) {
1535                 QSGParticleData* shadow = getShadowDatum(datum);
1536                 deformableVertices[i].rotation = shadow->rotation;
1537                 deformableVertices[i].rotationSpeed = shadow->rotationSpeed;
1538                 deformableVertices[i].autoRotate = shadow->autoRotate;
1539             } else {
1540                 deformableVertices[i].rotation = datum->rotation;
1541                 deformableVertices[i].rotationSpeed = datum->rotationSpeed;
1542                 deformableVertices[i].autoRotate = datum->autoRotate;
1543             }
1544             if (m_explicitColor && datum->colorOwner != this) {
1545                 QSGParticleData* shadow = getShadowDatum(datum);
1546                 deformableVertices[i].color.r = shadow->color.r;
1547                 deformableVertices[i].color.g = shadow->color.g;
1548                 deformableVertices[i].color.b = shadow->color.b;
1549                 deformableVertices[i].color.a = shadow->color.a;
1550             } else {
1551                 deformableVertices[i].color.r = datum->color.r;
1552                 deformableVertices[i].color.g = datum->color.g;
1553                 deformableVertices[i].color.b = datum->color.b;
1554                 deformableVertices[i].color.a = datum->color.a;
1555             }
1556         }
1557         break;
1558     case Colored:
1559         coloredVertices += pIdx*1;
1560         for (int i=0; i<1; i++){
1561             coloredVertices[i].x = datum->x  - m_systemOffset.x();
1562             coloredVertices[i].y = datum->y  - m_systemOffset.y();
1563             coloredVertices[i].t = datum->t;
1564             coloredVertices[i].lifeSpan = datum->lifeSpan;
1565             coloredVertices[i].size = datum->size;
1566             coloredVertices[i].endSize = datum->endSize;
1567             coloredVertices[i].vx = datum->vx;
1568             coloredVertices[i].vy = datum->vy;
1569             coloredVertices[i].ax = datum->ax;
1570             coloredVertices[i].ay = datum->ay;
1571             if (m_explicitColor && datum->colorOwner != this) {
1572                 QSGParticleData* shadow = getShadowDatum(datum);
1573                 coloredVertices[i].color.r = shadow->color.r;
1574                 coloredVertices[i].color.g = shadow->color.g;
1575                 coloredVertices[i].color.b = shadow->color.b;
1576                 coloredVertices[i].color.a = shadow->color.a;
1577             } else {
1578                 coloredVertices[i].color.r = datum->color.r;
1579                 coloredVertices[i].color.g = datum->color.g;
1580                 coloredVertices[i].color.b = datum->color.b;
1581                 coloredVertices[i].color.a = datum->color.a;
1582             }
1583         }
1584         break;
1585     case Simple:
1586         simpleVertices += pIdx*1;
1587         for (int i=0; i<1; i++){
1588             simpleVertices[i].x = datum->x - m_systemOffset.x();
1589             simpleVertices[i].y = datum->y - m_systemOffset.y();
1590             simpleVertices[i].t = datum->t;
1591             simpleVertices[i].lifeSpan = datum->lifeSpan;
1592             simpleVertices[i].size = datum->size;
1593             simpleVertices[i].endSize = datum->endSize;
1594             simpleVertices[i].vx = datum->vx;
1595             simpleVertices[i].vy = datum->vy;
1596             simpleVertices[i].ax = datum->ax;
1597             simpleVertices[i].ay = datum->ay;
1598         }
1599         break;
1600     default:
1601         break;
1602     }
1603
1604     node->setFlag(QSGNode::OwnsGeometry, true);
1605 }
1606
1607
1608
1609 QT_END_NAMESPACE