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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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