Avoid direct GL calls in Quick
[qt:qtdeclarative.git] / src / particles / qquickimageparticle.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtQuick module of the Qt Toolkit.
7 **
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.
16 **
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.
24 **
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.
28 **
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.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtQuick/private/qsgcontext_p.h>
43 #include <private/qsgadaptationlayer_p.h>
44 #include <private/qquickitem_p.h>
45 #include <QtQuick/qsgnode.h>
46 #include <QtQuick/qsgtexturematerial.h>
47 #include <QtQuick/qsgtexture.h>
48 #include <QFile>
49 #include "qquickimageparticle_p.h"
50 #include "qquickparticleemitter_p.h"
51 #include <private/qquicksprite_p.h>
52 #include <private/qquickspriteengine_p.h>
53 #include <QOpenGLFunctions>
54 #include <QtQuick/private/qsgshadersourcebuilder_p.h>
55 #include <QtQuick/private/qsgtexture_p.h>
56 #include <private/qqmlglobal_p.h>
57 #include <QtQml/qqmlinfo.h>
58 #include <cmath>
59
60 QT_BEGIN_NAMESPACE
61
62 #if defined(Q_OS_BLACKBERRY)
63 #define SHADER_PLATFORM_DEFINES "Q_OS_BLACKBERRY\n"
64 #else
65 #define SHADER_PLATFORM_DEFINES
66 #endif
67
68 //TODO: Make it larger on desktop? Requires fixing up shader code with the same define
69 #define UNIFORM_ARRAY_SIZE 64
70
71 const qreal CONV = 0.017453292519943295;
72 class ImageMaterialData
73 {
74     public:
75     ImageMaterialData()
76         : texture(0), colorTable(0)
77     {}
78
79     ~ImageMaterialData(){
80         delete texture;
81         delete colorTable;
82     }
83
84     QSGTexture *texture;
85     QSGTexture *colorTable;
86     float sizeTable[UNIFORM_ARRAY_SIZE];
87     float opacityTable[UNIFORM_ARRAY_SIZE];
88
89     qreal timestamp;
90     qreal entry;
91     QSizeF animSheetSize;
92 };
93
94 class TabledMaterialData : public ImageMaterialData {};
95 class TabledMaterial : public QSGSimpleMaterialShader<TabledMaterialData>
96 {
97     QSG_DECLARE_SIMPLE_SHADER(TabledMaterial, TabledMaterialData)
98
99 public:
100     TabledMaterial()
101     {
102         QSGShaderSourceBuilder builder;
103         const bool isES = QOpenGLContext::currentContext()->isOpenGLES();
104
105         builder.appendSourceFile(QStringLiteral(":/particles/shaders/imageparticle.vert"));
106         builder.addDefinition(QByteArray(SHADER_PLATFORM_DEFINES));
107         builder.addDefinition(QByteArrayLiteral("TABLE"));
108         builder.addDefinition(QByteArrayLiteral("DEFORM"));
109         builder.addDefinition(QByteArrayLiteral("COLOR"));
110         if (isES)
111             builder.removeVersion();
112
113         m_vertex_code = builder.source();
114         builder.clear();
115
116         builder.appendSourceFile(QStringLiteral(":/particles/shaders/imageparticle.frag"));
117         builder.addDefinition(QByteArray(SHADER_PLATFORM_DEFINES));
118         builder.addDefinition(QByteArrayLiteral("TABLE"));
119         builder.addDefinition(QByteArrayLiteral("DEFORM"));
120         builder.addDefinition(QByteArrayLiteral("COLOR"));
121         if (isES)
122             builder.removeVersion();
123
124         m_fragment_code = builder.source();
125
126         Q_ASSERT(!m_vertex_code.isNull());
127         Q_ASSERT(!m_fragment_code.isNull());
128     }
129
130     const char *vertexShader() const { return m_vertex_code.constData(); }
131     const char *fragmentShader() const { return m_fragment_code.constData(); }
132
133     QList<QByteArray> attributes() const {
134         return QList<QByteArray>() << "vPosTex" << "vData" << "vVec"
135             << "vColor" << "vDeformVec" << "vRotation";
136     };
137
138     void initialize() {
139         QSGSimpleMaterialShader<TabledMaterialData>::initialize();
140         program()->bind();
141         program()->setUniformValue("_qt_texture", 0);
142         program()->setUniformValue("colortable", 1);
143         glFuncs = QOpenGLContext::currentContext()->functions();
144         m_timestamp_id = program()->uniformLocation("timestamp");
145         m_entry_id = program()->uniformLocation("entry");
146         m_sizetable_id = program()->uniformLocation("sizetable");
147         m_opacitytable_id = program()->uniformLocation("opacitytable");
148     }
149
150     void updateState(const TabledMaterialData* d, const TabledMaterialData*) {
151         glFuncs->glActiveTexture(GL_TEXTURE1);
152         d->colorTable->bind();
153
154         glFuncs->glActiveTexture(GL_TEXTURE0);
155         d->texture->bind();
156
157         program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
158         program()->setUniformValue(m_entry_id, (float) d->entry);
159         program()->setUniformValueArray(m_sizetable_id, (float*) d->sizeTable, UNIFORM_ARRAY_SIZE, 1);
160         program()->setUniformValueArray(m_opacitytable_id, (float*) d->opacityTable, UNIFORM_ARRAY_SIZE, 1);
161     }
162
163     int m_entry_id;
164     int m_timestamp_id;
165     int m_sizetable_id;
166     int m_opacitytable_id;
167     QByteArray m_vertex_code;
168     QByteArray m_fragment_code;
169     QOpenGLFunctions* glFuncs;
170 };
171
172 class DeformableMaterialData : public ImageMaterialData {};
173 class DeformableMaterial : public QSGSimpleMaterialShader<DeformableMaterialData>
174 {
175     QSG_DECLARE_SIMPLE_SHADER(DeformableMaterial, DeformableMaterialData)
176
177 public:
178     DeformableMaterial()
179     {
180         QSGShaderSourceBuilder builder;
181         const bool isES = QOpenGLContext::currentContext()->isOpenGLES();
182
183         builder.appendSourceFile(QStringLiteral(":/particles/shaders/imageparticle.vert"));
184         builder.addDefinition(QByteArray(SHADER_PLATFORM_DEFINES));
185         builder.addDefinition(QByteArrayLiteral("DEFORM"));
186         builder.addDefinition(QByteArrayLiteral("COLOR"));
187         if (isES)
188             builder.removeVersion();
189
190         m_vertex_code = builder.source();
191         builder.clear();
192
193         builder.appendSourceFile(QStringLiteral(":/particles/shaders/imageparticle.frag"));
194         builder.addDefinition(QByteArray(SHADER_PLATFORM_DEFINES));
195         builder.addDefinition(QByteArrayLiteral("DEFORM"));
196         builder.addDefinition(QByteArrayLiteral("COLOR"));
197         if (isES)
198             builder.removeVersion();
199
200         m_fragment_code = builder.source();
201
202         Q_ASSERT(!m_vertex_code.isNull());
203         Q_ASSERT(!m_fragment_code.isNull());
204     }
205
206     const char *vertexShader() const { return m_vertex_code.constData(); }
207     const char *fragmentShader() const { return m_fragment_code.constData(); }
208
209     QList<QByteArray> attributes() const {
210         return QList<QByteArray>() << "vPosTex" << "vData" << "vVec"
211             << "vColor" << "vDeformVec" << "vRotation";
212     };
213
214     void initialize() {
215         QSGSimpleMaterialShader<DeformableMaterialData>::initialize();
216         program()->bind();
217         program()->setUniformValue("_qt_texture", 0);
218         glFuncs = QOpenGLContext::currentContext()->functions();
219         m_timestamp_id = program()->uniformLocation("timestamp");
220         m_entry_id = program()->uniformLocation("entry");
221     }
222
223     void updateState(const DeformableMaterialData* d, const DeformableMaterialData*) {
224         d->texture->bind();
225
226         program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
227         program()->setUniformValue(m_entry_id, (float) d->entry);
228     }
229
230     int m_entry_id;
231     int m_timestamp_id;
232     QByteArray m_vertex_code;
233     QByteArray m_fragment_code;
234     QOpenGLFunctions* glFuncs;
235 };
236
237 class SpriteMaterialData : public ImageMaterialData {};
238 class SpriteMaterial : public QSGSimpleMaterialShader<SpriteMaterialData>
239 {
240     QSG_DECLARE_SIMPLE_SHADER(SpriteMaterial, SpriteMaterialData)
241
242 public:
243     SpriteMaterial()
244     {
245         QSGShaderSourceBuilder builder;
246         const bool isES = QOpenGLContext::currentContext()->isOpenGLES();
247
248         builder.appendSourceFile(QStringLiteral(":/particles/shaders/imageparticle.vert"));
249         builder.addDefinition(QByteArray(SHADER_PLATFORM_DEFINES));
250         builder.addDefinition(QByteArrayLiteral("SPRITE"));
251         builder.addDefinition(QByteArrayLiteral("TABLE"));
252         builder.addDefinition(QByteArrayLiteral("DEFORM"));
253         builder.addDefinition(QByteArrayLiteral("COLOR"));
254         if (isES)
255             builder.removeVersion();
256
257         m_vertex_code = builder.source();
258         builder.clear();
259
260         builder.appendSourceFile(QStringLiteral(":/particles/shaders/imageparticle.frag"));
261         builder.addDefinition(QByteArray(SHADER_PLATFORM_DEFINES));
262         builder.addDefinition(QByteArrayLiteral("SPRITE"));
263         builder.addDefinition(QByteArrayLiteral("TABLE"));
264         builder.addDefinition(QByteArrayLiteral("DEFORM"));
265         builder.addDefinition(QByteArrayLiteral("COLOR"));
266         if (isES)
267             builder.removeVersion();
268
269         m_fragment_code = builder.source();
270
271         Q_ASSERT(!m_vertex_code.isNull());
272         Q_ASSERT(!m_fragment_code.isNull());
273     }
274
275     const char *vertexShader() const { return m_vertex_code.constData(); }
276     const char *fragmentShader() const { return m_fragment_code.constData(); }
277
278     QList<QByteArray> attributes() const {
279         return QList<QByteArray>() << "vPosTex" << "vData" << "vVec"
280             << "vColor" << "vDeformVec" << "vRotation" << "vAnimData" << "vAnimPos";
281     }
282
283     void initialize() {
284         QSGSimpleMaterialShader<SpriteMaterialData>::initialize();
285         program()->bind();
286         program()->setUniformValue("_qt_texture", 0);
287         program()->setUniformValue("colortable", 1);
288         glFuncs = QOpenGLContext::currentContext()->functions();
289         //Don't actually expose the animSheetSize in the shader, it's currently only used for CPU calculations.
290         m_timestamp_id = program()->uniformLocation("timestamp");
291         m_entry_id = program()->uniformLocation("entry");
292         m_sizetable_id = program()->uniformLocation("sizetable");
293         m_opacitytable_id = program()->uniformLocation("opacitytable");
294     }
295
296     void updateState(const SpriteMaterialData* d, const SpriteMaterialData*) {
297         glFuncs->glActiveTexture(GL_TEXTURE1);
298         d->colorTable->bind();
299
300         // make sure we end by setting GL_TEXTURE0 as active texture
301         glFuncs->glActiveTexture(GL_TEXTURE0);
302         d->texture->bind();
303
304         program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
305         program()->setUniformValue(m_entry_id, (float) d->entry);
306         program()->setUniformValueArray(m_sizetable_id, (float*) d->sizeTable, 64, 1);
307         program()->setUniformValueArray(m_opacitytable_id, (float*) d->opacityTable, UNIFORM_ARRAY_SIZE, 1);
308     }
309
310     int m_timestamp_id;
311     int m_entry_id;
312     int m_sizetable_id;
313     int m_opacitytable_id;
314     QByteArray m_vertex_code;
315     QByteArray m_fragment_code;
316     QOpenGLFunctions* glFuncs;
317 };
318
319 class ColoredMaterialData : public ImageMaterialData {};
320 class ColoredMaterial : public QSGSimpleMaterialShader<ColoredMaterialData>
321 {
322     QSG_DECLARE_SIMPLE_SHADER(ColoredMaterial, ColoredMaterialData)
323
324 public:
325     ColoredMaterial()
326     {
327         QSGShaderSourceBuilder builder;
328         const bool isES = QOpenGLContext::currentContext()->isOpenGLES();
329
330         builder.appendSourceFile(QStringLiteral(":/particles/shaders/imageparticle.vert"));
331         builder.addDefinition(QByteArray(SHADER_PLATFORM_DEFINES));
332         builder.addDefinition(QByteArrayLiteral("COLOR"));
333         if (isES)
334             builder.removeVersion();
335
336         m_vertex_code = builder.source();
337         builder.clear();
338
339         builder.appendSourceFile(QStringLiteral(":/particles/shaders/imageparticle.frag"));
340         builder.addDefinition(QByteArray(SHADER_PLATFORM_DEFINES));
341         builder.addDefinition(QByteArrayLiteral("COLOR"));
342         if (isES)
343             builder.removeVersion();
344
345         m_fragment_code = builder.source();
346
347         Q_ASSERT(!m_vertex_code.isNull());
348         Q_ASSERT(!m_fragment_code.isNull());
349     }
350
351     const char *vertexShader() const { return m_vertex_code.constData(); }
352     const char *fragmentShader() const { return m_fragment_code.constData(); }
353
354     void activate() {
355         QSGSimpleMaterialShader<ColoredMaterialData>::activate();
356 #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
357         glEnable(GL_POINT_SPRITE);
358         glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
359 #endif
360     }
361
362     void deactivate() {
363         QSGSimpleMaterialShader<ColoredMaterialData>::deactivate();
364 #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
365         glDisable(GL_POINT_SPRITE);
366         glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
367 #endif
368     }
369
370     QList<QByteArray> attributes() const {
371         return QList<QByteArray>() << "vPos" << "vData" << "vVec" << "vColor";
372     }
373
374     void initialize() {
375         QSGSimpleMaterialShader<ColoredMaterialData>::initialize();
376         program()->bind();
377         program()->setUniformValue("_qt_texture", 0);
378         glFuncs = QOpenGLContext::currentContext()->functions();
379         m_timestamp_id = program()->uniformLocation("timestamp");
380         m_entry_id = program()->uniformLocation("entry");
381     }
382
383     void updateState(const ColoredMaterialData* d, const ColoredMaterialData*) {
384         d->texture->bind();
385
386         program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
387         program()->setUniformValue(m_entry_id, (float) d->entry);
388     }
389
390     int m_timestamp_id;
391     int m_entry_id;
392     QByteArray m_vertex_code;
393     QByteArray m_fragment_code;
394     QOpenGLFunctions* glFuncs;
395 };
396
397 class SimpleMaterialData : public ImageMaterialData {};
398 class SimpleMaterial : public QSGSimpleMaterialShader<SimpleMaterialData>
399 {
400     QSG_DECLARE_SIMPLE_SHADER(SimpleMaterial, SimpleMaterialData)
401
402 public:
403     SimpleMaterial()
404     {
405         QSGShaderSourceBuilder builder;
406         const bool isES = QOpenGLContext::currentContext()->isOpenGLES();
407
408         builder.appendSourceFile(QStringLiteral(":/particles/shaders/imageparticle.vert"));
409         builder.addDefinition(QByteArray(SHADER_PLATFORM_DEFINES));
410         if (isES)
411             builder.removeVersion();
412
413         m_vertex_code = builder.source();
414         builder.clear();
415
416         builder.appendSourceFile(QStringLiteral(":/particles/shaders/imageparticle.frag"));
417         builder.addDefinition(QByteArray(SHADER_PLATFORM_DEFINES));
418         if (isES)
419             builder.removeVersion();
420
421         m_fragment_code = builder.source();
422
423         Q_ASSERT(!m_vertex_code.isNull());
424         Q_ASSERT(!m_fragment_code.isNull());
425     }
426
427     const char *vertexShader() const { return m_vertex_code.constData(); }
428     const char *fragmentShader() const { return m_fragment_code.constData(); }
429
430     void activate() {
431         QSGSimpleMaterialShader<SimpleMaterialData>::activate();
432 #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
433         glEnable(GL_POINT_SPRITE);
434         glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
435 #endif
436     }
437
438     void deactivate() {
439         QSGSimpleMaterialShader<SimpleMaterialData>::deactivate();
440 #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
441         glDisable(GL_POINT_SPRITE);
442         glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
443 #endif
444     }
445
446     QList<QByteArray> attributes() const {
447         return QList<QByteArray>() << "vPos" << "vData" << "vVec";
448     }
449
450     void initialize() {
451         QSGSimpleMaterialShader<SimpleMaterialData>::initialize();
452         program()->bind();
453         program()->setUniformValue("_qt_texture", 0);
454         glFuncs = QOpenGLContext::currentContext()->functions();
455         m_timestamp_id = program()->uniformLocation("timestamp");
456         m_entry_id = program()->uniformLocation("entry");
457     }
458
459     void updateState(const SimpleMaterialData* d, const SimpleMaterialData*) {
460         d->texture->bind();
461
462         program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
463         program()->setUniformValue(m_entry_id, (float) d->entry);
464     }
465
466     int m_timestamp_id;
467     int m_entry_id;
468     QByteArray m_vertex_code;
469     QByteArray m_fragment_code;
470     QOpenGLFunctions* glFuncs;
471 };
472
473 void fillUniformArrayFromImage(float* array, const QImage& img, int size)
474 {
475     if (img.isNull()){
476         for (int i=0; i<size; i++)
477             array[i] = 1.0;
478         return;
479     }
480     QImage scaled = img.scaled(size,1);
481     for (int i=0; i<size; i++)
482         array[i] = qAlpha(scaled.pixel(i,0))/255.0;
483 }
484
485 /*!
486     \qmltype ImageParticle
487     \instantiates QQuickImageParticle
488     \inqmlmodule QtQuick.Particles 2
489     \inherits ParticlePainter
490     \brief For visualizing logical particles using an image
491     \ingroup qtquick-particles
492
493     This element renders a logical particle as an image. The image can be
494     \list
495     \li colorized
496     \li rotated
497     \li deformed
498     \li a sprite-based animation
499     \endlist
500
501     ImageParticles implictly share data on particles if multiple ImageParticles are painting
502     the same logical particle group. This is broken down along the four capabilities listed
503     above. So if one ImageParticle defines data for rendering the particles in one of those
504     capabilities, and the other does not, then both will draw the particles the same in that
505     aspect automatically. This is primarily useful when there is some random variation on
506     the particle which is supposed to stay with it when switching painters. If both ImageParticles
507     define how they should appear for that aspect, they diverge and each appears as it is defined.
508
509     This sharing of data happens behind the scenes based off of whether properties were implicitly or explicitly
510     set. One drawback of the current implementation is that it is only possible to reset the capabilities as a whole.
511     So if you explicitly set an attribute affecting color, such as redVariation, and then reset it (by setting redVariation
512     to undefined), all color data will be reset and it will begin to have an implicit value of any shared color from
513     other ImageParticles.
514 */
515 /*!
516     \qmlproperty url QtQuick.Particles::ImageParticle::source
517
518     The source image to be used.
519
520     If the image is a sprite animation, use the sprite property instead.
521
522     Since Qt 5.2, some default images are provided as resources to aid prototyping:
523     \table
524     \row
525     \li qrc:///particleresources/star.png
526     \li \inlineimage particles/star.png
527     \row
528     \li qrc:///particleresources/glowdot.png
529     \li \inlineimage particles/glowdot.png
530     \row
531     \li qrc:///particleresources/fuzzydot.png
532     \li \inlineimage particles/fuzzydot.png
533     \endtable
534
535     Note that the images are white and semi-transparent, to allow colorization
536     and alpha levels to have maximum effect.
537 */
538 /*!
539     \qmlproperty list<Sprite> QtQuick.Particles::ImageParticle::sprites
540
541     The sprite or sprites used to draw this particle.
542
543     Note that the sprite image will be scaled to a square based on the size of
544     the particle being rendered.
545
546     For full details, see the \l{Sprite Animations} overview.
547 */
548 /*!
549     \qmlproperty url QtQuick.Particles::ImageParticle::colorTable
550
551     An image whose color will be used as a 1D texture to determine color over life. E.g. when
552     the particle is halfway through its lifetime, it will have the color specified halfway
553     across the image.
554
555     This color is blended with the color property and the color of the source image.
556 */
557 /*!
558     \qmlproperty url QtQuick.Particles::ImageParticle::sizeTable
559
560     An image whose opacity will be used as a 1D texture to determine size over life.
561
562     This property is expected to be removed shortly, in favor of custom easing curves to determine size over life.
563 */
564 /*!
565     \qmlproperty url QtQuick.Particles::ImageParticle::opacityTable
566
567     An image whose opacity will be used as a 1D texture to determine size over life.
568
569     This property is expected to be removed shortly, in favor of custom easing curves to determine opacity over life.
570 */
571 /*!
572     \qmlproperty color QtQuick.Particles::ImageParticle::color
573
574     If a color is specified, the provided image will be colorized with it.
575
576     Default is white (no change).
577 */
578 /*!
579     \qmlproperty real QtQuick.Particles::ImageParticle::colorVariation
580
581     This number represents the color variation applied to individual particles.
582     Setting colorVariation is the same as setting redVariation, greenVariation,
583     and blueVariation to the same number.
584
585     Each channel can vary between particle by up to colorVariation from its usual color.
586
587     Color is measured, per channel, from 0.0 to 1.0.
588
589     Default is 0.0
590 */
591 /*!
592     \qmlproperty real QtQuick.Particles::ImageParticle::redVariation
593     The variation in the red color channel between particles.
594
595     Color is measured, per channel, from 0.0 to 1.0.
596
597     Default is 0.0
598 */
599 /*!
600     \qmlproperty real QtQuick.Particles::ImageParticle::greenVariation
601     The variation in the green color channel between particles.
602
603     Color is measured, per channel, from 0.0 to 1.0.
604
605     Default is 0.0
606 */
607 /*!
608     \qmlproperty real QtQuick.Particles::ImageParticle::blueVariation
609     The variation in the blue color channel between particles.
610
611     Color is measured, per channel, from 0.0 to 1.0.
612
613     Default is 0.0
614 */
615 /*!
616     \qmlproperty real QtQuick.Particles::ImageParticle::alpha
617     An alpha to be applied to the image. This value is multiplied by the value in
618     the image, and the value in the color property.
619
620     Particles have additive blending, so lower alpha on single particles leads
621     to stronger effects when multiple particles overlap.
622
623     Alpha is measured from 0.0 to 1.0.
624
625     Default is 1.0
626 */
627 /*!
628     \qmlproperty real QtQuick.Particles::ImageParticle::alphaVariation
629     The variation in the alpha channel between particles.
630
631     Alpha is measured from 0.0 to 1.0.
632
633     Default is 0.0
634 */
635 /*!
636     \qmlproperty real QtQuick.Particles::ImageParticle::rotation
637
638     If set the image will be rotated by this many degrees before it is drawn.
639
640     The particle coordinates are not transformed.
641 */
642 /*!
643     \qmlproperty real QtQuick.Particles::ImageParticle::rotationVariation
644
645     If set the rotation of individual particles will vary by up to this much
646     between particles.
647
648 */
649 /*!
650     \qmlproperty real QtQuick.Particles::ImageParticle::rotationVelocity
651
652     If set particles will rotate at this velocity in degrees/second.
653 */
654 /*!
655     \qmlproperty real QtQuick.Particles::ImageParticle::rotationVelocityVariation
656
657     If set the rotationVelocity of individual particles will vary by up to this much
658     between particles.
659
660 */
661 /*!
662     \qmlproperty bool QtQuick.Particles::ImageParticle::autoRotation
663
664     If set to true then a rotation will be applied on top of the particles rotation, so
665     that it faces the direction of travel. So to face away from the direction of travel,
666     set autoRotation to true and rotation to 180.
667
668     Default is false
669 */
670 /*!
671     \qmlproperty StochasticDirection QtQuick.Particles::ImageParticle::xVector
672
673     Allows you to deform the particle image when drawn. The rectangular image will
674     be deformed so that the horizontal sides are in the shape of this vector instead
675     of (1,0).
676 */
677 /*!
678     \qmlproperty StochasticDirection QtQuick.Particles::ImageParticle::yVector
679
680     Allows you to deform the particle image when drawn. The rectangular image will
681     be deformed so that the vertical sides are in the shape of this vector instead
682     of (0,1).
683 */
684 /*!
685     \qmlproperty EntryEffect QtQuick.Particles::ImageParticle::entryEffect
686
687     This property provides basic and cheap entrance and exit effects for the particles.
688     For fine-grained control, see sizeTable and opacityTable.
689
690     Acceptable values are
691     \list
692     \li ImageParticle.None: Particles just appear and disappear.
693     \li ImageParticle.Fade: Particles fade in from 0 opacity at the start of their life, and fade out to 0 at the end.
694     \li ImageParticle.Scale: Particles scale in from 0 size at the start of their life, and scale back to 0 at the end.
695     \endlist
696
697     Default value is Fade.
698 */
699 /*!
700     \qmlproperty bool QtQuick.Particles::ImageParticle::spritesInterpolate
701
702     If set to true, sprite particles will interpolate between sprite frames each rendered frame, making
703     the sprites look smoother.
704
705     Default is true.
706 */
707
708 /*!
709     \qmlproperty Status QtQuick.Particles::ImageParticle::status
710
711     The status of loading the image.
712 */
713
714
715 QQuickImageParticle::QQuickImageParticle(QQuickItem* parent)
716     : QQuickParticlePainter(parent)
717     , m_image(0)
718     , m_colorTable(0)
719     , m_sizeTable(0)
720     , m_opacityTable(0)
721     , m_color_variation(0.0)
722     , m_material(0)
723     , m_alphaVariation(0.0)
724     , m_alpha(1.0)
725     , m_redVariation(0.0)
726     , m_greenVariation(0.0)
727     , m_blueVariation(0.0)
728     , m_rotation(0)
729     , m_rotationVariation(0)
730     , m_rotationVelocity(0)
731     , m_rotationVelocityVariation(0)
732     , m_autoRotation(false)
733     , m_xVector(0)
734     , m_yVector(0)
735     , m_spriteEngine(0)
736     , m_spritesInterpolate(true)
737     , m_explicitColor(false)
738     , m_explicitRotation(false)
739     , m_explicitDeformation(false)
740     , m_explicitAnimation(false)
741     , m_bypassOptimizations(false)
742     , perfLevel(Unknown)
743     , m_lastLevel(Unknown)
744     , m_debugMode(false)
745     , m_entryEffect(Fade)
746     , m_startedImageLoading(0)
747 {
748     setFlag(ItemHasContents);
749 }
750
751 QQuickImageParticle::~QQuickImageParticle()
752 {
753 }
754
755 QQmlListProperty<QQuickSprite> QQuickImageParticle::sprites()
756 {
757     return QQmlListProperty<QQuickSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
758 }
759
760 void QQuickImageParticle::sceneGraphInvalidated()
761 {
762     m_nodes.clear();
763     m_material = 0;
764 }
765
766 void QQuickImageParticle::setImage(const QUrl &image)
767 {
768     if (image.isEmpty()){
769         if (m_image) {
770             delete m_image;
771             m_image = 0;
772             emit imageChanged();
773         }
774         return;
775     }
776
777     if (!m_image)
778         m_image = new ImageData;
779     if (image == m_image->source)
780         return;
781     m_image->source = image;
782     emit imageChanged();
783     reset();
784 }
785
786
787 void QQuickImageParticle::setColortable(const QUrl &table)
788 {
789     if (table.isEmpty()){
790         if (m_colorTable) {
791             delete m_colorTable;
792             emit colortableChanged();
793         }
794         return;
795     }
796
797     if (!m_colorTable)
798         m_colorTable = new ImageData;
799     if (table == m_colorTable->source)
800         return;
801     m_colorTable->source = table;
802     emit colortableChanged();
803     reset();
804 }
805
806 void QQuickImageParticle::setSizetable(const QUrl &table)
807 {
808     if (table.isEmpty()){
809         if (m_sizeTable) {
810             delete m_sizeTable;
811             emit sizetableChanged();
812         }
813         return;
814     }
815
816     if (!m_sizeTable)
817         m_sizeTable = new ImageData;
818     if (table == m_sizeTable->source)
819         return;
820     m_sizeTable->source = table;
821     emit sizetableChanged();
822     reset();
823 }
824
825 void QQuickImageParticle::setOpacitytable(const QUrl &table)
826 {
827     if (table.isEmpty()){
828         if (m_opacityTable) {
829             delete m_opacityTable;
830             emit opacitytableChanged();
831         }
832         return;
833     }
834
835     if (!m_opacityTable)
836         m_opacityTable = new ImageData;
837     if (table == m_opacityTable->source)
838         return;
839     m_opacityTable->source = table;
840     emit opacitytableChanged();
841     reset();
842 }
843
844 void QQuickImageParticle::setColor(const QColor &color)
845 {
846     if (color == m_color)
847         return;
848     m_color = color;
849     emit colorChanged();
850     m_explicitColor = true;
851     if (perfLevel < Colored)
852         reset();
853 }
854
855 void QQuickImageParticle::setColorVariation(qreal var)
856 {
857     if (var == m_color_variation)
858         return;
859     m_color_variation = var;
860     emit colorVariationChanged();
861     m_explicitColor = true;
862     if (perfLevel < Colored)
863         reset();
864 }
865
866 void QQuickImageParticle::setAlphaVariation(qreal arg)
867 {
868     if (m_alphaVariation != arg) {
869         m_alphaVariation = arg;
870         emit alphaVariationChanged(arg);
871     }
872     m_explicitColor = true;
873     if (perfLevel < Colored)
874         reset();
875 }
876
877 void QQuickImageParticle::setAlpha(qreal arg)
878 {
879     if (m_alpha != arg) {
880         m_alpha = arg;
881         emit alphaChanged(arg);
882     }
883     m_explicitColor = true;
884     if (perfLevel < Colored)
885         reset();
886 }
887
888 void QQuickImageParticle::setRedVariation(qreal arg)
889 {
890     if (m_redVariation != arg) {
891         m_redVariation = arg;
892         emit redVariationChanged(arg);
893     }
894     m_explicitColor = true;
895     if (perfLevel < Colored)
896         reset();
897 }
898
899 void QQuickImageParticle::setGreenVariation(qreal arg)
900 {
901     if (m_greenVariation != arg) {
902         m_greenVariation = arg;
903         emit greenVariationChanged(arg);
904     }
905     m_explicitColor = true;
906     if (perfLevel < Colored)
907         reset();
908 }
909
910 void QQuickImageParticle::setBlueVariation(qreal arg)
911 {
912     if (m_blueVariation != arg) {
913         m_blueVariation = arg;
914         emit blueVariationChanged(arg);
915     }
916     m_explicitColor = true;
917     if (perfLevel < Colored)
918         reset();
919 }
920
921 void QQuickImageParticle::setRotation(qreal arg)
922 {
923     if (m_rotation != arg) {
924         m_rotation = arg;
925         emit rotationChanged(arg);
926     }
927     m_explicitRotation = true;
928     if (perfLevel < Deformable)
929         reset();
930 }
931
932 void QQuickImageParticle::setRotationVariation(qreal arg)
933 {
934     if (m_rotationVariation != arg) {
935         m_rotationVariation = arg;
936         emit rotationVariationChanged(arg);
937     }
938     m_explicitRotation = true;
939     if (perfLevel < Deformable)
940         reset();
941 }
942
943 void QQuickImageParticle::setRotationVelocity(qreal arg)
944 {
945     if (m_rotationVelocity != arg) {
946         m_rotationVelocity = arg;
947         emit rotationVelocityChanged(arg);
948     }
949     m_explicitRotation = true;
950     if (perfLevel < Deformable)
951         reset();
952 }
953
954 void QQuickImageParticle::setRotationVelocityVariation(qreal arg)
955 {
956     if (m_rotationVelocityVariation != arg) {
957         m_rotationVelocityVariation = arg;
958         emit rotationVelocityVariationChanged(arg);
959     }
960     m_explicitRotation = true;
961     if (perfLevel < Deformable)
962         reset();
963 }
964
965 void QQuickImageParticle::setAutoRotation(bool arg)
966 {
967     if (m_autoRotation != arg) {
968         m_autoRotation = arg;
969         emit autoRotationChanged(arg);
970     }
971     m_explicitRotation = true;
972     if (perfLevel < Deformable)
973         reset();
974 }
975
976 void QQuickImageParticle::setXVector(QQuickDirection* arg)
977 {
978     if (m_xVector != arg) {
979         m_xVector = arg;
980         emit xVectorChanged(arg);
981     }
982     m_explicitDeformation = true;
983     if (perfLevel < Deformable)
984         reset();
985 }
986
987 void QQuickImageParticle::setYVector(QQuickDirection* arg)
988 {
989     if (m_yVector != arg) {
990         m_yVector = arg;
991         emit yVectorChanged(arg);
992     }
993     m_explicitDeformation = true;
994     if (perfLevel < Deformable)
995         reset();
996 }
997
998 void QQuickImageParticle::setSpritesInterpolate(bool arg)
999 {
1000     if (m_spritesInterpolate != arg) {
1001         m_spritesInterpolate = arg;
1002         emit spritesInterpolateChanged(arg);
1003     }
1004 }
1005
1006 void QQuickImageParticle::setBypassOptimizations(bool arg)
1007 {
1008     if (m_bypassOptimizations != arg) {
1009         m_bypassOptimizations = arg;
1010         emit bypassOptimizationsChanged(arg);
1011     }
1012     // Applies regardless of perfLevel
1013     reset();
1014 }
1015
1016 void QQuickImageParticle::setEntryEffect(EntryEffect arg)
1017 {
1018     if (m_entryEffect != arg) {
1019         m_entryEffect = arg;
1020         if (m_material)
1021             getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect;
1022         emit entryEffectChanged(arg);
1023     }
1024 }
1025
1026 void QQuickImageParticle::resetColor()
1027 {
1028     m_explicitColor = false;
1029     foreach (const QString &str, m_groups)
1030         foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
1031             if (d->colorOwner == this)
1032                 d->colorOwner = 0;
1033     m_color = QColor();
1034     m_color_variation = 0.0f;
1035     m_redVariation = 0.0f;
1036     m_blueVariation = 0.0f;
1037     m_greenVariation = 0.0f;
1038     m_alpha = 1.0f;
1039     m_alphaVariation = 0.0f;
1040 }
1041
1042 void QQuickImageParticle::resetRotation()
1043 {
1044     m_explicitRotation = false;
1045     foreach (const QString &str, m_groups)
1046         foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
1047             if (d->rotationOwner == this)
1048                 d->rotationOwner = 0;
1049     m_rotation = 0;
1050     m_rotationVariation = 0;
1051     m_rotationVelocity = 0;
1052     m_rotationVelocityVariation = 0;
1053     m_autoRotation = false;
1054 }
1055
1056 void QQuickImageParticle::resetDeformation()
1057 {
1058     m_explicitDeformation = false;
1059     foreach (const QString &str, m_groups)
1060         foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
1061             if (d->deformationOwner == this)
1062                 d->deformationOwner = 0;
1063     if (m_xVector)
1064         delete m_xVector;
1065     if (m_yVector)
1066         delete m_yVector;
1067     m_xVector = 0;
1068     m_yVector = 0;
1069 }
1070
1071 void QQuickImageParticle::reset()
1072 {
1073     QQuickParticlePainter::reset();
1074     m_pleaseReset = true;
1075     update();
1076 }
1077
1078 void QQuickImageParticle::createEngine()
1079 {
1080     if (m_spriteEngine)
1081         delete m_spriteEngine;
1082     if (m_sprites.count()) {
1083         m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
1084         connect(m_spriteEngine, SIGNAL(stateChanged(int)),
1085                 this, SLOT(spriteAdvance(int)), Qt::DirectConnection);
1086         m_explicitAnimation = true;
1087     } else {
1088         m_spriteEngine = 0;
1089         m_explicitAnimation = false;
1090     }
1091     reset();
1092 }
1093
1094 static QSGGeometry::Attribute SimpleParticle_Attributes[] = {
1095     QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),             // Position
1096     QSGGeometry::Attribute::create(1, 4, GL_FLOAT),             // Data
1097     QSGGeometry::Attribute::create(2, 4, GL_FLOAT)             // Vectors
1098 };
1099
1100 static QSGGeometry::AttributeSet SimpleParticle_AttributeSet =
1101 {
1102     3, // Attribute Count
1103     ( 2 + 4 + 4 ) * sizeof(float),
1104     SimpleParticle_Attributes
1105 };
1106
1107 static QSGGeometry::Attribute ColoredParticle_Attributes[] = {
1108     QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),             // Position
1109     QSGGeometry::Attribute::create(1, 4, GL_FLOAT),             // Data
1110     QSGGeometry::Attribute::create(2, 4, GL_FLOAT),             // Vectors
1111     QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE),     // Colors
1112 };
1113
1114 static QSGGeometry::AttributeSet ColoredParticle_AttributeSet =
1115 {
1116     4, // Attribute Count
1117     ( 2 + 4 + 4 ) * sizeof(float) + 4 * sizeof(uchar),
1118     ColoredParticle_Attributes
1119 };
1120
1121 static QSGGeometry::Attribute DeformableParticle_Attributes[] = {
1122     QSGGeometry::Attribute::create(0, 4, GL_FLOAT),             // Position & TexCoord
1123     QSGGeometry::Attribute::create(1, 4, GL_FLOAT),             // Data
1124     QSGGeometry::Attribute::create(2, 4, GL_FLOAT),             // Vectors
1125     QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE),     // Colors
1126     QSGGeometry::Attribute::create(4, 4, GL_FLOAT),             // DeformationVectors
1127     QSGGeometry::Attribute::create(5, 3, GL_FLOAT),             // Rotation
1128 };
1129
1130 static QSGGeometry::AttributeSet DeformableParticle_AttributeSet =
1131 {
1132     6, // Attribute Count
1133     (4 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar),
1134     DeformableParticle_Attributes
1135 };
1136
1137 static QSGGeometry::Attribute SpriteParticle_Attributes[] = {
1138     QSGGeometry::Attribute::create(0, 4, GL_FLOAT),       // Position & TexCoord
1139     QSGGeometry::Attribute::create(1, 4, GL_FLOAT),             // Data
1140     QSGGeometry::Attribute::create(2, 4, GL_FLOAT),             // Vectors
1141     QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE),     // Colors
1142     QSGGeometry::Attribute::create(4, 4, GL_FLOAT),             // DeformationVectors
1143     QSGGeometry::Attribute::create(5, 3, GL_FLOAT),             // Rotation
1144     QSGGeometry::Attribute::create(6, 3, GL_FLOAT),             // Anim Data
1145     QSGGeometry::Attribute::create(7, 4, GL_FLOAT)              // Anim Pos
1146 };
1147
1148 static QSGGeometry::AttributeSet SpriteParticle_AttributeSet =
1149 {
1150     8, // Attribute Count
1151     (4 + 4 + 4 + 4 + 3 + 3 + 4) * sizeof(float) + 4 * sizeof(uchar),
1152     SpriteParticle_Attributes
1153 };
1154
1155 void QQuickImageParticle::clearShadows()
1156 {
1157     foreach (const QVector<QQuickParticleData*> data, m_shadowData)
1158         qDeleteAll(data);
1159     m_shadowData.clear();
1160 }
1161
1162 //Only call if you need to, may initialize the whole array first time
1163 QQuickParticleData* QQuickImageParticle::getShadowDatum(QQuickParticleData* datum)
1164 {
1165     //Will return datum if the datum is a sentinel or uninitialized, to centralize that one check
1166     if (datum->systemIndex == -1)
1167         return datum;
1168     QQuickParticleGroupData* gd = m_system->groupData[datum->group];
1169     if (!m_shadowData.contains(datum->group)) {
1170         QVector<QQuickParticleData*> data;
1171         for (int i=0; i<gd->size(); i++){
1172             QQuickParticleData* datum = new QQuickParticleData(m_system);
1173             *datum = *(gd->data[i]);
1174             data << datum;
1175         }
1176         m_shadowData.insert(datum->group, data);
1177     }
1178     //### If dynamic resize is added, remember to potentially resize the shadow data on out-of-bounds access request
1179
1180     return m_shadowData[datum->group][datum->index];
1181 }
1182
1183 bool QQuickImageParticle::loadingSomething()
1184 {
1185     return (m_image && m_image->pix.isLoading())
1186         || (m_colorTable && m_colorTable->pix.isLoading())
1187         || (m_sizeTable && m_sizeTable->pix.isLoading())
1188         || (m_opacityTable && m_opacityTable->pix.isLoading())
1189         || (m_spriteEngine && m_spriteEngine->isLoading());
1190 }
1191
1192 void QQuickImageParticle::mainThreadFetchImageData()
1193 {
1194     if (m_image) {//ImageData created on setSource
1195         m_image->pix.clear(this);
1196         m_image->pix.load(qmlEngine(this), m_image->source);
1197     }
1198
1199     if (m_spriteEngine)
1200         m_spriteEngine->startAssemblingImage();
1201
1202     if (m_colorTable)
1203         m_colorTable->pix.load(qmlEngine(this), m_colorTable->source);
1204
1205     if (m_sizeTable)
1206         m_sizeTable->pix.load(qmlEngine(this), m_sizeTable->source);
1207
1208     if (m_opacityTable)
1209         m_opacityTable->pix.load(qmlEngine(this), m_opacityTable->source);
1210
1211     m_startedImageLoading = 2;
1212 }
1213
1214 void QQuickImageParticle::buildParticleNodes(QSGNode** passThrough)
1215 {
1216     // Starts async parts, like loading images, on gui thread
1217     // Not on individual properties, because we delay until system is running
1218     if (*passThrough || loadingSomething())
1219         return;
1220
1221     if (m_startedImageLoading == 0) {
1222         m_startedImageLoading = 1;
1223         //stage 1 is in gui thread
1224         QQuickImageParticle::staticMetaObject.invokeMethod(this, "mainThreadFetchImageData", Qt::QueuedConnection);
1225     } else if (m_startedImageLoading == 2) {
1226         finishBuildParticleNodes(passThrough); //rest happens in render thread
1227     }
1228
1229     //No mutex, because it's slow and a compare that fails due to a race condition means just a dropped frame
1230 }
1231
1232 void QQuickImageParticle::finishBuildParticleNodes(QSGNode** node)
1233 {
1234     if (QOpenGLContext::currentContext()->isOpenGLES() && m_count * 4 > 0xffff) {
1235         printf("ImageParticle: Too many particles - maximum 16,000 per ImageParticle.\n");//ES 2 vertex count limit is ushort
1236         return;
1237     }
1238
1239     if (count() <= 0)
1240         return;
1241
1242     m_debugMode = m_system->m_debugMode;
1243
1244     if (m_sprites.count() || m_bypassOptimizations) {
1245         perfLevel = Sprites;
1246     } else if (m_colorTable || m_sizeTable || m_opacityTable) {
1247         perfLevel = Tabled;
1248     } else if (m_autoRotation || m_rotation || m_rotationVariation
1249                || m_rotationVelocity || m_rotationVelocityVariation
1250                || m_xVector || m_yVector) {
1251         perfLevel = Deformable;
1252     } else if (m_alphaVariation || m_alpha != 1.0 || m_color.isValid() || m_color_variation
1253                || m_redVariation || m_blueVariation || m_greenVariation) {
1254         perfLevel = Colored;
1255     } else {
1256         perfLevel = Simple;
1257     }
1258
1259     foreach (const QString &str, m_groups){//For sharing higher levels, need to have highest used so it renders
1260         int gIdx = m_system->groupIds[str];
1261         foreach (QQuickParticlePainter* p, m_system->groupData[gIdx]->painters){
1262             QQuickImageParticle* other = qobject_cast<QQuickImageParticle*>(p);
1263             if (other){
1264                 if (other->perfLevel > perfLevel) {
1265                     if (other->perfLevel >= Tabled){//Deformable is the highest level needed for this, anything higher isn't shared (or requires your own sprite)
1266                         if (perfLevel < Deformable)
1267                             perfLevel = Deformable;
1268                     } else {
1269                         perfLevel = other->perfLevel;
1270                     }
1271                 } else if (other->perfLevel < perfLevel) {
1272                     other->reset();
1273                 }
1274             }
1275         }
1276     }
1277 #ifdef Q_OS_WIN
1278     if (perfLevel < Deformable) //QTBUG-24540 , point sprite 'extension' isn't working on windows.
1279         perfLevel = Deformable;
1280 #endif
1281
1282 #ifdef Q_OS_MAC
1283     // Mac OS X 10.8.3 introduced a bug in the AMD drivers, for at least the 2011 macbook pros,
1284     // causing point sprites who read gl_PointCoord in the frag shader to come out as
1285     // green-red blobs.
1286     if (perfLevel < Deformable && strstr((char *) glGetString(GL_VENDOR), "ATI")) {
1287         perfLevel = Deformable;
1288     }
1289 #endif
1290
1291 #ifdef Q_OS_LINUX
1292     // Nouveau drivers can potentially freeze a machine entirely when taking the point-sprite path.
1293     if (perfLevel < Deformable && strstr((char *) glGetString(GL_VENDOR), "nouveau"))
1294         perfLevel = Deformable;
1295 #endif
1296
1297     if (perfLevel >= Colored  && !m_color.isValid())
1298         m_color = QColor(Qt::white);//Hidden default, but different from unset
1299
1300     clearShadows();
1301     if (m_material)
1302         m_material = 0;
1303
1304     //Setup material
1305     QImage colortable;
1306     QImage sizetable;
1307     QImage opacitytable;
1308     QImage image;
1309     bool imageLoaded = false;
1310     switch (perfLevel) {//Fallthrough intended
1311     case Sprites:
1312         if (!m_spriteEngine) {
1313             qWarning() << "ImageParticle: No sprite engine...";
1314             //Sprite performance mode with static image is supported, but not advised
1315             //Note that in this case it always uses shadow data
1316         } else {
1317             image = m_spriteEngine->assembledImage();
1318             if (image.isNull())//Warning is printed in engine
1319                 return;
1320             imageLoaded = true;
1321         }
1322         m_material = SpriteMaterial::createMaterial();
1323         if (imageLoaded)
1324             getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(image);
1325         getState<ImageMaterialData>(m_material)->animSheetSize = QSizeF(image.size());
1326         if (m_spriteEngine)
1327             m_spriteEngine->setCount(m_count);
1328     case Tabled:
1329         if (!m_material)
1330             m_material = TabledMaterial::createMaterial();
1331
1332         if (m_colorTable) {
1333             if (m_colorTable->pix.isReady())
1334                 colortable = m_colorTable->pix.image();
1335             else
1336                 qmlInfo(this) << "Error loading color table: " << m_colorTable->pix.error();
1337         }
1338
1339         if (m_sizeTable) {
1340             if (m_sizeTable->pix.isReady())
1341                 sizetable = m_sizeTable->pix.image();
1342             else
1343                 qmlInfo(this) << "Error loading size table: " << m_sizeTable->pix.error();
1344         }
1345
1346         if (m_opacityTable) {
1347             if (m_opacityTable->pix.isReady())
1348                 opacitytable = m_opacityTable->pix.image();
1349             else
1350                 qmlInfo(this) << "Error loading opacity table: " << m_opacityTable->pix.error();
1351         }
1352
1353         if (colortable.isNull()){//###Goes through image just for this
1354             colortable = QImage(1,1,QImage::Format_ARGB32_Premultiplied);
1355             colortable.fill(Qt::white);
1356         }
1357         getState<ImageMaterialData>(m_material)->colorTable = QSGPlainTexture::fromImage(colortable);
1358         fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->sizeTable, sizetable, UNIFORM_ARRAY_SIZE);
1359         fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->opacityTable, opacitytable, UNIFORM_ARRAY_SIZE);
1360     case Deformable:
1361         if (!m_material)
1362             m_material = DeformableMaterial::createMaterial();
1363     case Colored:
1364         if (!m_material)
1365             m_material = ColoredMaterial::createMaterial();
1366     default://Also Simple
1367         if (!m_material)
1368             m_material = SimpleMaterial::createMaterial();
1369         if (!imageLoaded) {
1370             if (!m_image || !m_image->pix.isReady()) {
1371                 if (m_image)
1372                     qmlInfo(this) << m_image->pix.error();
1373                 delete m_material;
1374                 return;
1375             }
1376             //getState<ImageMaterialData>(m_material)->texture //TODO: Shouldn't this be better? But not crash?
1377             //    = QQuickItemPrivate::get(this)->sceneGraphContext()->textureForFactory(m_imagePix.textureFactory());
1378             getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(m_image->pix.image());
1379         }
1380         getState<ImageMaterialData>(m_material)->texture->setFiltering(QSGTexture::Linear);
1381         getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect;
1382         m_material->setFlag(QSGMaterial::Blending | QSGMaterial::RequiresFullMatrix);
1383     }
1384
1385     m_nodes.clear();
1386     foreach (const QString &str, m_groups){
1387         int gIdx = m_system->groupIds[str];
1388         int count = m_system->groupData[gIdx]->size();
1389         QSGGeometryNode* node = new QSGGeometryNode();
1390         node->setMaterial(m_material);
1391         node->markDirty(QSGNode::DirtyMaterial);
1392
1393         m_nodes.insert(gIdx, node);
1394         m_idxStarts.insert(gIdx, m_lastIdxStart);
1395         m_startsIdx.append(qMakePair<int,int>(m_lastIdxStart, gIdx));
1396         m_lastIdxStart += count;
1397
1398         //Create Particle Geometry
1399         int vCount = count * 4;
1400         int iCount = count * 6;
1401
1402         QSGGeometry *g;
1403         if (perfLevel == Sprites)
1404             g = new QSGGeometry(SpriteParticle_AttributeSet, vCount, iCount);
1405         else if (perfLevel == Tabled)
1406             g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
1407         else if (perfLevel == Deformable)
1408             g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
1409         else if (perfLevel == Colored)
1410             g = new QSGGeometry(ColoredParticle_AttributeSet, count, 0);
1411         else //Simple
1412             g = new QSGGeometry(SimpleParticle_AttributeSet, count, 0);
1413
1414         node->setGeometry(g);
1415         if (perfLevel <= Colored){
1416             g->setDrawingMode(GL_POINTS);
1417             if (m_debugMode){
1418                 GLfloat pointSizeRange[2];
1419                 QOpenGLContext::currentContext()->functions()->glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
1420                 qDebug() << "Using point sprites, GL_ALIASED_POINT_SIZE_RANGE " <<pointSizeRange[0] << ":" << pointSizeRange[1];
1421             }
1422         }else
1423             g->setDrawingMode(GL_TRIANGLES);
1424
1425         for (int p=0; p < count; ++p)
1426             commit(gIdx, p);//commit sets geometry for the node, has its own perfLevel switch
1427
1428         if (perfLevel == Sprites)
1429             initTexCoords<SpriteVertex>((SpriteVertex*)g->vertexData(), vCount);
1430         else if (perfLevel == Tabled)
1431             initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
1432         else if (perfLevel == Deformable)
1433             initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
1434
1435         if (perfLevel > Colored){
1436             quint16 *indices = g->indexDataAsUShort();
1437             for (int i=0; i < count; ++i) {
1438                 int o = i * 4;
1439                 indices[0] = o;
1440                 indices[1] = o + 1;
1441                 indices[2] = o + 2;
1442                 indices[3] = o + 1;
1443                 indices[4] = o + 3;
1444                 indices[5] = o + 2;
1445                 indices += 6;
1446             }
1447         }
1448     }
1449
1450     if (perfLevel == Sprites)
1451         spritesUpdate();//Gives all vertexes the initial sprite data, then maintained per frame
1452
1453     foreach (QSGGeometryNode* node, m_nodes){
1454         if (node == *(m_nodes.begin()))
1455             node->setFlag(QSGGeometryNode::OwnsMaterial);//Root node owns the material for memory management purposes
1456         else
1457             (*(m_nodes.begin()))->appendChildNode(node);
1458     }
1459
1460     *node = *(m_nodes.begin());
1461     update();
1462 }
1463
1464 QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
1465 {
1466     if (m_pleaseReset){
1467         if (node)
1468             delete node;
1469         node = 0;
1470
1471         m_lastLevel = perfLevel;
1472         m_nodes.clear();
1473
1474         m_idxStarts.clear();
1475         m_startsIdx.clear();
1476         m_lastIdxStart = 0;
1477
1478         m_material = 0;
1479
1480         m_pleaseReset = false;
1481         m_startedImageLoading = 0;//Cancel a part-way build (may still have a pending load)
1482     }
1483
1484     if (m_system && m_system->isRunning() && !m_system->isPaused()){
1485         prepareNextFrame(&node);
1486         if (node) {
1487             update();
1488             foreach (QSGGeometryNode* n, m_nodes)
1489                 n->markDirty(QSGNode::DirtyGeometry);
1490         } else if (m_startedImageLoading < 2) {
1491             update();//To call prepareNextFrame() again from the renderThread
1492         }
1493     }
1494
1495     return node;
1496 }
1497
1498 void QQuickImageParticle::prepareNextFrame(QSGNode **node)
1499 {
1500     if (*node == 0){//TODO: Staggered loading (as emitted)
1501         buildParticleNodes(node);
1502         if (m_debugMode) {
1503             qDebug() << "QQuickImageParticle Feature level: " << perfLevel;
1504             qDebug() << "QQuickImageParticle Nodes: ";
1505             int count = 0;
1506             foreach (int i, m_nodes.keys()) {
1507                 qDebug() << "Group " << i << " (" << m_system->groupData[i]->size() << " particles)";
1508                 count += m_system->groupData[i]->size();
1509             }
1510             qDebug() << "Total count: " << count;
1511         }
1512         if (*node == 0)
1513             return;
1514     }
1515     qint64 timeStamp = m_system->systemSync(this);
1516
1517     qreal time = timeStamp / 1000.;
1518
1519     switch (perfLevel){//Fall-through intended
1520     case Sprites:
1521         //Advance State
1522         if (m_spriteEngine)
1523             m_spriteEngine->updateSprites(timeStamp);//fires signals if anim changed
1524         spritesUpdate(time);
1525     case Tabled:
1526     case Deformable:
1527     case Colored:
1528     case Simple:
1529     default: //Also Simple
1530         getState<ImageMaterialData>(m_material)->timestamp = time;
1531         break;
1532     }
1533     foreach (QSGGeometryNode* node, m_nodes)
1534         node->markDirty(QSGNode::DirtyMaterial);
1535 }
1536
1537 void QQuickImageParticle::spritesUpdate(qreal time)
1538 {
1539     // Sprite progression handled CPU side, so as to have per-frame control.
1540     foreach (const QString &str, m_groups) {
1541         int gIdx = m_system->groupIds[str];
1542         foreach (QQuickParticleData* mainDatum, m_system->groupData[gIdx]->data) {
1543             QSGGeometryNode *node = m_nodes[gIdx];
1544             if (!node)
1545                 continue;
1546             //TODO: Interpolate between two different animations if it's going to transition next frame
1547             //      This is particularly important for cut-up sprites.
1548             QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum));
1549             int spriteIdx = 0;
1550             for (int i = 0; i<m_startsIdx.count(); i++) {
1551                 if (m_startsIdx[i].second == gIdx){
1552                     spriteIdx = m_startsIdx[i].first + datum->index;
1553                     break;
1554                 }
1555             }
1556
1557             double frameAt;
1558             qreal progress = 0;
1559
1560             if (datum->frameDuration > 0) {
1561                 qreal frame = (time - datum->animT)/(datum->frameDuration / 1000.0);
1562                 frame = qBound((qreal)0.0, frame, (qreal)((qreal)datum->frameCount - 1.0));//Stop at count-1 frames until we have between anim interpolation
1563                 if (m_spritesInterpolate)
1564                     progress = std::modf(frame,&frameAt);
1565                 else
1566                     std::modf(frame,&frameAt);
1567             } else {
1568                 datum->frameAt++;
1569                 if (datum->frameAt >= datum->frameCount){
1570                     datum->frameAt = 0;
1571                     m_spriteEngine->advance(spriteIdx);
1572                 }
1573                 frameAt = datum->frameAt;
1574             }
1575             if (m_spriteEngine->sprite(spriteIdx)->reverse())//### Store this in datum too?
1576                 frameAt = (datum->frameCount - 1) - frameAt;
1577             QSizeF sheetSize = getState<ImageMaterialData>(m_material)->animSheetSize;
1578             qreal y = datum->animY / sheetSize.height();
1579             qreal w = datum->animWidth / sheetSize.width();
1580             qreal h = datum->animHeight / sheetSize.height();
1581             qreal x1 = datum->animX / sheetSize.width();
1582             x1 += frameAt * w;
1583             qreal x2 = x1;
1584             if (frameAt < (datum->frameCount-1))
1585                 x2 += w;
1586
1587             SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
1588             spriteVertices += datum->index*4;
1589             for (int i=0; i<4; i++) {
1590                 spriteVertices[i].animX1 = x1;
1591                 spriteVertices[i].animY1 = y;
1592                 spriteVertices[i].animX2 = x2;
1593                 spriteVertices[i].animY2 = y;
1594                 spriteVertices[i].animW = w;
1595                 spriteVertices[i].animH = h;
1596                 spriteVertices[i].animProgress = progress;
1597             }
1598         }
1599     }
1600 }
1601
1602 void QQuickImageParticle::spriteAdvance(int spriteIdx)
1603 {
1604     if (!m_startsIdx.count())//Probably overly defensive
1605         return;
1606
1607     int gIdx = -1;
1608     int i;
1609     for (i = 0; i<m_startsIdx.count(); i++) {
1610         if (spriteIdx < m_startsIdx[i].first) {
1611             gIdx = m_startsIdx[i-1].second;
1612             break;
1613         }
1614     }
1615     if (gIdx == -1)
1616         gIdx = m_startsIdx[i-1].second;
1617     int pIdx = spriteIdx - m_startsIdx[i-1].first;
1618
1619     QQuickParticleData* mainDatum = m_system->groupData[gIdx]->data[pIdx];
1620     QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum));
1621
1622     datum->animIdx = m_spriteEngine->spriteState(spriteIdx);
1623     datum->animT = m_spriteEngine->spriteStart(spriteIdx)/1000.0;
1624     datum->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
1625     datum->frameDuration = m_spriteEngine->spriteDuration(spriteIdx) / datum->frameCount;
1626     datum->animX = m_spriteEngine->spriteX(spriteIdx);
1627     datum->animY = m_spriteEngine->spriteY(spriteIdx);
1628     datum->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
1629     datum->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
1630 }
1631
1632 void QQuickImageParticle::reloadColor(const Color4ub &c, QQuickParticleData* d)
1633 {
1634     d->color = c;
1635     //TODO: get index for reload - or make function take an index
1636 }
1637
1638 void QQuickImageParticle::initialize(int gIdx, int pIdx)
1639 {
1640     Color4ub color;
1641     QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
1642     qreal redVariation = m_color_variation + m_redVariation;
1643     qreal greenVariation = m_color_variation + m_greenVariation;
1644     qreal blueVariation = m_color_variation + m_blueVariation;
1645     int spriteIdx = 0;
1646     if (m_spriteEngine) {
1647         spriteIdx = m_idxStarts[gIdx] + datum->index;
1648         if (spriteIdx >= m_spriteEngine->count())
1649             m_spriteEngine->setCount(spriteIdx+1);
1650     }
1651
1652     float rotation;
1653     float rotationVelocity;
1654     float autoRotate;
1655     switch (perfLevel){//Fall-through is intended on all of them
1656         case Sprites:
1657             // Initial Sprite State
1658             if (m_explicitAnimation && m_spriteEngine){
1659                 if (!datum->animationOwner)
1660                     datum->animationOwner = this;
1661                 QQuickParticleData* writeTo = (datum->animationOwner == this ? datum : getShadowDatum(datum));
1662                 writeTo->animT = writeTo->t;
1663                 //writeTo->animInterpolate = m_spritesInterpolate;
1664                 if (m_spriteEngine){
1665                     m_spriteEngine->start(spriteIdx);
1666                     writeTo->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
1667                     writeTo->frameDuration = m_spriteEngine->spriteDuration(spriteIdx) / writeTo->frameCount;
1668                     writeTo->animIdx = 0;//Always starts at 0
1669                     writeTo->frameAt = -1;
1670                     writeTo->animX = m_spriteEngine->spriteX(spriteIdx);
1671                     writeTo->animY = m_spriteEngine->spriteY(spriteIdx);
1672                     writeTo->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
1673                     writeTo->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
1674                 }
1675             } else {
1676                 QQuickParticleData* writeTo = getShadowDatum(datum);
1677                 writeTo->animT = datum->t;
1678                 writeTo->frameCount = 1;
1679                 writeTo->frameDuration = 60000000.0;
1680                 writeTo->frameAt = -1;
1681                 writeTo->animIdx = 0;
1682                 writeTo->animT = 0;
1683                 writeTo->animX = writeTo->animY = 0;
1684                 writeTo->animWidth = getState<ImageMaterialData>(m_material)->animSheetSize.width();
1685                 writeTo->animHeight = getState<ImageMaterialData>(m_material)->animSheetSize.height();
1686             }
1687         case Tabled:
1688         case Deformable:
1689             //Initial Rotation
1690             if (m_explicitDeformation){
1691                 if (!datum->deformationOwner)
1692                     datum->deformationOwner = this;
1693                 if (m_xVector){
1694                     const QPointF &ret = m_xVector->sample(QPointF(datum->x, datum->y));
1695                     if (datum->deformationOwner == this) {
1696                         datum->xx = ret.x();
1697                         datum->xy = ret.y();
1698                     } else {
1699                         getShadowDatum(datum)->xx = ret.x();
1700                         getShadowDatum(datum)->xy = ret.y();
1701                     }
1702                 }
1703                 if (m_yVector){
1704                     const QPointF &ret = m_yVector->sample(QPointF(datum->x, datum->y));
1705                     if (datum->deformationOwner == this) {
1706                         datum->yx = ret.x();
1707                         datum->yy = ret.y();
1708                     } else {
1709                         getShadowDatum(datum)->yx = ret.x();
1710                         getShadowDatum(datum)->yy = ret.y();
1711                     }
1712                 }
1713             }
1714
1715             if (m_explicitRotation){
1716                 if (!datum->rotationOwner)
1717                     datum->rotationOwner = this;
1718                 rotation =
1719                         (m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV;
1720                 rotationVelocity =
1721                         (m_rotationVelocity + (m_rotationVelocityVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVelocityVariation) ) * CONV;
1722                 autoRotate = m_autoRotation?1.0:0.0;
1723                 if (datum->rotationOwner == this) {
1724                     datum->rotation = rotation;
1725                     datum->rotationVelocity = rotationVelocity;
1726                     datum->autoRotate = autoRotate;
1727                 } else {
1728                     getShadowDatum(datum)->rotation = rotation;
1729                     getShadowDatum(datum)->rotationVelocity = rotationVelocity;
1730                     getShadowDatum(datum)->autoRotate = autoRotate;
1731                 }
1732             }
1733         case Colored:
1734             //Color initialization
1735             // Particle color
1736             if (m_explicitColor) {
1737                 if (!datum->colorOwner)
1738                     datum->colorOwner = this;
1739                 color.r = m_color.red() * (1 - redVariation) + rand() % 256 * redVariation;
1740                 color.g = m_color.green() * (1 - greenVariation) + rand() % 256 * greenVariation;
1741                 color.b = m_color.blue() * (1 - blueVariation) + rand() % 256 * blueVariation;
1742                 color.a = m_alpha * m_color.alpha() * (1 - m_alphaVariation) + rand() % 256 * m_alphaVariation;
1743                 if (datum->colorOwner == this)
1744                     datum->color = color;
1745                 else
1746                     getShadowDatum(datum)->color = color;
1747             }
1748         default:
1749             break;
1750     }
1751 }
1752
1753 void QQuickImageParticle::commit(int gIdx, int pIdx)
1754 {
1755     if (m_pleaseReset)
1756         return;
1757     QSGGeometryNode *node = m_nodes[gIdx];
1758     if (!node)
1759         return;
1760     QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
1761     SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
1762     DeformableVertex *deformableVertices = (DeformableVertex *) node->geometry()->vertexData();
1763     ColoredVertex *coloredVertices = (ColoredVertex *) node->geometry()->vertexData();
1764     SimpleVertex *simpleVertices = (SimpleVertex *) node->geometry()->vertexData();
1765     switch (perfLevel){//No automatic fall through intended on this one
1766     case Sprites:
1767         spriteVertices += pIdx*4;
1768         for (int i=0; i<4; i++){
1769             spriteVertices[i].x = datum->x  - m_systemOffset.x();
1770             spriteVertices[i].y = datum->y  - m_systemOffset.y();
1771             spriteVertices[i].t = datum->t;
1772             spriteVertices[i].lifeSpan = datum->lifeSpan;
1773             spriteVertices[i].size = datum->size;
1774             spriteVertices[i].endSize = datum->endSize;
1775             spriteVertices[i].vx = datum->vx;
1776             spriteVertices[i].vy = datum->vy;
1777             spriteVertices[i].ax = datum->ax;
1778             spriteVertices[i].ay = datum->ay;
1779             if (m_explicitDeformation && datum->deformationOwner != this) {
1780                 QQuickParticleData* shadow = getShadowDatum(datum);
1781                 spriteVertices[i].xx = shadow->xx;
1782                 spriteVertices[i].xy = shadow->xy;
1783                 spriteVertices[i].yx = shadow->yx;
1784                 spriteVertices[i].yy = shadow->yy;
1785             } else {
1786                 spriteVertices[i].xx = datum->xx;
1787                 spriteVertices[i].xy = datum->xy;
1788                 spriteVertices[i].yx = datum->yx;
1789                 spriteVertices[i].yy = datum->yy;
1790             }
1791             if (m_explicitRotation && datum->rotationOwner != this) {
1792                 QQuickParticleData* shadow = getShadowDatum(datum);
1793                 spriteVertices[i].rotation = shadow->rotation;
1794                 spriteVertices[i].rotationVelocity = shadow->rotationVelocity;
1795                 spriteVertices[i].autoRotate = shadow->autoRotate;
1796             } else {
1797                 spriteVertices[i].rotation = datum->rotation;
1798                 spriteVertices[i].rotationVelocity = datum->rotationVelocity;
1799                 spriteVertices[i].autoRotate = datum->autoRotate;
1800             }
1801             //Sprite-related vertices updated per-frame in spritesUpdate(), not on demand
1802             if (m_explicitColor && datum->colorOwner != this) {
1803                 QQuickParticleData* shadow = getShadowDatum(datum);
1804                 spriteVertices[i].color.r = shadow->color.r;
1805                 spriteVertices[i].color.g = shadow->color.g;
1806                 spriteVertices[i].color.b = shadow->color.b;
1807                 spriteVertices[i].color.a = shadow->color.a;
1808             } else {
1809                 spriteVertices[i].color.r = datum->color.r;
1810                 spriteVertices[i].color.g = datum->color.g;
1811                 spriteVertices[i].color.b = datum->color.b;
1812                 spriteVertices[i].color.a = datum->color.a;
1813             }
1814         }
1815         break;
1816     case Tabled: //Fall through until it has its own vertex class
1817     case Deformable:
1818         deformableVertices += pIdx*4;
1819         for (int i=0; i<4; i++){
1820             deformableVertices[i].x = datum->x  - m_systemOffset.x();
1821             deformableVertices[i].y = datum->y  - m_systemOffset.y();
1822             deformableVertices[i].t = datum->t;
1823             deformableVertices[i].lifeSpan = datum->lifeSpan;
1824             deformableVertices[i].size = datum->size;
1825             deformableVertices[i].endSize = datum->endSize;
1826             deformableVertices[i].vx = datum->vx;
1827             deformableVertices[i].vy = datum->vy;
1828             deformableVertices[i].ax = datum->ax;
1829             deformableVertices[i].ay = datum->ay;
1830             if (m_explicitDeformation && datum->deformationOwner != this) {
1831                 QQuickParticleData* shadow = getShadowDatum(datum);
1832                 deformableVertices[i].xx = shadow->xx;
1833                 deformableVertices[i].xy = shadow->xy;
1834                 deformableVertices[i].yx = shadow->yx;
1835                 deformableVertices[i].yy = shadow->yy;
1836             } else {
1837                 deformableVertices[i].xx = datum->xx;
1838                 deformableVertices[i].xy = datum->xy;
1839                 deformableVertices[i].yx = datum->yx;
1840                 deformableVertices[i].yy = datum->yy;
1841             }
1842             if (m_explicitRotation && datum->rotationOwner != this) {
1843                 QQuickParticleData* shadow = getShadowDatum(datum);
1844                 deformableVertices[i].rotation = shadow->rotation;
1845                 deformableVertices[i].rotationVelocity = shadow->rotationVelocity;
1846                 deformableVertices[i].autoRotate = shadow->autoRotate;
1847             } else {
1848                 deformableVertices[i].rotation = datum->rotation;
1849                 deformableVertices[i].rotationVelocity = datum->rotationVelocity;
1850                 deformableVertices[i].autoRotate = datum->autoRotate;
1851             }
1852             if (m_explicitColor && datum->colorOwner != this) {
1853                 QQuickParticleData* shadow = getShadowDatum(datum);
1854                 deformableVertices[i].color.r = shadow->color.r;
1855                 deformableVertices[i].color.g = shadow->color.g;
1856                 deformableVertices[i].color.b = shadow->color.b;
1857                 deformableVertices[i].color.a = shadow->color.a;
1858             } else {
1859                 deformableVertices[i].color.r = datum->color.r;
1860                 deformableVertices[i].color.g = datum->color.g;
1861                 deformableVertices[i].color.b = datum->color.b;
1862                 deformableVertices[i].color.a = datum->color.a;
1863             }
1864         }
1865         break;
1866     case Colored:
1867         coloredVertices += pIdx*1;
1868         for (int i=0; i<1; i++){
1869             coloredVertices[i].x = datum->x  - m_systemOffset.x();
1870             coloredVertices[i].y = datum->y  - m_systemOffset.y();
1871             coloredVertices[i].t = datum->t;
1872             coloredVertices[i].lifeSpan = datum->lifeSpan;
1873             coloredVertices[i].size = datum->size;
1874             coloredVertices[i].endSize = datum->endSize;
1875             coloredVertices[i].vx = datum->vx;
1876             coloredVertices[i].vy = datum->vy;
1877             coloredVertices[i].ax = datum->ax;
1878             coloredVertices[i].ay = datum->ay;
1879             if (m_explicitColor && datum->colorOwner != this) {
1880                 QQuickParticleData* shadow = getShadowDatum(datum);
1881                 coloredVertices[i].color.r = shadow->color.r;
1882                 coloredVertices[i].color.g = shadow->color.g;
1883                 coloredVertices[i].color.b = shadow->color.b;
1884                 coloredVertices[i].color.a = shadow->color.a;
1885             } else {
1886                 coloredVertices[i].color.r = datum->color.r;
1887                 coloredVertices[i].color.g = datum->color.g;
1888                 coloredVertices[i].color.b = datum->color.b;
1889                 coloredVertices[i].color.a = datum->color.a;
1890             }
1891         }
1892         break;
1893     case Simple:
1894         simpleVertices += pIdx*1;
1895         for (int i=0; i<1; i++){
1896             simpleVertices[i].x = datum->x - m_systemOffset.x();
1897             simpleVertices[i].y = datum->y - m_systemOffset.y();
1898             simpleVertices[i].t = datum->t;
1899             simpleVertices[i].lifeSpan = datum->lifeSpan;
1900             simpleVertices[i].size = datum->size;
1901             simpleVertices[i].endSize = datum->endSize;
1902             simpleVertices[i].vx = datum->vx;
1903             simpleVertices[i].vy = datum->vy;
1904             simpleVertices[i].ax = datum->ax;
1905             simpleVertices[i].ay = datum->ay;
1906         }
1907         break;
1908     default:
1909         break;
1910     }
1911 }
1912
1913
1914
1915 QT_END_NAMESPACE