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