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/private/qsgtexture_p.h>
55 #include <private/qqmlglobal_p.h>
56 #include <QtQml/qqmlinfo.h>
61 #ifndef QT_OPENGL_ES_2
62 #define SHADER_DEFINES "#version 120\n"
64 #define SHADER_DEFINES ""
67 //TODO: Make it larger on desktop? Requires fixing up shader code with the same define
68 #define UNIFORM_ARRAY_SIZE 64
70 static const char vertexShaderCode[] =
71 "#if defined(DEFORM)\n"
72 "attribute highp vec4 vPosTex;\n"
74 "attribute highp vec2 vPos;\n"
76 "attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize\n"
77 "attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration\n"
78 "uniform highp float entry;\n"
79 "#if defined(COLOR)\n"
80 "attribute highp vec4 vColor;\n"
82 "#if defined(DEFORM)\n"
83 "attribute highp vec4 vDeformVec; //x,y x unit vector; z,w = y unit vector\n"
84 "attribute highp vec3 vRotation; //x = radians of rotation, y=rotation speed, z= bool autoRotate\n"
86 "#if defined(SPRITE)\n"
87 "attribute highp vec3 vAnimData;// w,h(premultiplied of anim), interpolation progress\n"
88 "attribute highp vec4 vAnimPos;//x,y, x,y (two frames for interpolation)\n"
91 "uniform highp mat4 qt_Matrix;\n"
92 "uniform highp float timestamp;\n"
93 "#if defined(TABLE)\n"
94 "varying lowp vec2 tt;//y is progress if Sprite mode\n"
95 "uniform highp float sizetable[64];\n"
96 "uniform highp float opacitytable[64];\n"
98 "#if defined(SPRITE)\n"
99 "varying highp vec4 fTexS;\n"
100 "#elif defined(DEFORM)\n"
101 "varying highp vec2 fTex;\n"
103 "#if defined(COLOR)\n"
104 "varying lowp vec4 fColor;\n"
106 "varying lowp float fFade;\n"
112 " highp float t = (timestamp - vData.x) / vData.y;\n"
113 " if (t < 0. || t > 1.) {\n"
114 "#if defined(DEFORM)\n"
115 " gl_Position = qt_Matrix * vec4(vPosTex.x, vPosTex.y, 0., 1.);\n"
117 " gl_PointSize = 0.;\n"
120 "#if defined(SPRITE)\n"
121 " tt.y = vAnimData.z;\n"
122 " //Calculate frame location in texture\n"
123 " fTexS.xy = vAnimPos.xy + vPosTex.zw * vAnimData.xy;\n"
124 " //Next frame is also passed, for interpolation\n"
125 " fTexS.zw = vAnimPos.zw + vPosTex.zw * vAnimData.xy;\n"
127 "#elif defined(DEFORM)\n"
128 " fTex = vPosTex.zw;\n"
130 " highp float currentSize = mix(vData.z, vData.w, t * t);\n"
131 " lowp float fade = 1.;\n"
132 " highp float fadeIn = min(t * 10., 1.);\n"
133 " highp float fadeOut = 1. - clamp((t - 0.75) * 4.,0., 1.);\n"
135 "#if defined(TABLE)\n"
136 " currentSize = currentSize * sizetable[int(floor(t*64.))];\n"
137 " fade = fade * opacitytable[int(floor(t*64.))];\n"
140 " if (entry == 1.)\n"
141 " fade = fade * fadeIn * fadeOut;\n"
142 " else if (entry == 2.)\n"
143 " currentSize = currentSize * fadeIn * fadeOut;\n"
145 " if (currentSize <= 0.) {\n"
146 "#if defined(DEFORM)\n"
147 " gl_Position = qt_Matrix * vec4(vPosTex.x, vPosTex.y, 0., 1.);\n"
149 " gl_PointSize = 0.;\n"
152 " if (currentSize < 3.)//Sizes too small look jittery as they move\n"
153 " currentSize = 3.;\n"
156 "#if defined(DEFORM)\n"
157 " highp float rotation = vRotation.x + vRotation.y * t * vData.y;\n"
158 " if (vRotation.z == 1.0){\n"
159 " highp vec2 curVel = vVec.zw * t * vData.y + vVec.xy;\n"
160 " if (length(curVel) > 0.)\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 For visualizing logical particles using an image
591 \ingroup qtquick-particles
593 This element renders a logical particle as an image. The image can be
598 \li a sprite-based animation
601 ImageParticles implictly share data on particles if multiple ImageParticles are painting
602 the same logical particle group. This is broken down along the four capabilities listed
603 above. So if one ImageParticle defines data for rendering the particles in one of those
604 capabilities, and the other does not, then both will draw the particles the same in that
605 aspect automatically. This is primarily useful when there is some random variation on
606 the particle which is supposed to stay with it when switching painters. If both ImageParticles
607 define how they should appear for that aspect, they diverge and each appears as it is defined.
609 This sharing of data happens behind the scenes based off of whether properties were implicitly or explicitly
610 set. One drawback of the current implementation is that it is only possible to reset the capabilities as a whole.
611 So if you explicity set an attribute affecting color, such as redVariation, and then reset it (by setting redVariation
612 to undefined), all color data will be reset and it will begin to have an implicit value of any shared color from
613 other ImageParticles.
616 \qmlproperty url QtQuick.Particles2::ImageParticle::source
618 The source image to be used.
620 If the image is a sprite animation, use the sprite property instead.
623 \qmlproperty list<Sprite> QtQuick.Particles2::ImageParticle::sprites
625 The sprite or sprites used to draw this particle.
627 Note that the sprite image will be scaled to a square based on the size of
628 the particle being rendered.
630 For full details, see the \l{Sprite Animation} overview.
633 \qmlproperty url QtQuick.Particles2::ImageParticle::colorTable
635 An image whose color will be used as a 1D texture to determine color over life. E.g. when
636 the particle is halfway through its lifetime, it will have the color specified halfway
639 This color is blended with the color property and the color of the source image.
642 \qmlproperty url QtQuick.Particles2::ImageParticle::sizeTable
644 An image whose opacity will be used as a 1D texture to determine size over life.
646 This property is expected to be removed shortly, in favor of custom easing curves to determine size over life.
649 \qmlproperty url QtQuick.Particles2::ImageParticle::opacityTable
651 An image whose opacity will be used as a 1D texture to determine size over life.
653 This property is expected to be removed shortly, in favor of custom easing curves to determine opacity over life.
656 \qmlproperty color QtQuick.Particles2::ImageParticle::color
658 If a color is specified, the provided image will be colorized with it.
660 Default is white (no change).
663 \qmlproperty real QtQuick.Particles2::ImageParticle::colorVariation
665 This number represents the color variation applied to individual particles.
666 Setting colorVariation is the same as setting redVariation, greenVariation,
667 and blueVariation to the same number.
669 Each channel can vary between particle by up to colorVariation from its usual color.
671 Color is measured, per channel, from 0.0 to 1.0.
676 \qmlproperty real QtQuick.Particles2::ImageParticle::redVariation
677 The variation in the red color channel between particles.
679 Color is measured, per channel, from 0.0 to 1.0.
684 \qmlproperty real QtQuick.Particles2::ImageParticle::greenVariation
685 The variation in the green color channel between particles.
687 Color is measured, per channel, from 0.0 to 1.0.
692 \qmlproperty real QtQuick.Particles2::ImageParticle::blueVariation
693 The variation in the blue color channel between particles.
695 Color is measured, per channel, from 0.0 to 1.0.
700 \qmlproperty real QtQuick.Particles2::ImageParticle::alpha
701 An alpha to be applied to the image. This value is multiplied by the value in
702 the image, and the value in the color property.
704 Particles have additive blending, so lower alpha on single particles leads
705 to stronger effects when multiple particles overlap.
707 Alpha is measured from 0.0 to 1.0.
712 \qmlproperty real QtQuick.Particles2::ImageParticle::alphaVariation
713 The variation in the alpha channel between particles.
715 Alpha is measured from 0.0 to 1.0.
720 \qmlproperty real QtQuick.Particles2::ImageParticle::rotation
722 If set the image will be rotated by this many degrees before it is drawn.
724 The particle coordinates are not transformed.
727 \qmlproperty real QtQuick.Particles2::ImageParticle::rotationVariation
729 If set the rotation of individual particles will vary by up to this much
734 \qmlproperty real QtQuick.Particles2::ImageParticle::rotationSpeed
736 If set particles will rotate at this speed in degrees/second.
739 \qmlproperty real QtQuick.Particles2::ImageParticle::rotationSpeedVariation
741 If set the rotationSpeed of individual particles will vary by up to this much
746 \qmlproperty bool QtQuick.Particles2::ImageParticle::autoRotation
748 If set to true then a rotation will be applied on top of the particles rotation, so
749 that it faces the direction of travel. So to face away from the direction of travel,
750 set autoRotation to true and rotation to 180.
755 \qmlproperty StochasticDirection QtQuick.Particles2::ImageParticle::xVector
757 Allows you to deform the particle image when drawn. The rectangular image will
758 be deformed so that the horizontal sides are in the shape of this vector instead
762 \qmlproperty StochasticDirection QtQuick.Particles2::ImageParticle::yVector
764 Allows you to deform the particle image when drawn. The rectangular image will
765 be deformed so that the vertical sides are in the shape of this vector instead
769 \qmlproperty EntryEffect QtQuick.Particles2::ImageParticle::entryEffect
771 This property provides basic and cheap entrance and exit effects for the particles.
772 For fine-grained control, see sizeTable and opacityTable.
774 Acceptable values are
776 \li ImageParticle.None: Particles just appear and disappear.
777 \li ImageParticle.Fade: Particles fade in from 0 opacity at the start of their life, and fade out to 0 at the end.
778 \li ImageParticle.Scale: Particles scale in from 0 size at the start of their life, and scale back to 0 at the end.
781 Default value is Fade.
784 \qmlproperty bool QtQuick.Particles2::ImageParticle::spritesInterpolate
786 If set to true, sprite particles will interpolate between sprite frames each rendered frame, making
787 the sprites look smoother.
793 \qmlproperty Status QtQuick.Particles2::ImageParticle::status
795 The status of loading the image.
799 QQuickImageParticle::QQuickImageParticle(QQuickItem* parent)
800 : QQuickParticlePainter(parent)
805 , m_color_variation(0.0)
808 , m_alphaVariation(0.0)
810 , m_redVariation(0.0)
811 , m_greenVariation(0.0)
812 , m_blueVariation(0.0)
814 , m_rotationVariation(0)
816 , m_rotationSpeedVariation(0)
817 , m_autoRotation(false)
821 , m_spritesInterpolate(true)
822 , m_explicitColor(false)
823 , m_explicitRotation(false)
824 , m_explicitDeformation(false)
825 , m_explicitAnimation(false)
826 , m_bypassOptimizations(false)
828 , m_lastLevel(Unknown)
830 , m_entryEffect(Fade)
831 , m_buildingNodes(false)
833 setFlag(ItemHasContents);
836 QQuickImageParticle::~QQuickImageParticle()
840 QQmlListProperty<QQuickSprite> QQuickImageParticle::sprites()
842 return QQmlListProperty<QQuickSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
845 void QQuickImageParticle::sceneGraphInvalidated()
852 void QQuickImageParticle::setImage(const QUrl &image)
854 if (image.isEmpty()){
863 m_image = new ImageData;
864 if (image == m_image->source)
866 m_image->source = image;
872 void QQuickImageParticle::setColortable(const QUrl &table)
874 if (table.isEmpty()){
877 emit colortableChanged();
883 m_colorTable = new ImageData;
884 if (table == m_colorTable->source)
886 m_colorTable->source = table;
887 emit colortableChanged();
891 void QQuickImageParticle::setSizetable(const QUrl &table)
893 if (table.isEmpty()){
896 emit sizetableChanged();
902 m_sizeTable = new ImageData;
903 if (table == m_sizeTable->source)
905 m_sizeTable->source = table;
906 emit sizetableChanged();
910 void QQuickImageParticle::setOpacitytable(const QUrl &table)
912 if (table.isEmpty()){
913 if (m_opacityTable) {
914 delete m_opacityTable;
915 emit opacitytableChanged();
921 m_opacityTable = new ImageData;
922 if (table == m_opacityTable->source)
924 m_opacityTable->source = table;
925 emit opacitytableChanged();
929 void QQuickImageParticle::setColor(const QColor &color)
931 if (color == m_color)
935 m_explicitColor = true;
936 if (perfLevel < Colored)
940 void QQuickImageParticle::setColorVariation(qreal var)
942 if (var == m_color_variation)
944 m_color_variation = var;
945 emit colorVariationChanged();
946 m_explicitColor = true;
947 if (perfLevel < Colored)
951 void QQuickImageParticle::setAlphaVariation(qreal arg)
953 if (m_alphaVariation != arg) {
954 m_alphaVariation = arg;
955 emit alphaVariationChanged(arg);
957 m_explicitColor = true;
958 if (perfLevel < Colored)
962 void QQuickImageParticle::setAlpha(qreal arg)
964 if (m_alpha != arg) {
966 emit alphaChanged(arg);
968 m_explicitColor = true;
969 if (perfLevel < Colored)
973 void QQuickImageParticle::setRedVariation(qreal arg)
975 if (m_redVariation != arg) {
976 m_redVariation = arg;
977 emit redVariationChanged(arg);
979 m_explicitColor = true;
980 if (perfLevel < Colored)
984 void QQuickImageParticle::setGreenVariation(qreal arg)
986 if (m_greenVariation != arg) {
987 m_greenVariation = arg;
988 emit greenVariationChanged(arg);
990 m_explicitColor = true;
991 if (perfLevel < Colored)
995 void QQuickImageParticle::setBlueVariation(qreal arg)
997 if (m_blueVariation != arg) {
998 m_blueVariation = arg;
999 emit blueVariationChanged(arg);
1001 m_explicitColor = true;
1002 if (perfLevel < Colored)
1006 void QQuickImageParticle::setRotation(qreal arg)
1008 if (m_rotation != arg) {
1010 emit rotationChanged(arg);
1012 m_explicitRotation = true;
1013 if (perfLevel < Deformable)
1017 void QQuickImageParticle::setRotationVariation(qreal arg)
1019 if (m_rotationVariation != arg) {
1020 m_rotationVariation = arg;
1021 emit rotationVariationChanged(arg);
1023 m_explicitRotation = true;
1024 if (perfLevel < Deformable)
1028 void QQuickImageParticle::setRotationSpeed(qreal arg)
1030 if (m_rotationSpeed != arg) {
1031 m_rotationSpeed = arg;
1032 emit rotationSpeedChanged(arg);
1034 m_explicitRotation = true;
1035 if (perfLevel < Deformable)
1039 void QQuickImageParticle::setRotationSpeedVariation(qreal arg)
1041 if (m_rotationSpeedVariation != arg) {
1042 m_rotationSpeedVariation = arg;
1043 emit rotationSpeedVariationChanged(arg);
1045 m_explicitRotation = true;
1046 if (perfLevel < Deformable)
1050 void QQuickImageParticle::setAutoRotation(bool arg)
1052 if (m_autoRotation != arg) {
1053 m_autoRotation = arg;
1054 emit autoRotationChanged(arg);
1056 m_explicitRotation = true;
1057 if (perfLevel < Deformable)
1061 void QQuickImageParticle::setXVector(QQuickDirection* arg)
1063 if (m_xVector != arg) {
1065 emit xVectorChanged(arg);
1067 m_explicitDeformation = true;
1068 if (perfLevel < Deformable)
1072 void QQuickImageParticle::setYVector(QQuickDirection* arg)
1074 if (m_yVector != arg) {
1076 emit yVectorChanged(arg);
1078 m_explicitDeformation = true;
1079 if (perfLevel < Deformable)
1083 void QQuickImageParticle::setSpritesInterpolate(bool arg)
1085 if (m_spritesInterpolate != arg) {
1086 m_spritesInterpolate = arg;
1087 emit spritesInterpolateChanged(arg);
1091 void QQuickImageParticle::setBypassOptimizations(bool arg)
1093 if (m_bypassOptimizations != arg) {
1094 m_bypassOptimizations = arg;
1095 emit bypassOptimizationsChanged(arg);
1097 if (perfLevel < 9999)
1101 void QQuickImageParticle::setEntryEffect(EntryEffect arg)
1103 if (m_entryEffect != arg) {
1104 m_entryEffect = arg;
1106 getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect;
1107 emit entryEffectChanged(arg);
1111 void QQuickImageParticle::resetColor()
1113 m_explicitColor = false;
1114 foreach (const QString &str, m_groups)
1115 foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
1116 if (d->colorOwner == this)
1119 m_color_variation = 0.0f;
1120 m_redVariation = 0.0f;
1121 m_blueVariation = 0.0f;
1122 m_greenVariation = 0.0f;
1124 m_alphaVariation = 0.0f;
1127 void QQuickImageParticle::resetRotation()
1129 m_explicitRotation = false;
1130 foreach (const QString &str, m_groups)
1131 foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
1132 if (d->rotationOwner == this)
1133 d->rotationOwner = 0;
1135 m_rotationVariation = 0;
1136 m_rotationSpeed = 0;
1137 m_rotationSpeedVariation = 0;
1138 m_autoRotation = false;
1141 void QQuickImageParticle::resetDeformation()
1143 m_explicitDeformation = false;
1144 foreach (const QString &str, m_groups)
1145 foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
1146 if (d->deformationOwner == this)
1147 d->deformationOwner = 0;
1156 void QQuickImageParticle::reset()
1158 QQuickParticlePainter::reset();
1159 m_pleaseReset = true;
1163 void QQuickImageParticle::createEngine()
1166 delete m_spriteEngine;
1167 if (m_sprites.count()) {
1168 m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
1169 connect(m_spriteEngine, SIGNAL(stateChanged(int)),
1170 this, SLOT(spriteAdvance(int)), Qt::DirectConnection);
1171 m_explicitAnimation = true;
1174 m_explicitAnimation = false;
1179 static QSGGeometry::Attribute SimpleParticle_Attributes[] = {
1180 QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // Position
1181 QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
1182 QSGGeometry::Attribute::create(2, 4, GL_FLOAT) // Vectors
1185 static QSGGeometry::AttributeSet SimpleParticle_AttributeSet =
1187 3, // Attribute Count
1188 ( 2 + 4 + 4 ) * sizeof(float),
1189 SimpleParticle_Attributes
1192 static QSGGeometry::Attribute ColoredParticle_Attributes[] = {
1193 QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // Position
1194 QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
1195 QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Vectors
1196 QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors
1199 static QSGGeometry::AttributeSet ColoredParticle_AttributeSet =
1201 4, // Attribute Count
1202 ( 2 + 4 + 4 ) * sizeof(float) + 4 * sizeof(uchar),
1203 ColoredParticle_Attributes
1206 static QSGGeometry::Attribute DeformableParticle_Attributes[] = {
1207 QSGGeometry::Attribute::create(0, 4, GL_FLOAT), // Position & TexCoord
1208 QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
1209 QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Vectors
1210 QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors
1211 QSGGeometry::Attribute::create(4, 4, GL_FLOAT), // DeformationVectors
1212 QSGGeometry::Attribute::create(5, 3, GL_FLOAT), // Rotation
1215 static QSGGeometry::AttributeSet DeformableParticle_AttributeSet =
1217 6, // Attribute Count
1218 (4 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar),
1219 DeformableParticle_Attributes
1222 static QSGGeometry::Attribute SpriteParticle_Attributes[] = {
1223 QSGGeometry::Attribute::create(0, 4, GL_FLOAT), // Position & TexCoord
1224 QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
1225 QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Vectors
1226 QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors
1227 QSGGeometry::Attribute::create(4, 4, GL_FLOAT), // DeformationVectors
1228 QSGGeometry::Attribute::create(5, 3, GL_FLOAT), // Rotation
1229 QSGGeometry::Attribute::create(6, 3, GL_FLOAT), // Anim Data
1230 QSGGeometry::Attribute::create(7, 4, GL_FLOAT) // Anim Pos
1233 static QSGGeometry::AttributeSet SpriteParticle_AttributeSet =
1235 8, // Attribute Count
1236 (4 + 4 + 4 + 4 + 3 + 3 + 4) * sizeof(float) + 4 * sizeof(uchar),
1237 SpriteParticle_Attributes
1240 void QQuickImageParticle::clearShadows()
1242 foreach (const QVector<QQuickParticleData*> data, m_shadowData)
1244 m_shadowData.clear();
1247 //Only call if you need to, may initialize the whole array first time
1248 QQuickParticleData* QQuickImageParticle::getShadowDatum(QQuickParticleData* datum)
1250 //Will return datum if the datum is a sentinel or uninitialized, to centralize that one check
1251 if (datum->systemIndex == -1)
1253 QQuickParticleGroupData* gd = m_system->groupData[datum->group];
1254 if (!m_shadowData.contains(datum->group)) {
1255 QVector<QQuickParticleData*> data;
1256 for (int i=0; i<gd->size(); i++){
1257 QQuickParticleData* datum = new QQuickParticleData(m_system);
1258 *datum = *(gd->data[i]);
1261 m_shadowData.insert(datum->group, data);
1263 //### If dynamic resize is added, remember to potentially resize the shadow data on out-of-bounds access request
1265 return m_shadowData[datum->group][datum->index];
1268 bool QQuickImageParticle::loadingSomething()
1270 return (m_image && m_image->pix.isLoading())
1271 || (m_colorTable && m_colorTable->pix.isLoading())
1272 || (m_sizeTable && m_sizeTable->pix.isLoading())
1273 || (m_opacityTable && m_opacityTable->pix.isLoading())
1274 || (m_spriteEngine && m_spriteEngine->isLoading());
1277 void QQuickImageParticle::buildParticleNodes()//Starts async parts, like loading images.
1279 if (m_rootNode || loadingSomething())
1282 if (!m_buildingNodes) {
1283 if (m_image) {//ImageData created on setSource
1284 m_image->pix.clear(this);
1285 m_image->pix.load(qmlEngine(this), m_image->source);
1289 m_spriteEngine->startAssemblingImage();
1292 m_colorTable->pix.load(qmlEngine(this), m_colorTable->source);
1295 m_sizeTable->pix.load(qmlEngine(this), m_sizeTable->source);
1298 m_opacityTable->pix.load(qmlEngine(this), m_opacityTable->source);
1300 m_buildingNodes = true;
1301 if (loadingSomething())
1304 finishBuildParticleNodes();
1307 void QQuickImageParticle::finishBuildParticleNodes()
1309 m_buildingNodes = false;
1310 #ifdef QT_OPENGL_ES_2
1311 if (m_count * 4 > 0xffff) {
1312 printf("ImageParticle: Too many particles - maximum 16,000 per ImageParticle.\n");//ES 2 vertex count limit is ushort
1320 m_debugMode = m_system->m_debugMode;
1322 if (m_sprites.count() || m_bypassOptimizations) {
1323 perfLevel = Sprites;
1324 } else if (m_colorTable || m_sizeTable || m_opacityTable) {
1326 } else if (m_autoRotation || m_rotation || m_rotationVariation
1327 || m_rotationSpeed || m_rotationSpeedVariation
1328 || m_xVector || m_yVector) {
1329 perfLevel = Deformable;
1330 } else if (m_alphaVariation || m_alpha != 1.0 || m_color.isValid() || m_color_variation
1331 || m_redVariation || m_blueVariation || m_greenVariation) {
1332 perfLevel = Colored;
1337 foreach (const QString &str, m_groups){//For sharing higher levels, need to have highest used so it renders
1338 int gIdx = m_system->groupIds[str];
1339 foreach (QQuickParticlePainter* p, m_system->groupData[gIdx]->painters){
1340 QQuickImageParticle* other = qobject_cast<QQuickImageParticle*>(p);
1342 if (other->perfLevel > perfLevel) {
1343 if (other->perfLevel >= Tabled){//Deformable is the highest level needed for this, anything higher isn't shared (or requires your own sprite)
1344 if (perfLevel < Deformable)
1345 perfLevel = Deformable;
1347 perfLevel = other->perfLevel;
1349 } else if (other->perfLevel < perfLevel) {
1356 if (perfLevel < Deformable) //QTBUG-24540 , point sprite 'extension' isn't working on windows.
1357 perfLevel = Deformable;
1360 if (perfLevel >= Colored && !m_color.isValid())
1361 m_color = QColor(Qt::white);//Hidden default, but different from unset
1370 QImage opacitytable;
1372 bool imageLoaded = false;
1373 switch (perfLevel) {//Fallthrough intended
1375 if (!m_spriteEngine) {
1376 qWarning() << "ImageParticle: No sprite engine...";
1377 //Sprite performance mode with static image is supported, but not advised
1378 //Note that in this case it always uses shadow data
1380 image = m_spriteEngine->assembledImage();
1381 if (image.isNull())//Warning is printed in engine
1385 m_material = SpriteMaterial::createMaterial();
1387 getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(image);
1388 getState<ImageMaterialData>(m_material)->animSheetSize = QSizeF(image.size());
1390 m_spriteEngine->setCount(m_count);
1393 m_material = TabledMaterial::createMaterial();
1396 if (m_colorTable->pix.isReady())
1397 colortable = m_colorTable->pix.image();
1399 qmlInfo(this) << "Error loading color table: " << m_colorTable->pix.error();
1403 if (m_sizeTable->pix.isReady())
1404 sizetable = m_sizeTable->pix.image();
1406 qmlInfo(this) << "Error loading size table: " << m_sizeTable->pix.error();
1409 if (m_opacityTable) {
1410 if (m_opacityTable->pix.isReady())
1411 opacitytable = m_opacityTable->pix.image();
1413 qmlInfo(this) << "Error loading opacity table: " << m_opacityTable->pix.error();
1416 if (colortable.isNull()){//###Goes through image just for this
1417 colortable = QImage(1,1,QImage::Format_ARGB32_Premultiplied);
1418 colortable.fill(Qt::white);
1420 getState<ImageMaterialData>(m_material)->colorTable = QSGPlainTexture::fromImage(colortable);
1421 fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->sizeTable, sizetable, UNIFORM_ARRAY_SIZE);
1422 fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->opacityTable, opacitytable, UNIFORM_ARRAY_SIZE);
1425 m_material = DeformableMaterial::createMaterial();
1428 m_material = ColoredMaterial::createMaterial();
1429 default://Also Simple
1431 m_material = SimpleMaterial::createMaterial();
1433 if (!m_image->pix.isReady()) {
1434 qmlInfo(this) << m_image->pix.error();
1438 //getState<ImageMaterialData>(m_material)->texture //TODO: Shouldn't this be better? But not crash?
1439 // = QQuickItemPrivate::get(this)->sceneGraphContext()->textureForFactory(m_imagePix.textureFactory());
1440 getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(m_image->pix.image());
1442 getState<ImageMaterialData>(m_material)->texture->setFiltering(QSGTexture::Linear);
1443 getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect;
1444 m_material->setFlag(QSGMaterial::Blending | QSGMaterial::RequiresFullMatrix);
1448 foreach (const QString &str, m_groups){
1449 int gIdx = m_system->groupIds[str];
1450 int count = m_system->groupData[gIdx]->size();
1451 QSGGeometryNode* node = new QSGGeometryNode();
1452 node->setMaterial(m_material);
1453 node->markDirty(QSGNode::DirtyMaterial);
1455 m_nodes.insert(gIdx, node);
1456 m_idxStarts.insert(gIdx, m_lastIdxStart);
1457 m_startsIdx.append(qMakePair<int,int>(m_lastIdxStart, gIdx));
1458 m_lastIdxStart += count;
1460 //Create Particle Geometry
1461 int vCount = count * 4;
1462 int iCount = count * 6;
1465 if (perfLevel == Sprites)
1466 g = new QSGGeometry(SpriteParticle_AttributeSet, vCount, iCount);
1467 else if (perfLevel == Tabled)
1468 g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
1469 else if (perfLevel == Deformable)
1470 g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
1471 else if (perfLevel == Colored)
1472 g = new QSGGeometry(ColoredParticle_AttributeSet, count, 0);
1474 g = new QSGGeometry(SimpleParticle_AttributeSet, count, 0);
1476 node->setGeometry(g);
1477 if (perfLevel <= Colored){
1478 g->setDrawingMode(GL_POINTS);
1480 GLfloat pointSizeRange[2];
1481 glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
1482 qDebug() << "Using point sprites, GL_ALIASED_POINT_SIZE_RANGE " <<pointSizeRange[0] << ":" << pointSizeRange[1];
1485 g->setDrawingMode(GL_TRIANGLES);
1487 for (int p=0; p < count; ++p)
1488 commit(gIdx, p);//commit sets geometry for the node, has its own perfLevel switch
1490 if (perfLevel == Sprites)
1491 initTexCoords<SpriteVertex>((SpriteVertex*)g->vertexData(), vCount);
1492 else if (perfLevel == Tabled)
1493 initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
1494 else if (perfLevel == Deformable)
1495 initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
1497 if (perfLevel > Colored){
1498 quint16 *indices = g->indexDataAsUShort();
1499 for (int i=0; i < count; ++i) {
1512 if (perfLevel == Sprites)
1513 spritesUpdate();//Gives all vertexes the initial sprite data, then maintained per frame
1515 foreach (QSGGeometryNode* node, m_nodes){
1516 if (node == *(m_nodes.begin()))
1517 node->setFlag(QSGGeometryNode::OwnsMaterial);//Root node owns the material for memory management purposes
1519 (*(m_nodes.begin()))->appendChildNode(node);
1522 m_rootNode = *(m_nodes.begin());
1526 QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
1529 m_lastLevel = perfLevel;
1531 delete m_rootNode;//Automatically deletes children, and SG manages material lifetime
1535 m_idxStarts.clear();
1536 m_startsIdx.clear();
1541 m_pleaseReset = false;
1542 m_buildingNodes = false;//Cancel a part-way build
1545 if (m_system && m_system->isRunning() && !m_system->isPaused()){
1549 foreach (QSGGeometryNode* node, m_nodes)
1550 node->markDirty(QSGNode::DirtyGeometry);
1551 } else if (m_buildingNodes) {
1552 update();//To call prepareNextFrame() again from the renderThread
1559 void QQuickImageParticle::prepareNextFrame()
1561 if (m_rootNode == 0){//TODO: Staggered loading (as emitted)
1562 buildParticleNodes();
1564 qDebug() << "QQuickImageParticle Feature level: " << perfLevel;
1565 qDebug() << "QQuickImageParticle Nodes: ";
1567 foreach (int i, m_nodes.keys()) {
1568 qDebug() << "Group " << i << " (" << m_system->groupData[i]->size() << " particles)";
1569 count += m_system->groupData[i]->size();
1571 qDebug() << "Total count: " << count;
1573 if (m_rootNode == 0)
1576 qint64 timeStamp = m_system->systemSync(this);
1578 qreal time = timeStamp / 1000.;
1580 switch (perfLevel){//Fall-through intended
1584 m_spriteEngine->updateSprites(timeStamp);//fires signals if anim changed
1585 spritesUpdate(time);
1590 default: //Also Simple
1591 getState<ImageMaterialData>(m_material)->timestamp = time;
1594 foreach (QSGGeometryNode* node, m_nodes)
1595 node->markDirty(QSGNode::DirtyMaterial);
1598 void QQuickImageParticle::spritesUpdate(qreal time)
1600 // Sprite progression handled CPU side, so as to have per-frame control.
1601 foreach (const QString &str, m_groups) {
1602 int gIdx = m_system->groupIds[str];
1603 foreach (QQuickParticleData* mainDatum, m_system->groupData[gIdx]->data) {
1604 QSGGeometryNode *node = m_nodes[gIdx];
1607 //TODO: Interpolate between two different animations if it's going to transition next frame
1608 // This is particularly important for cut-up sprites.
1609 QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum));
1611 for (int i = 0; i<m_startsIdx.count(); i++) {
1612 if (m_startsIdx[i].second == gIdx){
1613 spriteIdx = m_startsIdx[i].first + datum->index;
1621 if (datum->frameDuration > 0) {
1622 qreal frame = (time - datum->animT)/(datum->frameDuration / 1000.0);
1623 frame = qBound((qreal)0.0, frame, (qreal)((qreal)datum->frameCount - 1.0));//Stop at count-1 frames until we have between anim interpolation
1624 if (m_spritesInterpolate)
1625 progress = modf(frame,&frameAt);
1627 modf(frame,&frameAt);
1630 if (datum->frameAt >= datum->frameCount){
1632 m_spriteEngine->advance(spriteIdx);
1634 frameAt = datum->frameAt;
1636 if (m_spriteEngine->sprite(spriteIdx)->reverse())//### Store this in datum too?
1637 frameAt = (datum->frameCount - 1) - frameAt;
1638 QSizeF sheetSize = getState<ImageMaterialData>(m_material)->animSheetSize;
1639 qreal y = datum->animY / sheetSize.height();
1640 qreal w = datum->animWidth / sheetSize.width();
1641 qreal h = datum->animHeight / sheetSize.height();
1642 qreal x1 = datum->animX / sheetSize.width();
1645 if (frameAt < (datum->frameCount-1))
1648 node->setFlag(QSGNode::OwnsGeometry, false);
1649 SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
1650 spriteVertices += datum->index*4;
1651 for (int i=0; i<4; i++) {
1652 spriteVertices[i].animX1 = x1;
1653 spriteVertices[i].animY1 = y;
1654 spriteVertices[i].animX2 = x2;
1655 spriteVertices[i].animY2 = y;
1656 spriteVertices[i].animW = w;
1657 spriteVertices[i].animH = h;
1658 spriteVertices[i].animProgress = progress;
1660 node->setFlag(QSGNode::OwnsGeometry, true);
1665 void QQuickImageParticle::spriteAdvance(int spriteIdx)
1667 if (!m_startsIdx.count())//Probably overly defensive
1672 for (i = 0; i<m_startsIdx.count(); i++) {
1673 if (spriteIdx < m_startsIdx[i].first) {
1674 gIdx = m_startsIdx[i-1].second;
1679 gIdx = m_startsIdx[i-1].second;
1680 int pIdx = spriteIdx - m_startsIdx[i-1].first;
1682 QQuickParticleData* mainDatum = m_system->groupData[gIdx]->data[pIdx];
1683 QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum));
1685 datum->animIdx = m_spriteEngine->spriteState(spriteIdx);
1686 datum->animT = m_spriteEngine->spriteStart(spriteIdx)/1000.0;
1687 datum->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
1688 datum->frameDuration = m_spriteEngine->spriteDuration(spriteIdx) / datum->frameCount;
1689 datum->animX = m_spriteEngine->spriteX(spriteIdx);
1690 datum->animY = m_spriteEngine->spriteY(spriteIdx);
1691 datum->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
1692 datum->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
1695 void QQuickImageParticle::reloadColor(const Color4ub &c, QQuickParticleData* d)
1698 //TODO: get index for reload - or make function take an index
1701 void QQuickImageParticle::initialize(int gIdx, int pIdx)
1704 QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
1705 qreal redVariation = m_color_variation + m_redVariation;
1706 qreal greenVariation = m_color_variation + m_greenVariation;
1707 qreal blueVariation = m_color_variation + m_blueVariation;
1709 if (m_spriteEngine) {
1710 spriteIdx = m_idxStarts[gIdx] + datum->index;
1711 if (spriteIdx >= m_spriteEngine->count())
1712 m_spriteEngine->setCount(spriteIdx+1);
1716 float rotationSpeed;
1718 switch (perfLevel){//Fall-through is intended on all of them
1720 // Initial Sprite State
1721 if (m_explicitAnimation && m_spriteEngine){
1722 if (!datum->animationOwner)
1723 datum->animationOwner = this;
1724 QQuickParticleData* writeTo = (datum->animationOwner == this ? datum : getShadowDatum(datum));
1725 writeTo->animT = writeTo->t;
1726 //writeTo->animInterpolate = m_spritesInterpolate;
1727 if (m_spriteEngine){
1728 m_spriteEngine->start(spriteIdx);
1729 writeTo->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
1730 writeTo->frameDuration = m_spriteEngine->spriteDuration(spriteIdx) / writeTo->frameCount;
1731 writeTo->animIdx = 0;//Always starts at 0
1732 writeTo->frameAt = -1;
1733 writeTo->animX = m_spriteEngine->spriteX(spriteIdx);
1734 writeTo->animY = m_spriteEngine->spriteY(spriteIdx);
1735 writeTo->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
1736 writeTo->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
1739 QQuickParticleData* writeTo = getShadowDatum(datum);
1740 writeTo->animT = datum->t;
1741 writeTo->frameCount = 1;
1742 writeTo->frameDuration = 60000000.0;
1743 writeTo->frameAt = -1;
1744 writeTo->animIdx = 0;
1746 writeTo->animX = writeTo->animY = 0;
1747 writeTo->animWidth = getState<ImageMaterialData>(m_material)->animSheetSize.width();
1748 writeTo->animHeight = getState<ImageMaterialData>(m_material)->animSheetSize.height();
1753 if (m_explicitDeformation){
1754 if (!datum->deformationOwner)
1755 datum->deformationOwner = this;
1757 const QPointF &ret = m_xVector->sample(QPointF(datum->x, datum->y));
1758 if (datum->deformationOwner == this) {
1759 datum->xx = ret.x();
1760 datum->xy = ret.y();
1762 getShadowDatum(datum)->xx = ret.x();
1763 getShadowDatum(datum)->xy = ret.y();
1767 const QPointF &ret = m_yVector->sample(QPointF(datum->x, datum->y));
1768 if (datum->deformationOwner == this) {
1769 datum->yx = ret.x();
1770 datum->yy = ret.y();
1772 getShadowDatum(datum)->yx = ret.x();
1773 getShadowDatum(datum)->yy = ret.y();
1778 if (m_explicitRotation){
1779 if (!datum->rotationOwner)
1780 datum->rotationOwner = this;
1782 (m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV;
1784 (m_rotationSpeed + (m_rotationSpeedVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationSpeedVariation) ) * CONV;
1785 autoRotate = m_autoRotation?1.0:0.0;
1786 if (datum->rotationOwner == this) {
1787 datum->rotation = rotation;
1788 datum->rotationSpeed = rotationSpeed;
1789 datum->autoRotate = autoRotate;
1791 getShadowDatum(datum)->rotation = rotation;
1792 getShadowDatum(datum)->rotationSpeed = rotationSpeed;
1793 getShadowDatum(datum)->autoRotate = autoRotate;
1797 //Color initialization
1799 if (m_explicitColor) {
1800 if (!datum->colorOwner)
1801 datum->colorOwner = this;
1802 color.r = m_color.red() * (1 - redVariation) + rand() % 256 * redVariation;
1803 color.g = m_color.green() * (1 - greenVariation) + rand() % 256 * greenVariation;
1804 color.b = m_color.blue() * (1 - blueVariation) + rand() % 256 * blueVariation;
1805 color.a = m_alpha * m_color.alpha() * (1 - m_alphaVariation) + rand() % 256 * m_alphaVariation;
1806 if (datum->colorOwner == this)
1807 datum->color = color;
1809 getShadowDatum(datum)->color = color;
1816 void QQuickImageParticle::commit(int gIdx, int pIdx)
1820 QSGGeometryNode *node = m_nodes[gIdx];
1823 QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
1824 node->setFlag(QSGNode::OwnsGeometry, false);
1825 SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
1826 DeformableVertex *deformableVertices = (DeformableVertex *) node->geometry()->vertexData();
1827 ColoredVertex *coloredVertices = (ColoredVertex *) node->geometry()->vertexData();
1828 SimpleVertex *simpleVertices = (SimpleVertex *) node->geometry()->vertexData();
1829 switch (perfLevel){//No automatic fall through intended on this one
1831 spriteVertices += pIdx*4;
1832 for (int i=0; i<4; i++){
1833 spriteVertices[i].x = datum->x - m_systemOffset.x();
1834 spriteVertices[i].y = datum->y - m_systemOffset.y();
1835 spriteVertices[i].t = datum->t;
1836 spriteVertices[i].lifeSpan = datum->lifeSpan;
1837 spriteVertices[i].size = datum->size;
1838 spriteVertices[i].endSize = datum->endSize;
1839 spriteVertices[i].vx = datum->vx;
1840 spriteVertices[i].vy = datum->vy;
1841 spriteVertices[i].ax = datum->ax;
1842 spriteVertices[i].ay = datum->ay;
1843 if (m_explicitDeformation && datum->deformationOwner != this) {
1844 QQuickParticleData* shadow = getShadowDatum(datum);
1845 spriteVertices[i].xx = shadow->xx;
1846 spriteVertices[i].xy = shadow->xy;
1847 spriteVertices[i].yx = shadow->yx;
1848 spriteVertices[i].yy = shadow->yy;
1850 spriteVertices[i].xx = datum->xx;
1851 spriteVertices[i].xy = datum->xy;
1852 spriteVertices[i].yx = datum->yx;
1853 spriteVertices[i].yy = datum->yy;
1855 if (m_explicitRotation && datum->rotationOwner != this) {
1856 QQuickParticleData* shadow = getShadowDatum(datum);
1857 spriteVertices[i].rotation = shadow->rotation;
1858 spriteVertices[i].rotationSpeed = shadow->rotationSpeed;
1859 spriteVertices[i].autoRotate = shadow->autoRotate;
1861 spriteVertices[i].rotation = datum->rotation;
1862 spriteVertices[i].rotationSpeed = datum->rotationSpeed;
1863 spriteVertices[i].autoRotate = datum->autoRotate;
1865 //Sprite-related vertices updated per-frame in spritesUpdate(), not on demand
1866 if (m_explicitColor && datum->colorOwner != this) {
1867 QQuickParticleData* shadow = getShadowDatum(datum);
1868 spriteVertices[i].color.r = shadow->color.r;
1869 spriteVertices[i].color.g = shadow->color.g;
1870 spriteVertices[i].color.b = shadow->color.b;
1871 spriteVertices[i].color.a = shadow->color.a;
1873 spriteVertices[i].color.r = datum->color.r;
1874 spriteVertices[i].color.g = datum->color.g;
1875 spriteVertices[i].color.b = datum->color.b;
1876 spriteVertices[i].color.a = datum->color.a;
1880 case Tabled: //Fall through until it has its own vertex class
1882 deformableVertices += pIdx*4;
1883 for (int i=0; i<4; i++){
1884 deformableVertices[i].x = datum->x - m_systemOffset.x();
1885 deformableVertices[i].y = datum->y - m_systemOffset.y();
1886 deformableVertices[i].t = datum->t;
1887 deformableVertices[i].lifeSpan = datum->lifeSpan;
1888 deformableVertices[i].size = datum->size;
1889 deformableVertices[i].endSize = datum->endSize;
1890 deformableVertices[i].vx = datum->vx;
1891 deformableVertices[i].vy = datum->vy;
1892 deformableVertices[i].ax = datum->ax;
1893 deformableVertices[i].ay = datum->ay;
1894 if (m_explicitDeformation && datum->deformationOwner != this) {
1895 QQuickParticleData* shadow = getShadowDatum(datum);
1896 deformableVertices[i].xx = shadow->xx;
1897 deformableVertices[i].xy = shadow->xy;
1898 deformableVertices[i].yx = shadow->yx;
1899 deformableVertices[i].yy = shadow->yy;
1901 deformableVertices[i].xx = datum->xx;
1902 deformableVertices[i].xy = datum->xy;
1903 deformableVertices[i].yx = datum->yx;
1904 deformableVertices[i].yy = datum->yy;
1906 if (m_explicitRotation && datum->rotationOwner != this) {
1907 QQuickParticleData* shadow = getShadowDatum(datum);
1908 deformableVertices[i].rotation = shadow->rotation;
1909 deformableVertices[i].rotationSpeed = shadow->rotationSpeed;
1910 deformableVertices[i].autoRotate = shadow->autoRotate;
1912 deformableVertices[i].rotation = datum->rotation;
1913 deformableVertices[i].rotationSpeed = datum->rotationSpeed;
1914 deformableVertices[i].autoRotate = datum->autoRotate;
1916 if (m_explicitColor && datum->colorOwner != this) {
1917 QQuickParticleData* shadow = getShadowDatum(datum);
1918 deformableVertices[i].color.r = shadow->color.r;
1919 deformableVertices[i].color.g = shadow->color.g;
1920 deformableVertices[i].color.b = shadow->color.b;
1921 deformableVertices[i].color.a = shadow->color.a;
1923 deformableVertices[i].color.r = datum->color.r;
1924 deformableVertices[i].color.g = datum->color.g;
1925 deformableVertices[i].color.b = datum->color.b;
1926 deformableVertices[i].color.a = datum->color.a;
1931 coloredVertices += pIdx*1;
1932 for (int i=0; i<1; i++){
1933 coloredVertices[i].x = datum->x - m_systemOffset.x();
1934 coloredVertices[i].y = datum->y - m_systemOffset.y();
1935 coloredVertices[i].t = datum->t;
1936 coloredVertices[i].lifeSpan = datum->lifeSpan;
1937 coloredVertices[i].size = datum->size;
1938 coloredVertices[i].endSize = datum->endSize;
1939 coloredVertices[i].vx = datum->vx;
1940 coloredVertices[i].vy = datum->vy;
1941 coloredVertices[i].ax = datum->ax;
1942 coloredVertices[i].ay = datum->ay;
1943 if (m_explicitColor && datum->colorOwner != this) {
1944 QQuickParticleData* shadow = getShadowDatum(datum);
1945 coloredVertices[i].color.r = shadow->color.r;
1946 coloredVertices[i].color.g = shadow->color.g;
1947 coloredVertices[i].color.b = shadow->color.b;
1948 coloredVertices[i].color.a = shadow->color.a;
1950 coloredVertices[i].color.r = datum->color.r;
1951 coloredVertices[i].color.g = datum->color.g;
1952 coloredVertices[i].color.b = datum->color.b;
1953 coloredVertices[i].color.a = datum->color.a;
1958 simpleVertices += pIdx*1;
1959 for (int i=0; i<1; i++){
1960 simpleVertices[i].x = datum->x - m_systemOffset.x();
1961 simpleVertices[i].y = datum->y - m_systemOffset.y();
1962 simpleVertices[i].t = datum->t;
1963 simpleVertices[i].lifeSpan = datum->lifeSpan;
1964 simpleVertices[i].size = datum->size;
1965 simpleVertices[i].endSize = datum->endSize;
1966 simpleVertices[i].vx = datum->vx;
1967 simpleVertices[i].vy = datum->vy;
1968 simpleVertices[i].ax = datum->ax;
1969 simpleVertices[i].ay = datum->ay;
1976 node->setFlag(QSGNode::OwnsGeometry, true);