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

source code of qtdeclarative/src/particles/qquickimageparticle.cpp