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