Rewrite particle system to cope with changing particle counts
[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 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
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 <QGLFunctions>
53 #include <qsgengine.h>
54
55 QT_BEGIN_NAMESPACE
56
57 const float CONV = 0.017453292519943295;
58 class UltraMaterial : public QSGMaterial
59 {
60 public:
61     UltraMaterial(bool withSprites=false)
62         : timestamp(0)
63         , framecount(1)
64         , animcount(1)
65         , usesSprites(withSprites)
66     {
67         setFlag(Blending, true);
68     }
69
70     ~UltraMaterial()
71     {
72         delete texture;
73         delete colortable;
74         delete sizetable;
75         delete opacitytable;
76     }
77
78     virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; }
79     virtual QSGMaterialShader *createShader() const;
80     virtual int compare(const QSGMaterial *other) const
81     {
82         return this - static_cast<const UltraMaterial *>(other);
83     }
84
85     QSGTexture *texture;
86     QSGTexture *colortable;
87     QSGTexture *sizetable;
88     QSGTexture *opacitytable;
89
90     qreal timestamp;
91     int framecount;
92     int animcount;
93     bool usesSprites;
94 };
95 class UltraMaterialData : public QSGMaterialShader
96 {
97 public:
98     UltraMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0)
99     {
100         QFile vf(vertexFile ? vertexFile : ":defaultshaders/ultravertex.shader");
101         vf.open(QFile::ReadOnly);
102         m_vertex_code = vf.readAll();
103
104         QFile ff(fragmentFile ? fragmentFile : ":defaultshaders/ultrafragment.shader");
105         ff.open(QFile::ReadOnly);
106         m_fragment_code = ff.readAll();
107
108         Q_ASSERT(!m_vertex_code.isNull());
109         Q_ASSERT(!m_fragment_code.isNull());
110     }
111
112     void deactivate() {
113         QSGMaterialShader::deactivate();
114
115         for (int i=0; i<8; ++i) {
116             program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0);
117         }
118     }
119
120     virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *)
121     {
122         UltraMaterial *m = static_cast<UltraMaterial *>(newEffect);
123         state.context()->functions()->glActiveTexture(GL_TEXTURE1);
124         m->colortable->bind();
125         program()->setUniformValue(m_colortable_id, 1);
126
127         state.context()->functions()->glActiveTexture(GL_TEXTURE2);
128         m->sizetable->bind();
129         program()->setUniformValue(m_sizetable_id, 2);
130
131         state.context()->functions()->glActiveTexture(GL_TEXTURE3);
132         m->opacitytable->bind();
133         program()->setUniformValue(m_opacitytable_id, 3);
134
135         state.context()->functions()->glActiveTexture(GL_TEXTURE0);//Investigate why this screws up Text{} if placed before 1
136         m->texture->bind();
137
138         program()->setUniformValue(m_opacity_id, state.opacity());
139         program()->setUniformValue(m_timestamp_id, (float) m->timestamp);
140         program()->setUniformValue(m_framecount_id, (float) m->framecount);
141         program()->setUniformValue(m_animcount_id, (float) m->animcount);
142
143         if (state.isMatrixDirty())
144             program()->setUniformValue(m_matrix_id, state.combinedMatrix());
145     }
146
147     virtual void initialize() {
148         m_colortable_id = program()->uniformLocation("colortable");
149         m_sizetable_id = program()->uniformLocation("sizetable");
150         m_opacitytable_id = program()->uniformLocation("opacitytable");
151         m_matrix_id = program()->uniformLocation("matrix");
152         m_opacity_id = program()->uniformLocation("opacity");
153         m_timestamp_id = program()->uniformLocation("timestamp");
154         m_framecount_id = program()->uniformLocation("framecount");
155         m_animcount_id = program()->uniformLocation("animcount");
156     }
157
158     virtual const char *vertexShader() const { return m_vertex_code.constData(); }
159     virtual const char *fragmentShader() const { return m_fragment_code.constData(); }
160
161     virtual char const *const *attributeNames() const {
162         static const char *attr[] = {
163             "vPos",
164             "vTex",
165             "vData",
166             "vVec",
167             "vColor",
168             "vDeformVec",
169             "vRotation",
170             "vAnimData",
171             0
172         };
173         return attr;
174     }
175
176     virtual bool isColorTable() const { return false; }
177
178     int m_matrix_id;
179     int m_opacity_id;
180     int m_timestamp_id;
181     int m_colortable_id;
182     int m_sizetable_id;
183     int m_opacitytable_id;
184     int m_framecount_id;
185     int m_animcount_id;
186
187     QByteArray m_vertex_code;
188     QByteArray m_fragment_code;
189
190     static float chunkOfBytes[1024];
191 };
192 float UltraMaterialData::chunkOfBytes[1024];
193
194 QSGMaterialShader *UltraMaterial::createShader() const
195 {
196     if(usesSprites)//TODO: Perhaps just swap the shaders, and don't mind the extra vector?
197         return new UltraMaterialData;
198     else
199         return new UltraMaterialData;
200 }
201
202
203 class SimpleMaterial : public UltraMaterial
204 {
205     virtual QSGMaterialShader *createShader() const;
206     virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; }
207 };
208
209 class SimpleMaterialData : public QSGMaterialShader
210 {
211 public:
212     SimpleMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0)
213     {
214         QFile vf(vertexFile ? vertexFile : ":defaultshaders/simplevertex.shader");
215         vf.open(QFile::ReadOnly);
216         m_vertex_code = vf.readAll();
217
218         QFile ff(fragmentFile ? fragmentFile : ":defaultshaders/simplefragment.shader");
219         ff.open(QFile::ReadOnly);
220         m_fragment_code = ff.readAll();
221
222         Q_ASSERT(!m_vertex_code.isNull());
223         Q_ASSERT(!m_fragment_code.isNull());
224     }
225
226     void deactivate() {
227         QSGMaterialShader::deactivate();
228
229         for (int i=0; i<8; ++i) {
230             program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0);
231         }
232     }
233
234     virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *)
235     {
236         UltraMaterial *m = static_cast<UltraMaterial *>(newEffect);
237         state.context()->functions()->glActiveTexture(GL_TEXTURE0);
238         m->texture->bind();
239
240         program()->setUniformValue(m_opacity_id, state.opacity());
241         program()->setUniformValue(m_timestamp_id, (float) m->timestamp);
242
243         if (state.isMatrixDirty())
244             program()->setUniformValue(m_matrix_id, state.combinedMatrix());
245     }
246
247     virtual void initialize() {
248         m_matrix_id = program()->uniformLocation("matrix");
249         m_opacity_id = program()->uniformLocation("opacity");
250         m_timestamp_id = program()->uniformLocation("timestamp");
251     }
252
253     virtual const char *vertexShader() const { return m_vertex_code.constData(); }
254     virtual const char *fragmentShader() const { return m_fragment_code.constData(); }
255
256     virtual char const *const *attributeNames() const {
257         static const char *attr[] = {
258             "vPos",
259             "vTex",
260             "vData",
261             "vVec",
262             0
263         };
264         return attr;
265     }
266
267     virtual bool isColorTable() const { return false; }
268
269     int m_matrix_id;
270     int m_opacity_id;
271     int m_timestamp_id;
272
273     QByteArray m_vertex_code;
274     QByteArray m_fragment_code;
275
276     static float chunkOfBytes[1024];
277 };
278 float SimpleMaterialData::chunkOfBytes[1024];
279
280 QSGMaterialShader *SimpleMaterial::createShader() const {
281     return new SimpleMaterialData;
282 }
283
284 QSGImageParticle::QSGImageParticle(QSGItem* parent)
285     : QSGParticlePainter(parent)
286     , m_do_reset(false)
287     , m_color_variation(0.0)
288     , m_node(0)
289     , m_material(0)
290     , m_alphaVariation(0.0)
291     , m_alpha(1.0)
292     , m_redVariation(0.0)
293     , m_greenVariation(0.0)
294     , m_blueVariation(0.0)
295     , m_rotation(0)
296     , m_autoRotation(false)
297     , m_xVector(0)
298     , m_yVector(0)
299     , m_rotationVariation(0)
300     , m_rotationSpeed(0)
301     , m_rotationSpeedVariation(0)
302     , m_spriteEngine(0)
303     , m_bloat(false)
304     , perfLevel(Unknown)
305     , m_lastLevel(Unknown)
306 {
307     setFlag(ItemHasContents);
308     //TODO: Clean up defaults here and in custom
309     m_defaultUltra = new UltraVertices;
310     m_defaultSimple = new SimpleVertices;
311     UltraVertex *vertices = (UltraVertex *) m_defaultUltra;
312     SimpleVertex *vertices2 = (SimpleVertex *) m_defaultSimple;
313     for (int i=0; i<4; ++i) {
314         vertices2[i].x = vertices[i].x = 0;
315         vertices2[i].y = vertices[i].y = 0;
316         vertices2[i].t = vertices[i].t = -1;
317         vertices2[i].lifeSpan = vertices[i].lifeSpan = 0;
318         vertices2[i].size = vertices[i].size = 0;
319         vertices2[i].endSize = vertices[i].endSize = 0;
320         vertices2[i].sx = vertices[i].sx = 0;
321         vertices2[i].sy = vertices[i].sy = 0;
322         vertices2[i].ax = vertices[i].ax = 0;
323         vertices2[i].ay = vertices[i].ay = 0;
324         vertices[i].xx = 1;
325         vertices[i].xy = 0;
326         vertices[i].yx = 0;
327         vertices[i].yy = 1;
328         vertices[i].rotation = 0;
329         vertices[i].rotationSpeed = 0;
330         vertices[i].autoRotate = 0;
331         vertices[i].animIdx = -1;
332         vertices[i].frameDuration = 1;
333         vertices[i].frameCount = 0;
334         vertices[i].animT = -1;
335         vertices[i].color.r = 255;
336         vertices[i].color.g = 255;
337         vertices[i].color.b = 255;
338         vertices[i].color.a = 255;
339     }
340
341     vertices[0].tx = 0;
342     vertices[0].ty = 0;
343
344     vertices[1].tx = 1;
345     vertices[1].ty = 0;
346
347     vertices[2].tx = 0;
348     vertices[2].ty = 1;
349
350     vertices[3].tx = 1;
351     vertices[3].ty = 1;
352
353     vertices2[0].tx = 0;
354     vertices2[0].ty = 0;
355
356     vertices2[1].tx = 1;
357     vertices2[1].ty = 0;
358
359     vertices2[2].tx = 0;
360     vertices2[2].ty = 1;
361
362     vertices2[3].tx = 1;
363     vertices2[3].ty = 1;
364 }
365
366 QDeclarativeListProperty<QSGSprite> QSGImageParticle::sprites()
367 {
368     return QDeclarativeListProperty<QSGSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
369 }
370
371 void QSGImageParticle::setImage(const QUrl &image)
372 {
373     if (image == m_image_name)
374         return;
375     m_image_name = image;
376     emit imageChanged();
377     reset();
378 }
379
380
381 void QSGImageParticle::setColortable(const QUrl &table)
382 {
383     if (table == m_colortable_name)
384         return;
385     m_colortable_name = table;
386     emit colortableChanged();
387     reset();
388 }
389
390 void QSGImageParticle::setSizetable(const QUrl &table)
391 {
392     if (table == m_sizetable_name)
393         return;
394     m_sizetable_name = table;
395     emit sizetableChanged();
396     reset();
397 }
398
399 void QSGImageParticle::setOpacitytable(const QUrl &table)
400 {
401     if (table == m_opacitytable_name)
402         return;
403     m_opacitytable_name = table;
404     emit opacitytableChanged();
405     reset();
406 }
407
408 void QSGImageParticle::setColor(const QColor &color)
409 {
410     if (color == m_color)
411         return;
412     m_color = color;
413     emit colorChanged();
414     if(perfLevel < Coloured)
415         reset();
416 }
417
418 void QSGImageParticle::setColorVariation(qreal var)
419 {
420     if (var == m_color_variation)
421         return;
422     m_color_variation = var;
423     emit colorVariationChanged();
424     if(perfLevel < Coloured)
425         reset();
426 }
427
428 void QSGImageParticle::setAlphaVariation(qreal arg)
429 {
430     if (m_alphaVariation != arg) {
431         m_alphaVariation = arg;
432         emit alphaVariationChanged(arg);
433     }
434     if(perfLevel < Coloured)
435         reset();
436 }
437
438 void QSGImageParticle::setAlpha(qreal arg)
439 {
440     if (m_alpha != arg) {
441         m_alpha = arg;
442         emit alphaChanged(arg);
443     }
444     if(perfLevel < Coloured)
445         reset();
446 }
447
448 void QSGImageParticle::setRedVariation(qreal arg)
449 {
450     if (m_redVariation != arg) {
451         m_redVariation = arg;
452         emit redVariationChanged(arg);
453     }
454     if(perfLevel < Coloured)
455         reset();
456 }
457
458 void QSGImageParticle::setGreenVariation(qreal arg)
459 {
460     if (m_greenVariation != arg) {
461         m_greenVariation = arg;
462         emit greenVariationChanged(arg);
463     }
464     if(perfLevel < Coloured)
465         reset();
466 }
467
468 void QSGImageParticle::setBlueVariation(qreal arg)
469 {
470     if (m_blueVariation != arg) {
471         m_blueVariation = arg;
472         emit blueVariationChanged(arg);
473     }
474     if(perfLevel < Coloured)
475         reset();
476 }
477
478 void QSGImageParticle::setRotation(qreal arg)
479 {
480     if (m_rotation != arg) {
481         m_rotation = arg;
482         emit rotationChanged(arg);
483     }
484     if(perfLevel < Deformable)
485         reset();
486 }
487
488 void QSGImageParticle::setRotationVariation(qreal arg)
489 {
490     if (m_rotationVariation != arg) {
491         m_rotationVariation = arg;
492         emit rotationVariationChanged(arg);
493     }
494     if(perfLevel < Deformable)
495         reset();
496 }
497
498 void QSGImageParticle::setRotationSpeed(qreal arg)
499 {
500     if (m_rotationSpeed != arg) {
501         m_rotationSpeed = arg;
502         emit rotationSpeedChanged(arg);
503     }
504     if(perfLevel < Deformable)
505         reset();
506 }
507
508 void QSGImageParticle::setRotationSpeedVariation(qreal arg)
509 {
510     if (m_rotationSpeedVariation != arg) {
511         m_rotationSpeedVariation = arg;
512         emit rotationSpeedVariationChanged(arg);
513     }
514     if(perfLevel < Deformable)
515         reset();
516 }
517
518 void QSGImageParticle::setAutoRotation(bool arg)
519 {
520     if (m_autoRotation != arg) {
521         m_autoRotation = arg;
522         emit autoRotationChanged(arg);
523     }
524     if(perfLevel < Deformable)
525         reset();
526 }
527
528 void QSGImageParticle::setXVector(QSGStochasticDirection* arg)
529 {
530     if (m_xVector != arg) {
531         m_xVector = arg;
532         emit xVectorChanged(arg);
533     }
534     if(perfLevel < Deformable)
535         reset();
536 }
537
538 void QSGImageParticle::setYVector(QSGStochasticDirection* arg)
539 {
540     if (m_yVector != arg) {
541         m_yVector = arg;
542         emit yVectorChanged(arg);
543     }
544     if(perfLevel < Deformable)
545         reset();
546 }
547
548 void QSGImageParticle::setBloat(bool arg)
549 {
550     if (m_bloat != arg) {
551         m_bloat = arg;
552         emit bloatChanged(arg);
553     }
554     if(perfLevel < 9999)
555         reset();
556 }
557
558 void QSGImageParticle::reset()
559 {
560     QSGParticlePainter::reset();
561      m_pleaseReset = true;
562 }
563
564 void QSGImageParticle::createEngine()
565 {
566     if(m_spriteEngine)
567         delete m_spriteEngine;
568     if(m_sprites.count())
569         m_spriteEngine = new QSGSpriteEngine(m_sprites, this);
570     else
571         m_spriteEngine = 0;
572     reset();
573 }
574
575 static QSGGeometry::Attribute SimpleParticle_Attributes[] = {
576     { 0, 2, GL_FLOAT },             // Position
577     { 1, 2, GL_FLOAT },             // TexCoord
578     { 2, 4, GL_FLOAT },             // Data
579     { 3, 4, GL_FLOAT }             // Vectors
580 };
581
582 static QSGGeometry::AttributeSet SimpleParticle_AttributeSet =
583 {
584     4, // Attribute Count
585     (2 + 2 + 4 + 4 ) * sizeof(float),
586     SimpleParticle_Attributes
587 };
588
589 static QSGGeometry::Attribute UltraParticle_Attributes[] = {
590     { 0, 2, GL_FLOAT },             // Position
591     { 1, 2, GL_FLOAT },             // TexCoord
592     { 2, 4, GL_FLOAT },             // Data
593     { 3, 4, GL_FLOAT },             // Vectors
594     { 4, 4, GL_UNSIGNED_BYTE },     // Colors
595     { 5, 4, GL_FLOAT },             // DeformationVectors
596     { 6, 3, GL_FLOAT },             // Rotation
597     { 7, 4, GL_FLOAT }              // Anim Data
598 };
599
600 static QSGGeometry::AttributeSet UltraParticle_AttributeSet =
601 {
602     8, // Attribute Count
603     (2 + 2 + 4 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar),
604     UltraParticle_Attributes
605 };
606
607 QSGGeometryNode* QSGImageParticle::buildSimpleParticleNode()
608 {
609     perfLevel = Simple;//TODO: Intermediate levels
610     QImage image = QImage(m_image_name.toLocalFile());
611     if (image.isNull()) {
612         printf("UltraParticle: loading image failed... '%s'\n", qPrintable(m_image_name.toLocalFile()));
613         return 0;
614     }
615     int vCount = m_count * 4;
616     int iCount = m_count * 6;
617     qDebug() << "Simple Case";
618
619     QSGGeometry *g = new QSGGeometry(SimpleParticle_AttributeSet, vCount, iCount);
620     g->setDrawingMode(GL_TRIANGLES);
621
622     SimpleVertices *vertices = (SimpleVertices *) g->vertexData();
623     for (int p=0; p<m_count; ++p)
624         memcpy(vertices++, m_defaultSimple, sizeof(SimpleVertices));
625
626     quint16 *indices = g->indexDataAsUShort();
627     for (int i=0; i<m_count; ++i) {
628         int o = i * 4;
629         indices[0] = o;
630         indices[1] = o + 1;
631         indices[2] = o + 2;
632         indices[3] = o + 1;
633         indices[4] = o + 3;
634         indices[5] = o + 2;
635         indices += 6;
636     }
637
638     if (m_material) {
639         delete m_material;
640         m_material = 0;
641     }
642
643     m_material = new SimpleMaterial();
644     m_material->texture = sceneGraphEngine()->createTextureFromImage(image);
645     m_material->texture->setFiltering(QSGTexture::Linear);
646     m_material->framecount = 1;
647     m_node = new QSGGeometryNode();
648     m_node->setGeometry(g);
649     m_node->setMaterial(m_material);
650
651     m_last_particle = 0;
652
653     return m_node;
654 }
655
656 QSGGeometryNode* QSGImageParticle::buildParticleNode()
657 {
658     if (m_count * 4 > 0xffff) {
659         printf("UltraParticle: Too many particles... \n");//####Why is this here?
660         return 0;
661     }
662
663     if(m_count <= 0) {
664         printf("UltraParticle: Too few particles... \n");
665         return 0;
666     }
667
668     m_resizePending = false;
669     if(!m_sprites.count() && !m_bloat
670             && m_colortable_name.isEmpty()
671             && m_sizetable_name.isEmpty()
672             && m_opacitytable_name.isEmpty()
673             && !m_autoRotation
674             && !m_rotation && !m_rotationVariation
675             && !m_rotationSpeed && !m_rotationSpeedVariation
676             && !m_alphaVariation && m_alpha == 1.0
677             && !m_redVariation && !m_blueVariation && !m_greenVariation
678             && !m_color.isValid()
679             )
680         return buildSimpleParticleNode();
681     perfLevel = Sprites;//TODO: intermediate levels
682     if(!m_color.isValid())//But we're in colored level (or higher)
683         m_color = QColor(Qt::white);
684     qDebug() << "Complex Case";
685
686     QImage image;
687     if(m_sprites.count()){
688         if (!m_spriteEngine) {
689             qWarning() << "UltraParticle: No sprite engine...";
690             return 0;
691         }
692         image = m_spriteEngine->assembledImage();
693         if(image.isNull())//Warning is printed in engine
694             return 0;
695     }else{
696         image = QImage(m_image_name.toLocalFile());
697         if (image.isNull()) {
698             printf("UltraParticle: loading image failed... '%s'\n", qPrintable(m_image_name.toLocalFile()));
699             return 0;
700         }
701     }
702
703     int vCount = m_count * 4;
704     int iCount = m_count * 6;
705
706     QSGGeometry *g = new QSGGeometry(UltraParticle_AttributeSet, vCount, iCount);
707     g->setDrawingMode(GL_TRIANGLES);
708
709     UltraVertex *vertices = (UltraVertex *) g->vertexData();
710     SimpleVertex *oldSimple = (SimpleVertex *) m_lastData;//TODO: Other levels
711     if(m_lastLevel == 1)
712         qDebug() << "Theta" << m_lastLevel << oldSimple[0].x << oldSimple[0].y << oldSimple[0].t;
713     for (int p=0; p<m_count; ++p) {
714         memcpy(vertices, m_defaultUltra, sizeof(UltraVertices));
715         if (m_lastLevel == 1 && m_lastCount > p) {//Transplant/IntermediateVertices?
716             for (int i=0; i<4; ++i) {
717                 vertices[i].x = oldSimple[i].x;
718                 vertices[i].y = oldSimple[i].y;
719                 vertices[i].t = oldSimple[i].t;
720                 vertices[i].lifeSpan = oldSimple[i].lifeSpan;
721                 vertices[i].size = oldSimple[i].size;
722                 vertices[i].endSize = oldSimple[i].endSize;
723                 vertices[i].sx = oldSimple[i].sx;
724                 vertices[i].sy = oldSimple[i].sy;
725                 vertices[i].ax = oldSimple[i].ax;
726                 vertices[i].ay = oldSimple[i].ay;
727             /*
728                 vertices[i].frameDuration = oldSimple[i].lifeSpan;
729                 vertices[i].frameCount = 1;
730                 vertices[i].animT = oldSimple[i].t;
731             */
732             }
733         }
734
735         vertices += 4;
736         oldSimple += 4;
737     }
738
739     quint16 *indices = g->indexDataAsUShort();//TODO: Speed gains by copying this over if count unchanged?
740     for (int i=0; i<m_count; ++i) {
741         int o = i * 4;
742         indices[0] = o;
743         indices[1] = o + 1;
744         indices[2] = o + 2;
745         indices[3] = o + 1;
746         indices[4] = o + 3;
747         indices[5] = o + 2;
748         indices += 6;
749     }
750
751     qFree(m_lastData);
752     if (m_material) {
753         delete m_material;
754         m_material = 0;
755     }
756
757     QImage colortable(m_colortable_name.toLocalFile());
758     QImage sizetable(m_sizetable_name.toLocalFile());
759     QImage opacitytable(m_opacitytable_name.toLocalFile());
760     m_material = new UltraMaterial();
761     if(colortable.isNull())
762         colortable = QImage(":defaultshaders/identitytable.png");
763     if(sizetable.isNull())
764         sizetable = QImage(":defaultshaders/identitytable.png");
765     if(opacitytable.isNull())
766         opacitytable = QImage(":defaultshaders/defaultFadeInOut.png");
767     Q_ASSERT(!colortable.isNull());
768     Q_ASSERT(!sizetable.isNull());
769     Q_ASSERT(!opacitytable.isNull());
770     m_material->colortable = sceneGraphEngine()->createTextureFromImage(colortable);
771     m_material->sizetable = sceneGraphEngine()->createTextureFromImage(sizetable);
772     m_material->opacitytable = sceneGraphEngine()->createTextureFromImage(opacitytable);
773
774     m_material->texture = sceneGraphEngine()->createTextureFromImage(image);
775     m_material->texture->setFiltering(QSGTexture::Linear);
776
777     m_material->framecount = 1;
778     if(m_spriteEngine){
779         m_material->framecount = m_spriteEngine->maxFrames();
780         m_spriteEngine->setCount(m_count);
781     }
782
783     m_node = new QSGGeometryNode();
784     m_node->setGeometry(g);
785     m_node->setMaterial(m_material);
786
787     m_last_particle = 0;
788
789     return m_node;
790 }
791
792 void QSGImageParticle::resize(int oldCount, int newCount)
793 {
794     //If perf level changes at the same time as a resize, we reset instead of doing pending resize
795     if(!m_node)
796         return;
797     switch(perfLevel){
798     default:
799     case Sprites:
800         if(m_spriteEngine)
801             reset();//TODO: Handle sprite resizeing (have to shuffle the engine too...)
802     case Coloured:
803     case Deformable:
804     case Tabled:
805         if(!m_resizePending){
806             m_resizePendingUltra.resize(oldCount);
807             UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData();
808             for(int i=0; i<oldCount; i++)
809                 m_resizePendingUltra[i] = &particles[i];
810         }
811         groupShuffle(m_resizePendingUltra, m_defaultUltra);
812         break;
813     case Simple:
814         if(!m_resizePending){
815             m_resizePendingSimple.resize(oldCount);
816             SimpleVertices *particles = (SimpleVertices *) m_node->geometry()->vertexData();
817             for(int i=0; i<oldCount; i++)
818                 m_resizePendingSimple[i] = &particles[i];
819         }
820         groupShuffle(m_resizePendingSimple, m_defaultSimple);
821         break;
822     }
823     m_resizePending = true;
824 }
825
826 void QSGImageParticle::performPendingResize()
827 {
828     m_resizePending = false;
829     if(!m_node)
830         return;
831     UltraVertices tmp1[m_count];//###More vast memcpys that will decrease performance
832     SimpleVertices tmp2[m_count];//###More vast memcpys that will decrease performance
833     switch(perfLevel){
834     case Sprites:
835     case Coloured:
836     case Deformable:
837     case Tabled:
838         Q_ASSERT(m_resizePendingUltra.size() == m_count);//XXX
839         for(int i=0; i<m_count; i++){
840             Q_ASSERT(m_resizePendingUltra[i]);
841             tmp1[i] = *m_resizePendingUltra[i];
842         }
843         m_node->setFlag(QSGNode::OwnsGeometry, false);
844         m_node->geometry()->allocate(m_count*4, m_count*6);
845         memcpy(m_node->geometry()->vertexData(), tmp1, sizeof(UltraVertices) * m_count);
846         m_node->setFlag(QSGNode::OwnsGeometry, true);
847         break;
848     case Simple:
849         Q_ASSERT(m_resizePendingSimple.size() == m_count);//XXX
850         for(int i=0; i<m_count; i++)
851                 tmp2[i] = *m_resizePendingSimple[i];
852         m_node->setFlag(QSGNode::OwnsGeometry, false);
853         m_node->geometry()->allocate(m_count*4, m_count*6);
854         memcpy(m_node->geometry()->vertexData(), tmp2, sizeof(SimpleVertices) * m_count);
855         m_node->setFlag(QSGNode::OwnsGeometry, true);
856         break;
857     }
858     quint16 *indices = m_node->geometry()->indexDataAsUShort();
859     for (int i=0; i<m_count; ++i) {
860         int o = i * 4;
861         indices[0] = o;
862         indices[1] = o + 1;
863         indices[2] = o + 2;
864         indices[3] = o + 1;
865         indices[4] = o + 3;
866         indices[5] = o + 2;
867         indices += 6;
868     }
869     m_node->setFlag(QSGNode::OwnsGeometry, true);
870 }
871
872 QSGNode *QSGImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
873 {
874     if(m_pleaseReset){
875         if(m_node){
876             if(perfLevel == 1){
877                 qDebug() << "Beta";
878                 m_lastCount = m_node->geometry()->vertexCount() / 4;
879                 m_lastData = qMalloc(m_lastCount*sizeof(SimpleVertices));
880                 memcpy(m_lastData, m_node->geometry()->vertexData(), m_lastCount * sizeof(SimpleVertices));//TODO: Multiple levels
881             }
882             m_lastLevel = perfLevel;
883             delete m_node;
884         }
885         if(m_material)
886             delete m_material;
887
888         m_node = 0;
889         m_material = 0;
890         m_pleaseReset = false;
891     }
892     if(m_resizePending)
893         performPendingResize();
894
895     if(m_system && m_system->isRunning())
896         prepareNextFrame();
897     if (m_node){
898         update();
899         m_node->markDirty(QSGNode::DirtyMaterial);
900     }
901
902     return m_node;
903 }
904
905 void QSGImageParticle::prepareNextFrame()
906 {
907     if (m_node == 0){    //TODO: Staggered loading (as emitted)
908         m_node = buildParticleNode();
909         if(m_node == 0)
910             return;
911         qDebug() << "Feature level: " << perfLevel;
912     }
913     qint64 timeStamp = m_system->systemSync(this);
914
915     qreal time = timeStamp / 1000.;
916     m_material->timestamp = time;
917
918     //Advance State
919     if(m_spriteEngine){//perfLevel == Sprites?
920         m_material->animcount = m_spriteEngine->spriteCount();
921         UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData();
922         m_spriteEngine->updateSprites(timeStamp);
923         for(int i=0; i<m_count; i++){
924             UltraVertices &p = particles[i];
925             int curIdx = m_spriteEngine->spriteState(i);
926             if(curIdx != p.v1.animIdx){
927                 p.v1.animIdx = p.v2.animIdx = p.v3.animIdx = p.v4.animIdx = curIdx;
928                 p.v1.animT = p.v2.animT = p.v3.animT = p.v4.animT = m_spriteEngine->spriteStart(i)/1000.0;
929                 p.v1.frameCount = p.v2.frameCount = p.v3.frameCount = p.v4.frameCount = m_spriteEngine->spriteFrames(i);
930                 p.v1.frameDuration = p.v2.frameDuration = p.v3.frameDuration = p.v4.frameDuration = m_spriteEngine->spriteDuration(i);
931             }
932         }
933     }else{
934         m_material->animcount = 1;
935     }
936 }
937
938 template <typename VT>
939 IntermediateVertices* transplant(IntermediateVertices* iv, VT &v)
940 {//Deliberate typemangling cast
941     iv->v1 = (UltraVertex*)&(v.v1);
942     iv->v2 = (UltraVertex*)&(v.v2);
943     iv->v3 = (UltraVertex*)&(v.v3);
944     iv->v4 = (UltraVertex*)&(v.v4);
945     return iv;
946 }
947
948 IntermediateVertices* QSGImageParticle::fetchIntermediateVertices(int pos)
949 {
950     //Note that this class ruins typesafety for you. Maybe even thread safety.
951     //TODO: Something better, possibly with templates or inheritance
952     static IntermediateVertices iv;
953     SimpleVertices *sv;
954     UltraVertices *uv;
955     switch(perfLevel){
956         case Simple:
957             sv = (SimpleVertices *) m_node->geometry()->vertexData();
958             return transplant(&iv, sv[pos]);
959         case Coloured:
960         case Deformable:
961         case Tabled:
962         case Sprites:
963         default:
964             uv = (UltraVertices *) m_node->geometry()->vertexData();
965             return transplant(&iv,uv[pos]);
966     }
967 }
968
969 void QSGImageParticle::reloadColor(const Color4ub &c, QSGParticleData* d)
970 {
971     UltraVertices *particles = (UltraVertices *) m_node->geometry()->vertexData();
972     int pos = particleTypeIndex(d);
973     UltraVertices &p = particles[pos];
974     p.v1.color = p.v2.color = p.v3.color = p.v4.color = c;
975 }
976
977 void QSGImageParticle::reload(QSGParticleData *d)
978 {
979     if (m_node == 0)
980         return;
981
982     int pos = particleTypeIndex(d);
983     IntermediateVertices* p = fetchIntermediateVertices(pos);
984
985     //Perhaps we could be more efficient?
986     vertexCopy(*p->v1, d->pv);
987     vertexCopy(*p->v2, d->pv);
988     vertexCopy(*p->v3, d->pv);
989     vertexCopy(*p->v4, d->pv);
990 }
991
992 void QSGImageParticle::load(QSGParticleData *d)
993 {
994     if (m_node == 0)
995         return;
996
997     int pos = particleTypeIndex(d);
998     IntermediateVertices* p = fetchIntermediateVertices(pos);//Remember this removes typesafety!
999     Color4ub color;
1000     qreal redVariation = m_color_variation + m_redVariation;
1001     qreal greenVariation = m_color_variation + m_greenVariation;
1002     qreal blueVariation = m_color_variation + m_blueVariation;
1003     switch(perfLevel){//Fall-through is intended on all of them
1004         case Sprites:
1005             // Initial Sprite State
1006             p->v1->animT = p->v2->animT = p->v3->animT = p->v4->animT = p->v1->t;
1007             p->v1->animIdx = p->v2->animIdx = p->v3->animIdx = p->v4->animIdx = 0;
1008             if(m_spriteEngine){
1009                 m_spriteEngine->startSprite(pos);
1010                 p->v1->frameCount = p->v2->frameCount = p->v3->frameCount = p->v4->frameCount = m_spriteEngine->spriteFrames(pos);
1011                 p->v1->frameDuration = p->v2->frameDuration = p->v3->frameDuration = p->v4->frameDuration = m_spriteEngine->spriteDuration(pos);
1012             }else{
1013                 p->v1->frameCount = p->v2->frameCount = p->v3->frameCount = p->v4->frameCount = 1;
1014                 p->v1->frameDuration = p->v2->frameDuration = p->v3->frameDuration = p->v4->frameDuration = 9999;
1015             }
1016         case Tabled:
1017         case Deformable:
1018             //Initial Rotation
1019             if(m_xVector){
1020                 const QPointF &ret = m_xVector->sample(QPointF(d->pv.x, d->pv.y));
1021                 p->v1->xx = p->v2->xx = p->v3->xx = p->v4->xx = ret.x();
1022                 p->v1->xy = p->v2->xy = p->v3->xy = p->v4->xy = ret.y();
1023             }
1024             if(m_yVector){
1025                 const QPointF &ret = m_yVector->sample(QPointF(d->pv.x, d->pv.y));
1026                 p->v1->yx = p->v2->yx = p->v3->yx = p->v4->yx = ret.x();
1027                 p->v1->yy = p->v2->yy = p->v3->yy = p->v4->yy = ret.y();
1028             }
1029             p->v1->rotation = p->v2->rotation = p->v3->rotation = p->v4->rotation =
1030                     (m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV;
1031             p->v1->rotationSpeed = p->v2->rotationSpeed = p->v3->rotationSpeed = p->v4->rotationSpeed =
1032                     (m_rotationSpeed + (m_rotationSpeedVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationSpeedVariation) ) * CONV;
1033             p->v1->autoRotate = p->v2->autoRotate = p->v3->autoRotate = p->v4->autoRotate = m_autoRotation?1.0:0.0;
1034         case Coloured:
1035             //Color initialization
1036             // Particle color
1037             color.r = m_color.red() * (1 - redVariation) + rand() % 256 * redVariation;
1038             color.g = m_color.green() * (1 - greenVariation) + rand() % 256 * greenVariation;
1039             color.b = m_color.blue() * (1 - blueVariation) + rand() % 256 * blueVariation;
1040             color.a = m_alpha * m_color.alpha() * (1 - m_alphaVariation) + rand() % 256 * m_alphaVariation;
1041             p->v1->color = p->v2->color = p->v3->color = p->v4->color = color;
1042         default:
1043             break;
1044     }
1045
1046     vertexCopy(*p->v1, d->pv);
1047     vertexCopy(*p->v2, d->pv);
1048     vertexCopy(*p->v3, d->pv);
1049     vertexCopy(*p->v4, d->pv);
1050 }
1051
1052 /*
1053 void QSGImageParticle::verticesUpgrade(void *prev, void *next)
1054 {
1055     PerformanceLevel copyLevel = qMin(perfLevel, m_lastLevel);
1056     switch(perfLevel){//Intentional fall-through
1057         case Sprites:
1058             if(copyLevel >= Sprites)
1059         case Tabled:
1060         case Deformable:
1061         case Coloured:
1062     }
1063
1064 }
1065 */
1066
1067
1068 QT_END_NAMESPACE