1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQuick module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <QtQuick/private/qsgcontext_p.h>
43 #include <private/qsgadaptationlayer_p.h>
44 #include <private/qquickitem_p.h>
45 #include <QtQuick/qsgnode.h>
46 #include <QtQuick/qsgtexturematerial.h>
47 #include <QtQuick/qsgtexture.h>
49 #include "qquickimageparticle_p.h"
50 #include "qquickparticleemitter_p.h"
51 #include <private/qquicksprite_p.h>
52 #include <private/qquickspriteengine_p.h>
53 #include <QOpenGLFunctions>
54 #include <QtQuick/qsgengine.h>
55 #include <QtQuick/private/qsgtexture_p.h>
56 #include <private/qqmlglobal_p.h>
57 #include <QtQml/qqmlinfo.h>
62 #ifndef QT_OPENGL_ES_2
63 #define SHADER_DEFINES "#version 120\n"
65 #define SHADER_DEFINES ""
68 //TODO: Make it larger on desktop? Requires fixing up shader code with the same define
69 #define UNIFORM_ARRAY_SIZE 64
71 static const char vertexShaderCode[] =
72 "#if defined(DEFORM)\n"
73 "attribute highp vec4 vPosTex;\n"
75 "attribute highp vec2 vPos;\n"
77 "attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize\n"
78 "attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration\n"
79 "uniform highp float entry;\n"
80 "#if defined(COLOR)\n"
81 "attribute highp vec4 vColor;\n"
83 "#if defined(DEFORM)\n"
84 "attribute highp vec4 vDeformVec; //x,y x unit vector; z,w = y unit vector\n"
85 "attribute highp vec3 vRotation; //x = radians of rotation, y=rotation speed, z= bool autoRotate\n"
87 "#if defined(SPRITE)\n"
88 "attribute highp vec3 vAnimData;// w,h(premultiplied of anim), interpolation progress\n"
89 "attribute highp vec4 vAnimPos;//x,y, x,y (two frames for interpolation)\n"
92 "uniform highp mat4 qt_Matrix;\n"
93 "uniform highp float timestamp;\n"
94 "#if defined(TABLE)\n"
95 "varying lowp vec2 tt;//y is progress if Sprite mode\n"
96 "uniform highp float sizetable[64];\n"
97 "uniform highp float opacitytable[64];\n"
99 "#if defined(SPRITE)\n"
100 "varying highp vec4 fTexS;\n"
101 "#elif defined(DEFORM)\n"
102 "varying highp vec2 fTex;\n"
104 "#if defined(COLOR)\n"
105 "varying lowp vec4 fColor;\n"
107 "varying lowp float fFade;\n"
113 " highp float t = (timestamp - vData.x) / vData.y;\n"
114 " if (t < 0. || t > 1.) {\n"
115 "#if defined(DEFORM)\n"
116 " gl_Position = qt_Matrix * vec4(vPosTex.x, vPosTex.y, 0., 1.);\n"
118 " gl_PointSize = 0.;\n"
121 "#if defined(SPRITE)\n"
122 " tt.y = vAnimData.z;\n"
123 " //Calculate frame location in texture\n"
124 " fTexS.xy = vAnimPos.xy + vPosTex.zw * vAnimData.xy;\n"
125 " //Next frame is also passed, for interpolation\n"
126 " fTexS.zw = vAnimPos.zw + vPosTex.zw * vAnimData.xy;\n"
128 "#elif defined(DEFORM)\n"
129 " fTex = vPosTex.zw;\n"
131 " highp float currentSize = mix(vData.z, vData.w, t * t);\n"
132 " lowp float fade = 1.;\n"
133 " highp float fadeIn = min(t * 10., 1.);\n"
134 " highp float fadeOut = 1. - clamp((t - 0.75) * 4.,0., 1.);\n"
136 "#if defined(TABLE)\n"
137 " currentSize = currentSize * sizetable[int(floor(t*64.))];\n"
138 " fade = fade * opacitytable[int(floor(t*64.))];\n"
141 " if (entry == 1.)\n"
142 " fade = fade * fadeIn * fadeOut;\n"
143 " else if (entry == 2.)\n"
144 " currentSize = currentSize * fadeIn * fadeOut;\n"
146 " if (currentSize <= 0.) {\n"
147 "#if defined(DEFORM)\n"
148 " gl_Position = qt_Matrix * vec4(vPosTex.x, vPosTex.y, 0., 1.);\n"
150 " gl_PointSize = 0.;\n"
153 " if (currentSize < 3.)//Sizes too small look jittery as they move\n"
154 " currentSize = 3.;\n"
157 "#if defined(DEFORM)\n"
158 " highp float rotation = vRotation.x + vRotation.y * t * vData.y;\n"
159 " if (vRotation.z == 1.0){\n"
160 " highp vec2 curVel = vVec.zw * t * vData.y + vVec.xy;\n"
161 " rotation += atan(curVel.y, curVel.x);\n"
163 " highp vec2 trigCalcs = vec2(cos(rotation), sin(rotation));\n"
164 " highp vec4 deform = vDeformVec * currentSize * (vPosTex.zzww - 0.5);\n"
165 " highp vec4 rotatedDeform = deform.xxzz * trigCalcs.xyxy;\n"
166 " rotatedDeform = rotatedDeform + (deform.yyww * trigCalcs.yxyx * vec4(-1.,1.,-1.,1.));\n"
167 " /* The readable version:\n"
168 " highp vec2 xDeform = vDeformVec.xy * currentSize * (vTex.x-0.5);\n"
169 " highp vec2 yDeform = vDeformVec.zw * currentSize * (vTex.y-0.5);\n"
170 " highp vec2 xRotatedDeform;\n"
171 " xRotatedDeform.x = trigCalcs.x*xDeform.x - trigCalcs.y*xDeform.y;\n"
172 " xRotatedDeform.y = trigCalcs.y*xDeform.x + trigCalcs.x*xDeform.y;\n"
173 " highp vec2 yRotatedDeform;\n"
174 " yRotatedDeform.x = trigCalcs.x*yDeform.x - trigCalcs.y*yDeform.y;\n"
175 " yRotatedDeform.y = trigCalcs.y*yDeform.x + trigCalcs.x*yDeform.y;\n"
177 " pos = vPosTex.xy\n"
178 " + rotatedDeform.xy\n"
179 " + rotatedDeform.zw\n"
180 " + vVec.xy * t * vData.y // apply speed\n"
181 " + 0.5 * vVec.zw * pow(t * vData.y, 2.); // apply acceleration\n"
184 " + vVec.xy * t * vData.y // apply speed vector..\n"
185 " + 0.5 * vVec.zw * pow(t * vData.y, 2.);\n"
186 " gl_PointSize = currentSize;\n"
188 " gl_Position = qt_Matrix * vec4(pos.x, pos.y, 0, 1);\n"
190 "#if defined(COLOR)\n"
191 " fColor = vColor * fade;\n"
195 "#if defined(TABLE)\n"
202 static const char fragmentShaderCode[] =
203 "uniform sampler2D texture;\n"
204 "uniform lowp float qt_Opacity;\n"
206 "#if defined(SPRITE)\n"
207 "varying highp vec4 fTexS;\n"
208 "#elif defined(DEFORM)\n"
209 "varying highp vec2 fTex;\n"
211 "#if defined(COLOR)\n"
212 "varying lowp vec4 fColor;\n"
214 "varying lowp float fFade;\n"
216 "#if defined(TABLE)\n"
217 "varying lowp vec2 tt;\n"
218 "uniform sampler2D colortable;\n"
222 "#if defined(SPRITE)\n"
223 " gl_FragColor = mix(texture2D(texture, fTexS.xy), texture2D(texture, fTexS.zw), tt.y)\n"
225 " * texture2D(colortable, tt)\n"
227 "#elif defined(TABLE)\n"
228 " gl_FragColor = texture2D(texture, fTex)\n"
230 " * texture2D(colortable, tt)\n"
232 "#elif defined(DEFORM)\n"
233 " gl_FragColor = (texture2D(texture, fTex)) * fColor * qt_Opacity;\n"
234 "#elif defined(COLOR)\n"
235 " gl_FragColor = (texture2D(texture, gl_PointCoord)) * fColor * qt_Opacity;\n"
237 " gl_FragColor = texture2D(texture, gl_PointCoord) * (fFade * qt_Opacity);\n"
241 const qreal CONV = 0.017453292519943295;
242 class ImageMaterialData
246 : texture(0), colorTable(0)
249 ~ImageMaterialData(){
255 QSGTexture *colorTable;
256 float sizeTable[UNIFORM_ARRAY_SIZE];
257 float opacityTable[UNIFORM_ARRAY_SIZE];
261 QSizeF animSheetSize;
264 class TabledMaterialData : public ImageMaterialData {};
265 class TabledMaterial : public QSGSimpleMaterialShader<TabledMaterialData>
267 QSG_DECLARE_SIMPLE_SHADER(TabledMaterial, TabledMaterialData)
272 m_vertex_code = QByteArray(SHADER_DEFINES)
273 + QByteArray("#define TABLE\n#define DEFORM\n#define COLOR\n")
276 m_fragment_code = QByteArray(SHADER_DEFINES)
277 + QByteArray("#define TABLE\n#define DEFORM\n#define COLOR\n")
278 + fragmentShaderCode;
280 Q_ASSERT(!m_vertex_code.isNull());
281 Q_ASSERT(!m_fragment_code.isNull());
284 const char *vertexShader() const { return m_vertex_code.constData(); }
285 const char *fragmentShader() const { return m_fragment_code.constData(); }
287 QList<QByteArray> attributes() const {
288 return QList<QByteArray>() << "vPosTex" << "vData" << "vVec"
289 << "vColor" << "vDeformVec" << "vRotation";
293 QSGSimpleMaterialShader<TabledMaterialData>::initialize();
295 program()->setUniformValue("texture", 0);
296 program()->setUniformValue("colortable", 1);
297 glFuncs = QOpenGLContext::currentContext()->functions();
298 m_timestamp_id = program()->uniformLocation("timestamp");
299 m_entry_id = program()->uniformLocation("entry");
300 m_sizetable_id = program()->uniformLocation("sizetable");
301 m_opacitytable_id = program()->uniformLocation("opacitytable");
304 void updateState(const TabledMaterialData* d, const TabledMaterialData*) {
305 glFuncs->glActiveTexture(GL_TEXTURE1);
306 d->colorTable->bind();
308 glFuncs->glActiveTexture(GL_TEXTURE0);
311 program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
312 program()->setUniformValue(m_entry_id, (float) d->entry);
313 program()->setUniformValueArray(m_sizetable_id, (float*) d->sizeTable, UNIFORM_ARRAY_SIZE, 1);
314 program()->setUniformValueArray(m_opacitytable_id, (float*) d->opacityTable, UNIFORM_ARRAY_SIZE, 1);
320 int m_opacitytable_id;
321 QByteArray m_vertex_code;
322 QByteArray m_fragment_code;
323 QOpenGLFunctions* glFuncs;
326 class DeformableMaterialData : public ImageMaterialData {};
327 class DeformableMaterial : public QSGSimpleMaterialShader<DeformableMaterialData>
329 QSG_DECLARE_SIMPLE_SHADER(DeformableMaterial, DeformableMaterialData)
334 m_vertex_code = QByteArray(SHADER_DEFINES)
335 + QByteArray("#define DEFORM\n#define COLOR\n")
338 m_fragment_code = QByteArray(SHADER_DEFINES)
339 + QByteArray("#define DEFORM\n#define COLOR\n")
340 + fragmentShaderCode;
342 Q_ASSERT(!m_vertex_code.isNull());
343 Q_ASSERT(!m_fragment_code.isNull());
346 const char *vertexShader() const { return m_vertex_code.constData(); }
347 const char *fragmentShader() const { return m_fragment_code.constData(); }
349 QList<QByteArray> attributes() const {
350 return QList<QByteArray>() << "vPosTex" << "vData" << "vVec"
351 << "vColor" << "vDeformVec" << "vRotation";
355 QSGSimpleMaterialShader<DeformableMaterialData>::initialize();
357 program()->setUniformValue("texture", 0);
358 glFuncs = QOpenGLContext::currentContext()->functions();
359 m_timestamp_id = program()->uniformLocation("timestamp");
360 m_entry_id = program()->uniformLocation("entry");
363 void updateState(const DeformableMaterialData* d, const DeformableMaterialData*) {
364 glFuncs->glActiveTexture(GL_TEXTURE0);
367 program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
368 program()->setUniformValue(m_entry_id, (float) d->entry);
373 QByteArray m_vertex_code;
374 QByteArray m_fragment_code;
375 QOpenGLFunctions* glFuncs;
378 class SpriteMaterialData : public ImageMaterialData {};
379 class SpriteMaterial : public QSGSimpleMaterialShader<SpriteMaterialData>
381 QSG_DECLARE_SIMPLE_SHADER(SpriteMaterial, SpriteMaterialData)
386 m_vertex_code = QByteArray(SHADER_DEFINES)
387 + QByteArray("#define SPRITE\n#define TABLE\n#define DEFORM\n#define COLOR\n")
390 m_fragment_code = QByteArray(SHADER_DEFINES)
391 + QByteArray("#define SPRITE\n#define TABLE\n#define DEFORM\n#define COLOR\n")
392 + fragmentShaderCode;
394 Q_ASSERT(!m_vertex_code.isNull());
395 Q_ASSERT(!m_fragment_code.isNull());
398 const char *vertexShader() const { return m_vertex_code.constData(); }
399 const char *fragmentShader() const { return m_fragment_code.constData(); }
401 QList<QByteArray> attributes() const {
402 return QList<QByteArray>() << "vPosTex" << "vData" << "vVec"
403 << "vColor" << "vDeformVec" << "vRotation" << "vAnimData" << "vAnimPos";
407 QSGSimpleMaterialShader<SpriteMaterialData>::initialize();
409 program()->setUniformValue("texture", 0);
410 program()->setUniformValue("colortable", 1);
411 glFuncs = QOpenGLContext::currentContext()->functions();
412 //Don't actually expose the animSheetSize in the shader, it's currently only used for CPU calculations.
413 m_timestamp_id = program()->uniformLocation("timestamp");
414 m_entry_id = program()->uniformLocation("entry");
415 m_sizetable_id = program()->uniformLocation("sizetable");
416 m_opacitytable_id = program()->uniformLocation("opacitytable");
419 void updateState(const SpriteMaterialData* d, const SpriteMaterialData*) {
420 glFuncs->glActiveTexture(GL_TEXTURE1);
421 d->colorTable->bind();
423 // make sure we end by setting GL_TEXTURE0 as active texture
424 glFuncs->glActiveTexture(GL_TEXTURE0);
427 program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
428 program()->setUniformValue(m_entry_id, (float) d->entry);
429 program()->setUniformValueArray(m_sizetable_id, (float*) d->sizeTable, 64, 1);
430 program()->setUniformValueArray(m_opacitytable_id, (float*) d->opacityTable, UNIFORM_ARRAY_SIZE, 1);
436 int m_opacitytable_id;
437 QByteArray m_vertex_code;
438 QByteArray m_fragment_code;
439 QOpenGLFunctions* glFuncs;
442 class ColoredMaterialData : public ImageMaterialData {};
443 class ColoredMaterial : public QSGSimpleMaterialShader<ColoredMaterialData>
445 QSG_DECLARE_SIMPLE_SHADER(ColoredMaterial, ColoredMaterialData)
450 m_vertex_code = QByteArray(SHADER_DEFINES)
451 + QByteArray("#define COLOR\n")
454 m_fragment_code = QByteArray(SHADER_DEFINES)
455 + QByteArray("#define COLOR\n")
456 + fragmentShaderCode;
458 Q_ASSERT(!m_vertex_code.isNull());
459 Q_ASSERT(!m_fragment_code.isNull());
462 const char *vertexShader() const { return m_vertex_code.constData(); }
463 const char *fragmentShader() const { return m_fragment_code.constData(); }
466 QSGSimpleMaterialShader<ColoredMaterialData>::activate();
467 #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
468 glEnable(GL_POINT_SPRITE);
469 glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
474 QSGSimpleMaterialShader<ColoredMaterialData>::deactivate();
475 #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
476 glDisable(GL_POINT_SPRITE);
477 glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
481 QList<QByteArray> attributes() const {
482 return QList<QByteArray>() << "vPos" << "vData" << "vVec" << "vColor";
486 QSGSimpleMaterialShader<ColoredMaterialData>::initialize();
488 program()->setUniformValue("texture", 0);
489 glFuncs = QOpenGLContext::currentContext()->functions();
490 m_timestamp_id = program()->uniformLocation("timestamp");
491 m_entry_id = program()->uniformLocation("entry");
494 void updateState(const ColoredMaterialData* d, const ColoredMaterialData*) {
495 glFuncs->glActiveTexture(GL_TEXTURE0);
498 program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
499 program()->setUniformValue(m_entry_id, (float) d->entry);
504 QByteArray m_vertex_code;
505 QByteArray m_fragment_code;
506 QOpenGLFunctions* glFuncs;
509 class SimpleMaterialData : public ImageMaterialData {};
510 class SimpleMaterial : public QSGSimpleMaterialShader<SimpleMaterialData>
512 QSG_DECLARE_SIMPLE_SHADER(SimpleMaterial, SimpleMaterialData)
517 m_vertex_code = QByteArray(SHADER_DEFINES)
520 m_fragment_code = QByteArray(SHADER_DEFINES)
521 + fragmentShaderCode;
523 Q_ASSERT(!m_vertex_code.isNull());
524 Q_ASSERT(!m_fragment_code.isNull());
527 const char *vertexShader() const { return m_vertex_code.constData(); }
528 const char *fragmentShader() const { return m_fragment_code.constData(); }
531 QSGSimpleMaterialShader<SimpleMaterialData>::activate();
532 #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
533 glEnable(GL_POINT_SPRITE);
534 glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
539 QSGSimpleMaterialShader<SimpleMaterialData>::deactivate();
540 #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
541 glDisable(GL_POINT_SPRITE);
542 glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
546 QList<QByteArray> attributes() const {
547 return QList<QByteArray>() << "vPos" << "vData" << "vVec";
551 QSGSimpleMaterialShader<SimpleMaterialData>::initialize();
553 program()->setUniformValue("texture", 0);
554 glFuncs = QOpenGLContext::currentContext()->functions();
555 m_timestamp_id = program()->uniformLocation("timestamp");
556 m_entry_id = program()->uniformLocation("entry");
559 void updateState(const SimpleMaterialData* d, const SimpleMaterialData*) {
560 glFuncs->glActiveTexture(GL_TEXTURE0);
563 program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
564 program()->setUniformValue(m_entry_id, (float) d->entry);
569 QByteArray m_vertex_code;
570 QByteArray m_fragment_code;
571 QOpenGLFunctions* glFuncs;
574 void fillUniformArrayFromImage(float* array, const QImage& img, int size)
577 for (int i=0; i<size; i++)
581 QImage scaled = img.scaled(size,1);
582 for (int i=0; i<size; i++)
583 array[i] = qAlpha(scaled.pixel(i,0))/255.0;
587 \qmlclass ImageParticle QQuickImageParticle
588 \inqmlmodule QtQuick.Particles 2
589 \inherits ParticlePainter
590 \brief The ImageParticle element visualizes logical particles using an image
592 This element renders a logical particle as an image. The image can be
597 \li a sprite-based animation
600 ImageParticles implictly share data on particles if multiple ImageParticles are painting
601 the same logical particle group. This is broken down along the four capabilities listed
602 above. So if one ImageParticle defines data for rendering the particles in one of those
603 capabilities, and the other does not, then both will draw the particles the same in that
604 aspect automatically. This is primarily useful when there is some random variation on
605 the particle which is supposed to stay with it when switching painters. If both ImageParticles
606 define how they should appear for that aspect, they diverge and each appears as it is defined.
608 This sharing of data happens behind the scenes based off of whether properties were implicitly or explicitly
609 set. One drawback of the current implementation is that it is only possible to reset the capabilities as a whole.
610 So if you explicity set an attribute affecting color, such as redVariation, and then reset it (by setting redVariation
611 to undefined), all color data will be reset and it will begin to have an implicit value of any shared color from
612 other ImageParticles.
615 \qmlproperty url QtQuick.Particles2::ImageParticle::source
617 The source image to be used.
619 If the image is a sprite animation, use the sprite property instead.
622 \qmlproperty list<Sprite> QtQuick.Particles2::ImageParticle::sprites
624 The sprite or sprites used to draw this particle.
626 Note that the sprite image will be scaled to a square based on the size of
627 the particle being rendered.
630 \qmlproperty url QtQuick.Particles2::ImageParticle::colorTable
632 An image whose color will be used as a 1D texture to determine color over life. E.g. when
633 the particle is halfway through its lifetime, it will have the color specified halfway
636 This color is blended with the color property and the color of the source image.
639 \qmlproperty url QtQuick.Particles2::ImageParticle::sizeTable
641 An image whose opacity will be used as a 1D texture to determine size over life.
643 This property is expected to be removed shortly, in favor of custom easing curves to determine size over life.
646 \qmlproperty url QtQuick.Particles2::ImageParticle::opacityTable
648 An image whose opacity will be used as a 1D texture to determine size over life.
650 This property is expected to be removed shortly, in favor of custom easing curves to determine opacity over life.
653 \qmlproperty color QtQuick.Particles2::ImageParticle::color
655 If a color is specified, the provided image will be colorized with it.
657 Default is white (no change).
660 \qmlproperty real QtQuick.Particles2::ImageParticle::colorVariation
662 This number represents the color variation applied to individual particles.
663 Setting colorVariation is the same as setting redVariation, greenVariation,
664 and blueVariation to the same number.
666 Each channel can vary between particle by up to colorVariation from its usual color.
668 Color is measured, per channel, from 0.0 to 1.0.
673 \qmlproperty real QtQuick.Particles2::ImageParticle::redVariation
674 The variation in the red color channel between particles.
676 Color is measured, per channel, from 0.0 to 1.0.
681 \qmlproperty real QtQuick.Particles2::ImageParticle::greenVariation
682 The variation in the green color channel between particles.
684 Color is measured, per channel, from 0.0 to 1.0.
689 \qmlproperty real QtQuick.Particles2::ImageParticle::blueVariation
690 The variation in the blue color channel between particles.
692 Color is measured, per channel, from 0.0 to 1.0.
697 \qmlproperty real QtQuick.Particles2::ImageParticle::alpha
698 An alpha to be applied to the image. This value is multiplied by the value in
699 the image, and the value in the color property.
701 Particles have additive blending, so lower alpha on single particles leads
702 to stronger effects when multiple particles overlap.
704 Alpha is measured from 0.0 to 1.0.
709 \qmlproperty real QtQuick.Particles2::ImageParticle::alphaVariation
710 The variation in the alpha channel between particles.
712 Alpha is measured from 0.0 to 1.0.
717 \qmlproperty real QtQuick.Particles2::ImageParticle::rotation
719 If set the image will be rotated by this many degrees before it is drawn.
721 The particle coordinates are not transformed.
724 \qmlproperty real QtQuick.Particles2::ImageParticle::rotationVariation
726 If set the rotation of individual particles will vary by up to this much
731 \qmlproperty real QtQuick.Particles2::ImageParticle::rotationSpeed
733 If set particles will rotate at this speed in degrees/second.
736 \qmlproperty real QtQuick.Particles2::ImageParticle::rotationSpeedVariation
738 If set the rotationSpeed of individual particles will vary by up to this much
743 \qmlproperty bool QtQuick.Particles2::ImageParticle::autoRotation
745 If set to true then a rotation will be applied on top of the particles rotation, so
746 that it faces the direction of travel. So to face away from the direction of travel,
747 set autoRotation to true and rotation to 180.
752 \qmlproperty StochasticDirection QtQuick.Particles2::ImageParticle::xVector
754 Allows you to deform the particle image when drawn. The rectangular image will
755 be deformed so that the horizontal sides are in the shape of this vector instead
759 \qmlproperty StochasticDirection QtQuick.Particles2::ImageParticle::yVector
761 Allows you to deform the particle image when drawn. The rectangular image will
762 be deformed so that the vertical sides are in the shape of this vector instead
766 \qmlproperty EntryEffect QtQuick.Particles2::ImageParticle::entryEffect
768 This property provides basic and cheap entrance and exit effects for the particles.
769 For fine-grained control, see sizeTable and opacityTable.
771 Acceptable values are
773 \li ImageParticle.None: Particles just appear and disappear.
774 \li ImageParticle.Fade: Particles fade in from 0 opacity at the start of their life, and fade out to 0 at the end.
775 \li ImageParticle.Scale: Particles scale in from 0 size at the start of their life, and scale back to 0 at the end.
778 Default value is Fade.
781 \qmlproperty bool QtQuick.Particles2::ImageParticle::spritesInterpolate
783 If set to true, sprite particles will interpolate between sprite frames each rendered frame, making
784 the sprites look smoother.
790 \qmlproperty Status QtQuick.Particles2::ImageParticle::status
792 The status of loading the image.
796 QQuickImageParticle::QQuickImageParticle(QQuickItem* parent)
797 : QQuickParticlePainter(parent)
802 , m_color_variation(0.0)
805 , m_alphaVariation(0.0)
807 , m_redVariation(0.0)
808 , m_greenVariation(0.0)
809 , m_blueVariation(0.0)
811 , m_rotationVariation(0)
813 , m_rotationSpeedVariation(0)
814 , m_autoRotation(false)
818 , m_spritesInterpolate(true)
819 , m_explicitColor(false)
820 , m_explicitRotation(false)
821 , m_explicitDeformation(false)
822 , m_explicitAnimation(false)
823 , m_bypassOptimizations(false)
825 , m_lastLevel(Unknown)
827 , m_entryEffect(Fade)
828 , m_buildingNodes(false)
830 setFlag(ItemHasContents);
833 QQuickImageParticle::~QQuickImageParticle()
837 QQmlListProperty<QQuickSprite> QQuickImageParticle::sprites()
839 return QQmlListProperty<QQuickSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
842 void QQuickImageParticle::sceneGraphInvalidated()
849 void QQuickImageParticle::setImage(const QUrl &image)
851 if (image.isEmpty()){
860 m_image = new ImageData;
861 if (image == m_image->source)
863 m_image->source = image;
869 void QQuickImageParticle::setColortable(const QUrl &table)
871 if (table.isEmpty()){
874 emit colortableChanged();
880 m_colorTable = new ImageData;
881 if (table == m_colorTable->source)
883 m_colorTable->source = table;
884 emit colortableChanged();
888 void QQuickImageParticle::setSizetable(const QUrl &table)
890 if (table.isEmpty()){
893 emit sizetableChanged();
899 m_sizeTable = new ImageData;
900 if (table == m_sizeTable->source)
902 m_sizeTable->source = table;
903 emit sizetableChanged();
907 void QQuickImageParticle::setOpacitytable(const QUrl &table)
909 if (table.isEmpty()){
910 if (m_opacityTable) {
911 delete m_opacityTable;
912 emit opacitytableChanged();
918 m_opacityTable = new ImageData;
919 if (table == m_opacityTable->source)
921 m_opacityTable->source = table;
922 emit opacitytableChanged();
926 void QQuickImageParticle::setColor(const QColor &color)
928 if (color == m_color)
932 m_explicitColor = true;
933 if (perfLevel < Colored)
937 void QQuickImageParticle::setColorVariation(qreal var)
939 if (var == m_color_variation)
941 m_color_variation = var;
942 emit colorVariationChanged();
943 m_explicitColor = true;
944 if (perfLevel < Colored)
948 void QQuickImageParticle::setAlphaVariation(qreal arg)
950 if (m_alphaVariation != arg) {
951 m_alphaVariation = arg;
952 emit alphaVariationChanged(arg);
954 m_explicitColor = true;
955 if (perfLevel < Colored)
959 void QQuickImageParticle::setAlpha(qreal arg)
961 if (m_alpha != arg) {
963 emit alphaChanged(arg);
965 m_explicitColor = true;
966 if (perfLevel < Colored)
970 void QQuickImageParticle::setRedVariation(qreal arg)
972 if (m_redVariation != arg) {
973 m_redVariation = arg;
974 emit redVariationChanged(arg);
976 m_explicitColor = true;
977 if (perfLevel < Colored)
981 void QQuickImageParticle::setGreenVariation(qreal arg)
983 if (m_greenVariation != arg) {
984 m_greenVariation = arg;
985 emit greenVariationChanged(arg);
987 m_explicitColor = true;
988 if (perfLevel < Colored)
992 void QQuickImageParticle::setBlueVariation(qreal arg)
994 if (m_blueVariation != arg) {
995 m_blueVariation = arg;
996 emit blueVariationChanged(arg);
998 m_explicitColor = true;
999 if (perfLevel < Colored)
1003 void QQuickImageParticle::setRotation(qreal arg)
1005 if (m_rotation != arg) {
1007 emit rotationChanged(arg);
1009 m_explicitRotation = true;
1010 if (perfLevel < Deformable)
1014 void QQuickImageParticle::setRotationVariation(qreal arg)
1016 if (m_rotationVariation != arg) {
1017 m_rotationVariation = arg;
1018 emit rotationVariationChanged(arg);
1020 m_explicitRotation = true;
1021 if (perfLevel < Deformable)
1025 void QQuickImageParticle::setRotationSpeed(qreal arg)
1027 if (m_rotationSpeed != arg) {
1028 m_rotationSpeed = arg;
1029 emit rotationSpeedChanged(arg);
1031 m_explicitRotation = true;
1032 if (perfLevel < Deformable)
1036 void QQuickImageParticle::setRotationSpeedVariation(qreal arg)
1038 if (m_rotationSpeedVariation != arg) {
1039 m_rotationSpeedVariation = arg;
1040 emit rotationSpeedVariationChanged(arg);
1042 m_explicitRotation = true;
1043 if (perfLevel < Deformable)
1047 void QQuickImageParticle::setAutoRotation(bool arg)
1049 if (m_autoRotation != arg) {
1050 m_autoRotation = arg;
1051 emit autoRotationChanged(arg);
1053 m_explicitRotation = true;
1054 if (perfLevel < Deformable)
1058 void QQuickImageParticle::setXVector(QQuickDirection* arg)
1060 if (m_xVector != arg) {
1062 emit xVectorChanged(arg);
1064 m_explicitDeformation = true;
1065 if (perfLevel < Deformable)
1069 void QQuickImageParticle::setYVector(QQuickDirection* arg)
1071 if (m_yVector != arg) {
1073 emit yVectorChanged(arg);
1075 m_explicitDeformation = true;
1076 if (perfLevel < Deformable)
1080 void QQuickImageParticle::setSpritesInterpolate(bool arg)
1082 if (m_spritesInterpolate != arg) {
1083 m_spritesInterpolate = arg;
1084 emit spritesInterpolateChanged(arg);
1088 void QQuickImageParticle::setBypassOptimizations(bool arg)
1090 if (m_bypassOptimizations != arg) {
1091 m_bypassOptimizations = arg;
1092 emit bypassOptimizationsChanged(arg);
1094 if (perfLevel < 9999)
1098 void QQuickImageParticle::setEntryEffect(EntryEffect arg)
1100 if (m_entryEffect != arg) {
1101 m_entryEffect = arg;
1103 getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect;
1104 emit entryEffectChanged(arg);
1108 void QQuickImageParticle::resetColor()
1110 m_explicitColor = false;
1111 foreach (const QString &str, m_groups)
1112 foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
1113 if (d->colorOwner == this)
1116 m_color_variation = 0.0f;
1117 m_redVariation = 0.0f;
1118 m_blueVariation = 0.0f;
1119 m_greenVariation = 0.0f;
1121 m_alphaVariation = 0.0f;
1124 void QQuickImageParticle::resetRotation()
1126 m_explicitRotation = false;
1127 foreach (const QString &str, m_groups)
1128 foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
1129 if (d->rotationOwner == this)
1130 d->rotationOwner = 0;
1132 m_rotationVariation = 0;
1133 m_rotationSpeed = 0;
1134 m_rotationSpeedVariation = 0;
1135 m_autoRotation = false;
1138 void QQuickImageParticle::resetDeformation()
1140 m_explicitDeformation = false;
1141 foreach (const QString &str, m_groups)
1142 foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
1143 if (d->deformationOwner == this)
1144 d->deformationOwner = 0;
1153 void QQuickImageParticle::reset()
1155 QQuickParticlePainter::reset();
1156 m_pleaseReset = true;
1160 void QQuickImageParticle::createEngine()
1163 delete m_spriteEngine;
1164 if (m_sprites.count()) {
1165 m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
1166 connect(m_spriteEngine, SIGNAL(stateChanged(int)),
1167 this, SLOT(spriteAdvance(int)), Qt::DirectConnection);
1168 m_explicitAnimation = true;
1171 m_explicitAnimation = false;
1176 static QSGGeometry::Attribute SimpleParticle_Attributes[] = {
1177 QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // Position
1178 QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
1179 QSGGeometry::Attribute::create(2, 4, GL_FLOAT) // Vectors
1182 static QSGGeometry::AttributeSet SimpleParticle_AttributeSet =
1184 3, // Attribute Count
1185 ( 2 + 4 + 4 ) * sizeof(float),
1186 SimpleParticle_Attributes
1189 static QSGGeometry::Attribute ColoredParticle_Attributes[] = {
1190 QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // Position
1191 QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
1192 QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Vectors
1193 QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors
1196 static QSGGeometry::AttributeSet ColoredParticle_AttributeSet =
1198 4, // Attribute Count
1199 ( 2 + 4 + 4 ) * sizeof(float) + 4 * sizeof(uchar),
1200 ColoredParticle_Attributes
1203 static QSGGeometry::Attribute DeformableParticle_Attributes[] = {
1204 QSGGeometry::Attribute::create(0, 4, GL_FLOAT), // Position & TexCoord
1205 QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
1206 QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Vectors
1207 QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors
1208 QSGGeometry::Attribute::create(4, 4, GL_FLOAT), // DeformationVectors
1209 QSGGeometry::Attribute::create(5, 3, GL_FLOAT), // Rotation
1212 static QSGGeometry::AttributeSet DeformableParticle_AttributeSet =
1214 6, // Attribute Count
1215 (4 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar),
1216 DeformableParticle_Attributes
1219 static QSGGeometry::Attribute SpriteParticle_Attributes[] = {
1220 QSGGeometry::Attribute::create(0, 4, GL_FLOAT), // Position & TexCoord
1221 QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
1222 QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Vectors
1223 QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors
1224 QSGGeometry::Attribute::create(4, 4, GL_FLOAT), // DeformationVectors
1225 QSGGeometry::Attribute::create(5, 3, GL_FLOAT), // Rotation
1226 QSGGeometry::Attribute::create(6, 3, GL_FLOAT), // Anim Data
1227 QSGGeometry::Attribute::create(7, 4, GL_FLOAT) // Anim Pos
1230 static QSGGeometry::AttributeSet SpriteParticle_AttributeSet =
1232 8, // Attribute Count
1233 (4 + 4 + 4 + 4 + 3 + 3 + 4) * sizeof(float) + 4 * sizeof(uchar),
1234 SpriteParticle_Attributes
1237 void QQuickImageParticle::clearShadows()
1239 foreach (const QVector<QQuickParticleData*> data, m_shadowData)
1241 m_shadowData.clear();
1244 //Only call if you need to, may initialize the whole array first time
1245 QQuickParticleData* QQuickImageParticle::getShadowDatum(QQuickParticleData* datum)
1247 //Will return datum if the datum is a sentinel or uninitialized, to centralize that one check
1248 if (datum->systemIndex == -1)
1250 QQuickParticleGroupData* gd = m_system->groupData[datum->group];
1251 if (!m_shadowData.contains(datum->group)) {
1252 QVector<QQuickParticleData*> data;
1253 for (int i=0; i<gd->size(); i++){
1254 QQuickParticleData* datum = new QQuickParticleData(m_system);
1255 *datum = *(gd->data[i]);
1258 m_shadowData.insert(datum->group, data);
1260 //### If dynamic resize is added, remember to potentially resize the shadow data on out-of-bounds access request
1262 return m_shadowData[datum->group][datum->index];
1265 bool QQuickImageParticle::loadingSomething()
1267 return (m_image && m_image->pix.isLoading())
1268 || (m_colorTable && m_colorTable->pix.isLoading())
1269 || (m_sizeTable && m_sizeTable->pix.isLoading())
1270 || (m_opacityTable && m_opacityTable->pix.isLoading())
1271 || (m_spriteEngine && m_spriteEngine->isLoading());
1274 void QQuickImageParticle::buildParticleNodes()//Starts async parts, like loading images.
1276 if (m_rootNode || loadingSomething())
1279 if (!m_buildingNodes) {
1280 if (m_image) {//ImageData created on setSource
1281 m_image->pix.clear(this);
1282 m_image->pix.load(qmlEngine(this), m_image->source);
1286 m_spriteEngine->startAssemblingImage();
1289 m_colorTable->pix.load(qmlEngine(this), m_colorTable->source);
1292 m_sizeTable->pix.load(qmlEngine(this), m_sizeTable->source);
1295 m_opacityTable->pix.load(qmlEngine(this), m_opacityTable->source);
1297 m_buildingNodes = true;
1298 if (loadingSomething())
1301 finishBuildParticleNodes();
1304 void QQuickImageParticle::finishBuildParticleNodes()
1306 m_buildingNodes = false;
1307 #ifdef QT_OPENGL_ES_2
1308 if (m_count * 4 > 0xffff) {
1309 printf("ImageParticle: Too many particles - maximum 16,000 per ImageParticle.\n");//ES 2 vertex count limit is ushort
1317 m_debugMode = m_system->m_debugMode;
1319 if (m_sprites.count() || m_bypassOptimizations) {
1320 perfLevel = Sprites;
1321 } else if (m_colorTable || m_sizeTable || m_opacityTable) {
1323 } else if (m_autoRotation || m_rotation || m_rotationVariation
1324 || m_rotationSpeed || m_rotationSpeedVariation
1325 || m_xVector || m_yVector) {
1326 perfLevel = Deformable;
1327 } else if (m_alphaVariation || m_alpha != 1.0 || m_color.isValid() || m_color_variation
1328 || m_redVariation || m_blueVariation || m_greenVariation) {
1329 perfLevel = Colored;
1334 foreach (const QString &str, m_groups){//For sharing higher levels, need to have highest used so it renders
1335 int gIdx = m_system->groupIds[str];
1336 foreach (QQuickParticlePainter* p, m_system->groupData[gIdx]->painters){
1337 QQuickImageParticle* other = qobject_cast<QQuickImageParticle*>(p);
1339 if (other->perfLevel > perfLevel) {
1340 if (other->perfLevel >= Tabled){//Deformable is the highest level needed for this, anything higher isn't shared (or requires your own sprite)
1341 if (perfLevel < Deformable)
1342 perfLevel = Deformable;
1344 perfLevel = other->perfLevel;
1346 } else if (other->perfLevel < perfLevel) {
1353 if (perfLevel < Deformable) //QTBUG-24540 , point sprite 'extension' isn't working on windows.
1354 perfLevel = Deformable;
1357 if (perfLevel >= Colored && !m_color.isValid())
1358 m_color = QColor(Qt::white);//Hidden default, but different from unset
1367 QImage opacitytable;
1369 bool imageLoaded = false;
1370 switch (perfLevel) {//Fallthrough intended
1372 if (!m_spriteEngine) {
1373 qWarning() << "ImageParticle: No sprite engine...";
1374 //Sprite performance mode with static image is supported, but not advised
1375 //Note that in this case it always uses shadow data
1377 image = m_spriteEngine->assembledImage();
1378 if (image.isNull())//Warning is printed in engine
1382 m_material = SpriteMaterial::createMaterial();
1384 getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(image);
1385 getState<ImageMaterialData>(m_material)->animSheetSize = QSizeF(image.size());
1387 m_spriteEngine->setCount(m_count);
1390 m_material = TabledMaterial::createMaterial();
1393 if (m_colorTable->pix.isReady())
1394 colortable = m_colorTable->pix.image();
1396 qmlInfo(this) << "Error loading color table: " << m_colorTable->pix.error();
1400 if (m_sizeTable->pix.isReady())
1401 sizetable = m_sizeTable->pix.image();
1403 qmlInfo(this) << "Error loading size table: " << m_sizeTable->pix.error();
1406 if (m_opacityTable) {
1407 if (m_opacityTable->pix.isReady())
1408 opacitytable = m_opacityTable->pix.image();
1410 qmlInfo(this) << "Error loading opacity table: " << m_opacityTable->pix.error();
1413 if (colortable.isNull()){//###Goes through image just for this
1414 colortable = QImage(1,1,QImage::Format_ARGB32_Premultiplied);
1415 colortable.fill(Qt::white);
1417 getState<ImageMaterialData>(m_material)->colorTable = QSGPlainTexture::fromImage(colortable);
1418 fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->sizeTable, sizetable, UNIFORM_ARRAY_SIZE);
1419 fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->opacityTable, opacitytable, UNIFORM_ARRAY_SIZE);
1422 m_material = DeformableMaterial::createMaterial();
1425 m_material = ColoredMaterial::createMaterial();
1426 default://Also Simple
1428 m_material = SimpleMaterial::createMaterial();
1430 if (!m_image->pix.isReady()) {
1431 qmlInfo(this) << m_image->pix.error();
1435 //getState<ImageMaterialData>(m_material)->texture //TODO: Shouldn't this be better? But not crash?
1436 // = QQuickItemPrivate::get(this)->sceneGraphContext()->textureForFactory(m_imagePix.textureFactory());
1437 getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(m_image->pix.image());
1439 getState<ImageMaterialData>(m_material)->texture->setFiltering(QSGTexture::Linear);
1440 getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect;
1441 m_material->setFlag(QSGMaterial::Blending);
1445 foreach (const QString &str, m_groups){
1446 int gIdx = m_system->groupIds[str];
1447 int count = m_system->groupData[gIdx]->size();
1448 QSGGeometryNode* node = new QSGGeometryNode();
1449 node->setMaterial(m_material);
1450 node->markDirty(QSGNode::DirtyMaterial);
1452 m_nodes.insert(gIdx, node);
1453 m_idxStarts.insert(gIdx, m_lastIdxStart);
1454 m_startsIdx.append(qMakePair<int,int>(m_lastIdxStart, gIdx));
1455 m_lastIdxStart += count;
1457 //Create Particle Geometry
1458 int vCount = count * 4;
1459 int iCount = count * 6;
1462 if (perfLevel == Sprites)
1463 g = new QSGGeometry(SpriteParticle_AttributeSet, vCount, iCount);
1464 else if (perfLevel == Tabled)
1465 g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
1466 else if (perfLevel == Deformable)
1467 g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
1468 else if (perfLevel == Colored)
1469 g = new QSGGeometry(ColoredParticle_AttributeSet, count, 0);
1471 g = new QSGGeometry(SimpleParticle_AttributeSet, count, 0);
1473 node->setGeometry(g);
1474 if (perfLevel <= Colored){
1475 g->setDrawingMode(GL_POINTS);
1477 GLfloat pointSizeRange[2];
1478 glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
1479 qDebug() << "Using point sprites, GL_ALIASED_POINT_SIZE_RANGE " <<pointSizeRange[0] << ":" << pointSizeRange[1];
1482 g->setDrawingMode(GL_TRIANGLES);
1484 for (int p=0; p < count; ++p)
1485 commit(gIdx, p);//commit sets geometry for the node, has its own perfLevel switch
1487 if (perfLevel == Sprites)
1488 initTexCoords<SpriteVertex>((SpriteVertex*)g->vertexData(), vCount);
1489 else if (perfLevel == Tabled)
1490 initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
1491 else if (perfLevel == Deformable)
1492 initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
1494 if (perfLevel > Colored){
1495 quint16 *indices = g->indexDataAsUShort();
1496 for (int i=0; i < count; ++i) {
1509 if (perfLevel == Sprites)
1510 spritesUpdate();//Gives all vertexes the initial sprite data, then maintained per frame
1512 foreach (QSGGeometryNode* node, m_nodes){
1513 if (node == *(m_nodes.begin()))
1514 node->setFlag(QSGGeometryNode::OwnsMaterial);//Root node owns the material for memory management purposes
1516 (*(m_nodes.begin()))->appendChildNode(node);
1519 m_rootNode = *(m_nodes.begin());
1523 QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
1526 m_lastLevel = perfLevel;
1528 delete m_rootNode;//Automatically deletes children, and SG manages material lifetime
1532 m_idxStarts.clear();
1533 m_startsIdx.clear();
1538 m_pleaseReset = false;
1539 m_buildingNodes = false;//Cancel a part-way build
1542 if (m_system && m_system->isRunning() && !m_system->isPaused()){
1546 foreach (QSGGeometryNode* node, m_nodes)
1547 node->markDirty(QSGNode::DirtyGeometry);
1548 } else if (m_buildingNodes) {
1549 update();//To call prepareNextFrame() again from the renderThread
1556 void QQuickImageParticle::prepareNextFrame()
1558 if (m_rootNode == 0){//TODO: Staggered loading (as emitted)
1559 buildParticleNodes();
1561 qDebug() << "QQuickImageParticle Feature level: " << perfLevel;
1562 qDebug() << "QQuickImageParticle Nodes: ";
1564 foreach (int i, m_nodes.keys()) {
1565 qDebug() << "Group " << i << " (" << m_system->groupData[i]->size() << " particles)";
1566 count += m_system->groupData[i]->size();
1568 qDebug() << "Total count: " << count;
1570 if (m_rootNode == 0)
1573 qint64 timeStamp = m_system->systemSync(this);
1575 qreal time = timeStamp / 1000.;
1577 switch (perfLevel){//Fall-through intended
1581 m_spriteEngine->updateSprites(timeStamp);//fires signals if anim changed
1582 spritesUpdate(time);
1587 default: //Also Simple
1588 getState<ImageMaterialData>(m_material)->timestamp = time;
1591 foreach (QSGGeometryNode* node, m_nodes)
1592 node->markDirty(QSGNode::DirtyMaterial);
1595 void QQuickImageParticle::spritesUpdate(qreal time)
1597 // Sprite progression handled CPU side, so as to have per-frame control.
1598 foreach (const QString &str, m_groups) {
1599 int gIdx = m_system->groupIds[str];
1600 foreach (QQuickParticleData* mainDatum, m_system->groupData[gIdx]->data) {
1601 QSGGeometryNode *node = m_nodes[gIdx];
1604 //TODO: Interpolate between two different animations if it's going to transition next frame
1605 // This is particularly important for cut-up sprites.
1606 QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum));
1608 for (int i = 0; i<m_startsIdx.count(); i++) {
1609 if (m_startsIdx[i].second == gIdx){
1610 spriteIdx = m_startsIdx[i].first + datum->index;
1618 if (datum->frameDuration > 0) {
1619 qreal frame = (time - datum->animT)/(datum->frameDuration / 1000.0);
1620 frame = qBound((qreal)0.0, frame, (qreal)((qreal)datum->frameCount - 1.0));//Stop at count-1 frames until we have between anim interpolation
1621 if (m_spritesInterpolate)
1622 progress = modf(frame,&frameAt);
1624 modf(frame,&frameAt);
1627 if (datum->frameAt >= datum->frameCount){
1629 m_spriteEngine->advance(spriteIdx);
1631 frameAt = datum->frameAt;
1633 if (m_spriteEngine->sprite(spriteIdx)->reverse())//### Store this in datum too?
1634 frameAt = (datum->frameCount - 1) - frameAt;
1635 QSizeF sheetSize = getState<ImageMaterialData>(m_material)->animSheetSize;
1636 qreal y = datum->animY / sheetSize.height();
1637 qreal w = datum->animWidth / sheetSize.width();
1638 qreal h = datum->animHeight / sheetSize.height();
1639 qreal x1 = datum->animX / sheetSize.width();
1642 if (frameAt < (datum->frameCount-1))
1645 node->setFlag(QSGNode::OwnsGeometry, false);
1646 SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
1647 spriteVertices += datum->index*4;
1648 for (int i=0; i<4; i++) {
1649 spriteVertices[i].animX1 = x1;
1650 spriteVertices[i].animY1 = y;
1651 spriteVertices[i].animX2 = x2;
1652 spriteVertices[i].animY2 = y;
1653 spriteVertices[i].animW = w;
1654 spriteVertices[i].animH = h;
1655 spriteVertices[i].animProgress = progress;
1657 node->setFlag(QSGNode::OwnsGeometry, true);
1662 void QQuickImageParticle::spriteAdvance(int spriteIdx)
1664 if (!m_startsIdx.count())//Probably overly defensive
1669 for (i = 0; i<m_startsIdx.count(); i++) {
1670 if (spriteIdx < m_startsIdx[i].first) {
1671 gIdx = m_startsIdx[i-1].second;
1676 gIdx = m_startsIdx[i-1].second;
1677 int pIdx = spriteIdx - m_startsIdx[i-1].first;
1679 QQuickParticleData* mainDatum = m_system->groupData[gIdx]->data[pIdx];
1680 QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum));
1682 datum->animIdx = m_spriteEngine->spriteState(spriteIdx);
1683 datum->animT = m_spriteEngine->spriteStart(spriteIdx)/1000.0;
1684 datum->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
1685 datum->frameDuration = m_spriteEngine->spriteDuration(spriteIdx) / datum->frameCount;
1686 datum->animX = m_spriteEngine->spriteX(spriteIdx);
1687 datum->animY = m_spriteEngine->spriteY(spriteIdx);
1688 datum->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
1689 datum->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
1692 void QQuickImageParticle::reloadColor(const Color4ub &c, QQuickParticleData* d)
1695 //TODO: get index for reload - or make function take an index
1698 void QQuickImageParticle::initialize(int gIdx, int pIdx)
1701 QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
1702 qreal redVariation = m_color_variation + m_redVariation;
1703 qreal greenVariation = m_color_variation + m_greenVariation;
1704 qreal blueVariation = m_color_variation + m_blueVariation;
1706 if (m_spriteEngine) {
1707 spriteIdx = m_idxStarts[gIdx] + datum->index;
1708 if (spriteIdx >= m_spriteEngine->count())
1709 m_spriteEngine->setCount(spriteIdx+1);
1713 float rotationSpeed;
1715 switch (perfLevel){//Fall-through is intended on all of them
1717 // Initial Sprite State
1718 if (m_explicitAnimation && m_spriteEngine){
1719 if (!datum->animationOwner)
1720 datum->animationOwner = this;
1721 QQuickParticleData* writeTo = (datum->animationOwner == this ? datum : getShadowDatum(datum));
1722 writeTo->animT = writeTo->t;
1723 //writeTo->animInterpolate = m_spritesInterpolate;
1724 if (m_spriteEngine){
1725 m_spriteEngine->start(spriteIdx);
1726 writeTo->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
1727 writeTo->frameDuration = m_spriteEngine->spriteDuration(spriteIdx) / writeTo->frameCount;
1728 writeTo->animIdx = 0;//Always starts at 0
1729 writeTo->frameAt = -1;
1730 writeTo->animX = m_spriteEngine->spriteX(spriteIdx);
1731 writeTo->animY = m_spriteEngine->spriteY(spriteIdx);
1732 writeTo->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
1733 writeTo->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
1736 QQuickParticleData* writeTo = getShadowDatum(datum);
1737 writeTo->animT = datum->t;
1738 writeTo->frameCount = 1;
1739 writeTo->frameDuration = 60000000.0;
1740 writeTo->frameAt = -1;
1741 writeTo->animIdx = 0;
1743 writeTo->animX = writeTo->animY = 0;
1744 writeTo->animWidth = getState<ImageMaterialData>(m_material)->animSheetSize.width();
1745 writeTo->animHeight = getState<ImageMaterialData>(m_material)->animSheetSize.height();
1750 if (m_explicitDeformation){
1751 if (!datum->deformationOwner)
1752 datum->deformationOwner = this;
1754 const QPointF &ret = m_xVector->sample(QPointF(datum->x, datum->y));
1755 if (datum->deformationOwner == this) {
1756 datum->xx = ret.x();
1757 datum->xy = ret.y();
1759 getShadowDatum(datum)->xx = ret.x();
1760 getShadowDatum(datum)->xy = ret.y();
1764 const QPointF &ret = m_yVector->sample(QPointF(datum->x, datum->y));
1765 if (datum->deformationOwner == this) {
1766 datum->yx = ret.x();
1767 datum->yy = ret.y();
1769 getShadowDatum(datum)->yx = ret.x();
1770 getShadowDatum(datum)->yy = ret.y();
1775 if (m_explicitRotation){
1776 if (!datum->rotationOwner)
1777 datum->rotationOwner = this;
1779 (m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV;
1781 (m_rotationSpeed + (m_rotationSpeedVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationSpeedVariation) ) * CONV;
1782 autoRotate = m_autoRotation?1.0:0.0;
1783 if (datum->rotationOwner == this) {
1784 datum->rotation = rotation;
1785 datum->rotationSpeed = rotationSpeed;
1786 datum->autoRotate = autoRotate;
1788 getShadowDatum(datum)->rotation = rotation;
1789 getShadowDatum(datum)->rotationSpeed = rotationSpeed;
1790 getShadowDatum(datum)->autoRotate = autoRotate;
1794 //Color initialization
1796 if (m_explicitColor) {
1797 if (!datum->colorOwner)
1798 datum->colorOwner = this;
1799 color.r = m_color.red() * (1 - redVariation) + rand() % 256 * redVariation;
1800 color.g = m_color.green() * (1 - greenVariation) + rand() % 256 * greenVariation;
1801 color.b = m_color.blue() * (1 - blueVariation) + rand() % 256 * blueVariation;
1802 color.a = m_alpha * m_color.alpha() * (1 - m_alphaVariation) + rand() % 256 * m_alphaVariation;
1803 if (datum->colorOwner == this)
1804 datum->color = color;
1806 getShadowDatum(datum)->color = color;
1813 void QQuickImageParticle::commit(int gIdx, int pIdx)
1817 QSGGeometryNode *node = m_nodes[gIdx];
1820 QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
1821 node->setFlag(QSGNode::OwnsGeometry, false);
1822 SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
1823 DeformableVertex *deformableVertices = (DeformableVertex *) node->geometry()->vertexData();
1824 ColoredVertex *coloredVertices = (ColoredVertex *) node->geometry()->vertexData();
1825 SimpleVertex *simpleVertices = (SimpleVertex *) node->geometry()->vertexData();
1826 switch (perfLevel){//No automatic fall through intended on this one
1828 spriteVertices += pIdx*4;
1829 for (int i=0; i<4; i++){
1830 spriteVertices[i].x = datum->x - m_systemOffset.x();
1831 spriteVertices[i].y = datum->y - m_systemOffset.y();
1832 spriteVertices[i].t = datum->t;
1833 spriteVertices[i].lifeSpan = datum->lifeSpan;
1834 spriteVertices[i].size = datum->size;
1835 spriteVertices[i].endSize = datum->endSize;
1836 spriteVertices[i].vx = datum->vx;
1837 spriteVertices[i].vy = datum->vy;
1838 spriteVertices[i].ax = datum->ax;
1839 spriteVertices[i].ay = datum->ay;
1840 if (m_explicitDeformation && datum->deformationOwner != this) {
1841 QQuickParticleData* shadow = getShadowDatum(datum);
1842 spriteVertices[i].xx = shadow->xx;
1843 spriteVertices[i].xy = shadow->xy;
1844 spriteVertices[i].yx = shadow->yx;
1845 spriteVertices[i].yy = shadow->yy;
1847 spriteVertices[i].xx = datum->xx;
1848 spriteVertices[i].xy = datum->xy;
1849 spriteVertices[i].yx = datum->yx;
1850 spriteVertices[i].yy = datum->yy;
1852 if (m_explicitRotation && datum->rotationOwner != this) {
1853 QQuickParticleData* shadow = getShadowDatum(datum);
1854 spriteVertices[i].rotation = shadow->rotation;
1855 spriteVertices[i].rotationSpeed = shadow->rotationSpeed;
1856 spriteVertices[i].autoRotate = shadow->autoRotate;
1858 spriteVertices[i].rotation = datum->rotation;
1859 spriteVertices[i].rotationSpeed = datum->rotationSpeed;
1860 spriteVertices[i].autoRotate = datum->autoRotate;
1862 //Sprite-related vertices updated per-frame in spritesUpdate(), not on demand
1863 if (m_explicitColor && datum->colorOwner != this) {
1864 QQuickParticleData* shadow = getShadowDatum(datum);
1865 spriteVertices[i].color.r = shadow->color.r;
1866 spriteVertices[i].color.g = shadow->color.g;
1867 spriteVertices[i].color.b = shadow->color.b;
1868 spriteVertices[i].color.a = shadow->color.a;
1870 spriteVertices[i].color.r = datum->color.r;
1871 spriteVertices[i].color.g = datum->color.g;
1872 spriteVertices[i].color.b = datum->color.b;
1873 spriteVertices[i].color.a = datum->color.a;
1877 case Tabled: //Fall through until it has its own vertex class
1879 deformableVertices += pIdx*4;
1880 for (int i=0; i<4; i++){
1881 deformableVertices[i].x = datum->x - m_systemOffset.x();
1882 deformableVertices[i].y = datum->y - m_systemOffset.y();
1883 deformableVertices[i].t = datum->t;
1884 deformableVertices[i].lifeSpan = datum->lifeSpan;
1885 deformableVertices[i].size = datum->size;
1886 deformableVertices[i].endSize = datum->endSize;
1887 deformableVertices[i].vx = datum->vx;
1888 deformableVertices[i].vy = datum->vy;
1889 deformableVertices[i].ax = datum->ax;
1890 deformableVertices[i].ay = datum->ay;
1891 if (m_explicitDeformation && datum->deformationOwner != this) {
1892 QQuickParticleData* shadow = getShadowDatum(datum);
1893 deformableVertices[i].xx = shadow->xx;
1894 deformableVertices[i].xy = shadow->xy;
1895 deformableVertices[i].yx = shadow->yx;
1896 deformableVertices[i].yy = shadow->yy;
1898 deformableVertices[i].xx = datum->xx;
1899 deformableVertices[i].xy = datum->xy;
1900 deformableVertices[i].yx = datum->yx;
1901 deformableVertices[i].yy = datum->yy;
1903 if (m_explicitRotation && datum->rotationOwner != this) {
1904 QQuickParticleData* shadow = getShadowDatum(datum);
1905 deformableVertices[i].rotation = shadow->rotation;
1906 deformableVertices[i].rotationSpeed = shadow->rotationSpeed;
1907 deformableVertices[i].autoRotate = shadow->autoRotate;
1909 deformableVertices[i].rotation = datum->rotation;
1910 deformableVertices[i].rotationSpeed = datum->rotationSpeed;
1911 deformableVertices[i].autoRotate = datum->autoRotate;
1913 if (m_explicitColor && datum->colorOwner != this) {
1914 QQuickParticleData* shadow = getShadowDatum(datum);
1915 deformableVertices[i].color.r = shadow->color.r;
1916 deformableVertices[i].color.g = shadow->color.g;
1917 deformableVertices[i].color.b = shadow->color.b;
1918 deformableVertices[i].color.a = shadow->color.a;
1920 deformableVertices[i].color.r = datum->color.r;
1921 deformableVertices[i].color.g = datum->color.g;
1922 deformableVertices[i].color.b = datum->color.b;
1923 deformableVertices[i].color.a = datum->color.a;
1928 coloredVertices += pIdx*1;
1929 for (int i=0; i<1; i++){
1930 coloredVertices[i].x = datum->x - m_systemOffset.x();
1931 coloredVertices[i].y = datum->y - m_systemOffset.y();
1932 coloredVertices[i].t = datum->t;
1933 coloredVertices[i].lifeSpan = datum->lifeSpan;
1934 coloredVertices[i].size = datum->size;
1935 coloredVertices[i].endSize = datum->endSize;
1936 coloredVertices[i].vx = datum->vx;
1937 coloredVertices[i].vy = datum->vy;
1938 coloredVertices[i].ax = datum->ax;
1939 coloredVertices[i].ay = datum->ay;
1940 if (m_explicitColor && datum->colorOwner != this) {
1941 QQuickParticleData* shadow = getShadowDatum(datum);
1942 coloredVertices[i].color.r = shadow->color.r;
1943 coloredVertices[i].color.g = shadow->color.g;
1944 coloredVertices[i].color.b = shadow->color.b;
1945 coloredVertices[i].color.a = shadow->color.a;
1947 coloredVertices[i].color.r = datum->color.r;
1948 coloredVertices[i].color.g = datum->color.g;
1949 coloredVertices[i].color.b = datum->color.b;
1950 coloredVertices[i].color.a = datum->color.a;
1955 simpleVertices += pIdx*1;
1956 for (int i=0; i<1; i++){
1957 simpleVertices[i].x = datum->x - m_systemOffset.x();
1958 simpleVertices[i].y = datum->y - m_systemOffset.y();
1959 simpleVertices[i].t = datum->t;
1960 simpleVertices[i].lifeSpan = datum->lifeSpan;
1961 simpleVertices[i].size = datum->size;
1962 simpleVertices[i].endSize = datum->endSize;
1963 simpleVertices[i].vx = datum->vx;
1964 simpleVertices[i].vy = datum->vy;
1965 simpleVertices[i].ax = datum->ax;
1966 simpleVertices[i].ay = datum->ay;
1973 node->setFlag(QSGNode::OwnsGeometry, true);