1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the Declarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** 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 <QtQuick/qsgnode.h>
45 #include <QtQuick/qsgtexturematerial.h>
46 #include <QtQuick/qsgtexture.h>
48 #include "qquickimageparticle_p.h"
49 #include "qquickparticleemitter_p.h"
50 #include <private/qquicksprite_p.h>
51 #include <private/qquickspriteengine_p.h>
52 #include <QOpenGLFunctions>
53 #include <QtQuick/qsgengine.h>
54 #include <QtQuick/private/qsgtexture_p.h>
55 #include <private/qdeclarativeglobal_p.h>
60 #ifndef QT_OPENGL_ES_2
61 #define SHADER_DEFINES "#version 120\n"
63 #define SHADER_DEFINES ""
66 //TODO: Make it larger on desktop? Requires fixing up shader code with the same define
67 #define UNIFORM_ARRAY_SIZE 64
69 static const char vertexShaderCode[] =
70 "#if defined(DEFORM)\n"
71 "attribute highp vec4 vPosTex;\n"
73 "attribute highp vec2 vPos;\n"
75 "attribute highp vec4 vData; // x = time, y = lifeSpan, z = size, w = endSize\n"
76 "attribute highp vec4 vVec; // x,y = constant speed, z,w = acceleration\n"
77 "uniform highp float entry;\n"
78 "#if defined(COLOR)\n"
79 "attribute highp vec4 vColor;\n"
81 "#if defined(DEFORM)\n"
82 "attribute highp vec4 vDeformVec; //x,y x unit vector; z,w = y unit vector\n"
83 "attribute highp vec3 vRotation; //x = radians of rotation, y=rotation speed, z= bool autoRotate\n"
85 "#if defined(SPRITE)\n"
86 "attribute highp vec3 vAnimData;// w,h(premultiplied of anim), interpolation progress\n"
87 "attribute highp vec4 vAnimPos;//x,y, x,y (two frames for interpolation)\n"
90 "uniform highp mat4 qt_Matrix;\n"
91 "uniform highp float timestamp;\n"
92 "#if defined(TABLE)\n"
93 "varying lowp vec2 tt;//y is progress if Sprite mode\n"
94 "uniform highp float sizetable[64];\n"
95 "uniform highp float opacitytable[64];\n"
97 "#if defined(SPRITE)\n"
98 "varying highp vec4 fTexS;\n"
99 "#elif defined(DEFORM)\n"
100 "varying highp vec2 fTex;\n"
102 "#if defined(COLOR)\n"
103 "varying lowp vec4 fColor;\n"
105 "varying lowp float fFade;\n"
111 " highp float t = (timestamp - vData.x) / vData.y;\n"
112 " if (t < 0. || t > 1.) {\n"
113 "#if defined(DEFORM)\n"
114 " gl_Position = qt_Matrix * vec4(vPosTex.x, vPosTex.y, 0., 1.);\n"
116 " gl_PointSize = 0.;\n"
119 "#if defined(SPRITE)\n"
120 " tt.y = vAnimData.z;\n"
121 " //Calculate frame location in texture\n"
122 " fTexS.xy = vAnimPos.xy + vPosTex.zw * vAnimData.xy;\n"
123 " //Next frame is also passed, for interpolation\n"
124 " fTexS.zw = vAnimPos.zw + vPosTex.zw * vAnimData.xy;\n"
126 "#elif defined(DEFORM)\n"
127 " fTex = vPosTex.zw;\n"
129 " highp float currentSize = mix(vData.z, vData.w, t * t);\n"
130 " lowp float fade = 1.;\n"
131 " highp float fadeIn = min(t * 10., 1.);\n"
132 " highp float fadeOut = 1. - clamp((t - 0.75) * 4.,0., 1.);\n"
134 "#if defined(TABLE)\n"
135 " currentSize = currentSize * sizetable[int(floor(t*64.))];\n"
136 " fade = fade * opacitytable[int(floor(t*64.))];\n"
139 " if (entry == 1.)\n"
140 " fade = fade * fadeIn * fadeOut;\n"
141 " else if (entry == 2.)\n"
142 " currentSize = currentSize * fadeIn * fadeOut;\n"
144 " if (currentSize <= 0.) {\n"
145 "#if defined(DEFORM)\n"
146 " gl_Position = qt_Matrix * vec4(vPosTex.x, vPosTex.y, 0., 1.);\n"
148 " gl_PointSize = 0.;\n"
151 " if (currentSize < 3.)//Sizes too small look jittery as they move\n"
152 " currentSize = 3.;\n"
155 "#if defined(DEFORM)\n"
156 " highp float rotation = vRotation.x + vRotation.y * t * vData.y;\n"
157 " if (vRotation.z == 1.0){\n"
158 " highp vec2 curVel = vVec.zw * t * vData.y + vVec.xy;\n"
159 " rotation += atan(curVel.y, curVel.x);\n"
161 " highp vec2 trigCalcs = vec2(cos(rotation), sin(rotation));\n"
162 " highp vec4 deform = vDeformVec * currentSize * (vPosTex.zzww - 0.5);\n"
163 " highp vec4 rotatedDeform = deform.xxzz * trigCalcs.xyxy;\n"
164 " rotatedDeform = rotatedDeform + (deform.yyww * trigCalcs.yxyx * vec4(-1.,1.,-1.,1.));\n"
165 " /* The readable version:\n"
166 " highp vec2 xDeform = vDeformVec.xy * currentSize * (vTex.x-0.5);\n"
167 " highp vec2 yDeform = vDeformVec.zw * currentSize * (vTex.y-0.5);\n"
168 " highp vec2 xRotatedDeform;\n"
169 " xRotatedDeform.x = trigCalcs.x*xDeform.x - trigCalcs.y*xDeform.y;\n"
170 " xRotatedDeform.y = trigCalcs.y*xDeform.x + trigCalcs.x*xDeform.y;\n"
171 " highp vec2 yRotatedDeform;\n"
172 " yRotatedDeform.x = trigCalcs.x*yDeform.x - trigCalcs.y*yDeform.y;\n"
173 " yRotatedDeform.y = trigCalcs.y*yDeform.x + trigCalcs.x*yDeform.y;\n"
175 " pos = vPosTex.xy\n"
176 " + rotatedDeform.xy\n"
177 " + rotatedDeform.zw\n"
178 " + vVec.xy * t * vData.y // apply speed\n"
179 " + 0.5 * vVec.zw * pow(t * vData.y, 2.); // apply acceleration\n"
182 " + vVec.xy * t * vData.y // apply speed vector..\n"
183 " + 0.5 * vVec.zw * pow(t * vData.y, 2.);\n"
184 " gl_PointSize = currentSize;\n"
186 " gl_Position = qt_Matrix * vec4(pos.x, pos.y, 0, 1);\n"
188 "#if defined(COLOR)\n"
189 " fColor = vColor * fade;\n"
193 "#if defined(TABLE)\n"
200 static const char fragmentShaderCode[] =
201 "uniform sampler2D texture;\n"
202 "uniform lowp float qt_Opacity;\n"
204 "#if defined(SPRITE)\n"
205 "varying highp vec4 fTexS;\n"
206 "#elif defined(DEFORM)\n"
207 "varying highp vec2 fTex;\n"
209 "#if defined(COLOR)\n"
210 "varying lowp vec4 fColor;\n"
212 "varying lowp float fFade;\n"
214 "#if defined(TABLE)\n"
215 "varying lowp vec2 tt;\n"
216 "uniform sampler2D colortable;\n"
220 "#if defined(SPRITE)\n"
221 " gl_FragColor = mix(texture2D(texture, fTexS.xy), texture2D(texture, fTexS.zw), tt.y)\n"
223 " * texture2D(colortable, tt)\n"
225 "#elif defined(TABLE)\n"
226 " gl_FragColor = texture2D(texture, fTex)\n"
228 " * texture2D(colortable, tt)\n"
230 "#elif defined(DEFORM)\n"
231 " gl_FragColor = (texture2D(texture, fTex)) * fColor * qt_Opacity;\n"
232 "#elif defined(COLOR)\n"
233 " gl_FragColor = (texture2D(texture, gl_PointCoord)) * fColor * qt_Opacity;\n"
235 " gl_FragColor = texture2D(texture, gl_PointCoord) * (fFade * qt_Opacity);\n"
239 const qreal CONV = 0.017453292519943295;
240 class ImageMaterialData
244 : texture(0), colorTable(0)
247 ~ImageMaterialData(){
253 QSGTexture *colorTable;
254 float sizeTable[UNIFORM_ARRAY_SIZE];
255 float opacityTable[UNIFORM_ARRAY_SIZE];
259 QSizeF animSheetSize;
262 class TabledMaterialData : public ImageMaterialData {};
263 class TabledMaterial : public QSGSimpleMaterialShader<TabledMaterialData>
265 QSG_DECLARE_SIMPLE_SHADER(TabledMaterial, TabledMaterialData)
270 m_vertex_code = QByteArray(SHADER_DEFINES)
271 + QByteArray("#define TABLE\n#define DEFORM\n#define COLOR\n")
274 m_fragment_code = QByteArray(SHADER_DEFINES)
275 + QByteArray("#define TABLE\n#define DEFORM\n#define COLOR\n")
276 + fragmentShaderCode;
278 Q_ASSERT(!m_vertex_code.isNull());
279 Q_ASSERT(!m_fragment_code.isNull());
282 const char *vertexShader() const { return m_vertex_code.constData(); }
283 const char *fragmentShader() const { return m_fragment_code.constData(); }
285 QList<QByteArray> attributes() const {
286 return QList<QByteArray>() << "vPosTex" << "vData" << "vVec"
287 << "vColor" << "vDeformVec" << "vRotation";
291 QSGSimpleMaterialShader<TabledMaterialData>::initialize();
293 program()->setUniformValue("texture", 0);
294 program()->setUniformValue("colortable", 1);
295 glFuncs = QOpenGLContext::currentContext()->functions();
296 m_timestamp_id = program()->uniformLocation("timestamp");
297 m_entry_id = program()->uniformLocation("entry");
298 m_sizetable_id = program()->uniformLocation("sizetable");
299 m_opacitytable_id = program()->uniformLocation("opacitytable");
302 void updateState(const TabledMaterialData* d, const TabledMaterialData*) {
303 glFuncs->glActiveTexture(GL_TEXTURE1);
304 d->colorTable->bind();
306 glFuncs->glActiveTexture(GL_TEXTURE0);
309 program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
310 program()->setUniformValue(m_entry_id, (float) d->entry);
311 program()->setUniformValueArray(m_sizetable_id, (float*) d->sizeTable, UNIFORM_ARRAY_SIZE, 1);
312 program()->setUniformValueArray(m_opacitytable_id, (float*) d->opacityTable, UNIFORM_ARRAY_SIZE, 1);
318 int m_opacitytable_id;
319 QByteArray m_vertex_code;
320 QByteArray m_fragment_code;
321 QOpenGLFunctions* glFuncs;
324 class DeformableMaterialData : public ImageMaterialData {};
325 class DeformableMaterial : public QSGSimpleMaterialShader<DeformableMaterialData>
327 QSG_DECLARE_SIMPLE_SHADER(DeformableMaterial, DeformableMaterialData)
332 m_vertex_code = QByteArray(SHADER_DEFINES)
333 + QByteArray("#define DEFORM\n#define COLOR\n")
336 m_fragment_code = QByteArray(SHADER_DEFINES)
337 + QByteArray("#define DEFORM\n#define COLOR\n")
338 + fragmentShaderCode;
340 Q_ASSERT(!m_vertex_code.isNull());
341 Q_ASSERT(!m_fragment_code.isNull());
344 const char *vertexShader() const { return m_vertex_code.constData(); }
345 const char *fragmentShader() const { return m_fragment_code.constData(); }
347 QList<QByteArray> attributes() const {
348 return QList<QByteArray>() << "vPosTex" << "vData" << "vVec"
349 << "vColor" << "vDeformVec" << "vRotation";
353 QSGSimpleMaterialShader<DeformableMaterialData>::initialize();
355 program()->setUniformValue("texture", 0);
356 glFuncs = QOpenGLContext::currentContext()->functions();
357 m_timestamp_id = program()->uniformLocation("timestamp");
358 m_entry_id = program()->uniformLocation("entry");
361 void updateState(const DeformableMaterialData* d, const DeformableMaterialData*) {
362 glFuncs->glActiveTexture(GL_TEXTURE0);
365 program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
366 program()->setUniformValue(m_entry_id, (float) d->entry);
371 QByteArray m_vertex_code;
372 QByteArray m_fragment_code;
373 QOpenGLFunctions* glFuncs;
376 class SpriteMaterialData : public ImageMaterialData {};
377 class SpriteMaterial : public QSGSimpleMaterialShader<SpriteMaterialData>
379 QSG_DECLARE_SIMPLE_SHADER(SpriteMaterial, SpriteMaterialData)
384 m_vertex_code = QByteArray(SHADER_DEFINES)
385 + QByteArray("#define SPRITE\n#define TABLE\n#define DEFORM\n#define COLOR\n")
388 m_fragment_code = QByteArray(SHADER_DEFINES)
389 + QByteArray("#define SPRITE\n#define TABLE\n#define DEFORM\n#define COLOR\n")
390 + fragmentShaderCode;
392 Q_ASSERT(!m_vertex_code.isNull());
393 Q_ASSERT(!m_fragment_code.isNull());
396 const char *vertexShader() const { return m_vertex_code.constData(); }
397 const char *fragmentShader() const { return m_fragment_code.constData(); }
399 QList<QByteArray> attributes() const {
400 return QList<QByteArray>() << "vPosTex" << "vData" << "vVec"
401 << "vColor" << "vDeformVec" << "vRotation" << "vAnimData" << "vAnimPos";
405 QSGSimpleMaterialShader<SpriteMaterialData>::initialize();
407 program()->setUniformValue("texture", 0);
408 program()->setUniformValue("colortable", 1);
409 glFuncs = QOpenGLContext::currentContext()->functions();
410 //Don't actually expose the animSheetSize in the shader, it's currently only used for CPU calculations.
411 m_timestamp_id = program()->uniformLocation("timestamp");
412 m_entry_id = program()->uniformLocation("entry");
413 m_sizetable_id = program()->uniformLocation("sizetable");
414 m_opacitytable_id = program()->uniformLocation("opacitytable");
417 void updateState(const SpriteMaterialData* d, const SpriteMaterialData*) {
418 glFuncs->glActiveTexture(GL_TEXTURE1);
419 d->colorTable->bind();
421 // make sure we end by setting GL_TEXTURE0 as active texture
422 glFuncs->glActiveTexture(GL_TEXTURE0);
425 program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
426 program()->setUniformValue(m_entry_id, (float) d->entry);
427 program()->setUniformValueArray(m_sizetable_id, (float*) d->sizeTable, 64, 1);
428 program()->setUniformValueArray(m_opacitytable_id, (float*) d->opacityTable, UNIFORM_ARRAY_SIZE, 1);
434 int m_opacitytable_id;
435 QByteArray m_vertex_code;
436 QByteArray m_fragment_code;
437 QOpenGLFunctions* glFuncs;
440 class ColoredMaterialData : public ImageMaterialData {};
441 class ColoredMaterial : public QSGSimpleMaterialShader<ColoredMaterialData>
443 QSG_DECLARE_SIMPLE_SHADER(ColoredMaterial, ColoredMaterialData)
448 m_vertex_code = QByteArray(SHADER_DEFINES)
449 + QByteArray("#define COLOR\n")
452 m_fragment_code = QByteArray(SHADER_DEFINES)
453 + QByteArray("#define COLOR\n")
454 + fragmentShaderCode;
456 Q_ASSERT(!m_vertex_code.isNull());
457 Q_ASSERT(!m_fragment_code.isNull());
460 const char *vertexShader() const { return m_vertex_code.constData(); }
461 const char *fragmentShader() const { return m_fragment_code.constData(); }
464 QSGSimpleMaterialShader<ColoredMaterialData>::activate();
465 #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
466 glEnable(GL_POINT_SPRITE);
467 glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
472 QSGSimpleMaterialShader<ColoredMaterialData>::deactivate();
473 #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
474 glDisable(GL_POINT_SPRITE);
475 glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
479 QList<QByteArray> attributes() const {
480 return QList<QByteArray>() << "vPos" << "vData" << "vVec" << "vColor";
484 QSGSimpleMaterialShader<ColoredMaterialData>::initialize();
486 program()->setUniformValue("texture", 0);
487 glFuncs = QOpenGLContext::currentContext()->functions();
488 m_timestamp_id = program()->uniformLocation("timestamp");
489 m_entry_id = program()->uniformLocation("entry");
492 void updateState(const ColoredMaterialData* d, const ColoredMaterialData*) {
493 glFuncs->glActiveTexture(GL_TEXTURE0);
496 program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
497 program()->setUniformValue(m_entry_id, (float) d->entry);
502 QByteArray m_vertex_code;
503 QByteArray m_fragment_code;
504 QOpenGLFunctions* glFuncs;
507 class SimpleMaterialData : public ImageMaterialData {};
508 class SimpleMaterial : public QSGSimpleMaterialShader<SimpleMaterialData>
510 QSG_DECLARE_SIMPLE_SHADER(SimpleMaterial, SimpleMaterialData)
515 m_vertex_code = QByteArray(SHADER_DEFINES)
518 m_fragment_code = QByteArray(SHADER_DEFINES)
519 + fragmentShaderCode;
521 Q_ASSERT(!m_vertex_code.isNull());
522 Q_ASSERT(!m_fragment_code.isNull());
525 const char *vertexShader() const { return m_vertex_code.constData(); }
526 const char *fragmentShader() const { return m_fragment_code.constData(); }
529 QSGSimpleMaterialShader<SimpleMaterialData>::activate();
530 #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
531 glEnable(GL_POINT_SPRITE);
532 glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
537 QSGSimpleMaterialShader<SimpleMaterialData>::deactivate();
538 #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN)
539 glDisable(GL_POINT_SPRITE);
540 glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
544 QList<QByteArray> attributes() const {
545 return QList<QByteArray>() << "vPos" << "vData" << "vVec";
549 QSGSimpleMaterialShader<SimpleMaterialData>::initialize();
551 program()->setUniformValue("texture", 0);
552 glFuncs = QOpenGLContext::currentContext()->functions();
553 m_timestamp_id = program()->uniformLocation("timestamp");
554 m_entry_id = program()->uniformLocation("entry");
557 void updateState(const SimpleMaterialData* d, const SimpleMaterialData*) {
558 glFuncs->glActiveTexture(GL_TEXTURE0);
561 program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
562 program()->setUniformValue(m_entry_id, (float) d->entry);
567 QByteArray m_vertex_code;
568 QByteArray m_fragment_code;
569 QOpenGLFunctions* glFuncs;
572 void fillUniformArrayFromImage(float* array, const QImage& img, int size)
575 for (int i=0; i<size; i++)
579 QImage scaled = img.scaled(size,1);
580 for (int i=0; i<size; i++)
581 array[i] = qAlpha(scaled.pixel(i,0))/255.0;
585 \qmlclass ImageParticle QQuickImageParticle
586 \inqmlmodule QtQuick.Particles 2
587 \inherits ParticlePainter
588 \brief The ImageParticle element visualizes logical particles using an image
590 This element renders a logical particle as an image. The image can be
595 \o a sprite-based animation
598 ImageParticles implictly share data on particles if multiple ImageParticles are painting
599 the same logical particle group. This is broken down along the four capabilities listed
600 above. So if one ImageParticle defines data for rendering the particles in one of those
601 capabilities, and the other does not, then both will draw the particles the same in that
602 aspect automatically. This is primarily useful when there is some random variation on
603 the particle which is supposed to stay with it when switching painters. If both ImageParticles
604 define how they should appear for that aspect, they diverge and each appears as it is defined.
606 This sharing of data happens behind the scenes based off of whether properties were implicitly or explicitly
607 set. One drawback of the current implementation is that it is only possible to reset the capabilities as a whole.
608 So if you explicity set an attribute affecting color, such as redVariation, and then reset it (by setting redVariation
609 to undefined), all color data will be reset and it will begin to have an implicit value of any shared color from
610 other ImageParticles.
613 \qmlproperty url QtQuick.Particles2::ImageParticle::source
615 The source image to be used.
617 If the image is a sprite animation, use the sprite property instead.
620 \qmlproperty list<Sprite> QtQuick.Particles2::ImageParticle::sprites
622 The sprite or sprites used to draw this particle.
624 Note that the sprite image will be scaled to a square based on the size of
625 the particle being rendered.
628 \qmlproperty url QtQuick.Particles2::ImageParticle::colorTable
630 An image whose color will be used as a 1D texture to determine color over life. E.g. when
631 the particle is halfway through its lifetime, it will have the color specified halfway
634 This color is blended with the color property and the color of the source image.
637 \qmlproperty url QtQuick.Particles2::ImageParticle::sizeTable
639 An image whose opacity will be used as a 1D texture to determine size over life.
641 This property is expected to be removed shortly, in favor of custom easing curves to determine size over life.
644 \qmlproperty url QtQuick.Particles2::ImageParticle::opacityTable
646 An image whose opacity will be used as a 1D texture to determine size over life.
648 This property is expected to be removed shortly, in favor of custom easing curves to determine opacity over life.
651 \qmlproperty color QtQuick.Particles2::ImageParticle::color
653 If a color is specified, the provided image will be colorized with it.
655 Default is white (no change).
658 \qmlproperty real QtQuick.Particles2::ImageParticle::colorVariation
660 This number represents the color variation applied to individual particles.
661 Setting colorVariation is the same as setting redVariation, greenVariation,
662 and blueVariation to the same number.
664 Each channel can vary between particle by up to colorVariation from its usual color.
666 Color is measured, per channel, from 0.0 to 1.0.
671 \qmlproperty real QtQuick.Particles2::ImageParticle::redVariation
672 The variation in the red color channel between particles.
674 Color is measured, per channel, from 0.0 to 1.0.
679 \qmlproperty real QtQuick.Particles2::ImageParticle::greenVariation
680 The variation in the green color channel between particles.
682 Color is measured, per channel, from 0.0 to 1.0.
687 \qmlproperty real QtQuick.Particles2::ImageParticle::blueVariation
688 The variation in the blue color channel between particles.
690 Color is measured, per channel, from 0.0 to 1.0.
695 \qmlproperty real QtQuick.Particles2::ImageParticle::alpha
696 An alpha to be applied to the image. This value is multiplied by the value in
697 the image, and the value in the color property.
699 Particles have additive blending, so lower alpha on single particles leads
700 to stronger effects when multiple particles overlap.
702 Alpha is measured from 0.0 to 1.0.
707 \qmlproperty real QtQuick.Particles2::ImageParticle::alphaVariation
708 The variation in the alpha channel between particles.
710 Alpha is measured from 0.0 to 1.0.
715 \qmlproperty real QtQuick.Particles2::ImageParticle::rotation
717 If set the image will be rotated by this many degrees before it is drawn.
719 The particle coordinates are not transformed.
722 \qmlproperty real QtQuick.Particles2::ImageParticle::rotationVariation
724 If set the rotation of individual particles will vary by up to this much
729 \qmlproperty real QtQuick.Particles2::ImageParticle::rotationSpeed
731 If set particles will rotate at this speed in degrees/second.
734 \qmlproperty real QtQuick.Particles2::ImageParticle::rotationSpeedVariation
736 If set the rotationSpeed of individual particles will vary by up to this much
741 \qmlproperty bool QtQuick.Particles2::ImageParticle::autoRotation
743 If set to true then a rotation will be applied on top of the particles rotation, so
744 that it faces the direction of travel. So to face away from the direction of travel,
745 set autoRotation to true and rotation to 180.
750 \qmlproperty StochasticDirection QtQuick.Particles2::ImageParticle::xVector
752 Allows you to deform the particle image when drawn. The rectangular image will
753 be deformed so that the horizontal sides are in the shape of this vector instead
757 \qmlproperty StochasticDirection QtQuick.Particles2::ImageParticle::yVector
759 Allows you to deform the particle image when drawn. The rectangular image will
760 be deformed so that the vertical sides are in the shape of this vector instead
764 \qmlproperty EntryEffect QtQuick.Particles2::ImageParticle::entryEffect
766 This property provides basic and cheap entrance and exit effects for the particles.
767 For fine-grained control, see sizeTable and opacityTable.
769 Acceptable values are
771 \o None: Particles just appear and disappear.
772 \o Fade: Particles fade in from 0 opacity at the start of their life, and fade out to 0 at the end.
773 \o Scale: Particles scale in from 0 size at the start of their life, and scale back to 0 at the end.
776 Default value is Fade.
779 \qmlproperty bool QtQuick.Particles2::ImageParticle::spritesInterpolate
781 If set to true, sprite particles will interpolate between sprite frames each rendered frame, making
782 the sprites look smoother.
788 QQuickImageParticle::QQuickImageParticle(QQuickItem* parent)
789 : QQuickParticlePainter(parent)
790 , m_color_variation(0.0)
793 , m_alphaVariation(0.0)
795 , m_redVariation(0.0)
796 , m_greenVariation(0.0)
797 , m_blueVariation(0.0)
799 , m_rotationVariation(0)
801 , m_rotationSpeedVariation(0)
802 , m_autoRotation(false)
806 , m_spritesInterpolate(true)
807 , m_explicitColor(false)
808 , m_explicitRotation(false)
809 , m_explicitDeformation(false)
810 , m_explicitAnimation(false)
811 , m_bypassOptimizations(false)
813 , m_lastLevel(Unknown)
815 , m_entryEffect(Fade)
817 setFlag(ItemHasContents);
820 QQuickImageParticle::~QQuickImageParticle()
824 QDeclarativeListProperty<QQuickSprite> QQuickImageParticle::sprites()
826 return QDeclarativeListProperty<QQuickSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
829 void QQuickImageParticle::setImage(const QUrl &image)
831 if (image == m_image_name)
833 m_image_name = image;
839 void QQuickImageParticle::setColortable(const QUrl &table)
841 if (table == m_colortable_name)
843 m_colortable_name = table;
844 emit colortableChanged();
848 void QQuickImageParticle::setSizetable(const QUrl &table)
850 if (table == m_sizetable_name)
852 m_sizetable_name = table;
853 emit sizetableChanged();
857 void QQuickImageParticle::setOpacitytable(const QUrl &table)
859 if (table == m_opacitytable_name)
861 m_opacitytable_name = table;
862 emit opacitytableChanged();
866 void QQuickImageParticle::setColor(const QColor &color)
868 if (color == m_color)
872 m_explicitColor = true;
873 if (perfLevel < Colored)
877 void QQuickImageParticle::setColorVariation(qreal var)
879 if (var == m_color_variation)
881 m_color_variation = var;
882 emit colorVariationChanged();
883 m_explicitColor = true;
884 if (perfLevel < Colored)
888 void QQuickImageParticle::setAlphaVariation(qreal arg)
890 if (m_alphaVariation != arg) {
891 m_alphaVariation = arg;
892 emit alphaVariationChanged(arg);
894 m_explicitColor = true;
895 if (perfLevel < Colored)
899 void QQuickImageParticle::setAlpha(qreal arg)
901 if (m_alpha != arg) {
903 emit alphaChanged(arg);
905 m_explicitColor = true;
906 if (perfLevel < Colored)
910 void QQuickImageParticle::setRedVariation(qreal arg)
912 if (m_redVariation != arg) {
913 m_redVariation = arg;
914 emit redVariationChanged(arg);
916 m_explicitColor = true;
917 if (perfLevel < Colored)
921 void QQuickImageParticle::setGreenVariation(qreal arg)
923 if (m_greenVariation != arg) {
924 m_greenVariation = arg;
925 emit greenVariationChanged(arg);
927 m_explicitColor = true;
928 if (perfLevel < Colored)
932 void QQuickImageParticle::setBlueVariation(qreal arg)
934 if (m_blueVariation != arg) {
935 m_blueVariation = arg;
936 emit blueVariationChanged(arg);
938 m_explicitColor = true;
939 if (perfLevel < Colored)
943 void QQuickImageParticle::setRotation(qreal arg)
945 if (m_rotation != arg) {
947 emit rotationChanged(arg);
949 m_explicitRotation = true;
950 if (perfLevel < Deformable)
954 void QQuickImageParticle::setRotationVariation(qreal arg)
956 if (m_rotationVariation != arg) {
957 m_rotationVariation = arg;
958 emit rotationVariationChanged(arg);
960 m_explicitRotation = true;
961 if (perfLevel < Deformable)
965 void QQuickImageParticle::setRotationSpeed(qreal arg)
967 if (m_rotationSpeed != arg) {
968 m_rotationSpeed = arg;
969 emit rotationSpeedChanged(arg);
971 m_explicitRotation = true;
972 if (perfLevel < Deformable)
976 void QQuickImageParticle::setRotationSpeedVariation(qreal arg)
978 if (m_rotationSpeedVariation != arg) {
979 m_rotationSpeedVariation = arg;
980 emit rotationSpeedVariationChanged(arg);
982 m_explicitRotation = true;
983 if (perfLevel < Deformable)
987 void QQuickImageParticle::setAutoRotation(bool arg)
989 if (m_autoRotation != arg) {
990 m_autoRotation = arg;
991 emit autoRotationChanged(arg);
993 m_explicitRotation = true;
994 if (perfLevel < Deformable)
998 void QQuickImageParticle::setXVector(QQuickDirection* arg)
1000 if (m_xVector != arg) {
1002 emit xVectorChanged(arg);
1004 m_explicitDeformation = true;
1005 if (perfLevel < Deformable)
1009 void QQuickImageParticle::setYVector(QQuickDirection* arg)
1011 if (m_yVector != arg) {
1013 emit yVectorChanged(arg);
1015 m_explicitDeformation = true;
1016 if (perfLevel < Deformable)
1020 void QQuickImageParticle::setSpritesInterpolate(bool arg)
1022 if (m_spritesInterpolate != arg) {
1023 m_spritesInterpolate = arg;
1024 emit spritesInterpolateChanged(arg);
1028 void QQuickImageParticle::setBypassOptimizations(bool arg)
1030 if (m_bypassOptimizations != arg) {
1031 m_bypassOptimizations = arg;
1032 emit bypassOptimizationsChanged(arg);
1034 if (perfLevel < 9999)
1038 void QQuickImageParticle::setEntryEffect(EntryEffect arg)
1040 if (m_entryEffect != arg) {
1041 m_entryEffect = arg;
1043 getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect;
1044 emit entryEffectChanged(arg);
1048 void QQuickImageParticle::resetColor()
1050 m_explicitColor = false;
1051 foreach (const QString &str, m_groups)
1052 foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
1053 if (d->colorOwner == this)
1056 m_color_variation = 0.0f;
1057 m_redVariation = 0.0f;
1058 m_blueVariation = 0.0f;
1059 m_greenVariation = 0.0f;
1061 m_alphaVariation = 0.0f;
1064 void QQuickImageParticle::resetRotation()
1066 m_explicitRotation = false;
1067 foreach (const QString &str, m_groups)
1068 foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
1069 if (d->rotationOwner == this)
1070 d->rotationOwner = 0;
1072 m_rotationVariation = 0;
1073 m_rotationSpeed = 0;
1074 m_rotationSpeedVariation = 0;
1075 m_autoRotation = false;
1078 void QQuickImageParticle::resetDeformation()
1080 m_explicitDeformation = false;
1081 foreach (const QString &str, m_groups)
1082 foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data)
1083 if (d->deformationOwner == this)
1084 d->deformationOwner = 0;
1093 void QQuickImageParticle::reset()
1095 QQuickParticlePainter::reset();
1096 m_pleaseReset = true;
1100 void QQuickImageParticle::createEngine()
1103 delete m_spriteEngine;
1104 if (m_sprites.count()) {
1105 m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
1106 connect(m_spriteEngine, SIGNAL(stateChanged(int)),
1107 this, SLOT(spriteAdvance(int)), Qt::DirectConnection);
1108 m_explicitAnimation = true;
1111 m_explicitAnimation = false;
1116 static QSGGeometry::Attribute SimpleParticle_Attributes[] = {
1117 QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // Position
1118 QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
1119 QSGGeometry::Attribute::create(2, 4, GL_FLOAT) // Vectors
1122 static QSGGeometry::AttributeSet SimpleParticle_AttributeSet =
1124 3, // Attribute Count
1125 ( 2 + 4 + 4 ) * sizeof(float),
1126 SimpleParticle_Attributes
1129 static QSGGeometry::Attribute ColoredParticle_Attributes[] = {
1130 QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // Position
1131 QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
1132 QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Vectors
1133 QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors
1136 static QSGGeometry::AttributeSet ColoredParticle_AttributeSet =
1138 4, // Attribute Count
1139 ( 2 + 4 + 4 ) * sizeof(float) + 4 * sizeof(uchar),
1140 ColoredParticle_Attributes
1143 static QSGGeometry::Attribute DeformableParticle_Attributes[] = {
1144 QSGGeometry::Attribute::create(0, 4, GL_FLOAT), // Position & TexCoord
1145 QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
1146 QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Vectors
1147 QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors
1148 QSGGeometry::Attribute::create(4, 4, GL_FLOAT), // DeformationVectors
1149 QSGGeometry::Attribute::create(5, 3, GL_FLOAT), // Rotation
1152 static QSGGeometry::AttributeSet DeformableParticle_AttributeSet =
1154 6, // Attribute Count
1155 (4 + 4 + 4 + 4 + 3) * sizeof(float) + 4 * sizeof(uchar),
1156 DeformableParticle_Attributes
1159 static QSGGeometry::Attribute SpriteParticle_Attributes[] = {
1160 QSGGeometry::Attribute::create(0, 4, GL_FLOAT), // Position & TexCoord
1161 QSGGeometry::Attribute::create(1, 4, GL_FLOAT), // Data
1162 QSGGeometry::Attribute::create(2, 4, GL_FLOAT), // Vectors
1163 QSGGeometry::Attribute::create(3, 4, GL_UNSIGNED_BYTE), // Colors
1164 QSGGeometry::Attribute::create(4, 4, GL_FLOAT), // DeformationVectors
1165 QSGGeometry::Attribute::create(5, 3, GL_FLOAT), // Rotation
1166 QSGGeometry::Attribute::create(6, 3, GL_FLOAT), // Anim Data
1167 QSGGeometry::Attribute::create(7, 4, GL_FLOAT) // Anim Pos
1170 static QSGGeometry::AttributeSet SpriteParticle_AttributeSet =
1172 8, // Attribute Count
1173 (4 + 4 + 4 + 4 + 3 + 3 + 4) * sizeof(float) + 4 * sizeof(uchar),
1174 SpriteParticle_Attributes
1177 void QQuickImageParticle::clearShadows()
1179 foreach (const QVector<QQuickParticleData*> data, m_shadowData)
1181 m_shadowData.clear();
1184 //Only call if you need to, may initialize the whole array first time
1185 QQuickParticleData* QQuickImageParticle::getShadowDatum(QQuickParticleData* datum)
1187 QQuickParticleGroupData* gd = m_system->groupData[datum->group];
1188 if (!m_shadowData.contains(datum->group)) {
1189 QVector<QQuickParticleData*> data;
1190 for (int i=0; i<gd->size(); i++){
1191 QQuickParticleData* datum = new QQuickParticleData(m_system);
1192 *datum = *(gd->data[i]);
1195 m_shadowData.insert(datum->group, data);
1197 //### If dynamic resize is added, remember to potentially resize the shadow data on out-of-bounds access request
1199 return m_shadowData[datum->group][datum->index];
1202 QSGGeometryNode* QQuickImageParticle::buildParticleNodes()
1204 #ifdef QT_OPENGL_ES_2
1205 if (m_count * 4 > 0xffff) {
1206 printf("ImageParticle: Too many particles - maximum 16,000 per ImageParticle.\n");//ES 2 vertex count limit is ushort
1214 m_debugMode = m_system->m_debugMode;
1216 if (m_sprites.count() || m_bypassOptimizations) {
1217 perfLevel = Sprites;
1218 } else if (!m_colortable_name.isEmpty() || !m_sizetable_name.isEmpty()
1219 || !m_opacitytable_name.isEmpty()) {
1221 } else if (m_autoRotation || m_rotation || m_rotationVariation
1222 || m_rotationSpeed || m_rotationSpeedVariation
1223 || m_xVector || m_yVector) {
1224 perfLevel = Deformable;
1225 } else if (m_alphaVariation || m_alpha != 1.0 || m_color.isValid() || m_color_variation
1226 || m_redVariation || m_blueVariation || m_greenVariation) {
1227 perfLevel = Colored;
1232 foreach (const QString &str, m_groups){//For sharing higher levels, need to have highest used so it renders
1233 int gIdx = m_system->groupIds[str];
1234 foreach (QQuickParticlePainter* p, m_system->groupData[gIdx]->painters){
1235 QQuickImageParticle* other = qobject_cast<QQuickImageParticle*>(p);
1237 if (other->perfLevel > perfLevel) {
1238 if (other->perfLevel >= Tabled){//Deformable is the highest level needed for this, anything higher isn't shared (or requires your own sprite)
1239 if (perfLevel < Deformable)
1240 perfLevel = Deformable;
1242 perfLevel = other->perfLevel;
1244 } else if (other->perfLevel < perfLevel) {
1251 if (perfLevel >= Colored && !m_color.isValid())
1252 m_color = QColor(Qt::white);//Hidden default, but different from unset
1255 if (perfLevel >= Sprites){
1256 if (!m_spriteEngine) {
1257 qWarning() << "ImageParticle: No sprite engine...";
1258 //Sprite performance mode with static image is supported, but not advised
1259 //Note that in this case it always uses shadow data
1261 image = m_spriteEngine->assembledImage();
1262 if (image.isNull())//Warning is printed in engine
1267 if ( image.isNull() ) {
1268 image = QImage(m_image_name.toLocalFile());
1269 if (image.isNull()) {
1270 printf("ImageParticle: loading image failed '%s'\n", qPrintable(m_image_name.toLocalFile()));
1282 QImage opacitytable;
1283 switch (perfLevel) {//Fallthrough intended
1285 m_material = SpriteMaterial::createMaterial();
1286 getState<ImageMaterialData>(m_material)->animSheetSize = QSizeF(image.size());
1288 m_spriteEngine->setCount(m_count);
1291 m_material = TabledMaterial::createMaterial();
1292 colortable = QImage(m_colortable_name.toLocalFile());
1293 sizetable = QImage(m_sizetable_name.toLocalFile());
1294 opacitytable = QImage(m_opacitytable_name.toLocalFile());
1295 if (colortable.isNull()){
1296 colortable = QImage(1,1,QImage::Format_ARGB32);
1297 colortable.fill(Qt::white);
1299 Q_ASSERT(!colortable.isNull());
1300 getState<ImageMaterialData>(m_material)->colorTable = QSGPlainTexture::fromImage(colortable);
1301 fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->sizeTable, sizetable, UNIFORM_ARRAY_SIZE);
1302 fillUniformArrayFromImage(getState<ImageMaterialData>(m_material)->opacityTable, opacitytable, UNIFORM_ARRAY_SIZE);
1305 m_material = DeformableMaterial::createMaterial();
1308 m_material = ColoredMaterial::createMaterial();
1309 default://Also Simple
1311 m_material = SimpleMaterial::createMaterial();
1312 getState<ImageMaterialData>(m_material)->texture = QSGPlainTexture::fromImage(image);
1313 getState<ImageMaterialData>(m_material)->texture->setFiltering(QSGTexture::Linear);
1314 getState<ImageMaterialData>(m_material)->entry = (qreal) m_entryEffect;
1315 m_material->setFlag(QSGMaterial::Blending);
1319 foreach (const QString &str, m_groups){
1320 int gIdx = m_system->groupIds[str];
1321 int count = m_system->groupData[gIdx]->size();
1322 QSGGeometryNode* node = new QSGGeometryNode();
1323 node->setMaterial(m_material);
1324 node->markDirty(QSGNode::DirtyMaterial);
1326 m_nodes.insert(gIdx, node);
1327 m_idxStarts.insert(gIdx, m_lastIdxStart);
1328 m_startsIdx.append(qMakePair<int,int>(m_lastIdxStart, gIdx));
1329 m_lastIdxStart += count;
1331 //Create Particle Geometry
1332 int vCount = count * 4;
1333 int iCount = count * 6;
1336 if (perfLevel == Sprites)
1337 g = new QSGGeometry(SpriteParticle_AttributeSet, vCount, iCount);
1338 else if (perfLevel == Tabled)
1339 g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
1340 else if (perfLevel == Deformable)
1341 g = new QSGGeometry(DeformableParticle_AttributeSet, vCount, iCount);
1342 else if (perfLevel == Colored)
1343 g = new QSGGeometry(ColoredParticle_AttributeSet, count, 0);
1345 g = new QSGGeometry(SimpleParticle_AttributeSet, count, 0);
1347 node->setGeometry(g);
1348 if (perfLevel <= Colored){
1349 g->setDrawingMode(GL_POINTS);
1351 GLfloat pointSizeRange[2];
1352 glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange);
1353 qDebug() << "Using point sprites, GL_ALIASED_POINT_SIZE_RANGE " <<pointSizeRange[0] << ":" << pointSizeRange[1];
1356 g->setDrawingMode(GL_TRIANGLES);
1358 for (int p=0; p < count; ++p)
1359 commit(gIdx, p);//commit sets geometry for the node, has its own perfLevel switch
1361 if (perfLevel == Sprites)
1362 initTexCoords<SpriteVertex>((SpriteVertex*)g->vertexData(), vCount);
1363 else if (perfLevel == Tabled)
1364 initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
1365 else if (perfLevel == Deformable)
1366 initTexCoords<DeformableVertex>((DeformableVertex*)g->vertexData(), vCount);
1368 if (perfLevel > Colored){
1369 quint16 *indices = g->indexDataAsUShort();
1370 for (int i=0; i < count; ++i) {
1383 if (perfLevel == Sprites)
1384 spritesUpdate();//Gives all vertexes the initial sprite data, then maintained per frame
1386 foreach (QSGGeometryNode* node, m_nodes){
1387 if (node == *(m_nodes.begin()))
1388 node->setFlag(QSGGeometryNode::OwnsMaterial);//Root node owns the material for memory management purposes
1390 (*(m_nodes.begin()))->appendChildNode(node);
1393 return *(m_nodes.begin());
1396 QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
1399 m_lastLevel = perfLevel;
1401 delete m_rootNode;//Automatically deletes children, and SG manages material lifetime
1405 m_idxStarts.clear();
1406 m_startsIdx.clear();
1411 m_pleaseReset = false;
1414 if (m_system && m_system->isRunning() && !m_system->isPaused()){
1418 foreach (QSGGeometryNode* node, m_nodes)
1419 node->markDirty(QSGNode::DirtyGeometry);
1426 void QQuickImageParticle::prepareNextFrame()
1428 if (m_rootNode == 0){//TODO: Staggered loading (as emitted)
1429 m_rootNode = buildParticleNodes();
1431 qDebug() << "QQuickImageParticle Feature level: " << perfLevel;
1432 qDebug() << "QQuickImageParticle Nodes: ";
1434 foreach (int i, m_nodes.keys()) {
1435 qDebug() << "Group " << i << " (" << m_system->groupData[i]->size() << " particles)";
1436 count += m_system->groupData[i]->size();
1438 qDebug() << "Total count: " << count;
1440 if (m_rootNode == 0)
1443 qint64 timeStamp = m_system->systemSync(this);
1445 qreal time = timeStamp / 1000.;
1447 switch (perfLevel){//Fall-through intended
1451 m_spriteEngine->updateSprites(timeStamp);//fires signals if anim changed
1452 spritesUpdate(time);
1457 default: //Also Simple
1458 getState<ImageMaterialData>(m_material)->timestamp = time;
1461 foreach (QSGGeometryNode* node, m_nodes)
1462 node->markDirty(QSGNode::DirtyMaterial);
1465 void QQuickImageParticle::spritesUpdate(qreal time)
1467 // Sprite progression handled CPU side, so as to have per-frame control.
1468 foreach (const QString &str, m_groups) {
1469 int gIdx = m_system->groupIds[str];
1470 foreach (QQuickParticleData* mainDatum, m_system->groupData[gIdx]->data) {
1471 QSGGeometryNode *node = m_nodes[gIdx];
1474 //TODO: Interpolate between two different animations if it's going to transition next frame
1475 // This is particularly important for cut-up sprites.
1476 QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum));
1479 if (datum->frameDuration > 0) {
1480 qreal frame = (time - datum->animT)/(datum->frameDuration / 1000.0);
1481 frame = qBound(0.0, frame, (qreal)datum->frameCount - 1.0);//Stop at count-1 frames until we have between anim interpolation
1482 progress = modf(frame,&frameAt);
1485 if (datum->frameAt >= datum->frameCount){
1487 for (int i = 0; i<m_startsIdx.count(); i++) {
1488 if (m_startsIdx[i].second == gIdx){
1489 m_spriteEngine->advance(m_startsIdx[i].first + datum->index);
1494 frameAt = datum->frameAt;
1497 QSizeF sheetSize = getState<ImageMaterialData>(m_material)->animSheetSize;
1498 qreal y = datum->animY / sheetSize.height();
1499 qreal w = datum->animWidth / sheetSize.width();
1500 qreal h = datum->animHeight / sheetSize.height();
1501 qreal x1 = datum->animX / sheetSize.width();
1504 if (frameAt < (datum->frameCount-1))
1507 node->setFlag(QSGNode::OwnsGeometry, false);
1508 SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
1509 spriteVertices += datum->index*4;
1510 for (int i=0; i<4; i++) {
1511 spriteVertices[i].animX1 = x1;
1512 spriteVertices[i].animY1 = y;
1513 spriteVertices[i].animX2 = x2;
1514 spriteVertices[i].animY2 = y;
1515 spriteVertices[i].animW = w;
1516 spriteVertices[i].animH = h;
1517 spriteVertices[i].animProgress = progress;
1519 node->setFlag(QSGNode::OwnsGeometry, true);
1524 void QQuickImageParticle::spriteAdvance(int spriteIdx)
1526 if (!m_startsIdx.count())//Probably overly defensive
1531 for (i = 0; i<m_startsIdx.count(); i++) {
1532 if (spriteIdx < m_startsIdx[i].first) {
1533 gIdx = m_startsIdx[i-1].second;
1538 gIdx = m_startsIdx[i-1].second;
1539 int pIdx = spriteIdx - m_startsIdx[i-1].first;
1541 QQuickParticleData* mainDatum = m_system->groupData[gIdx]->data[pIdx];
1542 QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum));
1544 datum->animIdx = m_spriteEngine->spriteState(spriteIdx);
1545 datum->animT = m_spriteEngine->spriteStart(spriteIdx)/1000.0;
1546 datum->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
1547 datum->frameDuration = m_spriteEngine->spriteDuration(spriteIdx);
1548 datum->animX = m_spriteEngine->spriteX(spriteIdx);
1549 datum->animY = m_spriteEngine->spriteY(spriteIdx);
1550 datum->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
1551 datum->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
1554 void QQuickImageParticle::reloadColor(const Color4ub &c, QQuickParticleData* d)
1557 //TODO: get index for reload - or make function take an index
1560 void QQuickImageParticle::initialize(int gIdx, int pIdx)
1563 QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
1564 qreal redVariation = m_color_variation + m_redVariation;
1565 qreal greenVariation = m_color_variation + m_greenVariation;
1566 qreal blueVariation = m_color_variation + m_blueVariation;
1568 if (m_spriteEngine) {
1569 spriteIdx = m_idxStarts[gIdx] + datum->index;
1570 if (spriteIdx >= m_spriteEngine->count())
1571 m_spriteEngine->setCount(spriteIdx+1);
1575 float rotationSpeed;
1577 switch (perfLevel){//Fall-through is intended on all of them
1579 // Initial Sprite State
1580 if (m_explicitAnimation && m_spriteEngine){
1581 if (!datum->animationOwner)
1582 datum->animationOwner = this;
1583 QQuickParticleData* writeTo = (datum->animationOwner == this ? datum : getShadowDatum(datum));
1584 writeTo->animT = writeTo->t;
1585 //writeTo->animInterpolate = m_spritesInterpolate;
1586 if (m_spriteEngine){
1587 m_spriteEngine->start(spriteIdx);
1588 writeTo->frameCount = m_spriteEngine->spriteFrames(spriteIdx);
1589 writeTo->frameDuration = m_spriteEngine->spriteDuration(spriteIdx);
1590 writeTo->animIdx = 0;//Always starts at 0
1591 writeTo->frameAt = -1;
1592 writeTo->animX = m_spriteEngine->spriteX(spriteIdx);
1593 writeTo->animY = m_spriteEngine->spriteY(spriteIdx);
1594 writeTo->animWidth = m_spriteEngine->spriteWidth(spriteIdx);
1595 writeTo->animHeight = m_spriteEngine->spriteHeight(spriteIdx);
1598 QQuickParticleData* writeTo = getShadowDatum(datum);
1599 writeTo->animT = datum->t;
1600 writeTo->frameCount = 1;
1601 writeTo->frameDuration = 60000000.0;
1602 writeTo->frameAt = -1;
1603 writeTo->animIdx = 0;
1605 writeTo->animX = writeTo->animY = 0;
1606 writeTo->animWidth = getState<ImageMaterialData>(m_material)->animSheetSize.width();
1607 writeTo->animHeight = getState<ImageMaterialData>(m_material)->animSheetSize.height();
1612 if (m_explicitDeformation){
1613 if (!datum->deformationOwner)
1614 datum->deformationOwner = this;
1616 const QPointF &ret = m_xVector->sample(QPointF(datum->x, datum->y));
1617 if (datum->deformationOwner == this) {
1618 datum->xx = ret.x();
1619 datum->xy = ret.y();
1621 getShadowDatum(datum)->xx = ret.x();
1622 getShadowDatum(datum)->xy = ret.y();
1626 const QPointF &ret = m_yVector->sample(QPointF(datum->x, datum->y));
1627 if (datum->deformationOwner == this) {
1628 datum->yx = ret.x();
1629 datum->yy = ret.y();
1631 getShadowDatum(datum)->yx = ret.x();
1632 getShadowDatum(datum)->yy = ret.y();
1637 if (m_explicitRotation){
1638 if (!datum->rotationOwner)
1639 datum->rotationOwner = this;
1641 (m_rotation + (m_rotationVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationVariation) ) * CONV;
1643 (m_rotationSpeed + (m_rotationSpeedVariation - 2*((qreal)rand()/RAND_MAX)*m_rotationSpeedVariation) ) * CONV;
1644 autoRotate = m_autoRotation?1.0:0.0;
1645 if (datum->rotationOwner == this) {
1646 datum->rotation = rotation;
1647 datum->rotationSpeed = rotationSpeed;
1648 datum->autoRotate = autoRotate;
1650 getShadowDatum(datum)->rotation = rotation;
1651 getShadowDatum(datum)->rotationSpeed = rotationSpeed;
1652 getShadowDatum(datum)->autoRotate = autoRotate;
1656 //Color initialization
1658 if (m_explicitColor) {
1659 if (!datum->colorOwner)
1660 datum->colorOwner = this;
1661 color.r = m_color.red() * (1 - redVariation) + rand() % 256 * redVariation;
1662 color.g = m_color.green() * (1 - greenVariation) + rand() % 256 * greenVariation;
1663 color.b = m_color.blue() * (1 - blueVariation) + rand() % 256 * blueVariation;
1664 color.a = m_alpha * m_color.alpha() * (1 - m_alphaVariation) + rand() % 256 * m_alphaVariation;
1665 if (datum->colorOwner == this)
1666 datum->color = color;
1668 getShadowDatum(datum)->color = color;
1675 void QQuickImageParticle::commit(int gIdx, int pIdx)
1679 QSGGeometryNode *node = m_nodes[gIdx];
1682 QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
1683 node->setFlag(QSGNode::OwnsGeometry, false);
1684 SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
1685 DeformableVertex *deformableVertices = (DeformableVertex *) node->geometry()->vertexData();
1686 ColoredVertex *coloredVertices = (ColoredVertex *) node->geometry()->vertexData();
1687 SimpleVertex *simpleVertices = (SimpleVertex *) node->geometry()->vertexData();
1688 switch (perfLevel){//No automatic fall through intended on this one
1690 spriteVertices += pIdx*4;
1691 for (int i=0; i<4; i++){
1692 spriteVertices[i].x = datum->x - m_systemOffset.x();
1693 spriteVertices[i].y = datum->y - m_systemOffset.y();
1694 spriteVertices[i].t = datum->t;
1695 spriteVertices[i].lifeSpan = datum->lifeSpan;
1696 spriteVertices[i].size = datum->size;
1697 spriteVertices[i].endSize = datum->endSize;
1698 spriteVertices[i].vx = datum->vx;
1699 spriteVertices[i].vy = datum->vy;
1700 spriteVertices[i].ax = datum->ax;
1701 spriteVertices[i].ay = datum->ay;
1702 if (m_explicitDeformation && datum->deformationOwner != this) {
1703 QQuickParticleData* shadow = getShadowDatum(datum);
1704 spriteVertices[i].xx = shadow->xx;
1705 spriteVertices[i].xy = shadow->xy;
1706 spriteVertices[i].yx = shadow->yx;
1707 spriteVertices[i].yy = shadow->yy;
1709 spriteVertices[i].xx = datum->xx;
1710 spriteVertices[i].xy = datum->xy;
1711 spriteVertices[i].yx = datum->yx;
1712 spriteVertices[i].yy = datum->yy;
1714 if (m_explicitRotation && datum->rotationOwner != this) {
1715 QQuickParticleData* shadow = getShadowDatum(datum);
1716 spriteVertices[i].rotation = shadow->rotation;
1717 spriteVertices[i].rotationSpeed = shadow->rotationSpeed;
1718 spriteVertices[i].autoRotate = shadow->autoRotate;
1720 spriteVertices[i].rotation = datum->rotation;
1721 spriteVertices[i].rotationSpeed = datum->rotationSpeed;
1722 spriteVertices[i].autoRotate = datum->autoRotate;
1724 //Sprite-related vertices updated per-frame in spritesUpdate(), not on demand
1725 if (m_explicitColor && datum->colorOwner != this) {
1726 QQuickParticleData* shadow = getShadowDatum(datum);
1727 spriteVertices[i].color.r = shadow->color.r;
1728 spriteVertices[i].color.g = shadow->color.g;
1729 spriteVertices[i].color.b = shadow->color.b;
1730 spriteVertices[i].color.a = shadow->color.a;
1732 spriteVertices[i].color.r = datum->color.r;
1733 spriteVertices[i].color.g = datum->color.g;
1734 spriteVertices[i].color.b = datum->color.b;
1735 spriteVertices[i].color.a = datum->color.a;
1739 case Tabled: //Fall through until it has its own vertex class
1741 deformableVertices += pIdx*4;
1742 for (int i=0; i<4; i++){
1743 deformableVertices[i].x = datum->x - m_systemOffset.x();
1744 deformableVertices[i].y = datum->y - m_systemOffset.y();
1745 deformableVertices[i].t = datum->t;
1746 deformableVertices[i].lifeSpan = datum->lifeSpan;
1747 deformableVertices[i].size = datum->size;
1748 deformableVertices[i].endSize = datum->endSize;
1749 deformableVertices[i].vx = datum->vx;
1750 deformableVertices[i].vy = datum->vy;
1751 deformableVertices[i].ax = datum->ax;
1752 deformableVertices[i].ay = datum->ay;
1753 if (m_explicitDeformation && datum->deformationOwner != this) {
1754 QQuickParticleData* shadow = getShadowDatum(datum);
1755 deformableVertices[i].xx = shadow->xx;
1756 deformableVertices[i].xy = shadow->xy;
1757 deformableVertices[i].yx = shadow->yx;
1758 deformableVertices[i].yy = shadow->yy;
1760 deformableVertices[i].xx = datum->xx;
1761 deformableVertices[i].xy = datum->xy;
1762 deformableVertices[i].yx = datum->yx;
1763 deformableVertices[i].yy = datum->yy;
1765 if (m_explicitRotation && datum->rotationOwner != this) {
1766 QQuickParticleData* shadow = getShadowDatum(datum);
1767 deformableVertices[i].rotation = shadow->rotation;
1768 deformableVertices[i].rotationSpeed = shadow->rotationSpeed;
1769 deformableVertices[i].autoRotate = shadow->autoRotate;
1771 deformableVertices[i].rotation = datum->rotation;
1772 deformableVertices[i].rotationSpeed = datum->rotationSpeed;
1773 deformableVertices[i].autoRotate = datum->autoRotate;
1775 if (m_explicitColor && datum->colorOwner != this) {
1776 QQuickParticleData* shadow = getShadowDatum(datum);
1777 deformableVertices[i].color.r = shadow->color.r;
1778 deformableVertices[i].color.g = shadow->color.g;
1779 deformableVertices[i].color.b = shadow->color.b;
1780 deformableVertices[i].color.a = shadow->color.a;
1782 deformableVertices[i].color.r = datum->color.r;
1783 deformableVertices[i].color.g = datum->color.g;
1784 deformableVertices[i].color.b = datum->color.b;
1785 deformableVertices[i].color.a = datum->color.a;
1790 coloredVertices += pIdx*1;
1791 for (int i=0; i<1; i++){
1792 coloredVertices[i].x = datum->x - m_systemOffset.x();
1793 coloredVertices[i].y = datum->y - m_systemOffset.y();
1794 coloredVertices[i].t = datum->t;
1795 coloredVertices[i].lifeSpan = datum->lifeSpan;
1796 coloredVertices[i].size = datum->size;
1797 coloredVertices[i].endSize = datum->endSize;
1798 coloredVertices[i].vx = datum->vx;
1799 coloredVertices[i].vy = datum->vy;
1800 coloredVertices[i].ax = datum->ax;
1801 coloredVertices[i].ay = datum->ay;
1802 if (m_explicitColor && datum->colorOwner != this) {
1803 QQuickParticleData* shadow = getShadowDatum(datum);
1804 coloredVertices[i].color.r = shadow->color.r;
1805 coloredVertices[i].color.g = shadow->color.g;
1806 coloredVertices[i].color.b = shadow->color.b;
1807 coloredVertices[i].color.a = shadow->color.a;
1809 coloredVertices[i].color.r = datum->color.r;
1810 coloredVertices[i].color.g = datum->color.g;
1811 coloredVertices[i].color.b = datum->color.b;
1812 coloredVertices[i].color.a = datum->color.a;
1817 simpleVertices += pIdx*1;
1818 for (int i=0; i<1; i++){
1819 simpleVertices[i].x = datum->x - m_systemOffset.x();
1820 simpleVertices[i].y = datum->y - m_systemOffset.y();
1821 simpleVertices[i].t = datum->t;
1822 simpleVertices[i].lifeSpan = datum->lifeSpan;
1823 simpleVertices[i].size = datum->size;
1824 simpleVertices[i].endSize = datum->endSize;
1825 simpleVertices[i].vx = datum->vx;
1826 simpleVertices[i].vy = datum->vy;
1827 simpleVertices[i].ax = datum->ax;
1828 simpleVertices[i].ay = datum->ay;
1835 node->setFlag(QSGNode::OwnsGeometry, true);