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 alpha channel is used as a 1D lookup table to scale the particle's size over
548 its lifetime.
549
550 The image is read from left to right: the left edge corresponds to the start of the
551 particle's life, and the right edge to its end. Only the alpha channel is used. A fully
552 opaque pixel leaves the size unchanged, and a fully transparent pixel scales the particle
553 down to nothing. The image is scaled to 64 samples wide and one pixel high, so a narrow
554 image with a horizontal alpha gradient is sufficient.
555
556 The value from the table multiplies the size that the particle has at that point in its
557 life, as determined by the \l Emitter's \l {Emitter::}{size} and \l {Emitter::}{endSize}.
558
559 \sa opacityTable, colorTable, entryEffect
560*/
561/*!
562 \qmlproperty url QtQuick.Particles::ImageParticle::opacityTable
563
564 An image whose alpha channel is used as a 1D lookup table to scale the particle's opacity
565 over its lifetime.
566
567 The image is read from left to right: the left edge corresponds to the start of the
568 particle's life, and the right edge to its end. Only the alpha channel is used. A fully
569 opaque pixel leaves the opacity unchanged, and a fully transparent pixel makes the particle
570 invisible. The image is scaled to 64 samples wide and one pixel high, so a narrow image
571 with a horizontal alpha gradient is sufficient.
572
573 The value from the table is combined with the particle's \l alpha and with any fading
574 applied by \l entryEffect.
575
576 \sa sizeTable, colorTable, entryEffect
577*/
578/*!
579 \qmlproperty color QtQuick.Particles::ImageParticle::color
580
581 If a color is specified, the provided image will be colorized with it.
582
583 Default is white (no change).
584*/
585/*!
586 \qmlproperty real QtQuick.Particles::ImageParticle::colorVariation
587
588 This number represents the color variation applied to individual particles.
589 Setting colorVariation is the same as setting redVariation, greenVariation,
590 and blueVariation to the same number.
591
592 Each channel can vary between particle by up to colorVariation from its usual color.
593
594 Color is measured, per channel, from 0.0 to 1.0.
595
596 Default is 0.0
597*/
598/*!
599 \qmlproperty real QtQuick.Particles::ImageParticle::redVariation
600 The variation in the red color channel between particles.
601
602 Color is measured, per channel, from 0.0 to 1.0.
603
604 Default is 0.0
605*/
606/*!
607 \qmlproperty real QtQuick.Particles::ImageParticle::greenVariation
608 The variation in the green color channel between particles.
609
610 Color is measured, per channel, from 0.0 to 1.0.
611
612 Default is 0.0
613*/
614/*!
615 \qmlproperty real QtQuick.Particles::ImageParticle::blueVariation
616 The variation in the blue color channel between particles.
617
618 Color is measured, per channel, from 0.0 to 1.0.
619
620 Default is 0.0
621*/
622/*!
623 \qmlproperty real QtQuick.Particles::ImageParticle::alpha
624 An alpha to be applied to the image. This value is multiplied by the value in
625 the image, and the value in the color property.
626
627 Particles have additive blending, so lower alpha on single particles leads
628 to stronger effects when multiple particles overlap.
629
630 Alpha is measured from 0.0 to 1.0.
631
632 Default is 1.0
633*/
634/*!
635 \qmlproperty real QtQuick.Particles::ImageParticle::alphaVariation
636 The variation in the alpha channel between particles.
637
638 Alpha is measured from 0.0 to 1.0.
639
640 Default is 0.0
641*/
642/*!
643 \qmlproperty real QtQuick.Particles::ImageParticle::rotation
644
645 If set the image will be rotated by this many degrees before it is drawn.
646
647 The particle coordinates are not transformed.
648*/
649/*!
650 \qmlproperty real QtQuick.Particles::ImageParticle::rotationVariation
651
652 If set the rotation of individual particles will vary by up to this much
653 between particles.
654
655*/
656/*!
657 \qmlproperty real QtQuick.Particles::ImageParticle::rotationVelocity
658
659 If set particles will rotate at this velocity in degrees/second.
660*/
661/*!
662 \qmlproperty real QtQuick.Particles::ImageParticle::rotationVelocityVariation
663
664 If set the rotationVelocity of individual particles will vary by up to this much
665 between particles.
666
667*/
668/*!
669 \qmlproperty bool QtQuick.Particles::ImageParticle::autoRotation
670
671 If set to true then a rotation will be applied on top of the particles rotation, so
672 that it faces the direction of travel. So to face away from the direction of travel,
673 set autoRotation to true and rotation to 180.
674
675 Default is false
676*/
677/*!
678 \qmlproperty StochasticDirection QtQuick.Particles::ImageParticle::xVector
679
680 Allows you to deform the particle image when drawn. The rectangular image will
681 be deformed so that the horizontal sides are in the shape of this vector instead
682 of (1,0).
683*/
684/*!
685 \qmlproperty StochasticDirection QtQuick.Particles::ImageParticle::yVector
686
687 Allows you to deform the particle image when drawn. The rectangular image will
688 be deformed so that the vertical sides are in the shape of this vector instead
689 of (0,1).
690*/
691/*!
692 \qmlproperty EntryEffect QtQuick.Particles::ImageParticle::entryEffect
693
694 This property provides basic and cheap entrance and exit effects for the particles.
695 For fine-grained control, see sizeTable and opacityTable.
696
697 Acceptable values are
698
699 \value ImageParticle.None Particles just appear and disappear.
700 \value ImageParticle.Fade Particles fade in from 0 opacity at the start of their life, and fade out to 0 at the end.
701 \value ImageParticle.Scale Particles scale in from 0 size at the start of their life, and scale back to 0 at the end.
702
703 The default value is \c ImageParticle.Fade.
704*/
705/*!
706 \qmlproperty bool QtQuick.Particles::ImageParticle::spritesInterpolate
707
708 If set to true, sprite particles will interpolate between sprite frames each rendered frame, making
709 the sprites look smoother.
710
711 Default is true.
712*/
713
714/*!
715 \qmlproperty Status QtQuick.Particles::ImageParticle::status
716
717 The status of loading the image.
718*/
719
720
721QQuickImageParticle::QQuickImageParticle(QQuickItem* parent)
722 : QQuickParticlePainter(parent)
723 , m_color_variation(0.0)
724 , m_outgoingNode(nullptr)
725 , m_material(nullptr)
726 , m_alphaVariation(0.0)
727 , m_alpha(1.0)
728 , m_redVariation(0.0)
729 , m_greenVariation(0.0)
730 , m_blueVariation(0.0)
731 , m_rotation(0)
732 , m_rotationVariation(0)
733 , m_rotationVelocity(0)
734 , m_rotationVelocityVariation(0)
735 , m_autoRotation(false)
736 , m_xVector(nullptr)
737 , m_yVector(nullptr)
738 , m_spriteEngine(nullptr)
739 , m_spritesInterpolate(true)
740 , m_explicitColor(false)
741 , m_explicitRotation(false)
742 , m_explicitDeformation(false)
743 , m_explicitAnimation(false)
744 , m_bypassOptimizations(false)
745 , perfLevel(Unknown)
746 , m_targetPerfLevel(Unknown)
747 , m_debugMode(false)
748 , m_entryEffect(Fade)
749 , m_startedImageLoading(0)
750 , m_rhi(nullptr)
751 , m_apiChecked(false)
752 , m_dpr(1.0)
753 , m_previousActive(false)
754{
755 setFlag(ItemHasContents);
756}
757
758QQuickImageParticle::~QQuickImageParticle()
759{
760 clearShadows();
761}
762
763QQmlListProperty<QQuickSprite> QQuickImageParticle::sprites()
764{
765 return QQmlListProperty<QQuickSprite>(this, &m_sprites,
766 spriteAppend, spriteCount, spriteAt,
767 spriteClear, spriteReplace, spriteRemoveLast);
768}
769
770void QQuickImageParticle::sceneGraphInvalidated()
771{
772 m_nodes.clear();
773 m_material = nullptr;
774 delete m_outgoingNode;
775 m_outgoingNode = nullptr;
776 m_apiChecked = false;
777}
778
779void QQuickImageParticle::setImage(const QUrl &image)
780{
781 if (image.isEmpty()){
782 if (m_image) {
783 m_image.reset();
784 emit imageChanged();
785 }
786 return;
787 }
788
789 if (!m_image)
790 m_image.reset(new ImageData);
791 if (image == m_image->source)
792 return;
793 m_image->source = image;
794 emit imageChanged();
795 reset();
796}
797
798
799void QQuickImageParticle::setColortable(const QUrl &table)
800{
801 if (table.isEmpty()){
802 if (m_colorTable) {
803 m_colorTable.reset();
804 emit colortableChanged();
805 }
806 return;
807 }
808
809 if (!m_colorTable)
810 m_colorTable.reset(new ImageData);
811 if (table == m_colorTable->source)
812 return;
813 m_colorTable->source = table;
814 emit colortableChanged();
815 reset();
816}
817
818void QQuickImageParticle::setSizetable(const QUrl &table)
819{
820 if (table.isEmpty()){
821 if (m_sizeTable) {
822 m_sizeTable.reset();
823 emit sizetableChanged();
824 }
825 return;
826 }
827
828 if (!m_sizeTable)
829 m_sizeTable.reset(new ImageData);
830 if (table == m_sizeTable->source)
831 return;
832 m_sizeTable->source = table;
833 emit sizetableChanged();
834 reset();
835}
836
837void QQuickImageParticle::setOpacitytable(const QUrl &table)
838{
839 if (table.isEmpty()){
840 if (m_opacityTable) {
841 m_opacityTable.reset();
842 emit opacitytableChanged();
843 }
844 return;
845 }
846
847 if (!m_opacityTable)
848 m_opacityTable.reset(new ImageData);
849 if (table == m_opacityTable->source)
850 return;
851 m_opacityTable->source = table;
852 emit opacitytableChanged();
853 reset();
854}
855
856void QQuickImageParticle::setColor(const QColor &color)
857{
858 if (color == m_color)
859 return;
860 m_color = color;
861 emit colorChanged();
862 m_explicitColor = true;
863 checkPerfLevel(ColoredPoint);
864}
865
866void QQuickImageParticle::setColorVariation(qreal var)
867{
868 if (var == m_color_variation)
869 return;
870 m_color_variation = var;
871 emit colorVariationChanged();
872 m_explicitColor = true;
873 checkPerfLevel(ColoredPoint);
874}
875
876void QQuickImageParticle::setAlphaVariation(qreal arg)
877{
878 if (m_alphaVariation != arg) {
879 m_alphaVariation = arg;
880 emit alphaVariationChanged(arg);
881 }
882 m_explicitColor = true;
883 checkPerfLevel(ColoredPoint);
884}
885
886void QQuickImageParticle::setAlpha(qreal arg)
887{
888 if (m_alpha != arg) {
889 m_alpha = arg;
890 emit alphaChanged(arg);
891 }
892 m_explicitColor = true;
893 checkPerfLevel(ColoredPoint);
894}
895
896void QQuickImageParticle::setRedVariation(qreal arg)
897{
898 if (m_redVariation != arg) {
899 m_redVariation = arg;
900 emit redVariationChanged(arg);
901 }
902 m_explicitColor = true;
903 checkPerfLevel(ColoredPoint);
904}
905
906void QQuickImageParticle::setGreenVariation(qreal arg)
907{
908 if (m_greenVariation != arg) {
909 m_greenVariation = arg;
910 emit greenVariationChanged(arg);
911 }
912 m_explicitColor = true;
913 checkPerfLevel(ColoredPoint);
914}
915
916void QQuickImageParticle::setBlueVariation(qreal arg)
917{
918 if (m_blueVariation != arg) {
919 m_blueVariation = arg;
920 emit blueVariationChanged(arg);
921 }
922 m_explicitColor = true;
923 checkPerfLevel(ColoredPoint);
924}
925
926void QQuickImageParticle::setRotation(qreal arg)
927{
928 if (m_rotation != arg) {
929 m_rotation = arg;
930 emit rotationChanged(arg);
931 }
932 m_explicitRotation = true;
933 checkPerfLevel(Deformable);
934}
935
936void QQuickImageParticle::setRotationVariation(qreal arg)
937{
938 if (m_rotationVariation != arg) {
939 m_rotationVariation = arg;
940 emit rotationVariationChanged(arg);
941 }
942 m_explicitRotation = true;
943 checkPerfLevel(Deformable);
944}
945
946void QQuickImageParticle::setRotationVelocity(qreal arg)
947{
948 if (m_rotationVelocity != arg) {
949 m_rotationVelocity = arg;
950 emit rotationVelocityChanged(arg);
951 }
952 m_explicitRotation = true;
953 checkPerfLevel(Deformable);
954}
955
956void QQuickImageParticle::setRotationVelocityVariation(qreal arg)
957{
958 if (m_rotationVelocityVariation != arg) {
959 m_rotationVelocityVariation = arg;
960 emit rotationVelocityVariationChanged(arg);
961 }
962 m_explicitRotation = true;
963 checkPerfLevel(Deformable);
964}
965
966void QQuickImageParticle::setAutoRotation(bool arg)
967{
968 if (m_autoRotation != arg) {
969 m_autoRotation = arg;
970 emit autoRotationChanged(arg);
971 }
972 m_explicitRotation = true;
973 checkPerfLevel(Deformable);
974}
975
976void QQuickImageParticle::setXVector(QQuickDirection* arg)
977{
978 if (m_xVector != arg) {
979 m_xVector = arg;
980 emit xVectorChanged(arg);
981 }
982 m_explicitDeformation = true;
983 checkPerfLevel(Deformable);
984}
985
986void QQuickImageParticle::setYVector(QQuickDirection* arg)
987{
988 if (m_yVector != arg) {
989 m_yVector = arg;
990 emit yVectorChanged(arg);
991 }
992 m_explicitDeformation = true;
993 checkPerfLevel(Deformable);
994}
995
996void QQuickImageParticle::setSpritesInterpolate(bool arg)
997{
998 if (m_spritesInterpolate != arg) {
999 m_spritesInterpolate = arg;
1000 emit spritesInterpolateChanged(arg);
1001 }
1002}
1003
1004void QQuickImageParticle::setBypassOptimizations(bool arg)
1005{
1006 if (m_bypassOptimizations != arg) {
1007 m_bypassOptimizations = arg;
1008 emit bypassOptimizationsChanged(arg);
1009 }
1010 // Applies regardless of perfLevel
1011 reset();
1012}
1013
1014void QQuickImageParticle::setEntryEffect(EntryEffect arg)
1015{
1016 if (m_entryEffect != arg) {
1017 m_entryEffect = arg;
1018 if (m_material)
1019 getState(m_material)->entry = (qreal) m_entryEffect;
1020 emit entryEffectChanged(arg);
1021 }
1022}
1023
1024void QQuickImageParticle::resetColor()
1025{
1026 m_explicitColor = false;
1027 for (auto groupId : groupIds()) {
1028 for (QQuickParticleData* d : std::as_const(m_system->groupData[groupId]->data)) {
1029 if (d->colorOwner == this) {
1030 d->colorOwner = nullptr;
1031 }
1032 }
1033 }
1034 m_color = QColor();
1035 m_color_variation = 0.0f;
1036 m_redVariation = 0.0f;
1037 m_blueVariation = 0.0f;
1038 m_greenVariation = 0.0f;
1039 m_alpha = 1.0f;
1040 m_alphaVariation = 0.0f;
1041}
1042
1043void QQuickImageParticle::resetRotation()
1044{
1045 m_explicitRotation = false;
1046 for (auto groupId : groupIds()) {
1047 for (QQuickParticleData* d : std::as_const(m_system->groupData[groupId]->data)) {
1048 if (d->rotationOwner == this) {
1049 d->rotationOwner = nullptr;
1050 }
1051 }
1052 }
1053 m_rotation = 0;
1054 m_rotationVariation = 0;
1055 m_rotationVelocity = 0;
1056 m_rotationVelocityVariation = 0;
1057 m_autoRotation = false;
1058}
1059
1060void QQuickImageParticle::resetDeformation()
1061{
1062 m_explicitDeformation = false;
1063 for (auto groupId : groupIds()) {
1064 for (QQuickParticleData* d : std::as_const(m_system->groupData[groupId]->data)) {
1065 if (d->deformationOwner == this) {
1066 d->deformationOwner = nullptr;
1067 }
1068 }
1069 }
1070 if (m_xVector)
1071 delete m_xVector;
1072 if (m_yVector)
1073 delete m_yVector;
1074 m_xVector = nullptr;
1075 m_yVector = nullptr;
1076}
1077
1078void QQuickImageParticle::reset()
1079{
1080 QQuickParticlePainter::reset();
1081 m_pleaseReset = true;
1082 update();
1083}
1084
1085
1086void QQuickImageParticle::invalidateSceneGraph()
1087{
1088 reset();
1089}
1090
1091void QQuickImageParticle::createEngine()
1092{
1093 if (m_spriteEngine)
1094 delete m_spriteEngine;
1095 if (m_sprites.size()) {
1096 m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
1097 connect(m_spriteEngine, &QQuickStochasticEngine::stateChanged,
1098 this, &QQuickImageParticle::spriteAdvance, Qt::DirectConnection);
1099 m_explicitAnimation = true;
1100 } else {
1101 m_spriteEngine = nullptr;
1102 m_explicitAnimation = false;
1103 }
1104 reset();
1105}
1106
1108 QSGGeometry::Attribute::create(0, 2, QSGGeometry::FloatType, true), // Position
1109 QSGGeometry::Attribute::create(1, 4, QSGGeometry::FloatType), // Data
1110 QSGGeometry::Attribute::create(2, 4, QSGGeometry::FloatType) // Vectors
1111};
1112
1114{
1115 3, // Attribute Count
1116 ( 2 + 4 + 4 ) * sizeof(float),
1117 SimplePointParticle_Attributes
1118};
1119
1121 QSGGeometry::Attribute::create(0, 2, QSGGeometry::FloatType, true), // Position
1122 QSGGeometry::Attribute::create(1, 4, QSGGeometry::FloatType), // Data
1123 QSGGeometry::Attribute::create(2, 4, QSGGeometry::FloatType), // Vectors
1124 QSGGeometry::Attribute::create(3, 4, QSGGeometry::UnsignedByteType), // Colors
1125};
1126
1128{
1129 4, // Attribute Count
1130 ( 2 + 4 + 4 ) * sizeof(float) + 4 * sizeof(uchar),
1131 ColoredPointParticle_Attributes
1132};
1133
1135 QSGGeometry::Attribute::create(0, 2, QSGGeometry::FloatType, true), // Position
1136 QSGGeometry::Attribute::create(1, 4, QSGGeometry::FloatType), // Data
1137 QSGGeometry::Attribute::create(2, 4, QSGGeometry::FloatType), // Vectors
1138 QSGGeometry::Attribute::create(3, 4, QSGGeometry::UnsignedByteType), // Colors
1139 QSGGeometry::Attribute::create(4, 4, QSGGeometry::UnsignedByteType), // TexCoord
1140};
1141
1143{
1144 5, // Attribute Count
1145 ( 2 + 4 + 4 ) * sizeof(float) + (4 + 4) * sizeof(uchar),
1146 ColoredParticle_Attributes
1147};
1148
1150 QSGGeometry::Attribute::create(0, 4, QSGGeometry::FloatType), // Position & Rotation
1151 QSGGeometry::Attribute::create(1, 4, QSGGeometry::FloatType), // Data
1152 QSGGeometry::Attribute::create(2, 4, QSGGeometry::FloatType), // Vectors
1153 QSGGeometry::Attribute::create(3, 4, QSGGeometry::UnsignedByteType), // Colors
1154 QSGGeometry::Attribute::create(4, 4, QSGGeometry::FloatType), // DeformationVectors
1155 QSGGeometry::Attribute::create(5, 4, QSGGeometry::UnsignedByteType), // TexCoord & autoRotate
1156};
1157
1159{
1160 6, // Attribute Count
1161 (4 + 4 + 4 + 4) * sizeof(float) + (4 + 4) * sizeof(uchar),
1162 DeformableParticle_Attributes
1163};
1164
1166 QSGGeometry::Attribute::create(0, 4, QSGGeometry::FloatType), // Position & Rotation
1167 QSGGeometry::Attribute::create(1, 4, QSGGeometry::FloatType), // Data
1168 QSGGeometry::Attribute::create(2, 4, QSGGeometry::FloatType), // Vectors
1169 QSGGeometry::Attribute::create(3, 4, QSGGeometry::UnsignedByteType), // Colors
1170 QSGGeometry::Attribute::create(4, 4, QSGGeometry::FloatType), // DeformationVectors
1171 QSGGeometry::Attribute::create(5, 4, QSGGeometry::UnsignedByteType), // TexCoord & autoRotate
1172 QSGGeometry::Attribute::create(6, 3, QSGGeometry::FloatType), // Anim Data
1173 QSGGeometry::Attribute::create(7, 3, QSGGeometry::FloatType) // Anim Pos
1174};
1175
1177{
1178 8, // Attribute Count
1179 (4 + 4 + 4 + 4 + 3 + 3) * sizeof(float) + (4 + 4) * sizeof(uchar),
1180 SpriteParticle_Attributes
1181};
1182
1183void QQuickImageParticle::clearShadows()
1184{
1185 foreach (const QList<QQuickParticleData*> data, m_shadowData)
1186 qDeleteAll(data);
1187 m_shadowData.clear();
1188}
1189
1190//Only call if you need to, may initialize the whole array first time
1191QQuickParticleData* QQuickImageParticle::getShadowDatum(QQuickParticleData* datum)
1192{
1193 //Will return datum if the datum is a sentinel or uninitialized, to centralize that one check
1194 if (datum->systemIndex == -1)
1195 return datum;
1196 if (!m_shadowData.contains(datum->groupId)) {
1197 QQuickParticleGroupData* gd = m_system->groupData[datum->groupId];
1198 QList<QQuickParticleData*> data;
1199 const int gdSize = gd->size();
1200 data.reserve(gdSize);
1201 for (int i = 0; i < gdSize; i++) {
1202 QQuickParticleData* datum = new QQuickParticleData;
1203 *datum = *(gd->data[i]);
1204 data << datum;
1205 }
1206 m_shadowData.insert(datum->groupId, data);
1207 }
1208 //### If dynamic resize is added, remember to potentially resize the shadow data on out-of-bounds access request
1209
1210 return m_shadowData[datum->groupId][datum->index];
1211}
1212
1213void QQuickImageParticle::checkPerfLevel(PerformanceLevel level)
1214{
1215 if (m_targetPerfLevel < level) {
1216 m_targetPerfLevel = level;
1217 reset();
1218 }
1219}
1220
1221bool QQuickImageParticle::loadingSomething()
1222{
1223 return (m_image && m_image->pix.isLoading())
1224 || (m_colorTable && m_colorTable->pix.isLoading())
1225 || (m_sizeTable && m_sizeTable->pix.isLoading())
1226 || (m_opacityTable && m_opacityTable->pix.isLoading())
1227 || (m_spriteEngine && m_spriteEngine->isLoading());
1228}
1229
1230void QQuickImageParticle::mainThreadFetchImageData()
1231{
1232 const QQmlContext *context = nullptr;
1233 QQmlEngine *engine = nullptr;
1234 const auto loadPix = [&](ImageData *image) {
1235 if (!engine) {
1236 context = qmlContext(this);
1237 engine = context->engine();
1238 }
1239 image->pix.load(engine, context->resolvedUrl(image->source));
1240 };
1241
1242
1243 if (m_image) {//ImageData created on setSource
1244 m_image->pix.clear(this);
1245 loadPix(m_image.get());
1246 }
1247
1248 if (m_spriteEngine)
1249 m_spriteEngine->startAssemblingImage();
1250
1251 if (m_colorTable)
1252 loadPix(m_colorTable.get());
1253
1254 if (m_sizeTable)
1255 loadPix(m_sizeTable.get());
1256
1257 if (m_opacityTable)
1258 loadPix(m_opacityTable.get());
1259
1260 m_startedImageLoading = 2;
1261}
1262
1263void QQuickImageParticle::buildParticleNodes(QSGNode** passThrough)
1264{
1265 // Starts async parts, like loading images, on gui thread
1266 // Not on individual properties, because we delay until system is running
1267 if (*passThrough || loadingSomething())
1268 return;
1269
1270 if (m_startedImageLoading == 0) {
1271 m_startedImageLoading = 1;
1272 //stage 1 is in gui thread
1273 QQuickImageParticle::staticMetaObject.invokeMethod(this, "mainThreadFetchImageData", Qt::QueuedConnection);
1274 } else if (m_startedImageLoading == 2) {
1275 finishBuildParticleNodes(passThrough); //rest happens in render thread
1276 }
1277
1278 //No mutex, because it's slow and a compare that fails due to a race condition means just a dropped frame
1279}
1280
1281void QQuickImageParticle::finishBuildParticleNodes(QSGNode** node)
1282{
1283 if (!m_rhi)
1284 return;
1285
1286 if (m_count * 4 > 0xffff) {
1287 // Index data is ushort.
1288 qmlInfo(this) << "ImageParticle: Too many particles - maximum 16383 per ImageParticle";
1289 return;
1290 }
1291
1292 if (m_count <= 0)
1293 return;
1294
1295 m_debugMode = m_system->m_debugMode;
1296
1297 if (m_sprites.size() || m_bypassOptimizations) {
1298 perfLevel = Sprites;
1299 } else if (m_colorTable || m_sizeTable || m_opacityTable) {
1300 perfLevel = Tabled;
1301 } else if (m_autoRotation || m_rotation || m_rotationVariation
1302 || m_rotationVelocity || m_rotationVelocityVariation
1303 || m_xVector || m_yVector) {
1304 perfLevel = Deformable;
1305 } else if (m_alphaVariation || m_alpha != 1.0 || m_color.isValid() || m_color_variation
1306 || m_redVariation || m_blueVariation || m_greenVariation) {
1307 perfLevel = ColoredPoint;
1308 } else {
1309 perfLevel = SimplePoint;
1310 }
1311
1312 for (auto groupId : groupIds()) {
1313 //For sharing higher levels, need to have highest used so it renders
1314 for (QQuickParticlePainter* p : std::as_const(m_system->groupData[groupId]->painters)) {
1315 QQuickImageParticle* other = qobject_cast<QQuickImageParticle*>(p);
1316 if (other){
1317 if (other->perfLevel > perfLevel) {
1318 if (other->perfLevel >= Tabled){//Deformable is the highest level needed for this, anything higher isn't shared (or requires your own sprite)
1319 if (perfLevel < Deformable)
1320 perfLevel = Deformable;
1321 } else {
1322 perfLevel = other->perfLevel;
1323 }
1324 } else if (other->perfLevel < perfLevel) {
1325 other->reset();
1326 }
1327 }
1328 }
1329 }
1330
1331 // Points with a size other than 1 are an optional feature with QRhi
1332 // because some of the underlying APIs have no support for this.
1333 // Therefore, avoid the point sprite path with APIs like Direct3D.
1334 if (perfLevel < Colored && !m_rhi->isFeatureSupported(QRhi::VertexShaderPointSize))
1335 perfLevel = Colored;
1336
1337 if (perfLevel >= ColoredPoint && !m_color.isValid())
1338 m_color = QColor(Qt::white);//Hidden default, but different from unset
1339
1340 m_targetPerfLevel = perfLevel;
1341
1342 clearShadows();
1343 if (m_material)
1344 m_material = nullptr;
1345
1346 //Setup material
1347 QImage colortable;
1348 QImage sizetable;
1349 QImage opacitytable;
1350 QImage image;
1351 bool imageLoaded = false;
1352 switch (perfLevel) {//Fallthrough intended
1353 case Sprites:
1354 {
1355 if (!m_spriteEngine) {
1356 qWarning() << "ImageParticle: No sprite engine...";
1357 //Sprite performance mode with static image is supported, but not advised
1358 //Note that in this case it always uses shadow data
1359 } else {
1360 image = m_spriteEngine->assembledImage();
1361 if (image.isNull())//Warning is printed in engine
1362 return;
1363 imageLoaded = true;
1364 }
1365 m_material = new SpriteMaterial;
1366 ImageMaterialData *state = getState(m_material);
1367 if (imageLoaded)
1368 state->texture = QSGPlainTexture::fromImage(image);
1369 state->animSheetSize = QSizeF(image.size() / image.devicePixelRatio());
1370 if (m_spriteEngine)
1371 m_spriteEngine->setCount(m_count);
1372 }
1373 Q_FALLTHROUGH();
1374 case Tabled:
1375 {
1376 if (!m_material)
1377 m_material = new TabledMaterial;
1378
1379 if (m_colorTable) {
1380 if (m_colorTable->pix.isReady())
1381 colortable = m_colorTable->pix.image();
1382 else
1383 qmlWarning(this) << "Error loading color table: " << m_colorTable->pix.error();
1384 }
1385
1386 if (m_sizeTable) {
1387 if (m_sizeTable->pix.isReady())
1388 sizetable = m_sizeTable->pix.image();
1389 else
1390 qmlWarning(this) << "Error loading size table: " << m_sizeTable->pix.error();
1391 }
1392
1393 if (m_opacityTable) {
1394 if (m_opacityTable->pix.isReady())
1395 opacitytable = m_opacityTable->pix.image();
1396 else
1397 qmlWarning(this) << "Error loading opacity table: " << m_opacityTable->pix.error();
1398 }
1399
1400 if (colortable.isNull()){//###Goes through image just for this
1401 colortable = QImage(1,1,QImage::Format_ARGB32_Premultiplied);
1402 colortable.fill(Qt::white);
1403 }
1404 ImageMaterialData *state = getState(m_material);
1405 state->colorTable = QSGPlainTexture::fromImage(colortable);
1406 fillUniformArrayFromImage(state->sizeTable, sizetable, UNIFORM_ARRAY_SIZE);
1407 fillUniformArrayFromImage(state->opacityTable, opacitytable, UNIFORM_ARRAY_SIZE);
1408 }
1409 Q_FALLTHROUGH();
1410 case Deformable:
1411 {
1412 if (!m_material)
1413 m_material = new DeformableMaterial;
1414 }
1415 Q_FALLTHROUGH();
1416 case Colored:
1417 {
1418 if (!m_material)
1419 m_material = new ColoredMaterial;
1420 }
1421 Q_FALLTHROUGH();
1422 case ColoredPoint:
1423 {
1424 if (!m_material)
1425 m_material = new ColoredPointMaterial;
1426 }
1427 Q_FALLTHROUGH();
1428 default://Also Simple
1429 {
1430 if (!m_material)
1431 m_material = new SimplePointMaterial;
1432 ImageMaterialData *state = getState(m_material);
1433 if (!imageLoaded) {
1434 if (!m_image || !m_image->pix.isReady()) {
1435 if (m_image)
1436 qmlWarning(this) << m_image->pix.error();
1437 delete m_material;
1438 return;
1439 }
1440 //state->texture //TODO: Shouldn't this be better? But not crash?
1441 // = QQuickItemPrivate::get(this)->sceneGraphContext()->textureForFactory(m_imagePix.textureFactory());
1442 state->texture = QSGPlainTexture::fromImage(m_image->pix.image());
1443 }
1444 state->texture->setFiltering(QSGTexture::Linear);
1445 state->entry = (qreal) m_entryEffect;
1446 state->dpr = m_dpr;
1447
1448 m_material->setFlag(QSGMaterial::Blending | QSGMaterial::RequiresFullMatrix);
1449 }
1450 }
1451
1452 m_nodes.clear();
1453 for (auto groupId : groupIds()) {
1454 int count = m_system->groupData[groupId]->size();
1455 QSGGeometryNode* node = new QSGGeometryNode();
1456 node->setMaterial(m_material);
1457 node->markDirty(QSGNode::DirtyMaterial);
1458
1459 m_nodes.insert(groupId, node);
1460 m_idxStarts.insert(groupId, m_lastIdxStart);
1461 m_startsIdx.append(std::make_pair(m_lastIdxStart, groupId));
1462 m_lastIdxStart += count;
1463
1464 //Create Particle Geometry
1465 int vCount = count * 4;
1466 int iCount = count * 6;
1467
1468 QSGGeometry *g;
1469 if (perfLevel == Sprites)
1470 g = new QSGGeometry(SpriteParticle_AttributeSet, vCount, iCount);
1471 else if (perfLevel == Tabled)
1472 g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
1473 else if (perfLevel == Deformable)
1474 g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
1475 else if (perfLevel == Colored)
1476 g = new QSGGeometry(ColoredParticle_AttributeSet, vCount, iCount);
1477 else if (perfLevel == ColoredPoint)
1478 g = new QSGGeometry(ColoredPointParticle_AttributeSet, count, 0);
1479 else //Simple
1480 g = new QSGGeometry(SimplePointParticle_AttributeSet, count, 0);
1481
1482 node->setFlag(QSGNode::OwnsGeometry);
1483 node->setGeometry(g);
1484 if (perfLevel <= ColoredPoint){
1485 g->setDrawingMode(QSGGeometry::DrawPoints);
1486 if (m_debugMode)
1487 qDebug("Using point sprites");
1488 } else {
1489 g->setDrawingMode(QSGGeometry::DrawTriangles);
1490 }
1491
1492 for (int p=0; p < count; ++p)
1493 commit(groupId, p);//commit sets geometry for the node, has its own perfLevel switch
1494
1495 if (perfLevel == Sprites)
1496 initTexCoords<SpriteVertex>((SpriteVertex*)g->vertexData(), vCount);
1497 else if (perfLevel == Tabled)
1498 initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
1499 else if (perfLevel == Deformable)
1500 initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
1501 else if (perfLevel == Colored)
1502 initTexCoords<ColoredVertex>((ColoredVertex*)g->vertexData(), vCount);
1503
1504 if (perfLevel > ColoredPoint){
1505 quint16 *indices = g->indexDataAsUShort();
1506 for (int i=0; i < count; ++i) {
1507 int o = i * 4;
1508 indices[0] = o;
1509 indices[1] = o + 1;
1510 indices[2] = o + 2;
1511 indices[3] = o + 1;
1512 indices[4] = o + 3;
1513 indices[5] = o + 2;
1514 indices += 6;
1515 }
1516 }
1517 }
1518
1519 if (perfLevel == Sprites)
1520 spritesUpdate();//Gives all vertexes the initial sprite data, then maintained per frame
1521
1522 foreach (QSGGeometryNode* node, m_nodes){
1523 if (node == *(m_nodes.begin()))
1524 node->setFlag(QSGGeometryNode::OwnsMaterial);//Root node owns the material for memory management purposes
1525 else
1526 (*(m_nodes.begin()))->appendChildNode(node);
1527 }
1528
1529 *node = *(m_nodes.begin());
1530 update();
1531}
1532
1533QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
1534{
1535 if (!m_apiChecked || m_windowChanged) {
1536 m_apiChecked = true;
1537 m_windowChanged = false;
1538
1539 QSGRenderContext *rc = QQuickItemPrivate::get(this)->sceneGraphRenderContext();
1540 QSGRendererInterface *rif = rc->sceneGraphContext()->rendererInterface(rc);
1541 if (!rif)
1542 return nullptr;
1543
1544 QSGRendererInterface::GraphicsApi api = rif->graphicsApi();
1545 const bool isRhi = QSGRendererInterface::isApiRhiBased(api);
1546
1547 if (!node && !isRhi)
1548 return nullptr;
1549
1550 if (isRhi)
1551 m_rhi = static_cast<QRhi *>(rif->getResource(m_window, QSGRendererInterface::RhiResource));
1552 else
1553 m_rhi = nullptr;
1554
1555 if (isRhi && !m_rhi) {
1556 qWarning("Failed to query QRhi, particles disabled");
1557 return nullptr;
1558 }
1559 // Get the pixel ratio of the window, used for pointsize scaling
1560 m_dpr = m_window ? m_window->devicePixelRatio() : 1.0;
1561 }
1562
1563 if (m_pleaseReset){
1564 // Cannot just destroy the node and then return null (in case image
1565 // loading is still in progress). Rather, keep track of the old node
1566 // until we have a new one.
1567 delete m_outgoingNode;
1568 m_outgoingNode = node;
1569 node = nullptr;
1570
1571 m_nodes.clear();
1572
1573 m_idxStarts.clear();
1574 m_startsIdx.clear();
1575 m_lastIdxStart = 0;
1576
1577 m_material = nullptr;
1578
1579 m_pleaseReset = false;
1580 m_startedImageLoading = 0;//Cancel a part-way build (may still have a pending load)
1581 } else if (!m_material) {
1582 delete node;
1583 node = nullptr;
1584 }
1585
1586 if (m_system && m_system->isRunning() && !m_system->isPaused()){
1587 bool dirty = prepareNextFrame(&node);
1588 if (node) {
1589 update();
1590 if (dirty) {
1591 foreach (QSGGeometryNode* n, m_nodes)
1592 n->markDirty(QSGNode::DirtyGeometry);
1593 }
1594 } else if (m_startedImageLoading < 2) {
1595 update();//To call prepareNextFrame() again from the renderThread
1596 }
1597 }
1598
1599 if (!node) {
1600 node = m_outgoingNode;
1601 m_outgoingNode = nullptr;
1602 }
1603
1604 return node;
1605}
1606
1607bool QQuickImageParticle::prepareNextFrame(QSGNode **node)
1608{
1609 if (*node == nullptr){//TODO: Staggered loading (as emitted)
1610 buildParticleNodes(node);
1611 if (m_debugMode) {
1612 qDebug() << "QQuickImageParticle Feature level: " << perfLevel;
1613 qDebug() << "QQuickImageParticle Nodes: ";
1614 int count = 0;
1615 for (auto it = m_nodes.keyBegin(), end = m_nodes.keyEnd(); it != end; ++it) {
1616 qDebug() << "Group " << *it << " (" << m_system->groupData[*it]->size()
1617 << " particles)";
1618 count += m_system->groupData[*it]->size();
1619 }
1620 qDebug() << "Total count: " << count;
1621 }
1622 if (*node == nullptr)
1623 return false;
1624 }
1625 qint64 timeStamp = m_system->systemSync(this);
1626
1627 qreal time = timeStamp / 1000.;
1628
1629 switch (perfLevel){//Fall-through intended
1630 case Sprites:
1631 //Advance State
1632 if (m_spriteEngine)
1633 m_spriteEngine->updateSprites(timeStamp);//fires signals if anim changed
1634 spritesUpdate(time);
1635 Q_FALLTHROUGH();
1636 case Tabled:
1637 case Deformable:
1638 case Colored:
1639 case ColoredPoint:
1640 case SimplePoint:
1641 default: //Also Simple
1642 getState(m_material)->timestamp = time;
1643 break;
1644 }
1645
1646 bool active = false;
1647 for (auto groupId : groupIds()) {
1648 if (m_system->groupData[groupId]->isActive()) {
1649 active = true;
1650 break;
1651 }
1652 }
1653
1654 const bool dirty = active || m_previousActive;
1655 if (dirty) {
1656 foreach (QSGGeometryNode* node, m_nodes)
1657 node->markDirty(QSGNode::DirtyMaterial);
1658 }
1659
1660 m_previousActive = active;
1661 return dirty;
1662}
1663
1664void QQuickImageParticle::spritesUpdate(qreal time)
1665{
1666 ImageMaterialData *state = getState(m_material);
1667 // Sprite progression handled CPU side, so as to have per-frame control.
1668 for (auto groupId : groupIds()) {
1669 for (QQuickParticleData* mainDatum : std::as_const(m_system->groupData[groupId]->data)) {
1670 QSGGeometryNode *node = m_nodes[groupId];
1671 if (!node)
1672 continue;
1673 //TODO: Interpolate between two different animations if it's going to transition next frame
1674 // This is particularly important for cut-up sprites.
1675 QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum));
1676 int spriteIdx = 0;
1677 for (int i = 0; i<m_startsIdx.size(); i++) {
1678 if (m_startsIdx[i].second == groupId){
1679 spriteIdx = m_startsIdx[i].first + datum->index;
1680 break;
1681 }
1682 }
1683
1684 double frameAt;
1685 qreal progress = 0;
1686
1687 if (datum->frameDuration > 0) {
1688 qreal frame = (time - datum->animT)/(datum->frameDuration / 1000.0);
1689 frame = qBound((qreal)0.0, frame, (qreal)((qreal)datum->frameCount - 1.0));//Stop at count-1 frames until we have between anim interpolation
1690 if (m_spritesInterpolate)
1691 progress = std::modf(frame,&frameAt);
1692 else
1693 std::modf(frame,&frameAt);
1694 } else {
1695 datum->frameAt++;
1696 if (datum->frameAt >= datum->frameCount){
1697 datum->frameAt = 0;
1698 m_spriteEngine->advance(spriteIdx);
1699 }
1700 frameAt = datum->frameAt;
1701 }
1702 if (m_spriteEngine->sprite(spriteIdx)->reverse())//### Store this in datum too?
1703 frameAt = (datum->frameCount - 1) - frameAt;
1704 QSizeF sheetSize = state->animSheetSize;
1705 qreal y = datum->animY / sheetSize.height();
1706 qreal w = datum->animWidth / sheetSize.width();
1707 qreal h = datum->animHeight / sheetSize.height();
1708 qreal x1 = datum->animX / sheetSize.width();
1709 x1 += frameAt * w;
1710 qreal x2 = x1;
1711 if (frameAt < (datum->frameCount-1))
1712 x2 += w;
1713
1714 SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
1715 spriteVertices += datum->index*4;
1716 for (int i=0; i<4; i++) {
1717 spriteVertices[i].animX1 = x1;
1718 spriteVertices[i].animY1 = y;
1719 spriteVertices[i].animX2 = x2;
1720 spriteVertices[i].animW = w;
1721 spriteVertices[i].animH = h;
1722 spriteVertices[i].animProgress = progress;
1723 }
1724 }
1725 }
1726}
1727
1728void QQuickImageParticle::spriteAdvance(int spriteIdx)
1729{
1730 if (!m_startsIdx.size())//Probably overly defensive
1731 return;
1732
1733 int gIdx = -1;
1734 int i;
1735 for (i = 0; i<m_startsIdx.size(); i++) {
1736 if (spriteIdx < m_startsIdx[i].first) {
1737 gIdx = m_startsIdx[i-1].second;
1738 break;
1739 }
1740 }
1741 if (gIdx == -1)
1742 gIdx = m_startsIdx[i-1].second;
1743 int pIdx = spriteIdx - m_startsIdx[i-1].first;
1744
1745 QQuickParticleData* mainDatum = m_system->groupData[gIdx]->data[pIdx];
1746 QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum));
1747
1748 datum->animIdx = m_spriteEngine->spriteState(spriteIdx);
1749 datum->animT = m_spriteEngine->spriteStart(spriteIdx)/1000.0;
1750 datum->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
1751 datum->frameDuration = m_spriteEngine->spriteDuration(spriteIdx) / datum->frameCount;
1752 datum->animX = m_spriteEngine->spriteX(spriteIdx);
1753 datum->animY = m_spriteEngine->spriteY(spriteIdx);
1754 datum->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
1755 datum->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
1756}
1757
1758void QQuickImageParticle::initialize(int gIdx, int pIdx)
1759{
1760 Color4ub color;
1761 QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
1762 qreal redVariation = m_color_variation + m_redVariation;
1763 qreal greenVariation = m_color_variation + m_greenVariation;
1764 qreal blueVariation = m_color_variation + m_blueVariation;
1765 int spriteIdx = 0;
1766 if (m_spriteEngine) {
1767 spriteIdx = m_idxStarts[gIdx] + datum->index;
1768 if (spriteIdx >= m_spriteEngine->count())
1769 m_spriteEngine->setCount(spriteIdx+1);
1770 }
1771
1772 float rotation;
1773 float rotationVelocity;
1774 uchar autoRotate;
1775 switch (perfLevel){//Fall-through is intended on all of them
1776 case Sprites:
1777 // Initial Sprite State
1778 if (m_explicitAnimation && m_spriteEngine){
1779 if (!datum->animationOwner)
1780 datum->animationOwner = this;
1781 QQuickParticleData* writeTo = (datum->animationOwner == this ? datum : getShadowDatum(datum));
1782 writeTo->animT = writeTo->t;
1783 //writeTo->animInterpolate = m_spritesInterpolate;
1784 if (m_spriteEngine){
1785 m_spriteEngine->start(spriteIdx);
1786 writeTo->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
1787 writeTo->frameDuration = m_spriteEngine->spriteDuration(spriteIdx) / writeTo->frameCount;
1788 writeTo->animIdx = 0;//Always starts at 0
1789 writeTo->frameAt = -1;
1790 writeTo->animX = m_spriteEngine->spriteX(spriteIdx);
1791 writeTo->animY = m_spriteEngine->spriteY(spriteIdx);
1792 writeTo->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
1793 writeTo->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
1794 }
1795 } else {
1796 ImageMaterialData *state = getState(m_material);
1797 QQuickParticleData* writeTo = getShadowDatum(datum);
1798 writeTo->animT = datum->t;
1799 writeTo->frameCount = 1;
1800 writeTo->frameDuration = 60000000.0;
1801 writeTo->frameAt = -1;
1802 writeTo->animIdx = 0;
1803 writeTo->animT = 0;
1804 writeTo->animX = writeTo->animY = 0;
1805 writeTo->animWidth = state->animSheetSize.width();
1806 writeTo->animHeight = state->animSheetSize.height();
1807 }
1808 Q_FALLTHROUGH();
1809 case Tabled:
1810 case Deformable:
1811 //Initial Rotation
1812 if (m_explicitDeformation){
1813 if (!datum->deformationOwner)
1814 datum->deformationOwner = this;
1815 if (m_xVector){
1816 const QPointF &ret = m_xVector->sample(QPointF(datum->x, datum->y));
1817 if (datum->deformationOwner == this) {
1818 datum->xx = ret.x();
1819 datum->xy = ret.y();
1820 } else {
1821 QQuickParticleData* shadow = getShadowDatum(datum);
1822 shadow->xx = ret.x();
1823 shadow->xy = ret.y();
1824 }
1825 }
1826 if (m_yVector){
1827 const QPointF &ret = m_yVector->sample(QPointF(datum->x, datum->y));
1828 if (datum->deformationOwner == this) {
1829 datum->yx = ret.x();
1830 datum->yy = ret.y();
1831 } else {
1832 QQuickParticleData* shadow = getShadowDatum(datum);
1833 shadow->yx = ret.x();
1834 shadow->yy = ret.y();
1835 }
1836 }
1837 }
1838
1839 if (m_explicitRotation){
1840 if (!datum->rotationOwner)
1841 datum->rotationOwner = this;
1842 rotation = qDegreesToRadians(
1843 m_rotation + (m_rotationVariation
1844 - 2 * QRandomGenerator::global()->bounded(m_rotationVariation)));
1845 rotationVelocity = qDegreesToRadians(
1846 m_rotationVelocity
1847 + (m_rotationVelocityVariation
1848 - 2 * QRandomGenerator::global()->bounded(m_rotationVelocityVariation)));
1849 autoRotate = m_autoRotation ? 1 : 0;
1850 if (datum->rotationOwner == this) {
1851 datum->rotation = rotation;
1852 datum->rotationVelocity = rotationVelocity;
1853 datum->autoRotate = autoRotate;
1854 } else {
1855 QQuickParticleData* shadow = getShadowDatum(datum);
1856 shadow->rotation = rotation;
1857 shadow->rotationVelocity = rotationVelocity;
1858 shadow->autoRotate = autoRotate;
1859 }
1860 }
1861 Q_FALLTHROUGH();
1862 case Colored:
1863 Q_FALLTHROUGH();
1864 case ColoredPoint:
1865 //Color initialization
1866 // Particle color
1867 if (m_explicitColor) {
1868 if (!datum->colorOwner)
1869 datum->colorOwner = this;
1870 const auto rgbColor = m_color.toRgb();
1871 color.r = rgbColor.red() * (1 - redVariation) + QRandomGenerator::global()->bounded(256) * redVariation;
1872 color.g = rgbColor.green() * (1 - greenVariation) + QRandomGenerator::global()->bounded(256) * greenVariation;
1873 color.b = rgbColor.blue() * (1 - blueVariation) + QRandomGenerator::global()->bounded(256) * blueVariation;
1874 color.a = m_alpha * rgbColor.alpha() * (1 - m_alphaVariation) + QRandomGenerator::global()->bounded(256) * m_alphaVariation;
1875 if (datum->colorOwner == this)
1876 datum->color = color;
1877 else
1878 getShadowDatum(datum)->color = color;
1879 }
1880 break;
1881 default:
1882 break;
1883 }
1884}
1885
1886void QQuickImageParticle::commit(int gIdx, int pIdx)
1887{
1888 if (m_pleaseReset)
1889 return;
1890 QSGGeometryNode *node = m_nodes[gIdx];
1891 if (!node)
1892 return;
1893 QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
1894 SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
1895 DeformableVertex *deformableVertices = (DeformableVertex *) node->geometry()->vertexData();
1896 ColoredVertex *coloredVertices = (ColoredVertex *) node->geometry()->vertexData();
1897 ColoredPointVertex *coloredPointVertices = (ColoredPointVertex *) node->geometry()->vertexData();
1898 SimplePointVertex *simplePointVertices = (SimplePointVertex *) node->geometry()->vertexData();
1899 switch (perfLevel){//No automatic fall through intended on this one
1900 case Sprites:
1901 spriteVertices += pIdx*4;
1902 for (int i=0; i<4; i++){
1903 spriteVertices[i].x = datum->x - m_systemOffset.x();
1904 spriteVertices[i].y = datum->y - m_systemOffset.y();
1905 spriteVertices[i].t = datum->t;
1906 spriteVertices[i].lifeSpan = datum->lifeSpan;
1907 spriteVertices[i].size = datum->size;
1908 spriteVertices[i].endSize = datum->endSize;
1909 spriteVertices[i].vx = datum->vx;
1910 spriteVertices[i].vy = datum->vy;
1911 spriteVertices[i].ax = datum->ax;
1912 spriteVertices[i].ay = datum->ay;
1913 if (m_explicitDeformation && datum->deformationOwner != this) {
1914 QQuickParticleData* shadow = getShadowDatum(datum);
1915 spriteVertices[i].xx = shadow->xx;
1916 spriteVertices[i].xy = shadow->xy;
1917 spriteVertices[i].yx = shadow->yx;
1918 spriteVertices[i].yy = shadow->yy;
1919 } else {
1920 spriteVertices[i].xx = datum->xx;
1921 spriteVertices[i].xy = datum->xy;
1922 spriteVertices[i].yx = datum->yx;
1923 spriteVertices[i].yy = datum->yy;
1924 }
1925 if (m_explicitRotation && datum->rotationOwner != this) {
1926 QQuickParticleData* shadow = getShadowDatum(datum);
1927 spriteVertices[i].rotation = shadow->rotation;
1928 spriteVertices[i].rotationVelocity = shadow->rotationVelocity;
1929 spriteVertices[i].autoRotate = shadow->autoRotate;
1930 } else {
1931 spriteVertices[i].rotation = datum->rotation;
1932 spriteVertices[i].rotationVelocity = datum->rotationVelocity;
1933 spriteVertices[i].autoRotate = datum->autoRotate;
1934 }
1935 //Sprite-related vertices updated per-frame in spritesUpdate(), not on demand
1936 if (m_explicitColor && datum->colorOwner != this) {
1937 QQuickParticleData* shadow = getShadowDatum(datum);
1938 spriteVertices[i].color = shadow->color;
1939 } else {
1940 spriteVertices[i].color = datum->color;
1941 }
1942 }
1943 break;
1944 case Tabled: //Fall through until it has its own vertex class
1945 case Deformable:
1946 deformableVertices += pIdx*4;
1947 for (int i=0; i<4; i++){
1948 deformableVertices[i].x = datum->x - m_systemOffset.x();
1949 deformableVertices[i].y = datum->y - m_systemOffset.y();
1950 deformableVertices[i].t = datum->t;
1951 deformableVertices[i].lifeSpan = datum->lifeSpan;
1952 deformableVertices[i].size = datum->size;
1953 deformableVertices[i].endSize = datum->endSize;
1954 deformableVertices[i].vx = datum->vx;
1955 deformableVertices[i].vy = datum->vy;
1956 deformableVertices[i].ax = datum->ax;
1957 deformableVertices[i].ay = datum->ay;
1958 if (m_explicitDeformation && datum->deformationOwner != this) {
1959 QQuickParticleData* shadow = getShadowDatum(datum);
1960 deformableVertices[i].xx = shadow->xx;
1961 deformableVertices[i].xy = shadow->xy;
1962 deformableVertices[i].yx = shadow->yx;
1963 deformableVertices[i].yy = shadow->yy;
1964 } else {
1965 deformableVertices[i].xx = datum->xx;
1966 deformableVertices[i].xy = datum->xy;
1967 deformableVertices[i].yx = datum->yx;
1968 deformableVertices[i].yy = datum->yy;
1969 }
1970 if (m_explicitRotation && datum->rotationOwner != this) {
1971 QQuickParticleData* shadow = getShadowDatum(datum);
1972 deformableVertices[i].rotation = shadow->rotation;
1973 deformableVertices[i].rotationVelocity = shadow->rotationVelocity;
1974 deformableVertices[i].autoRotate = shadow->autoRotate;
1975 } else {
1976 deformableVertices[i].rotation = datum->rotation;
1977 deformableVertices[i].rotationVelocity = datum->rotationVelocity;
1978 deformableVertices[i].autoRotate = datum->autoRotate;
1979 }
1980 if (m_explicitColor && datum->colorOwner != this) {
1981 QQuickParticleData* shadow = getShadowDatum(datum);
1982 deformableVertices[i].color = shadow->color;
1983 } else {
1984 deformableVertices[i].color = datum->color;
1985 }
1986 }
1987 break;
1988 case Colored:
1989 coloredVertices += pIdx*4;
1990 for (int i=0; i<4; i++){
1991 coloredVertices[i].x = datum->x - m_systemOffset.x();
1992 coloredVertices[i].y = datum->y - m_systemOffset.y();
1993 coloredVertices[i].t = datum->t;
1994 coloredVertices[i].lifeSpan = datum->lifeSpan;
1995 coloredVertices[i].size = datum->size;
1996 coloredVertices[i].endSize = datum->endSize;
1997 coloredVertices[i].vx = datum->vx;
1998 coloredVertices[i].vy = datum->vy;
1999 coloredVertices[i].ax = datum->ax;
2000 coloredVertices[i].ay = datum->ay;
2001 if (m_explicitColor && datum->colorOwner != this) {
2002 QQuickParticleData* shadow = getShadowDatum(datum);
2003 coloredVertices[i].color = shadow->color;
2004 } else {
2005 coloredVertices[i].color = datum->color;
2006 }
2007 }
2008 break;
2009 case ColoredPoint:
2010 coloredPointVertices += pIdx*1;
2011 for (int i=0; i<1; i++){
2012 coloredPointVertices[i].x = datum->x - m_systemOffset.x();
2013 coloredPointVertices[i].y = datum->y - m_systemOffset.y();
2014 coloredPointVertices[i].t = datum->t;
2015 coloredPointVertices[i].lifeSpan = datum->lifeSpan;
2016 coloredPointVertices[i].size = datum->size;
2017 coloredPointVertices[i].endSize = datum->endSize;
2018 coloredPointVertices[i].vx = datum->vx;
2019 coloredPointVertices[i].vy = datum->vy;
2020 coloredPointVertices[i].ax = datum->ax;
2021 coloredPointVertices[i].ay = datum->ay;
2022 if (m_explicitColor && datum->colorOwner != this) {
2023 QQuickParticleData* shadow = getShadowDatum(datum);
2024 coloredPointVertices[i].color = shadow->color;
2025 } else {
2026 coloredPointVertices[i].color = datum->color;
2027 }
2028 }
2029 break;
2030 case SimplePoint:
2031 simplePointVertices += pIdx*1;
2032 for (int i=0; i<1; i++){
2033 simplePointVertices[i].x = datum->x - m_systemOffset.x();
2034 simplePointVertices[i].y = datum->y - m_systemOffset.y();
2035 simplePointVertices[i].t = datum->t;
2036 simplePointVertices[i].lifeSpan = datum->lifeSpan;
2037 simplePointVertices[i].size = datum->size;
2038 simplePointVertices[i].endSize = datum->endSize;
2039 simplePointVertices[i].vx = datum->vx;
2040 simplePointVertices[i].vy = datum->vy;
2041 simplePointVertices[i].ax = datum->ax;
2042 simplePointVertices[i].ay = datum->ay;
2043 }
2044 break;
2045 default:
2046 break;
2047 }
2048}
2049
2050
2051
2052QT_END_NAMESPACE
2053
2054#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