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 Declarative 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/qdeclarativeglobal_p.h>
57 #include <QtDeclarative/qdeclarativeinfo.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 \o 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 \o ImageParticle.None: Particles just appear and disappear.
774 \o ImageParticle.Fade: Particles fade in from 0 opacity at the start of their life, and fade out to 0 at the end.
775 \o 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 QDeclarativeListProperty<QQuickSprite> QQuickImageParticle::sprites()
839 return QDeclarativeListProperty<QQuickSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
842 void QQuickImageParticle::setImage(const QUrl &image)
844 if (image.isEmpty()){
853 m_image = new ImageData;
854 if (image == m_image->source)
856 m_image->source = image;
862 void QQuickImageParticle::setColortable(const QUrl &table)
864 if (table.isEmpty()){
867 emit colortableChanged();
873 m_colorTable = new ImageData;
874 if (table == m_colorTable->source)
876 m_colorTable->source = table;
877 emit colortableChanged();
881 void QQuickImageParticle::setSizetable(const QUrl &table)
883 if (table.isEmpty()){
886 emit sizetableChanged();
892 m_sizeTable = new ImageData;
893 if (table == m_sizeTable->source)
895 m_sizeTable->source = table;
896 emit sizetableChanged();
900 void QQuickImageParticle::setOpacitytable(const QUrl &table)
902 if (table.isEmpty()){
903 if (m_opacityTable) {
904 delete m_opacityTable;
905 emit opacitytableChanged();
911 m_opacityTable = new ImageData;
912 if (table == m_opacityTable->source)
914 m_opacityTable->source = table;
915 emit opacitytableChanged();
919 void QQuickImageParticle::setColor(const QColor &color)
921 if (color == m_color)
925 m_explicitColor = true;
926 if (perfLevel < Colored)
930 void QQuickImageParticle::setColorVariation(qreal var)
932 if (var == m_color_variation)
934 m_color_variation = var;
935 emit colorVariationChanged();
936 m_explicitColor = true;
937 if (perfLevel < Colored)
941 void QQuickImageParticle::setAlphaVariation(qreal arg)
943 if (m_alphaVariation != arg) {
944 m_alphaVariation = arg;
945 emit alphaVariationChanged(arg);
947 m_explicitColor = true;
948 if (perfLevel < Colored)
952 void QQuickImageParticle::setAlpha(qreal arg)
954 if (m_alpha != arg) {
956 emit alphaChanged(arg);
958 m_explicitColor = true;
959 if (perfLevel < Colored)
963 void QQuickImageParticle::setRedVariation(qreal arg)
965 if (m_redVariation != arg) {
966 m_redVariation = arg;
967 emit redVariationChanged(arg);
969 m_explicitColor = true;
970 if (perfLevel < Colored)
974 void QQuickImageParticle::setGreenVariation(qreal arg)
976 if (m_greenVariation != arg) {
977 m_greenVariation = arg;
978 emit greenVariationChanged(arg);
980 m_explicitColor = true;
981 if (perfLevel < Colored)
985 void QQuickImageParticle::setBlueVariation(qreal arg)
987 if (m_blueVariation != arg) {
988 m_blueVariation = arg;
989 emit blueVariationChanged(arg);
991 m_explicitColor = true;
992 if (perfLevel < Colored)
996 void QQuickImageParticle::setRotation(qreal arg)
998 if (m_rotation != arg) {
1000 emit rotationChanged(arg);
1002 m_explicitRotation = true;
1003 if (perfLevel < Deformable)
1007 void QQuickImageParticle::setRotationVariation(qreal arg)
1009 if (m_rotationVariation != arg) {
1010 m_rotationVariation = arg;
1011 emit rotationVariationChanged(arg);
1013 m_explicitRotation = true;
1014 if (perfLevel < Deformable)
1018 void QQuickImageParticle::setRotationSpeed(qreal arg)
1020 if (m_rotationSpeed != arg) {
1021 m_rotationSpeed = arg;
1022 emit rotationSpeedChanged(arg);
1024 m_explicitRotation = true;
1025 if (perfLevel < Deformable)
1029 void QQuickImageParticle::setRotationSpeedVariation(qreal arg)
1031 if (m_rotationSpeedVariation != arg) {
1032 m_rotationSpeedVariation = arg;
1033 emit rotationSpeedVariationChanged(arg);
1035 m_explicitRotation = true;
1036 if (perfLevel < Deformable)
1040 void QQuickImageParticle::setAutoRotation(bool arg)
1042 if (m_autoRotation != arg) {
1043 m_autoRotation = arg;
1044 emit autoRotationChanged(arg);
1046 m_explicitRotation = true;
1047 if (perfLevel < Deformable)
1051 void QQuickImageParticle::setXVector(QQuickDirection* arg)
1053 if (m_xVector != arg) {
1055 emit xVectorChanged(arg);
1057 m_explicitDeformation = true;
1058 if (perfLevel < Deformable)
1062 void QQuickImageParticle::setYVector(QQuickDirection* arg)
1064 if (m_yVector != arg) {
1066 emit yVectorChanged(arg);
1068 m_explicitDeformation = true;
1069 if (perfLevel < Deformable)
1073 void QQuickImageParticle::setSpritesInterpolate(bool arg)
1075 if (m_spritesInterpolate != arg) {
1076 m_spritesInterpolate = arg;
1077 emit spritesInterpolateChanged(arg);
1081 void QQuickImageParticle::setBypassOptimizations(bool arg)
1083 if (m_bypassOptimizations != arg) {
1084 m_bypassOptimizations = arg;
1085 emit bypassOptimizationsChanged(arg);
1087 if (perfLevel < 9999)
1091 void QQuickImageParticle::setEntryEffect(EntryEffect arg)
1093 if (m_entryEffect != arg) {
1094 m_entryEffect = arg;
1096 getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect;
1097 emit entryEffectChanged(arg);
1101 void QQuickImageParticle::resetColor()
1103 m_explicitColor = false;
1104 foreach (const QString &str, m_groups)
1105 foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
1106 if (d->colorOwner == this)
1109 m_color_variation = 0.0f;
1110 m_redVariation = 0.0f;
1111 m_blueVariation = 0.0f;
1112 m_greenVariation = 0.0f;
1114 m_alphaVariation = 0.0f;
1117 void QQuickImageParticle::resetRotation()
1119 m_explicitRotation = false;
1120 foreach (const QString &str, m_groups)
1121 foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
1122 if (d->rotationOwner == this)
1123 d->rotationOwner = 0;
1125 m_rotationVariation = 0;
1126 m_rotationSpeed = 0;
1127 m_rotationSpeedVariation = 0;
1128 m_autoRotation = false;
1131 void QQuickImageParticle::resetDeformation()
1133 m_explicitDeformation = false;
1134 foreach (const QString &str, m_groups)
1135 foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
1136 if (d->deformationOwner == this)
1137 d->deformationOwner = 0;
1146 void QQuickImageParticle::reset()
1148 QQuickParticlePainter::reset();
1149 m_pleaseReset = true;
1153 void QQuickImageParticle::createEngine()
1156 delete m_spriteEngine;
1157 if (m_sprites.count()) {
1158 m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
1159 connect(m_spriteEngine, SIGNAL(stateChanged(int)),
1160 this, SLOT(spriteAdvance(int)), Qt::DirectConnection);
1161 m_explicitAnimation = true;
1164 m_explicitAnimation = false;
1169 static QSGGeometry::Attribute SimpleParticle_Attributes[] = {
1170 QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // Position
1171 QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
1172 QSGGeometry::Attribute::create(2, 4, GL_FLOAT) // Vectors
1175 static QSGGeometry::AttributeSet SimpleParticle_AttributeSet =
1177 3, // Attribute Count
1178 ( 2 + 4 + 4 ) * sizeof(float),
1179 SimpleParticle_Attributes
1182 static QSGGeometry::Attribute ColoredParticle_Attributes[] = {
1183 QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // Position
1184 QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
1185 QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Vectors
1186 QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors
1189 static QSGGeometry::AttributeSet ColoredParticle_AttributeSet =
1191 4, // Attribute Count
1192 ( 2 + 4 + 4 ) * sizeof(float) + 4 * sizeof(uchar),
1193 ColoredParticle_Attributes
1196 static QSGGeometry::Attribute DeformableParticle_Attributes[] = {
1197 QSGGeometry::Attribute::create(0, 4, GL_FLOAT), // Position & TexCoord
1198 QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
1199 QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Vectors
1200 QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors
1201 QSGGeometry::Attribute::create(4, 4, GL_FLOAT), // DeformationVectors
1202 QSGGeometry::Attribute::create(5, 3, GL_FLOAT), // Rotation
1205 static QSGGeometry::AttributeSet DeformableParticle_AttributeSet =
1207 6, // Attribute Count
1208 (4 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar),
1209 DeformableParticle_Attributes
1212 static QSGGeometry::Attribute SpriteParticle_Attributes[] = {
1213 QSGGeometry::Attribute::create(0, 4, GL_FLOAT), // Position & TexCoord
1214 QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
1215 QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Vectors
1216 QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors
1217 QSGGeometry::Attribute::create(4, 4, GL_FLOAT), // DeformationVectors
1218 QSGGeometry::Attribute::create(5, 3, GL_FLOAT), // Rotation
1219 QSGGeometry::Attribute::create(6, 3, GL_FLOAT), // Anim Data
1220 QSGGeometry::Attribute::create(7, 4, GL_FLOAT) // Anim Pos
1223 static QSGGeometry::AttributeSet SpriteParticle_AttributeSet =
1225 8, // Attribute Count
1226 (4 + 4 + 4 + 4 + 3 + 3 + 4) * sizeof(float) + 4 * sizeof(uchar),
1227 SpriteParticle_Attributes
1230 void QQuickImageParticle::clearShadows()
1232 foreach (const QVector<QQuickParticleData*> data, m_shadowData)
1234 m_shadowData.clear();
1237 //Only call if you need to, may initialize the whole array first time
1238 QQuickParticleData* QQuickImageParticle::getShadowDatum(QQuickParticleData* datum)
1240 //Will return datum if the datum is a sentinel or uninitialized, to centralize that one check
1241 if (datum->systemIndex == -1)
1243 QQuickParticleGroupData* gd = m_system->groupData[datum->group];
1244 if (!m_shadowData.contains(datum->group)) {
1245 QVector<QQuickParticleData*> data;
1246 for (int i=0; i<gd->size(); i++){
1247 QQuickParticleData* datum = new QQuickParticleData(m_system);
1248 *datum = *(gd->data[i]);
1251 m_shadowData.insert(datum->group, data);
1253 //### If dynamic resize is added, remember to potentially resize the shadow data on out-of-bounds access request
1255 return m_shadowData[datum->group][datum->index];
1258 bool QQuickImageParticle::loadingSomething()
1260 return (m_image && m_image->pix.isLoading())
1261 || (m_colorTable && m_colorTable->pix.isLoading())
1262 || (m_sizeTable && m_sizeTable->pix.isLoading())
1263 || (m_opacityTable && m_opacityTable->pix.isLoading())
1264 || (m_spriteEngine && m_spriteEngine->isLoading());
1267 void QQuickImageParticle::buildParticleNodes()//Starts async parts, like loading images.
1269 if (m_rootNode || loadingSomething())
1272 if (!m_buildingNodes) {
1273 if (m_image) {//ImageData created on setSource
1274 m_image->pix.clear(this);
1275 m_image->pix.load(qmlEngine(this), m_image->source);
1279 m_spriteEngine->startAssemblingImage();
1282 m_colorTable->pix.load(qmlEngine(this), m_colorTable->source);
1285 m_sizeTable->pix.load(qmlEngine(this), m_sizeTable->source);
1288 m_opacityTable->pix.load(qmlEngine(this), m_opacityTable->source);
1290 m_buildingNodes = true;
1291 if (loadingSomething())
1294 finishBuildParticleNodes();
1297 void QQuickImageParticle::finishBuildParticleNodes()
1299 m_buildingNodes = false;
1300 #ifdef QT_OPENGL_ES_2
1301 if (m_count * 4 > 0xffff) {
1302 printf("ImageParticle: Too many particles - maximum 16,000 per ImageParticle.\n");//ES 2 vertex count limit is ushort
1310 m_debugMode = m_system->m_debugMode;
1312 if (m_sprites.count() || m_bypassOptimizations) {
1313 perfLevel = Sprites;
1314 } else if (m_colorTable || m_sizeTable || m_opacityTable) {
1316 } else if (m_autoRotation || m_rotation || m_rotationVariation
1317 || m_rotationSpeed || m_rotationSpeedVariation
1318 || m_xVector || m_yVector) {
1319 perfLevel = Deformable;
1320 } else if (m_alphaVariation || m_alpha != 1.0 || m_color.isValid() || m_color_variation
1321 || m_redVariation || m_blueVariation || m_greenVariation) {
1322 perfLevel = Colored;
1327 foreach (const QString &str, m_groups){//For sharing higher levels, need to have highest used so it renders
1328 int gIdx = m_system->groupIds[str];
1329 foreach (QQuickParticlePainter* p, m_system->groupData[gIdx]->painters){
1330 QQuickImageParticle* other = qobject_cast<QQuickImageParticle*>(p);
1332 if (other->perfLevel > perfLevel) {
1333 if (other->perfLevel >= Tabled){//Deformable is the highest level needed for this, anything higher isn't shared (or requires your own sprite)
1334 if (perfLevel < Deformable)
1335 perfLevel = Deformable;
1337 perfLevel = other->perfLevel;
1339 } else if (other->perfLevel < perfLevel) {
1346 if (perfLevel < Deformable) //QTBUG-24540 , point sprite 'extension' isn't working on windows.
1347 perfLevel = Deformable;
1350 if (perfLevel >= Colored && !m_color.isValid())
1351 m_color = QColor(Qt::white);//Hidden default, but different from unset
1360 QImage opacitytable;
1362 bool imageLoaded = false;
1363 switch (perfLevel) {//Fallthrough intended
1365 if (!m_spriteEngine) {
1366 qWarning() << "ImageParticle: No sprite engine...";
1367 //Sprite performance mode with static image is supported, but not advised
1368 //Note that in this case it always uses shadow data
1370 image = m_spriteEngine->assembledImage();
1371 if (image.isNull())//Warning is printed in engine
1375 m_material = SpriteMaterial::createMaterial();
1377 getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(image);
1378 getState<ImageMaterialData>(m_material)->animSheetSize = QSizeF(image.size());
1380 m_spriteEngine->setCount(m_count);
1383 m_material = TabledMaterial::createMaterial();
1386 if (m_colorTable->pix.isReady())
1387 colortable = m_colorTable->pix.image();
1389 qmlInfo(this) << "Error loading color table: " << m_colorTable->pix.error();
1393 if (m_sizeTable->pix.isReady())
1394 sizetable = m_sizeTable->pix.image();
1396 qmlInfo(this) << "Error loading size table: " << m_sizeTable->pix.error();
1399 if (m_opacityTable) {
1400 if (m_opacityTable->pix.isReady())
1401 opacitytable = m_opacityTable->pix.image();
1403 qmlInfo(this) << "Error loading opacity table: " << m_opacityTable->pix.error();
1406 if (colortable.isNull()){//###Goes through image just for this
1407 colortable = QImage(1,1,QImage::Format_ARGB32);
1408 colortable.fill(Qt::white);
1410 getState<ImageMaterialData>(m_material)->colorTable = QSGPlainTexture::fromImage(colortable);
1411 fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->sizeTable, sizetable, UNIFORM_ARRAY_SIZE);
1412 fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->opacityTable, opacitytable, UNIFORM_ARRAY_SIZE);
1415 m_material = DeformableMaterial::createMaterial();
1418 m_material = ColoredMaterial::createMaterial();
1419 default://Also Simple
1421 m_material = SimpleMaterial::createMaterial();
1423 if (!m_image->pix.isReady()) {
1424 qmlInfo(this) << m_image->pix.error();
1428 //getState<ImageMaterialData>(m_material)->texture //TODO: Shouldn't this be better? But not crash?
1429 // = QQuickItemPrivate::get(this)->sceneGraphContext()->textureForFactory(m_imagePix.textureFactory());
1430 getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(m_image->pix.image());
1432 getState<ImageMaterialData>(m_material)->texture->setFiltering(QSGTexture::Linear);
1433 getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect;
1434 m_material->setFlag(QSGMaterial::Blending);
1438 foreach (const QString &str, m_groups){
1439 int gIdx = m_system->groupIds[str];
1440 int count = m_system->groupData[gIdx]->size();
1441 QSGGeometryNode* node = new QSGGeometryNode();
1442 node->setMaterial(m_material);
1443 node->markDirty(QSGNode::DirtyMaterial);
1445 m_nodes.insert(gIdx, node);
1446 m_idxStarts.insert(gIdx, m_lastIdxStart);
1447 m_startsIdx.append(qMakePair<int,int>(m_lastIdxStart, gIdx));
1448 m_lastIdxStart += count;
1450 //Create Particle Geometry
1451 int vCount = count * 4;
1452 int iCount = count * 6;
1455 if (perfLevel == Sprites)
1456 g = new QSGGeometry(SpriteParticle_AttributeSet, vCount, iCount);
1457 else if (perfLevel == Tabled)
1458 g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
1459 else if (perfLevel == Deformable)
1460 g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
1461 else if (perfLevel == Colored)
1462 g = new QSGGeometry(ColoredParticle_AttributeSet, count, 0);
1464 g = new QSGGeometry(SimpleParticle_AttributeSet, count, 0);
1466 node->setGeometry(g);
1467 if (perfLevel <= Colored){
1468 g->setDrawingMode(GL_POINTS);
1470 GLfloat pointSizeRange[2];
1471 glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
1472 qDebug() << "Using point sprites, GL_ALIASED_POINT_SIZE_RANGE " <<pointSizeRange[0] << ":" << pointSizeRange[1];
1475 g->setDrawingMode(GL_TRIANGLES);
1477 for (int p=0; p < count; ++p)
1478 commit(gIdx, p);//commit sets geometry for the node, has its own perfLevel switch
1480 if (perfLevel == Sprites)
1481 initTexCoords<SpriteVertex>((SpriteVertex*)g->vertexData(), vCount);
1482 else if (perfLevel == Tabled)
1483 initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
1484 else if (perfLevel == Deformable)
1485 initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
1487 if (perfLevel > Colored){
1488 quint16 *indices = g->indexDataAsUShort();
1489 for (int i=0; i < count; ++i) {
1502 if (perfLevel == Sprites)
1503 spritesUpdate();//Gives all vertexes the initial sprite data, then maintained per frame
1505 foreach (QSGGeometryNode* node, m_nodes){
1506 if (node == *(m_nodes.begin()))
1507 node->setFlag(QSGGeometryNode::OwnsMaterial);//Root node owns the material for memory management purposes
1509 (*(m_nodes.begin()))->appendChildNode(node);
1512 m_rootNode = *(m_nodes.begin());
1516 QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
1519 m_lastLevel = perfLevel;
1521 delete m_rootNode;//Automatically deletes children, and SG manages material lifetime
1525 m_idxStarts.clear();
1526 m_startsIdx.clear();
1531 m_pleaseReset = false;
1532 m_buildingNodes = false;//Cancel a part-way build
1535 if (m_system && m_system->isRunning() && !m_system->isPaused()){
1539 foreach (QSGGeometryNode* node, m_nodes)
1540 node->markDirty(QSGNode::DirtyGeometry);
1541 } else if (m_buildingNodes) {
1542 update();//To call prepareNextFrame() again from the renderThread
1549 void QQuickImageParticle::prepareNextFrame()
1551 if (m_rootNode == 0){//TODO: Staggered loading (as emitted)
1552 buildParticleNodes();
1554 qDebug() << "QQuickImageParticle Feature level: " << perfLevel;
1555 qDebug() << "QQuickImageParticle Nodes: ";
1557 foreach (int i, m_nodes.keys()) {
1558 qDebug() << "Group " << i << " (" << m_system->groupData[i]->size() << " particles)";
1559 count += m_system->groupData[i]->size();
1561 qDebug() << "Total count: " << count;
1563 if (m_rootNode == 0)
1566 qint64 timeStamp = m_system->systemSync(this);
1568 qreal time = timeStamp / 1000.;
1570 switch (perfLevel){//Fall-through intended
1574 m_spriteEngine->updateSprites(timeStamp);//fires signals if anim changed
1575 spritesUpdate(time);
1580 default: //Also Simple
1581 getState<ImageMaterialData>(m_material)->timestamp = time;
1584 foreach (QSGGeometryNode* node, m_nodes)
1585 node->markDirty(QSGNode::DirtyMaterial);
1588 void QQuickImageParticle::spritesUpdate(qreal time)
1590 // Sprite progression handled CPU side, so as to have per-frame control.
1591 foreach (const QString &str, m_groups) {
1592 int gIdx = m_system->groupIds[str];
1593 foreach (QQuickParticleData* mainDatum, m_system->groupData[gIdx]->data) {
1594 QSGGeometryNode *node = m_nodes[gIdx];
1597 //TODO: Interpolate between two different animations if it's going to transition next frame
1598 // This is particularly important for cut-up sprites.
1599 QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum));
1601 for (int i = 0; i<m_startsIdx.count(); i++) {
1602 if (m_startsIdx[i].second == gIdx){
1603 spriteIdx = m_startsIdx[i].first + datum->index;
1611 if (datum->frameDuration > 0) {
1612 qreal frame = (time - datum->animT)/(datum->frameDuration / 1000.0);
1613 frame = qBound((qreal)0.0, frame, (qreal)((qreal)datum->frameCount - 1.0));//Stop at count-1 frames until we have between anim interpolation
1614 if (m_spritesInterpolate)
1615 progress = modf(frame,&frameAt);
1617 modf(frame,&frameAt);
1620 if (datum->frameAt >= datum->frameCount){
1622 m_spriteEngine->advance(spriteIdx);
1624 frameAt = datum->frameAt;
1626 if (m_spriteEngine->sprite(spriteIdx)->reverse())//### Store this in datum too?
1627 frameAt = (datum->frameCount - 1) - frameAt;
1628 QSizeF sheetSize = getState<ImageMaterialData>(m_material)->animSheetSize;
1629 qreal y = datum->animY / sheetSize.height();
1630 qreal w = datum->animWidth / sheetSize.width();
1631 qreal h = datum->animHeight / sheetSize.height();
1632 qreal x1 = datum->animX / sheetSize.width();
1635 if (frameAt < (datum->frameCount-1))
1638 node->setFlag(QSGNode::OwnsGeometry, false);
1639 SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
1640 spriteVertices += datum->index*4;
1641 for (int i=0; i<4; i++) {
1642 spriteVertices[i].animX1 = x1;
1643 spriteVertices[i].animY1 = y;
1644 spriteVertices[i].animX2 = x2;
1645 spriteVertices[i].animY2 = y;
1646 spriteVertices[i].animW = w;
1647 spriteVertices[i].animH = h;
1648 spriteVertices[i].animProgress = progress;
1650 node->setFlag(QSGNode::OwnsGeometry, true);
1655 void QQuickImageParticle::spriteAdvance(int spriteIdx)
1657 if (!m_startsIdx.count())//Probably overly defensive
1662 for (i = 0; i<m_startsIdx.count(); i++) {
1663 if (spriteIdx < m_startsIdx[i].first) {
1664 gIdx = m_startsIdx[i-1].second;
1669 gIdx = m_startsIdx[i-1].second;
1670 int pIdx = spriteIdx - m_startsIdx[i-1].first;
1672 QQuickParticleData* mainDatum = m_system->groupData[gIdx]->data[pIdx];
1673 QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum));
1675 datum->animIdx = m_spriteEngine->spriteState(spriteIdx);
1676 datum->animT = m_spriteEngine->spriteStart(spriteIdx)/1000.0;
1677 datum->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
1678 datum->frameDuration = m_spriteEngine->spriteDuration(spriteIdx) / datum->frameCount;
1679 datum->animX = m_spriteEngine->spriteX(spriteIdx);
1680 datum->animY = m_spriteEngine->spriteY(spriteIdx);
1681 datum->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
1682 datum->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
1685 void QQuickImageParticle::reloadColor(const Color4ub &c, QQuickParticleData* d)
1688 //TODO: get index for reload - or make function take an index
1691 void QQuickImageParticle::initialize(int gIdx, int pIdx)
1694 QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
1695 qreal redVariation = m_color_variation + m_redVariation;
1696 qreal greenVariation = m_color_variation + m_greenVariation;
1697 qreal blueVariation = m_color_variation + m_blueVariation;
1699 if (m_spriteEngine) {
1700 spriteIdx = m_idxStarts[gIdx] + datum->index;
1701 if (spriteIdx >= m_spriteEngine->count())
1702 m_spriteEngine->setCount(spriteIdx+1);
1706 float rotationSpeed;
1708 switch (perfLevel){//Fall-through is intended on all of them
1710 // Initial Sprite State
1711 if (m_explicitAnimation && m_spriteEngine){
1712 if (!datum->animationOwner)
1713 datum->animationOwner = this;
1714 QQuickParticleData* writeTo = (datum->animationOwner == this ? datum : getShadowDatum(datum));
1715 writeTo->animT = writeTo->t;
1716 //writeTo->animInterpolate = m_spritesInterpolate;
1717 if (m_spriteEngine){
1718 m_spriteEngine->start(spriteIdx);
1719 writeTo->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
1720 writeTo->frameDuration = m_spriteEngine->spriteDuration(spriteIdx) / writeTo->frameCount;
1721 writeTo->animIdx = 0;//Always starts at 0
1722 writeTo->frameAt = -1;
1723 writeTo->animX = m_spriteEngine->spriteX(spriteIdx);
1724 writeTo->animY = m_spriteEngine->spriteY(spriteIdx);
1725 writeTo->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
1726 writeTo->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
1729 QQuickParticleData* writeTo = getShadowDatum(datum);
1730 writeTo->animT = datum->t;
1731 writeTo->frameCount = 1;
1732 writeTo->frameDuration = 60000000.0;
1733 writeTo->frameAt = -1;
1734 writeTo->animIdx = 0;
1736 writeTo->animX = writeTo->animY = 0;
1737 writeTo->animWidth = getState<ImageMaterialData>(m_material)->animSheetSize.width();
1738 writeTo->animHeight = getState<ImageMaterialData>(m_material)->animSheetSize.height();
1743 if (m_explicitDeformation){
1744 if (!datum->deformationOwner)
1745 datum->deformationOwner = this;
1747 const QPointF &ret = m_xVector->sample(QPointF(datum->x, datum->y));
1748 if (datum->deformationOwner == this) {
1749 datum->xx = ret.x();
1750 datum->xy = ret.y();
1752 getShadowDatum(datum)->xx = ret.x();
1753 getShadowDatum(datum)->xy = ret.y();
1757 const QPointF &ret = m_yVector->sample(QPointF(datum->x, datum->y));
1758 if (datum->deformationOwner == this) {
1759 datum->yx = ret.x();
1760 datum->yy = ret.y();
1762 getShadowDatum(datum)->yx = ret.x();
1763 getShadowDatum(datum)->yy = ret.y();
1768 if (m_explicitRotation){
1769 if (!datum->rotationOwner)
1770 datum->rotationOwner = this;
1772 (m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV;
1774 (m_rotationSpeed + (m_rotationSpeedVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationSpeedVariation) ) * CONV;
1775 autoRotate = m_autoRotation?1.0:0.0;
1776 if (datum->rotationOwner == this) {
1777 datum->rotation = rotation;
1778 datum->rotationSpeed = rotationSpeed;
1779 datum->autoRotate = autoRotate;
1781 getShadowDatum(datum)->rotation = rotation;
1782 getShadowDatum(datum)->rotationSpeed = rotationSpeed;
1783 getShadowDatum(datum)->autoRotate = autoRotate;
1787 //Color initialization
1789 if (m_explicitColor) {
1790 if (!datum->colorOwner)
1791 datum->colorOwner = this;
1792 color.r = m_color.red() * (1 - redVariation) + rand() % 256 * redVariation;
1793 color.g = m_color.green() * (1 - greenVariation) + rand() % 256 * greenVariation;
1794 color.b = m_color.blue() * (1 - blueVariation) + rand() % 256 * blueVariation;
1795 color.a = m_alpha * m_color.alpha() * (1 - m_alphaVariation) + rand() % 256 * m_alphaVariation;
1796 if (datum->colorOwner == this)
1797 datum->color = color;
1799 getShadowDatum(datum)->color = color;
1806 void QQuickImageParticle::commit(int gIdx, int pIdx)
1810 QSGGeometryNode *node = m_nodes[gIdx];
1813 QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
1814 node->setFlag(QSGNode::OwnsGeometry, false);
1815 SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
1816 DeformableVertex *deformableVertices = (DeformableVertex *) node->geometry()->vertexData();
1817 ColoredVertex *coloredVertices = (ColoredVertex *) node->geometry()->vertexData();
1818 SimpleVertex *simpleVertices = (SimpleVertex *) node->geometry()->vertexData();
1819 switch (perfLevel){//No automatic fall through intended on this one
1821 spriteVertices += pIdx*4;
1822 for (int i=0; i<4; i++){
1823 spriteVertices[i].x = datum->x - m_systemOffset.x();
1824 spriteVertices[i].y = datum->y - m_systemOffset.y();
1825 spriteVertices[i].t = datum->t;
1826 spriteVertices[i].lifeSpan = datum->lifeSpan;
1827 spriteVertices[i].size = datum->size;
1828 spriteVertices[i].endSize = datum->endSize;
1829 spriteVertices[i].vx = datum->vx;
1830 spriteVertices[i].vy = datum->vy;
1831 spriteVertices[i].ax = datum->ax;
1832 spriteVertices[i].ay = datum->ay;
1833 if (m_explicitDeformation && datum->deformationOwner != this) {
1834 QQuickParticleData* shadow = getShadowDatum(datum);
1835 spriteVertices[i].xx = shadow->xx;
1836 spriteVertices[i].xy = shadow->xy;
1837 spriteVertices[i].yx = shadow->yx;
1838 spriteVertices[i].yy = shadow->yy;
1840 spriteVertices[i].xx = datum->xx;
1841 spriteVertices[i].xy = datum->xy;
1842 spriteVertices[i].yx = datum->yx;
1843 spriteVertices[i].yy = datum->yy;
1845 if (m_explicitRotation && datum->rotationOwner != this) {
1846 QQuickParticleData* shadow = getShadowDatum(datum);
1847 spriteVertices[i].rotation = shadow->rotation;
1848 spriteVertices[i].rotationSpeed = shadow->rotationSpeed;
1849 spriteVertices[i].autoRotate = shadow->autoRotate;
1851 spriteVertices[i].rotation = datum->rotation;
1852 spriteVertices[i].rotationSpeed = datum->rotationSpeed;
1853 spriteVertices[i].autoRotate = datum->autoRotate;
1855 //Sprite-related vertices updated per-frame in spritesUpdate(), not on demand
1856 if (m_explicitColor && datum->colorOwner != this) {
1857 QQuickParticleData* shadow = getShadowDatum(datum);
1858 spriteVertices[i].color.r = shadow->color.r;
1859 spriteVertices[i].color.g = shadow->color.g;
1860 spriteVertices[i].color.b = shadow->color.b;
1861 spriteVertices[i].color.a = shadow->color.a;
1863 spriteVertices[i].color.r = datum->color.r;
1864 spriteVertices[i].color.g = datum->color.g;
1865 spriteVertices[i].color.b = datum->color.b;
1866 spriteVertices[i].color.a = datum->color.a;
1870 case Tabled: //Fall through until it has its own vertex class
1872 deformableVertices += pIdx*4;
1873 for (int i=0; i<4; i++){
1874 deformableVertices[i].x = datum->x - m_systemOffset.x();
1875 deformableVertices[i].y = datum->y - m_systemOffset.y();
1876 deformableVertices[i].t = datum->t;
1877 deformableVertices[i].lifeSpan = datum->lifeSpan;
1878 deformableVertices[i].size = datum->size;
1879 deformableVertices[i].endSize = datum->endSize;
1880 deformableVertices[i].vx = datum->vx;
1881 deformableVertices[i].vy = datum->vy;
1882 deformableVertices[i].ax = datum->ax;
1883 deformableVertices[i].ay = datum->ay;
1884 if (m_explicitDeformation && datum->deformationOwner != this) {
1885 QQuickParticleData* shadow = getShadowDatum(datum);
1886 deformableVertices[i].xx = shadow->xx;
1887 deformableVertices[i].xy = shadow->xy;
1888 deformableVertices[i].yx = shadow->yx;
1889 deformableVertices[i].yy = shadow->yy;
1891 deformableVertices[i].xx = datum->xx;
1892 deformableVertices[i].xy = datum->xy;
1893 deformableVertices[i].yx = datum->yx;
1894 deformableVertices[i].yy = datum->yy;
1896 if (m_explicitRotation && datum->rotationOwner != this) {
1897 QQuickParticleData* shadow = getShadowDatum(datum);
1898 deformableVertices[i].rotation = shadow->rotation;
1899 deformableVertices[i].rotationSpeed = shadow->rotationSpeed;
1900 deformableVertices[i].autoRotate = shadow->autoRotate;
1902 deformableVertices[i].rotation = datum->rotation;
1903 deformableVertices[i].rotationSpeed = datum->rotationSpeed;
1904 deformableVertices[i].autoRotate = datum->autoRotate;
1906 if (m_explicitColor && datum->colorOwner != this) {
1907 QQuickParticleData* shadow = getShadowDatum(datum);
1908 deformableVertices[i].color.r = shadow->color.r;
1909 deformableVertices[i].color.g = shadow->color.g;
1910 deformableVertices[i].color.b = shadow->color.b;
1911 deformableVertices[i].color.a = shadow->color.a;
1913 deformableVertices[i].color.r = datum->color.r;
1914 deformableVertices[i].color.g = datum->color.g;
1915 deformableVertices[i].color.b = datum->color.b;
1916 deformableVertices[i].color.a = datum->color.a;
1921 coloredVertices += pIdx*1;
1922 for (int i=0; i<1; i++){
1923 coloredVertices[i].x = datum->x - m_systemOffset.x();
1924 coloredVertices[i].y = datum->y - m_systemOffset.y();
1925 coloredVertices[i].t = datum->t;
1926 coloredVertices[i].lifeSpan = datum->lifeSpan;
1927 coloredVertices[i].size = datum->size;
1928 coloredVertices[i].endSize = datum->endSize;
1929 coloredVertices[i].vx = datum->vx;
1930 coloredVertices[i].vy = datum->vy;
1931 coloredVertices[i].ax = datum->ax;
1932 coloredVertices[i].ay = datum->ay;
1933 if (m_explicitColor && datum->colorOwner != this) {
1934 QQuickParticleData* shadow = getShadowDatum(datum);
1935 coloredVertices[i].color.r = shadow->color.r;
1936 coloredVertices[i].color.g = shadow->color.g;
1937 coloredVertices[i].color.b = shadow->color.b;
1938 coloredVertices[i].color.a = shadow->color.a;
1940 coloredVertices[i].color.r = datum->color.r;
1941 coloredVertices[i].color.g = datum->color.g;
1942 coloredVertices[i].color.b = datum->color.b;
1943 coloredVertices[i].color.a = datum->color.a;
1948 simpleVertices += pIdx*1;
1949 for (int i=0; i<1; i++){
1950 simpleVertices[i].x = datum->x - m_systemOffset.x();
1951 simpleVertices[i].y = datum->y - m_systemOffset.y();
1952 simpleVertices[i].t = datum->t;
1953 simpleVertices[i].lifeSpan = datum->lifeSpan;
1954 simpleVertices[i].size = datum->size;
1955 simpleVertices[i].endSize = datum->endSize;
1956 simpleVertices[i].vx = datum->vx;
1957 simpleVertices[i].vy = datum->vy;
1958 simpleVertices[i].ax = datum->ax;
1959 simpleVertices[i].ay = datum->ay;
1966 node->setFlag(QSGNode::OwnsGeometry, true);